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, oreasyapiin 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):
docs/— the long-form knowledge-base pages the plugin bundles for its built-in agent'sget_plugin_doctool. Kept in sync by thesyncKnowledgeBaseGradle task.ai/detection/+ai/key-guides/— the per-detection / per-key guide catalog the in-plugin agent loads viaPromptCatalog(the same filesget_detection_promptandget_rule_detailread at runtime). Kept in sync by thesyncAgentCatalogGradle task.rule-keys.json+rule-keys.md— the auto-generated rule-key scheme catalog.RuleKeySchemeExporterreflects every*RuleKeysobject (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 itslist_rule_keystool. Regenerated by thesyncRuleKeySchemesGradle task, so any key added to a*RuleKeysobject is picked up automatically (a brand-new channel/framework must be registered inRuleKeyCatalog.SOURCES).rule-contexts.json+rule-contexts.md— the auto-generated script-context mirror of the in-pluginget_rule_contexttool.RuleContextExporterreuses the exact same key assembly (so it covers rule keys and the implicit keys read viaconfigReader.getFirst— one interface, the same one the built-in agent sees) and, per key, reflects the runtime bindings + script-object method signatures. Regenerated by thesyncRuleContextsGradle 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/rgcapabilities, 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.testormethod.additional.header, or a key with noRuleKeyconstant likemarkdown.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.nameread directly from the app'sapplication.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 usegroovy:(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, whiledefineClass()is the original declaring class. - When a rule key accepts several context kinds (e.g.
custom.method.is.apievaluatesitas a class OR a method), discriminate withit.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 asit.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), prependgroovy:; the expression is evaluated with the PSIitcontext (and helpers) and its result becomes the value:
For multi-condition logic, use a groovy value-block (multi-line):method.additional.header=groovy: '{"name":"X-Echo","value":"' + it.name() + '","required":false}'method.additional.header=groovy:``` def cls = it.containingClass()?.qualifiedName() if (cls?.startsWith("com.example.merchant.")) { return '{"name":"X-Merchant","value":"gateway","required":true}' } return nullSingle-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 thevalue()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@returntext;#mock→ the@mockvalue). 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 asit.regexGroupsinside agroovy: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
extensionsource") 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]=deprecatedis 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"}
- 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=valueform, the~regex prefix, or the bareclass:prefix — they are invalid. Usekey[filter]=value,#regex:, and$class:respectively. - Do not use the non-existent keys
api.header,api.header.additional, orpath.prefix— usemethod.additional.header/class.prefix.path/endpoint.prefix.path. - Do not generate blanket
field.ignorerules 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/*.configfiles. Those are the plugin's own; user rules go in.easyapi/or~/.easyapi/.