| name | easy-api-assistant |
| description | Help author EasyApi rule files (.easyapi/ folder). Invoke when the user is editing an EasyApi rule file or asks to add/modify EasyApi rules (annotations, field rename, custom headers, etc.). |
EasyApi Rule Authoring Assistant
This skill is the external mirror of EasyApi's built-in rule-authoring
agent. It helps you write or modify EasyApi rule files (.rules /
.properties files in .easyapi/) that the EasyApi IntelliJ plugin reads to
customise API export (Postman/Markdown/cURL/Hoppscotch), 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 EasyApi context.
- The user mentions
easy.api, EasyApi, 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 two verbatim mirrors of the in-plugin agent's
authoritative sources, kept in sync by Gradle tasks in the easy-api repo:
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.
ai/detection/ + ai/rules/ — the per-detection / per-key recipe
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.
Both 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 EasyApi concepts. |
docs/settings-guide.md | settings-guide | Plugin settings reference. |
docs/usage-guide.md | usage-guide | Usage guidance. |
docs/easyapi-script-reference.md | easyapi-script-reference | Scripting reference. |
docs/rule-keys.md | (built-in list_rule_keys tool) | Complete rule-key catalog (snapshot of RuleKeys.kt). |
Toolset — CLI mirrors of the built-in agent tools
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.
EasyApi-domain tools (bundled as scripts/)
These mirror the EasyApi-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 docs/rule-keys.md | Lists every supported rule key (≡ RuleKeys.kt snapshot). |
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. |
scripts/list_rule_files.sh is also bundled — it lists every
.properties / .rules file in <project>/.easyapi/ and ~/.easyapi/,
labeled [global] / [project] (ambient equivalent of
RuleFileResolver.listRuleFiles).
Usage examples:
scripts/list_rule_files.sh
scripts/read_rule_file.sh security.properties
scripts/read_rule_file.sh global:jwt.rules
scripts/read_rule_file.sh project:custom.rules
scripts/get_existing_rules_for_key.sh field.name
scripts/get_existing_rules_for_key.sh method.doc 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,rules}/ in the easy-api
repo. This skill ships a verbatim copy under ai/{detection,rules}/ next to
SKILL.md, kept in sync by the syncAgentCatalog Gradle task. Four 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.
| 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_rule_detail.sh <key> | Fetches the full per-key rule recipe by rule key (e.g. postman.test, method.additional.header). Strips the YAML front-matter; prints the body. 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("rules") | scripts/list_rule_details.sh | Lists every per-key rule recipe: key — title: cue per file. Complements (does NOT replace) docs/rule-keys.md, which lists every supported rule key including those without a per-key recipe file. |
Usage examples:
scripts/list_detections.sh
scripts/get_detection_prompt.sh spring-filters-interceptors
scripts/get_detection_prompt.sh static-auth
scripts/list_rule_details.sh
scripts/get_rule_detail.sh postman.test
scripts/get_rule_detail.sh method.additional.header
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 recipe
question, the preferred first stop is scripts/get_rule_detail.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 recipe 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+<Type>|implements\s+.*<Type>" then resolve imports | The most common blind spot — annotation-only scans miss inheritance-declared components (filters extending OncePerRequestFilter). Use BOTH this and the annotation scan. |
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):
rg -t java -t kt "extends\s+OncePerRequestFilter|implements\s+.*Filter"
rg -t java -t kt "implements\s+HandlerInterceptor"
rg -t java -t kt "implements\s+ResponseBodyAdvice"
rg -t java -t kt "implements\s+HandlerMethodArgumentResolver"
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 method.doc field.name field.ignore 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.
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
docs/rule-keys.md catalog (the snapshot of RuleKeys.kt). Cross-check
against scripts/list_rule_details.sh to see whether a per-key recipe file
exists for it. Never invent keys not in the docs/rule-keys.md
catalog — unknown keys are silently ignored by the plugin's config loader.
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 — EasyApi walks up the tree for backwards compatibility).
scripts/list_rule_files.sh
scripts/read_rule_file.sh security.properties
scripts/get_existing_rules_for_key.sh method.doc field.name
EasyApi 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. EasyApi 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.
scripts/list_detections.sh
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 — EasyApi 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.
Critical Rule File Format (follow exactly — inlined from the agent preamble)
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.
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.
There is no ~ prefix and no bare class: prefix — the older
class:com.example.Foo and ~regex forms are invalid; use $class: and
#regex: respectively.
Critical Quality Rules (follow exactly — inlined from the agent preamble)
These mirror the rules the built-in agent enforces via its system prompt.
1. Check existing rules before writing (avoid duplicates)
Before proposing any rule for a key, read the existing rule files and check
whether an equivalent rule already exists in any source — project
(.easyapi/), global (~/.easyapi/), or bundled extension (Swagger /
Jackson / etc.).
- If an equivalent rule already exists, do NOT write a duplicate. Tell the
user where it already lives and skip it.
- If a broader rule already covers your case (e.g. a
groovy: filter matching
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. 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. See the rule guide's Groovy
Binding Reference for the it object API.
Bad (unreadable single-line filter):
method.additional.header[groovy: it.containingClass()?.qualifiedName()?.startsWith("com.example.merchant.") && it.containingClass()?.qualifiedName() != "com.example.merchant.AuthController"]={"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") {
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.
### 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).
## Common Key-Name Mistakes (do not use these)
These keys do **not** exist — use the correct alternative:
| Does NOT exist | Use instead |
|----------------|-------------|
| `api.header` | `method.additional.header` |
| `api.header.additional` | `method.additional.header` |
| `path.prefix` | `class.prefix.path` / `endpoint.prefix.path` |
`method.additional.header` and `method.additional.param` values are **JSON
objects** (one per line):
`{"name":"…","value":"…","desc":"…","required":…}`, not `Name:Value`.
## 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 → EasyApi → Rules → Chat / Magic)** — In
IntelliJ with EasyApi installed, open Settings → EasyApi → 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 → EasyApi → AI.
- It does not modify the EasyApi 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.
- `docs/rule-keys.md` — complete rule-key catalog (snapshot of `RuleKeys.kt`).
- `docs/index.md`, `docs/README.md`, `docs/settings-guide.md`, `docs/usage-guide.md`,
`docs/easyapi-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/rules/*.md` — per-key rule recipe catalog (verbatim mirror of the in-plugin
agent's `src/main/resources/ai/rules/`). Read via `scripts/get_rule_detail.sh <key>`;
listed by `scripts/list_rule_details.sh`.
- `scripts/` — CLI tools mirroring the built-in AI perception tools (see
"EasyApi-domain tools" and "Catalog recipe tools" above).
Plugin home: https://github.com/tangcent/easy-api
## 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 recipe exists for `field.name`:
```bash
scripts/get_rule_detail.sh field.name # → "error: unknown rule key" (no recipe file)
No per-key recipe — 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 docs/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
docs/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/.