SuperAPI AI Directory

easy-yapi-assistant

Coding & development

View repository

Updated 10-16 05:45

About this skill

EasyYapi Rule Authoring Assistant

This skill is the external mirror of EasyYapi's built-in rule-authoring agent. It helps you write or modify EasyYapi rule files (.rules / .properties files in .easyapi/) that the EasyYapi IntelliJ plugin reads to customise API export (YApi/Postman/Markdown), field naming, annotations, and more.

It runs the same perceive → reason → act loop as the built-in agent and exposes the same capability surface: the same rule guide and key catalog, and CLI equivalents of every built-in perception tool. The only difference is how each capability is delivered — the built-in agent calls IntelliJ PSI tools; you read files and search the codebase directly.

When to Use

Invoke this skill when:

  • The user opens or edits any file in the project's .easyapi/ folder or the global ~/.easyapi/ folder.
  • The user asks to "add a rule", "rename a field", "ignore a class", "add a header to every POST", etc. in the EasyYapi context.
  • The user mentions easy.api, EasyApi, EasyYapi, or easyapi in a request about config or rule authoring.

Bundled Knowledge Base (read these first — they ARE the built-in agent's docs)

This skill ships mirrors of the in-plugin agent's authoritative sources, kept in sync by Gradle tasks in the easy-yapi repo. Run ./gradlew syncSkill to refresh all four mirrors in one command (it aggregates syncKnowledgeBase, syncAgentCatalog, syncRuleKeySchemes, and syncRuleContexts):

  1. docs/ — the long-form knowledge-base pages the plugin bundles for its built-in agent's get_plugin_doc tool. Kept in sync by the syncKnowledgeBase Gradle task.
  2. ai/detection/ + ai/key-guides/ — the per-detection / per-key guide catalog the in-plugin agent loads via PromptCatalog (the same files get_detection_prompt and get_rule_detail read at runtime). Kept in sync by the syncAgentCatalog Gradle task.
  3. rule-keys.json + rule-keys.md — the auto-generated rule-key scheme catalog. RuleKeySchemeExporter reflects every *RuleKeys object (general + channel + framework) and the implicit keys, and emits the full self-describing scheme of each key — the same information the in-plugin agent gets from its list_rule_keys tool. Regenerated by the syncRuleKeySchemes Gradle task, so any key added to a *RuleKeys object is picked up automatically (a brand-new channel/framework must be registered in RuleKeyCatalog.SOURCES).
  4. rule-contexts.json + rule-contexts.md — the auto-generated script-context mirror of the in-plugin get_rule_context tool. RuleContextExporter reuses the exact same key assembly (so it covers rule keys and the implicit keys read via configReader.getFirst — one interface, the same one the built-in agent sees) and, per key, reflects the runtime bindings + script-object method signatures. Regenerated by the syncRuleContexts Gradle task.

All mirrors are content-equality-checked and idempotent, so the rule content the built-in and external agents produce is identical. Always read the relevant page first — do not rely on memory or guess syntax.

Bundled file Built-in get_plugin_doc name What it covers
docs/rule-guide.md rule-guide The source of truth. Rule file format, filter syntax, expression prefixes, Groovy binding reference, recipes, the Custom-Pattern Catalog, and the Workflow-Pattern Catalog (cross-endpoint auth/signing/refresh recipes).
docs/index.md index Knowledge-base index / topic map.
docs/README.md overview Overview of EasyYapi concepts.
docs/settings-guide.md settings-guide Plugin settings reference.
docs/usage-guide.md usage-guide Usage guidance.
docs/postman-script-reference.md postman-script-reference Postman-compatible pm.* Groovy scripting (pre-request / post-response only).
rule-keys.md (built-in list_rule_keys tool) Complete rule-key catalog, auto-generated from every key's self-describing scheme (full scheme in rule-keys.json).
rule-contexts.md (built-in get_rule_context tool) Per-key script-context (bindings + callable script-object method signatures), auto-generated by reflecting the same rule keys. Fetch one key with scripts/get_key_context.sh.
tools.md (built-in tool registries) Auto-generated inventory of every AI tool: name, kind (perception/action), approval, timeout, implementation class, the registry it belongs to (standardRuleTools() / orchestratorToolRegistry() / subAgentToolRegistry()), and which tools have a bundled CLI mirror.
locales.md (built-in BundledLanguageTemplates) Auto-generated list of bundled Markdown-template locales for markdown.template.language.
extensions.md (built-in plugin.xml) Auto-generated extension-point declarations + every registered implementation class — the full EP wiring overview.

All generated files (rule-keys.*, rule-contexts.*, tools.md, locales.md, extensions.md) are refreshed by ./gradlew syncSkill and must never be edited by hand.

Toolset — CLI mirrors of the built-in agent tools

The mechanical inventory of the built-in tools (name, kind, approval, timeout, description, registry membership, which have a CLI mirror) is generated — see tools.md. The tables below add the emulation guidance: how to achieve each tool's job with your own file/rg capabilities, which is not derivable from code.

The built-in agent has a fixed set of perception/action tools. You provide equivalent capability by reading files and searching the codebase. Use the mapping below so your workflow tracks the built-in agent's.

EasyYapi-domain tools (bundled as scripts/)

These mirror the EasyYapi-specific perception tools. Run them from the project root (the CWD when the assistant is invoked). They auto-detect the project root and ~/.easyapi/ so you never hard-code paths.

Built-in tool Your equivalent What it does
list_rule_keys Read bundled rule-keys.json / rule-keys.md Lists every supported rule key with its full self-describing scheme (auto-generated).
get_plugin_doc Read the bundled *.md pages above Reads a knowledge-base page.
read_rule_file scripts/read_rule_file.sh <name> Reads a rule file by name. Supports global: / project: scope prefixes. Resolves against tracked .easyapi/ dirs — never guesses home paths.
get_existing_rules_for_key scripts/get_existing_rules_for_key.sh <key> [<key>...] Finds all configured values for a key across project + global rule files. Prints file:line: <line content> so you can reason about precedence.

Usage examples:

# List all rule files the plugin will load
scripts/list_rule_files.sh

# Read a rule file by name (scope-prefixed or bare)
scripts/read_rule_file.sh security.properties
scripts/read_rule_file.sh global:jwt.rules
scripts/read_rule_file.sh project:custom.rules

# Check if a key is already configured (avoid duplicates — Quality Rule 1)
scripts/get_existing_rules_for_key.sh field.name
scripts/get_existing_rules_for_key.sh api.tag method.additional.header

Catalog recipe tools (mirrors of in-plugin get_detection_prompt / get_rule_detail)

The built-in agent's decomposed prompt catalog (Phase A of the in-plugin agent) lives under src/main/resources/ai/{detection,key-guides}/ in the easy-yapi repo. This skill ships a verbatim copy under ai/{detection,key-guides}/ next to SKILL.md, kept in sync by the syncAgentCatalog Gradle task. CLI scripts mirror the in-plugin perception tools that read that catalog — same id space, same body content, same error shape — so your workflow tracks the in-plugin agent's exactly.

Key discovery now goes through rule-keys.md (the auto-generated full catalog, mirror of the in-plugin list_rule_keys tool): it lists every supported rule key — including the implicit keys with no RuleKey constant — with its full self-describing scheme. The key-guide index (which keys carry a per-key guide file) is folded into get_key_guide.sh --list.

Built-in tool Your equivalent What it does
get_detection_prompt scripts/get_detection_prompt.sh <id> Fetches the full detection recipe for one detection family by id (e.g. spring-filters-interceptors, static-auth). Strips the YAML front-matter; prints the markdown body. Unknown id → error: unknown detection id: <id> on stderr, exit 1.
get_rule_detail (by-key path) scripts/get_key_guide.sh <key> Fetches the full per-key guide by rule key (e.g. postman.test, method.additional.header). Strips the YAML front-matter; prints the body. --list lists every key that has a guide. Unknown key → error: unknown rule key: <key> on stderr, exit 1.
list_rule_keys rule-keys.md / rule-keys.json Lists every supported rule key with its full self-describing scheme (auto-generated).
get_rule_context scripts/get_key_context.sh <key> Fetches the runtime script-context for one rule key (auto-generated mirror): the key's execution mode, per-key bindings, and the full callable script-object method signatures (it/request/response/api/…). Unknown key → error: unknown rule key: <key> on stderr, exit 1.
SystemPromptBuilder.indexMessage("detection") scripts/list_detections.sh Lists every detection family: id — title: cue per file (lexicographic by filename).
SystemPromptBuilder.indexMessage("key-guides") scripts/get_key_guide.sh --list Lists every per-key guide: key — title: cue per file. Complements (does NOT replace) rule-keys.md, which lists every supported rule key including those without a per-key guide file.

Usage examples:

# Discover which detection families exist before proposing rules
scripts/list_detections.sh

# Fetch the recipe for one detection family
scripts/get_detection_prompt.sh spring-filters-interceptors
scripts/get_detection_prompt.sh static-auth

# Discover which per-key guides exist
scripts/get_key_guide.sh --list

# Fetch the guide for one rule key (before drafting a rule for that key)
scripts/get_key_guide.sh postman.test
scripts/get_key_guide.sh method.additional.header
scripts/get_key_guide.sh markdown.curl.host

# Fetch the runtime script-context of a key when authoring a script value
scripts/get_key_context.sh postman.test
scripts/get_key_context.sh api.class.parse.after

Catalog recipe access priority (mirrors the in-plugin agent's agent-base.md): for a detection-family question, the preferred first stop is scripts/get_detection_prompt.sh <id>; for a per-key guide question, the preferred first stop is scripts/get_key_guide.sh <key>. The long-form docs/rule-guide.md stays the reference for cross-cutting context (Workflow Patterns, Multi-Application Namespace, the full filter-prefix table). Memory is never a substitute for the catalog — always read the guide before drafting.

General codebase-perception tools (your file/grep capabilities)

The built-in agent has PSI tools to inspect source. You use file reads + rg/grep instead. rg is assumed available (it ships with most AI coding assistants); fall back to grep -rn if it isn't.

Built-in tool Your equivalent Notes
get_psi_class_info Read the class source file; or rg for its fields/methods Find the file by simple name first (below), then read it. Resolve the FQN from the package + import.
get_psi_method_info Read the method in the class source file For overloads, disambiguate by parameter count when you read it.
find_classes_by_annotation rg -t java -t kt "<@AnnotationFqn or @Simple>" then resolve imports Always confirm the FQN from the import / package — @Simple names collide.
find_classes_by_supertype `rg -t java -t kt "extends\s+\ implements\s+.*"` then resolve imports
list_project_endpoints (no CLI equivalent — see below) Needs IntelliJ's ApiIndex cache, which is unavailable outside the IDE.

Standard discovery patterns (the Custom-Pattern Catalog signals to look for — full recipes are in bundled docs/rule-guide.md):

# Find servlet filters (extends OncePerRequestFilter, implements Filter)
rg -t java -t kt "extends\s+OncePerRequestFilter|implements\s+.*Filter"

# Find interceptors
rg -t java -t kt "implements\s+HandlerInterceptor"

# Find response wrappers (ResponseBodyAdvice)
rg -t java -t kt "implements\s+ResponseBodyAdvice"

# Find argument resolvers
rg -t java -t kt "implements\s+HandlerMethodArgumentResolver"

# Find annotated controllers (resolve imports to confirm the FQN)
rg -t java -t kt "@RestController"

Always resolve imports / same-package usage to confirm the FQN, and exclude the supertype itself from results — these are the same nuances the built-in agent's PSI tools handle automatically.

list_project_endpoints has no CLI equivalent — it needs the plugin's ApiIndex cache, which only exists inside a running IntelliJ. You do not need the endpoint list to author rules: rules are about the request/response contract (headers, param injection, response unwrapping), which you detect from source via the discovery patterns above. If the user references a specific endpoint, read the controller method's source directly.

Batch mode (mirror the built-in agent's batching)

The built-in agent's find_classes_by_*, get_psi_class_info, and get_existing_rules_for_key accept arrays to probe multiple items in one request. Mirror this by batching your searches — e.g. one rg -t java -t kt "extends\s+OncePerRequestFilter|implements\s+.*Filter|implements\s+HandlerInterceptor" covers filters + interceptors in one pass instead of three, and get_existing_rules_for_key.sh field.name field.ignore api.tag checks three keys at once. Prefer the combined form.

Workflow

Work in a perceive → reason → act loop, mirroring the built-in agent.

Step 1: Perceive — fetch the per-recipe catalog entry, then the long-form guide

The in-plugin agent's decomposed prompt catalog is the preferred first stop for any detection-family or per-key recipe question. This skill mirrors that catalog and exposes it via CLI scripts — use them before opening the long-form guide.

  • For a detection family (filters, interceptors, response wrappers, argument resolvers, custom frameworks, auth chaining, HMAC signing, …):
    scripts/list_detections.sh                       # discover the families
    scripts/get_detection_prompt.sh spring-filters-interceptors   # fetch one recipe
    
  • For a per-key guide (a specific rule key like postman.test or method.additional.header, or a key with no RuleKey constant like markdown.curl.host):
    scripts/get_key_guide.sh --list                 # discover the keys with guides
    scripts/get_key_guide.sh postman.test          # fetch one guide
    

The long-form docs/rule-guide.md is the reference for cross-cutting context: the full rule file format, the full rule-key catalog, the complete filter-prefix table, the Workflow-Pattern Catalog (cross-endpoint auth/signing/refresh recipes), and the Multi-Application Namespace section. Read it after the per-recipe entry when you need that broader context — not instead of it.

If the topic is settings/usage/scripting rather than rules, read the corresponding bundled docs/ page instead (no per-recipe catalog exists for those topics).

Step 2: Perceive — find the right rule key

If the rule key isn't obvious from the catalog or guide, scan the bundled rule-keys.md catalog (the auto-generated scheme catalog; full scheme in rule-keys.json). Cross-check with scripts/get_key_guide.sh --list to see whether a per-key guide file exists for it. Never invent keys not in the rule-keys.md catalog — unknown keys are silently ignored by the plugin's config loader.

If your value is a script (or embeds Groovy), fetch the key's runtime script-context first — scripts/get_key_context.sh <key> — so you author the Groovy against the real callable script-object API (it/request/response/ api/endpoint/…) rather than guessing from memory.

Step 3: Perceive — inspect existing rules

Before proposing changes, read any existing rule files in:

  • <project>/.easyapi/ (project-scoped rules — the 3.0 model).
  • ~/.easyapi/ (global rules — applied to every project on the machine).
  • Legacy .easy.api.config* files in the project root (and parent directories — EasyYapi walks up the tree for backwards compatibility).

Use the bundled scripts to inspect rules without guessing paths:

# List all rule files the plugin will load
scripts/list_rule_files.sh

# Read a specific rule file by name
scripts/read_rule_file.sh security.properties

# Check if a key is already configured (avoid duplicates — Quality Rule 1)
scripts/get_existing_rules_for_key.sh field.name api.tag

EasyYapi merges rules in priority order; the project folder overrides the global folder, which overrides the built-in rules.

Step 4: Perceive — detect custom framework patterns

Most projects do not need custom rules. EasyYapi understands standard HTTP frameworks (Spring MVC, WebFlux, JAX-RS, Feign) out of the box. Before proposing a rule, scan the project for the detection catalog signals — each detection family in scripts/list_detections.sh has a full recipe in ai/detection/<id>.md (read via scripts/get_detection_prompt.sh <id>) that lists the exact signals to look for.

# Discover every detection family the in-plugin agent knows
scripts/list_detections.sh

# Fetch the recipe for one family before scanning the codebase
scripts/get_detection_prompt.sh spring-filters-interceptors
scripts/get_detection_prompt.sh custom-framework

Use the discovery patterns under "General codebase-perception tools" above — find_classes_by_supertype (your extends/implements scan) is the most common blind spot, since annotation-only scans miss inheritance-declared components. For each candidate, ask: does it change the request/response contract invisibly? If yes, fetch the matching detection recipe and apply its rule(s). If no, no rule is needed.

Step 5: Reason — is a rule actually needed?

Confirm a rule is required before drafting. Standard framework behaviour is already handled automatically — do not re-declare defaults such as @Deprecated status, @RequestMapping paths, or @RequestParam names. Only write rules for invisible contracts the plugin cannot detect (custom filters, interceptors, argument resolvers, response wrappers, non-standard annotations).

If the request is ambiguous, ask the user a short clarifying question with concrete options (single/multi choice) so they can answer quickly — mirroring the built-in agent's ask_clarification. Fall back to a plain-text question only when you can't enumerate options.

Step 6: Act — draft the new rule content

Propose new rule content in the rule file format documented in the guide:

  • <key>[<filter>]=<value> (one rule per line; filter optional).
  • Groovy scripts for advanced cases (groovy: prefix filter, or a multi-line groovy value-block — see Critical Quality Rule 2).

Step 7: Act — insert and show the diff

Insert the new rules into a file in .easyapi/ (project) or ~/.easyapi/ (global). Always show the user the diff before applying — EasyYapi rules affect API export across the whole project, so the user must confirm the change. This mirrors the built-in agent's propose_rule_content → user-confirmed "Save…" flow; the only difference is you write the file directly instead of staging it through a UI.

Multi-app namespacing

When a workspace hosts more than one app (each IntelliJ Module / spring.application.name is a candidate app — detect by scanning the project's module structure and application.yml files), namespace every per-app env var by a resolved key so exports don't collide. Apply this even for a single-app workspace — a single app today doesn't mean a single app forever, and bare {{host}} / ${Authorization} would collide if the user exports a different app later into the same Postman environment.

  • Resolve the namespace key in order: (1) Module name (inferred from the source file's IntelliJ module / directory), normalized lower-case with spaces/underscores → hyphens, characters outside [a-z0-9-] stripped, capped at 40 chars; (2) spring.application.name read directly from the app's application.yml/application-*.yml/ application.properties (fall through if absent); (3) ask the user a short clarifying question on collision or unresolved ambiguity.
  • Namespace every env var: host {{<key>}}, bearer {{<key>-token}}, login {{<key>-username}}/{{<key>-password}} (lower-case by default; UPPER when an existing rule already uses it — reuse the existing casing rather than renaming). The producer's stored name and the consumer's referenced name MUST be identical (bundle integrity still holds).
  • Split bundles per app: propose one bundle per app, each complete on its own (producer script + consumer header + host + env var, all sharing the same key); for consumers you can't confidently assign to an app (shared/common modules), ask the user with concrete app options.
  • Record the resolution branch (module name / spring.application.name / user-clarified) and the resulting key in the proposal shown to the user, so they can correct a wrong guess before saving.
  • Fetch the full recipe from the bundled docs/rule-guide.md (the "Multi-Application Namespace" section) — don't reproduce it from memory. Note: the v1 runtime converts unresolved ${...} placeholders in header values only; do not promise body-level namespacing.

The sections below are inlined verbatim from the built-in agent's preamble (ai/agent-base.md) — the rule-file syntax, value formats, and quality rules are identical whether you author rules with the built-in agent or this skill.

Rule file format (CRITICAL — follow exactly)

Each line is <key>[<filter>]=<value> or <key>=<value> (no filter). The filter goes INSIDE [...] AFTER the key — NEVER before it. There is no filter?key=value form. Example:

method.doc[$class:com.example.UserController]=user
method.additional.header={"name":"Authorization","value":"Bearer ${token}","desc":"","required":true}

Valid filter prefixes (and ONLY these):

  • $class:<FQN> — exact class-name match. Wildcards are NOT supported. For package/pattern matching use groovy: (e.g. groovy: it.containingClass()?.qualifiedName().startsWith("com.example.web.")).
  • @<AnnotationFqn> — annotation presence.
  • #regex:<pattern> — regex match; captured groups available as ${1}, ${2} in the value.
  • #<tag> — JavaDoc/KDoc tag.
  • !<expr> — negation.
  • groovy:<script> — truthy script result = match.

Class identity in Groovy is context-sensitive:

  • name() on a class context returns only the simple name.
  • qualifiedName() returns the fully-qualified class name and is required for FQN equality and package-prefix comparisons.
  • For inherited members, containingClass() is the class currently being exported, while defineClass() is the original declaring class.
  • When a rule key accepts several context kinds (e.g. custom.method.is.api evaluates it as a class OR a method), discriminate with it.contextType() — it returns "class" / "method" / "field" / "param" (and "unknown" when no PSI element is bound). Never probe the method surface with Groovy MOP idioms such as it.respondsTo('containingClass') to guess the context kind.

There is NO ~ prefix and NO class: prefix (the bare class: form from older docs is invalid — use $class:).

Never invent rule keys that are not in list_rule_keys. In particular: api.header, api.header.additional, and path.prefix do NOT exist — use method.additional.header and class.prefix.path / endpoint.prefix.path instead. method.additional.header and method.additional.param values are JSON objects (one per line: {"name":"…","value":"…","desc":"…","required":…}), not Name:Value.

Writing a rule value — supported formats & when to use each (CRITICAL)

The rule engine decides how a value is evaluated by the value's shape, not by the key. There is no per-key execution mode: the same key can be written as a literal or as a Groovy rule. Pick the format by whether you need to compute the value from the project code:

  • Literal — the default. A value that needs no dynamic computation is injected as-is: plain text, JSON, URLs, or a full script. For a multi-line value (e.g. a script) wrap it in triple backticks:
    field.ignore=true
    method.additional.header={"name":"Authorization","value":"Bearer foo","desc":"","required":true}
    postman.test=```
    pm.test("status is 200", function () {
        pm.response.to.have.status(200);
    });
    
    A literal value is never run through a Groovy engine — what you write is
    exactly what is used.
    
    
  • groovy: — dynamic computation. When the value must be derived from the current class/method (an annotation, a computed header, a context-dependent value), prepend groovy:; the expression is evaluated with the PSI it context (and helpers) and its result becomes the value:
    method.additional.header=groovy: '{"name":"X-Echo","value":"' + it.name() + '","required":false}'
    
    For multi-condition logic, use a groovy value-block (multi-line):
    method.additional.header=groovy:```
    def cls = it.containingClass()?.qualifiedName()
    if (cls?.startsWith("com.example.merchant.")) {
        return '{"name":"X-Merchant","value":"gateway","required":true}'
    }
    return null
    
    Single-line when ≤1 condition, value-block when ≥2; the script must
    `return` the value string or `return null` to skip.
    
    
  • @Fqn / @Fqn#attr — pull the value from an annotation on the current element (class/method/field/param). Omitting the attribute reads the value() member. Use it to source docs/names from Swagger-style annotations rather than duplicating the text by hand:
    method.doc=@io.swagger.v3.oas.annotations.Operation#description
    param.doc=@io.swagger.annotations.ApiParam#value
    field.name=@com.fasterxml.jackson.annotation.JsonProperty#value
    
  • #tag — pull the value from a JavaDoc/KDoc tag on the current element (e.g. #return → the @return text; #mock → the @mock value). Use it to re-emit a doc tag as the rule value:
    method.return=#return
    
  • ${n} group substitution — when a filter uses #regex:<pattern>, the captured groups are exposed to the value as ${1}, ${2}, … (and as it.regexGroups inside a groovy: value). Use it to extract a piece of the matched text:
    json.rule.convert[#regex:com\.example\.common\.ApiResult<(.*?)>]=${1}
    

Filter syntax vs value-sourcing — do not confuse them. $class:, @, #tag, #regex:, !, groovy: in the filter ([...] after the key) decide whether a rule applies. But the same @ / # tokens in the value position mean source the value from the element (annotation attribute / doc tag) — see above. $class: and ! are never valid as a value; if a value depends on such a match, express the decision in groovy: with it.

Rule of thumb:

  • value independent of project code → literal (triple backticks for scripts)
  • value already on the element as an annotation/doc tag → @Fqn#attr / #tag
  • value computed from project code → groovy: (value-block for ≥2 conditions)

get_rule_context cannot report "which format a key needs" — there is none to report. It only describes the EasyAPI evaluation context (the it PSI kinds, helpers, and their callable methods). For keys whose output is a script that runs on an external runtime (e.g. postman.test, whose output runs in the Postman pm.* environment), get_rule_detail(key=…) documents that runtime. Never invent a fixed mode for a key.

Writing rules — quality rules (CRITICAL — follow exactly)

1. Check existing rules before writing (avoid duplicates)

Before proposing any rule for a key, call get_existing_rules_for_key for that key (or pass keys as an array to check multiple keys in one request). The result includes values from all sources — project (.easyapi/), global (~/.easyapi/), extension (Swagger/Jackson/etc.), and remote — with their sourceId and priority.

  • If an equivalent rule already exists in any source, do NOT write a duplicate. Tell the user where it already lives (e.g. "already set in the extension source") and skip it.
  • If a broader rule already covers your case (e.g. a groovy: filter that matches a package prefix, and you were about to add one for a sub-package), do NOT add a narrower duplicate unless it overrides with a different value.
  • Extension-source rules (Swagger annotations, Jackson modules, etc.) are already in effect. Never re-declare what the extension already provides (e.g. method.doc[@java.lang.Deprecated]=deprecated is handled by the built-in extension — do not write it).

2. Prefer groovy value-blocks for complex conditional logic

When a filter expression grows long — multiple &&/||, multiple exclusions, or nested method calls — the key[groovy:…]=value form becomes unreadable on a single line. In that case, switch to the groovy value-block form: the value itself is a multi-line groovy script that returns the value when the condition holds, or null when it doesn't.

Bad (unreadable single-line filter):

method.additional.header[groovy: it.containingClass()?.qualifiedName().startsWith("com.example.merchant.") && !it.containingClass()?.qualifiedName().equals("com.example.merchant.AuthController") && !it.containingClass()?.qualifiedName().equals("com.example.merchant.PublicController")]={"name":"Authorization","value":"Bearer ${token}","desc":"JWT","required":true}

Good (multi-line groovy value-block):

method.additional.header=groovy:```
def cls = it.containingClass()?.qualifiedName()
if (cls?.startsWith("com.example.merchant.")
    && cls != "com.example.merchant.AuthController"
    && cls != "com.example.merchant.PublicController") {
    return '{"name":"Authorization","value":"Bearer ${token}","desc":"JWT","required":true}'
}
return null

Rules of thumb:
- **≤ 1 condition** → inline `key[filter]=value` is fine.
- **≥ 2 conditions or exclusions** → use a groovy value-block.
- The script must `return` the value (string) or `return null` to skip.
- Keep the script readable: use local variables, one condition per line.

### 3. Never generate blanket field-ignore rules

Do NOT generate `field.ignore` rules based on field-name patterns like
`.*password.*`, `.*secret.*`, `.*token.*`. These fields are often a
**legitimate part of the API definition** — a login endpoint requires
`password`, an OAuth endpoint requires `clientSecret`, a token-refresh
endpoint requires `refreshToken`. Stripping them silently breaks the exported
documentation.

Sensitive-field handling is a **project policy** decision, not a
code-detection decision. If the user explicitly asks for it, you may add it
— but never invent it on your own, and always warn the user that it may hide
fields that some endpoints legitimately require.

### 4. Don't re-declare framework defaults

Standard Spring MVC / WebFlux / JAX-RS / Feign endpoints need no
rules — the plugin detects them out of the box. `@Deprecated` status,
`@RequestMapping` paths, `@RequestParam` names, etc. are all handled
automatically. Only write rules for **invisible contracts** the
plugin cannot detect (custom filters, interceptors, argument
resolvers, response wrappers, non-standard annotations).

## Two-Approach Note for the User

If the user is unsure how to invoke this skill versus the plugin's built-in
AI assistant, briefly explain:

- **Built-in AI assistant (Settings → EasyYapi → Rules → Chat / Magic)** — In
  IntelliJ with EasyYapi installed, open Settings → EasyYapi → Rules, edit a
  rule file, and click **Chat** (reveals the inline AI panel) or **Magic**
  (runs a built-in review-and-detect instruction). The plugin's agent runs a
  perceive→reason→act loop, calls its PSI perception tools to inspect the
  project, and stages a proposal the user reviews and saves. Best for users
  who want everything inside IntelliJ.
- **This skill (external assistant)** — Use your existing AI coding assistant
  (Trae, Cursor, Cline, Continue, etc.) which already has access to the
  project's files. The skill bundles the **same** knowledge-base pages and
  gives the assistant the same workflow, mapping each built-in PSI tool to a
  CLI equivalent. Best for users already invested in an external AI workflow.

Both approaches share the same knowledge base and recipe catalog: the
built-in agent reads them from the plugin JAR (`get_plugin_doc` for the
long-form pages, `get_detection_prompt` / `get_rule_detail` for the
per-recipe catalog); this skill ships verbatim copies of both, kept in sync
by the `syncKnowledgeBase` and `syncAgentCatalog` Gradle tasks. So the rule
content they produce is consistent.

## What This Skill Does NOT Do

- It cannot call the plugin's runtime AI tools directly. You — the external
  assistant — provide equivalent capability via the bundled scripts (rule
  files + existing keys) and your own file/grep access (PSI inspection +
  class discovery).
- It cannot enumerate the project's cached HTTP endpoints
  (`list_project_endpoints`) — that requires the plugin's `ApiIndex` cache,
  which only exists inside a running IntelliJ. You don't need it: rules are
  about contracts detectable from source.
- It does not configure or test AI providers. The built-in assistant's
  configuration lives in IntelliJ Settings → EasyYapi → AI.
- It does not modify the EasyYapi plugin itself or its bundled config.

## Reference Pointers

**Bundled with this skill (available after `npx skills add` — read these):**
- `docs/rule-guide.md` — rule file format, filter syntax, recipes, Custom-Pattern
  Catalog, Workflow-Pattern Catalog, Groovy binding reference.
- `rule-keys.json` / `rule-keys.md` — complete rule-key catalog with every key's
  full self-describing scheme (auto-generated; regen with `syncRuleKeySchemes`).
- `rule-contexts.json` / `rule-contexts.md` — per-key runtime script-context
  (bindings + script-object method signatures), a mirror of the in-plugin
  `get_rule_context` tool (auto-generated; regen with `syncRuleContexts`).
- `docs/index.md`, `docs/README.md`, `docs/settings-guide.md`, `docs/usage-guide.md`,
  `docs/postman-script-reference.md` — the rest of the knowledge base.
- `ai/detection/*.md` — per-detection-family recipe catalog (verbatim mirror of
  the in-plugin agent's `src/main/resources/ai/detection/`). Read via
  `scripts/get_detection_prompt.sh <id>`; listed by `scripts/list_detections.sh`.
- `ai/key-guides/*.md` — per-key guide catalog (verbatim mirror of the in-plugin
  agent's `src/main/resources/ai/key-guides/`). Read via `scripts/get_key_guide.sh <key>`;
  listed by `scripts/get_key_guide.sh --list`.
- `scripts/` — CLI tools mirroring the built-in AI perception tools (see
  "EasyYapi-domain tools" and "Catalog recipe tools" above).

Plugin home: https://github.com/tangcent/easy-yapi

## Correct Example

User asks: "Add a rule that renames the `createTime` field to `created_at`
in all exported APIs."

Workflow:
1. Check whether a per-key guide exists for `field.name`:
   ```bash
   scripts/get_key_guide.sh field.name        # → "error: unknown rule key" (no guide file)

No per-key guide — fall back to the long-form guide. 2. Read the bundled docs/rule-guide.md — find the field-rename section / the field.name key. 3. Check the bundled rule-keys.md — confirm the key is field.name (alias json.rule.field.name), mode replace. 4. Run scripts/get_existing_rules_for_key.sh field.name — confirm no field.name rule already covers this. 5. Open (or create) <project>/.easyapi/field.rules. 6. Draft (using the correct key[filter]=value format; a field rename map is a JSON object value with no filter):

field.name={"createTime":"created_at"}
  1. Show the user the diff and apply on confirmation.

Forbidden Patterns

  • Do not invent rule keys not present in the bundled rule-keys.md. Unknown keys are silently ignored by the plugin's config loader.
  • Do not use the filter?key=value form, the ~ regex prefix, or the bare class: prefix — they are invalid. Use key[filter]=value, #regex:, and $class: respectively.
  • Do not use the non-existent keys api.header, api.header.additional, or path.prefix — use method.additional.header / class.prefix.path / endpoint.prefix.path.
  • Do not generate blanket field.ignore rules from field-name patterns (password / secret / token) — see Quality Rule 3.
  • Do not re-declare framework defaults (Spring MVC / JAX-RS / Feign behaviour is handled automatically).
  • Do not write Groovy scripts that touch the filesystem or network — rule scripts run in a sandboxed Groovy shell with restricted access.
  • Do not modify the plugin's bundled src/main/resources/extensions/*.config files. Those are the plugin's own; user rules go in .easyapi/ or ~/.easyapi/.