feat(kyberforge): add instructions-author skill for .apm/instructions files #154

Open
Claude wants to merge 5 commits from feat/148-instructions-author into main
pull from: feat/148-instructions-author
20 changed files with 719 additions and 16 deletions
Showing only changes of commit c52e351954 - Show all commits

No files matched your search

+2 -2
View File
@@ -1,7 +1,7 @@
{ {
"name": "holocron", "name": "holocron",
"description": "AI development skills for Claude Code, and for GitHub Copilot through apm — factory, design, implement, review, and cross-cutting workflows.", "description": "AI development skills for Claude Code, and for GitHub Copilot through apm — factory, design, implement, review, and cross-cutting workflows.",
"version": "0.5.1", "version": "0.5.2",
"owner": { "owner": {
"name": "Defame1297", "name": "Defame1297",
"email": "[email protected]", "email": "[email protected]",
@@ -11,7 +11,7 @@
{ {
"name": "kyberforge", "name": "kyberforge",
"description": "Skills and agents for creating, maintaining, and managing an apm plugin marketplace for Claude Code and GitHub Copilot.", "description": "Skills and agents for creating, maintaining, and managing an apm plugin marketplace for Claude Code and GitHub Copilot.",
"version": "2.0.2", "version": "2.1.0",
"category": "Developer Tools", "category": "Developer Tools",
"source": "./plugins/kyberforge" "source": "./plugins/kyberforge"
}, },
+3 -3
View File
@@ -1,5 +1,5 @@
name: holocron name: holocron
version: 0.5.1 version: 0.5.2
description: AI development skills for Claude Code, and for GitHub Copilot through apm — factory, design, implement, review, and cross-cutting workflows. description: AI development skills for Claude Code, and for GitHub Copilot through apm — factory, design, implement, review, and cross-cutting workflows.
license: MIT license: MIT
@@ -61,7 +61,7 @@ dependencies:
# an apm mechanic. # an apm mechanic.
executables: executables:
allow: allow:
kyberforge#2.0.2: kyberforge#2.1.0:
hooks: true hooks: true
bin: true bin: true
@@ -71,7 +71,7 @@ marketplace:
# top-level apm.yml description:/version: above are NOT inherited into the # top-level apm.yml description:/version: above are NOT inherited into the
# compiled output despite being used elsewhere (e.g. by `apm audit`). # compiled output despite being used elsewhere (e.g. by `apm audit`).
description: AI development skills for Claude Code, and for GitHub Copilot through apm — factory, design, implement, review, and cross-cutting workflows. description: AI development skills for Claude Code, and for GitHub Copilot through apm — factory, design, implement, review, and cross-cutting workflows.
version: 0.5.1 version: 0.5.2
owner: owner:
name: Defame1297 name: Defame1297
email: [email protected] email: [email protected]
@@ -12,7 +12,7 @@ apm compile --clean # zero-write sanity check; use for skill/agent-o
apm compile --clean --dry-run # pure preview, no writes apm compile --clean --dry-run # pure preview, no writes
``` ```
Compiles `.apm/instructions/` + `.apm/agents/*.agent.md` primitives into consumer-side context files (AGENTS.md/CLAUDE.md CONTEXT files) for the deployment target, per the `compilation:` block in `apm.yml`. This is the consumer/deployment side — it is NOT the producer of `plugin.json`/`marketplace.json`; that's `apm pack`'s job (below). Run `apm compile` after any change to `.apm/instructions/`/`.apm/agents/` content or to `compilation:`/`targets:` in `apm.yml`. Compiles `.apm/instructions/` + `.apm/agents/*.agent.md` primitives into consumer-side context files (AGENTS.md/CLAUDE.md CONTEXT files) for the deployment target, per the `compilation:` block in `apm.yml`. This is the consumer/deployment side — it is NOT the producer of `plugin.json`/`marketplace.json`; that's `apm pack`'s job (below). Run `apm compile` after any change to `.apm/instructions/`/`.apm/agents/` content or to `compilation:`/`targets:` in `apm.yml`. To author an instructions file, use `instructions-author` — it covers which fields each target drops.
## Pack ## Pack
@@ -23,7 +23,7 @@ Call `grill-with-docs` unless a grill session has already run and is available i
`grill-with-docs` ships in a sibling plugin kyberforge does not declare as an apm dependency, so it can be absent where kyberforge is installed alone. If it does not resolve, grill inline yourself rather than skipping the step: what problem the artifact solves, who invokes it and how, what it must refuse, and which existing skill or plugin already owns part of the job. Say which path you took. `grill-with-docs` ships in a sibling plugin kyberforge does not declare as an apm dependency, so it can be absent where kyberforge is installed alone. If it does not resolve, grill inline yourself rather than skipping the step: what problem the artifact solves, who invokes it and how, what it must refuse, and which existing skill or plugin already owns part of the job. Say which path you took.
Grilling regularly overturns the artifact type assumed at the start, or splits one idea into several artifacts, so it runs before classification rather than confirming it. Run it inline in the current conversation — grilling is interactive and a subagent cannot hold the back-and-forth. Grilling often overturns the assumed artifact type or splits one idea into several, so it runs before classification. Run it inline: a subagent cannot hold the back-and-forth.
## Step 2 — Classify and dispatch ## Step 2 — Classify and dispatch
@@ -33,6 +33,7 @@ Match the grilled intent against exactly one row — or more than one, if the in
|---|---|---|---| |---|---|---|---|
| A reusable capability the agent loads inline in the main conversation, triggered by description-matching, free to bundle its own `references/`, `scripts/` or `assets/` | Skill | `skill-author` | `references/author-routes.md` | | A reusable capability the agent loads inline in the main conversation, triggered by description-matching, free to bundle its own `references/`, `scripts/` or `assets/` | Skill | `skill-author` | `references/author-routes.md` |
| A recurring task needs its own reusable definition — dedicated system prompt, tools and description, invokable by name across sessions | Agent / subagent | `agent-author` | `references/author-routes.md` | | A recurring task needs its own reusable definition — dedicated system prompt, tools and description, invokable by name across sessions | Agent / subagent | `agent-author` | `references/author-routes.md` |
| Always-on or path-scoped agent guidance in `.apm/instructions/*.instructions.md` | Instructions file | `instructions-author` | `references/author-routes.md` |
| A new distributable unit — no existing plugin is the right home for the skill, agent, hook or MCP server being built, or the bundle needs its own manifest, versioning and install lifecycle | Plugin | `apm-workflow` (`apm plugin init`) | `references/apm-routes.md` | | A new distributable unit — no existing plugin is the right home for the skill, agent, hook or MCP server being built, or the bundle needs its own manifest, versioning and install lifecycle | Plugin | `apm-workflow` (`apm plugin init`) | `references/apm-routes.md` |
| The plugin already exists and only its marketplace-facing metadata changes — a first listing, or a version/description update, never the plugin's contents | Marketplace entry | `apm-workflow` (`apm marketplace package add`) | `references/apm-routes.md` | | The plugin already exists and only its marketplace-facing metadata changes — a first listing, or a version/description update, never the plugin's contents | Marketplace entry | `apm-workflow` (`apm marketplace package add`) | `references/apm-routes.md` |
@@ -47,4 +48,4 @@ When the intent spans several rows, chain the routes in dependency order — an
## Step 3 — Closing gates, common to every route ## Step 3 — Closing gates, common to every route
- **Resolve before closing.** A route is finished only when its verification reports nothing unresolved. An actionable finding reopens the route; it is never reported onward as a caveat. - **Resolve before closing.** A route is finished only when its verification reports nothing unresolved. An actionable finding reopens the route; it is never reported onward as a caveat.
- **Bump the package version.** A skill route always lands here: `skill-author` moves only a skill's own `metadata.version`, which is not the package `apm.yml`'s number — so read `references/version-bump.md` after one. `agent-author` and the apm routes bump the package themselves at plugin scope; after those, read it only when their output does not say they did. - **Bump the package version.** A skill route always lands here: `skill-author` moves only a skill's own `metadata.version`, which is not the package `apm.yml`'s number — so read `references/version-bump.md` after one. `agent-author`, `instructions-author` and the apm routes bump the package themselves at plugin scope; after those, read it only when their output does not say they did.
@@ -4,12 +4,14 @@ source_keys:
- context7-websites-code-claude - context7-websites-code-claude
--- ---
# Routing a skill or agent to its author skill # Routing a skill, agent or instructions file to its author skill
Reached from `SKILL.md` Step 2 when the classified artifact is a skill or an agent/subagent Reached from `SKILL.md` Step 2 when the classified artifact is a skill, an agent/subagent
definition. Route a skill to `skill-author` and an agent to `agent-author`. The two branches definition or an instructions file. Route a skill to `skill-author`, an agent to `agent-author` and
differ on the author skill only — both verify the result with `factory-audit`, which detects the an instructions file to `instructions-author`. The branches differ on the author skill only, and
artifact type itself — and everything below applies to both. everything below applies to all three — except that `factory-audit` has no instructions flow yet, so
an instructions file is verified by `instructions-author`'s own throwaway-package check and the
clean-context rerun below is skipped for it.
## Gotcha: `context: fork` is not `/fork` ## Gotcha: `context: fork` is not `/fork`
@@ -7,8 +7,8 @@ source_keys:
Reached from `SKILL.md` Step 3 after a route has finished. A skill route always lands here: Reached from `SKILL.md` Step 3 after a route has finished. A skill route always lands here:
`skill-author` moves only a skill's own `metadata.version`, which is not the package manifest's `skill-author` moves only a skill's own `metadata.version`, which is not the package manifest's
number, so the package version is still behind when it reports done. `agent-author` bumps the number, so the package version is still behind when it reports done. `agent-author` and `instructions-author` bump the
resolved package's `apm.yml` itself at plugin/APM scope, and `apm-workflow`'s configure flow resolved package's `apm.yml` themselves (`agent-author` at plugin/APM scope), and `apm-workflow`'s configure flow
carries the same policy — read those routes' output before acting here, because a second bump for carries the same policy — read those routes' output before acting here, because a second bump for
one change is wrong. one change is wrong.
@@ -0,0 +1,57 @@
---
name: instructions-author
description: >
Use when creating or revising an apm instructions file
(`.apm/instructions/*.instructions.md`). Not read-only review ->
`factory-audit`. Not skills -> `skill-author`. Not agents -> `agent-author`.
Not AGENTS.md -> `agentsmd-author`.
compatibility: Requires the apm CLI; behaviour verified against apm 0.28.0.
allowed-tools: Bash Read Write Edit
metadata:
version: "0.1.0"
category: factory
source_keys:
- apm-docs-site
- apm-cli-0-28-0-experiments
- claude-code-memory-docs
---
## Gotchas
Review

Are all of these really gotcha's? Or things that should be added as rules/requirements or generalisatons elsewhere?

Are all of these really gotcha's? Or things that should be added as rules/requirements or generalisatons elsewhere?
- Claude Code drops `description`; only Copilot and Cursor keep it. Write a body that explains itself.
- Quote every `applyTo`. An unquoted `**/*.py` fails to parse, compile skips the file, and `apm install` still deploys it with no `paths:`, so it loads in every session and nothing errors.
- `apm compile --validate` always exits 0 and hides the missing-`description`, missing-`applyTo` and empty-body warnings. It is not a lint gate.
- Once rules sit in `.claude/rules/`, `apm compile --target claude` writes no `CLAUDE.md` and still exits 0; an exit-code check verifies nothing.
- A source must be flat in `.apm/instructions/` and end `.instructions.md`; anything else is ignored or never installed.
## Step 1 — Dispatch
| Condition | Flow | Reference |
|---|---|---|
| No file at the target path | Create | `references/create.md` |
| A file exists, at least one improvement signal present | Improve | `references/improve.md` |
| A file exists, no signals | Stop and ask | — |
Signals: grill output, audit findings, inline feedback, a session describing a rule that loaded when it should not or failed to load. With none, ask whether the user meant to create a new file or has feedback to apply.
Read only the reference for the resolved flow. Capture `rtk git log --oneline -1` before touching the filesystem; Step 3 needs it.
## Step 2 — Contract
Gates on every file, whichever flow wrote it:
- **One topic per file.** Two topics are two files.
- **Scope.** Omit `applyTo` only for a rule that must load in every session, and tell the user it then costs context at every launch.
- **Stem.** It becomes the deployed filename, and install overwrites a hand-authored rule of the same name on most targets without a prompt. Check for a collision before choosing it.
- **Body.** Bullets, paths in backticks, nothing assuming another file is loaded, under 200 lines.
Review

Is it clear what should be an instruction and what typer of info/content should be in the bod (either here or in the template?)

Is it clear what should be an instruction and what typer of info/content should be in the bod (either here or in the template?)
If a field, glob or location is in question, read `references/schema.md`. If the question is which target keeps which field, or what compile does, read `references/target-mapping.md`.
## Step 3 — Validate and close
- [ ] Verify with a real compile and a throwaway deploy: read `references/verify.md`. Resolve every warning and confirm a scoped rule deploys with `paths:`.
- [ ] Bump the owning package's `apm.yml` `version` — **minor** on create, **patch** on improve — because consumers compare it to detect updates.
`factory-audit` has no instructions checks yet, so nothing else gates the file; report only what the verification showed.
**Commit verification.** Once verification is clean, run `rtk git add` and `rtk git commit`. Re-run `rtk git log --oneline -1` and confirm the hash changed from Step 1's. A non-empty `git diff --stat` is not proof: staged-but-uncommitted work is lost if the tree is cleaned up. Report done only once the hash has changed.
Outdated
Review

Commit steps should be out of scope for author skills.

Commit steps should be out of scope for author skills.
@@ -0,0 +1,5 @@
# assets/
## templates/
- **`instructions.md`** — minimal valid `.apm/instructions/<name>.instructions.md`, copied by `scripts/new-instructions.sh`. Carries a `description`, a quoted `applyTo` and a one-topic body, each marked `FILL IN:`. The `applyTo` comment is the only guidance it carries; field semantics are in `references/schema.md`.
@@ -0,0 +1,11 @@
---
# Delete these comments once filled in; Copilot receives this file verbatim.
description: FILL IN: one line on what this rule covers. Only Copilot and Cursor keep it.
applyTo: "FILL IN: quoted glob, e.g. **/*.py"
# applyTo is always quoted: an unquoted ** is a YAML alias error and the rule
# deploys unscoped. Several globs: "**/*.css,**/*.scss". Delete the line only
# for a rule that must load in every session.
---
# FILL IN: one topic per file
- FILL IN: the first rule, stated as a bullet.
Outdated
Review

See body comment in skill.md

See body comment in skill.md
@@ -0,0 +1,39 @@
---
source_keys:
- apm-docs-site
- apm-cli-0-28-0-experiments
---
# Creating a new instructions file
Return to `SKILL.md` Step 3 once Step 3 below is done.
## Before touching the filesystem
Confirm, and ask the user for anything missing:
- [ ] The one topic the file covers. Two topics are two files.
- [ ] Which files it governs, as a glob, or that it must load in every session.
- [ ] A kebab-case stem. It becomes the deployed filename.
## Step 1 — Check the stem
Install overwrites a hand-authored file at `.claude/rules/<stem>.md`, `.cursor/rules/<stem>.mdc`, `.windsurf/rules/<stem>.md`, `.kiro/steering/<stem>.md` and `.agents/rules/<stem>.md` without a prompt. List those paths in the consuming project and choose another stem on any hit.
## Step 2 — Scaffold
```bash
bash scripts/new-instructions.sh <name> <path-inside-the-package>
```
The script walks up for a `type:`-bearing `apm.yml`. With none it exits 1 and names `/apm-workflow configure`; run that first, then retry. It never overwrites an existing file.
## Step 3 — Fill in
Replace every `FILL IN:` and delete the template's comments.
- `applyTo`: quoted. Omit it only for a rule that must load in every session, and say so to the user; it costs context at every launch.
- `description`: one line. Write the body as if it were absent, because Claude Code never sees it.
- Body: bullets, one topic, paths in backticks, nothing that assumes another file is loaded.
For glob syntax or a field question, read `references/schema.md`.
@@ -0,0 +1,31 @@
---
source_keys:
- apm-docs-site
- apm-cli-0-28-0-experiments
---
# Improving an existing instructions file
Return to `SKILL.md` Step 3 once the edits are made.
## Step 1 — Read the file and the signals
Read the file whole. Signals are grill output, audit findings, inline feedback, or a session describing a rule that loaded when it should not, or failed to load. Apply what the signals name and nothing else.
## Step 2 — Diagnose by symptom
| Symptom | Cause | Fix |
|---|---|---|
| A scoped rule loads in every Claude session | `applyTo` is unquoted or malformed, so install deployed no `paths:` | Quote it, then confirm with `references/verify.md` |
| Compile warns "Failed to parse" | Broken frontmatter YAML | Repair the YAML; do not delete the field |
| The rule is in `CLAUDE.md` but not `.claude/rules/` | The file is nested under `.apm/instructions/` | Move it up to the flat directory |
| The rule appears nowhere | The name lacks `.instructions.md` | Rename it |
| Claude ignores guidance written in `description` | Claude Code drops `description` | Move the substance into the body |
| A hand-written rule vanished after install | The stem collided with a deployed name | Restore it from version control and rename the source stem |
| The same rule reaches the agent twice | Cursor, Windsurf, Kiro, Codex and OpenCode get both a native file and an `AGENTS.md` copy | State it to the user; it is apm behaviour, not a defect in the file |
Cases not in the table: read `references/target-mapping.md`.
## Step 3 — Split or trim
A file covering two topics, or longer than 200 lines, becomes several files. Do the split only when a signal names it.
@@ -0,0 +1,50 @@
---
source_keys:
- apm-docs-site
- apm-github-repo
- apm-cli-0-28-0-experiments
- claude-code-memory-docs
---
# The instructions source file
Verified against apm 0.28.0. Reached from `SKILL.md` Step 2 when a frontmatter field, a glob or the file's location is in question.
## Location and name
`.apm/instructions/<name>.instructions.md`, flat. The double extension is the discovery key and the stem is the primitive's name; there is no `name` field.
- A plain `.md` in that directory is ignored by both `apm compile` and `apm install`.
- A file in a subdirectory is folded into compiled root files by compile but never deployed by install, so it reaches `CLAUDE.md` and `AGENTS.md` and no native rules directory.
- The stem becomes the deployed filename: `<stem>.md`, `<stem>.mdc`, or `<stem>.instructions.md`, by target.
## Frontmatter
Only `description` and `applyTo` carry meaning. `author` and `version` are parsed and never emitted to any target.
- `description`: one line. The apm docs call it required; the binary only warns. Copilot and Cursor keep it; Claude Code, Windsurf, Kiro, Antigravity and every compiled root file drop it. Cursor auto-generates one from the first body sentence when it is missing.
- `applyTo`: a glob scoping the rule. The apm docs list it as both required and optional; the binary treats it as optional, with a warning. Empty or absent means an unconditional rule.
### `applyTo` grammar
- One glob: `"**/*.py"`.
- Several globs in one string, comma-separated: `"**/*.css,**/*.scss"`. Whitespace around segments is trimmed.
- A YAML sequence is joined into the same comma form.
- Brace alternation is never split: `"**/*.{css,scss},**/*.py"` is two patterns.
- A literal comma in a pattern is `\,`; a literal backslash is `\\`.
- Always quote the value. An unquoted `**/*.py` is a YAML alias error; see `SKILL.md` Gotchas for what apm then does.
## Body
Plain markdown. Official guidance: bullets over prose, one topic per file (`python-style` and `python-testing` are two files), paths in backticks, no greetings or meta-commentary, no assumption that other files are loaded. apm sets no size limit. The downstream tools do: Claude Code recommends under 200 lines per file and Cursor under 500.
## Validation
`Instruction.validate()` yields three findings, all demoted to warnings: missing `description`, missing `applyTo` ("will apply globally") and empty content. A broken relative link in the body is a fourth, also non-fatal.
- A real `apm compile` prints them. `apm compile --validate` prints none and exits 0 even for a file with all three problems.
- `apm install` prints none.
- `apm audit --ci` checks lockfile, deployed-file presence, content hash and hidden Unicode, not instruction content.
- A file whose frontmatter does not parse is skipped by compile ("Failed to parse") but still deployed by install.
No standalone instructions validator exists, so enforcement is this skill's checks and `references/verify.md`.
@@ -0,0 +1,59 @@
---
source_keys:
- apm-docs-site
- apm-github-repo
- apm-cli-0-28-0-experiments
- claude-code-memory-docs
- github-copilot-custom-instructions-docs
- cursor-rules-docs
---
# Sources
## apm-docs-site
- **URL:** https://microsoft.github.io/apm/
- **Research doc:** plugins/kyberforge/docs/research/docs/microsoft-apm/sources.md (digest: plugins/kyberforge/docs/research/docs/microsoft-apm/instructions-primitive-schema.md)
- **Description:** Official apm documentation, the instructions-and-agents authoring page plus targets and compile pages: frontmatter requirements, per-target deploy paths, compile behaviour and flags.
- **Contributing files:** SKILL.md, references/schema.md, references/target-mapping.md, references/create.md, references/improve.md
- **Status:** `extracted`
## apm-github-repo
- **URL:** https://github.com/microsoft/apm
- **Research doc:** plugins/kyberforge/docs/research/docs/microsoft-apm/sources.md (digest: plugins/kyberforge/docs/research/docs/microsoft-apm/instructions-primitive-schema.md)
- **Description:** apm's own Python source read for the Instruction model, discovery globs and per-target integrators.
- **Contributing files:** references/schema.md
- **Status:** `extracted`
## apm-cli-0-28-0-experiments
- **URL:** https://pypi.org/project/apm-cli/0.28.0/
- **Research doc:** plugins/kyberforge/docs/research/docs/microsoft-apm/sources.md (digest: plugins/kyberforge/docs/research/docs/microsoft-apm/instructions-gotchas.md)
- **Description:** The installed apm-cli 0.28.0 package plus throwaway install, compile and audit experiments confirming validation severity, unquoted-glob handling, discovery asymmetry, dedup and overwrite behaviour.
- **Contributing files:** SKILL.md, references/schema.md, references/target-mapping.md, references/verify.md, references/create.md, references/improve.md
- **Status:** `extracted`
## claude-code-memory-docs
- **URL:** https://code.claude.com/docs/en/memory
- **Research doc:** plugins/kyberforge/docs/research/docs/microsoft-apm/sources.md (digest: plugins/kyberforge/docs/research/docs/microsoft-apm/instructions-target-mapping.md)
- **Description:** Claude Code memory documentation: `.claude/rules/` loading, the `paths` field as the only field read, invalid YAML ignored, size guidance.
- **Contributing files:** SKILL.md, references/schema.md, references/target-mapping.md
- **Status:** `extracted`
## github-copilot-custom-instructions-docs
- **URL:** https://docs.github.com/en/copilot/how-tos/configure-custom-instructions/add-repository-instructions
- **Research doc:** plugins/kyberforge/docs/research/docs/microsoft-apm/sources.md (digest: plugins/kyberforge/docs/research/docs/microsoft-apm/instructions-target-mapping.md)
- **Description:** GitHub Copilot repository custom instructions: `.github/instructions/*.instructions.md`, `applyTo` and `excludeAgent`, the separate repo-wide file.
- **Contributing files:** references/target-mapping.md
- **Status:** `extracted`
## cursor-rules-docs
- **URL:** https://cursor.com/docs/context/rules
- **Research doc:** plugins/kyberforge/docs/research/docs/microsoft-apm/sources.md (digest: plugins/kyberforge/docs/research/docs/microsoft-apm/instructions-target-mapping.md)
- **Description:** Cursor project rules: the `.mdc` requirement, `description`, `globs` and `alwaysApply`, rule types, size guidance.
- **Contributing files:** references/target-mapping.md
- **Status:** `extracted`
@@ -0,0 +1,63 @@
---
source_keys:
- apm-cli-0-28-0-experiments
- apm-docs-site
- claude-code-memory-docs
- github-copilot-custom-instructions-docs
- cursor-rules-docs
---
# What each target receives
Verified against apm 0.28.0 and throwaway installs. Reached from `SKILL.md` Step 2 when the question is which target keeps which field. Source-file syntax is in `references/schema.md`.
## Two output paths
`apm install` writes one native file per instruction into each target's rules directory. `apm compile` writes root context files that concatenate instruction bodies, grouped by `applyTo`. Treat install as the primary path for Claude Code and Copilot, and compile as the path for targets with no native instructions directory.
## Install: deployed path and transform
| Target | Deployed path | Transform |
|---|---|---|
| copilot | `.github/instructions/<n>.instructions.md` | Verbatim copy |
| claude | `.claude/rules/<n>.md` | `applyTo` becomes a `paths:` list; `description` dropped; no frontmatter at all without `applyTo` |
| cursor | `.cursor/rules/<n>.mdc` | `applyTo` becomes `globs`; `description` kept; no `alwaysApply` written |
| windsurf | `.windsurf/rules/<n>.md` | `trigger: glob` plus `globs`, or `trigger: always_on`; `description` dropped |
| kiro | `.kiro/steering/<n>.md` | `inclusion: fileMatch` plus `fileMatchPattern`, or `inclusion: always`; `description` dropped |
| antigravity | `.agents/rules/<n>.md` | `trigger: glob` plus `globs`, or no frontmatter; `description` dropped |
| grok-build | `.grok/rules/<n>.instructions.md` | Verbatim copy |
| codex, gemini, opencode and the rest | none | Reach instructions only through compile |
Windsurf, Kiro, Antigravity and Cursor do not deploy at user scope.
## Field survival
| Field | Claude | Copilot | Cursor | Windsurf, Kiro, Antigravity | Compiled root file |
|---|---|---|---|---|---|
| `applyTo` | as `paths` | verbatim | as `globs` | as each target's glob key | grouping only |
| `description` | dropped | kept | kept | dropped | dropped |
| `author`, `version` | dropped | kept only because the file is verbatim | dropped | dropped | dropped |
For Claude Code the body's first line or heading is the only descriptive text that survives, so the body must explain itself.
## Ownership and overwrite
- Claude, Cursor, Windsurf, Kiro and Antigravity treat each deployed file as apm-owned: install replaces a hand-authored file at the same path without a prompt. Copilot skips an unmanaged file ("local files exist, not managed by APM") until `apm install --force`.
- Removing or renaming a source makes the next install delete the file it deployed.
## Compile
- `--target claude` writes `CLAUDE.md`; Gemini writes `GEMINI.md` and `AGENTS.md`; every other target writes `AGENTS.md`.
- Compile skips instructions already deployed natively, for Claude, Copilot and Antigravity only. With rules populated, `--target claude` exits 0, prints "produced no output files" and writes nothing. `--force-instructions` (alias `--no-dedup`) overrides.
- Cursor, Windsurf, Kiro, Grok, Codex and OpenCode have no dedup: compile writes `AGENTS.md` that repeats rules the tool already loads natively.
- A compile with no instruction primitives exits 0.
## Native format facts
- Claude Code reads `.claude/rules/**/*.md` recursively. `paths` is the only field it reads, as a list or a comma-separated string; other fields are ignored. A rule without `paths` loads at every launch. Frontmatter that fails to parse is ignored and the rule loads without `paths`.
- Copilot path-specific files need `applyTo` as a quoted comma-joined string; `excludeAgent` is the only other documented key. Repository-wide instructions are the separate `.github/copilot-instructions.md`.
- Cursor ignores a plain `.md` in `.cursor/rules`. A rule with only a `description` is "Apply Intelligently", not always-on.
## Unverified
Cursor's handling of a YAML-list `globs`, Copilot's handling of unknown frontmatter keys, and runtime behaviour on Windsurf, Kiro and Antigravity. Say so rather than asserting any of them.
@@ -0,0 +1,32 @@
---
source_keys:
- apm-cli-0-28-0-experiments
---
# Verifying a file in a throwaway package
Reached from `SKILL.md` Step 3. Run step 2 outside the repo: `apm install` writes `apm_modules/`, `apm.lock.yaml` and a rules directory, and install overwrites hand-authored rule files without warning.
1. From the package root, a real compile, never `--validate`:
```bash
apm compile --dry-run --target claude
```
Resolve every warning it prints: missing `description`, missing `applyTo`, empty content, broken link, "Failed to parse".
2. For a scoped rule, deploy it where nothing else can be overwritten:
```bash
d=$(mktemp -d)
printf 'name: scratch\nversion: 0.1.0\ntype: instructions\ntargets:\n - claude\n' > "$d/apm.yml"
mkdir -p "$d/.apm/instructions"
cp <package-root>/.apm/instructions/<name>.instructions.md "$d/.apm/instructions/"
(cd "$d" && apm install && cat .claude/rules/<name>.md)
```
3. The deployed file must open with `paths:` listing the intended globs. No frontmatter block at all means `applyTo` was missing or did not parse: the rule would load in every session.
4. To check the compiled root file instead, compile in that same clean directory *before* installing, or pass `--force-instructions`; after an install, `--target claude` writes nothing.
Delete the directory afterwards. Report only what was observed; Cursor's list-form `globs` and the Windsurf, Kiro and Antigravity runtimes stay unverified.
@@ -0,0 +1,3 @@
# scripts/
- **`new-instructions.sh <name> <root>`** — scaffolds `<package-root>/.apm/instructions/<name>.instructions.md` from `assets/templates/instructions.md`. Walks up from `<root>` for the nearest `type:`-bearing `apm.yml`; exits 1 with a pointer to `/apm-workflow configure` when there is none. Never overwrites an existing file. Run `--help` for the full contract.
@@ -0,0 +1,116 @@
#!/usr/bin/env bash
set -euo pipefail
SKILL_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
TEMPLATE="$SKILL_DIR/../assets/templates/instructions.md"
usage() {
cat <<USAGE
Usage: new-instructions.sh <name> <root>
Scaffold an apm instructions file from the bundled template.
Arguments:
name Kebab-case stem. Becomes <name>.instructions.md and, after install,
the deployed rule's filename.
root Existing path at or below the target package. The script walks up for
the nearest apm.yml with a top-level type: field (instructions, skill,
hybrid or prompts); an apm.yml without type: is a marketplace-only
manifest and is skipped. Creates
<package-root>/.apm/instructions/<name>.instructions.md
Exit codes:
0 File created, or already existed (no-op)
1 Invalid arguments, missing root, no package found, or template not found
USAGE
}
if [[ "${1:-}" == "--help" || "${1:-}" == "-h" ]]; then
usage
exit 0
fi
if [[ $# -lt 2 ]]; then
echo "Error: name and root are required." >&2
echo "" >&2
usage >&2
exit 1
fi
NAME="$1"
ROOT="${2/#\~/$HOME}"
if ! grep -qE '^[a-z0-9]+(-[a-z0-9]+)*$' <<< "$NAME"; then
echo "Error: name must use lowercase letters, numbers, and hyphens only." >&2
echo " No leading, trailing, or consecutive hyphens." >&2
echo " Received: '$NAME'" >&2
exit 1
fi
if [[ ! -f "$TEMPLATE" ]]; then
echo "Error: template not found at '$TEMPLATE'." >&2
echo " Run this script from its original location inside the instructions-author skill." >&2
exit 1
fi
if [[ ! -d "$ROOT" ]]; then
echo "Error: root directory '$ROOT' does not exist." >&2
exit 1
fi
ROOT="$(cd "$ROOT" && pwd)"
# Same marker as agent-author's new-agent.sh: a top-level type: naming one of
# the four package types, with matching quotes if quoted.
is_apm_package_manifest() {
local apm_yml="$1" line
while IFS= read -r line || [[ -n "$line" ]]; do
if [[ "$line" =~ ^type:[[:space:]]*(instructions|skill|hybrid|prompts)([[:space:]]|$) ]]; then
return 0
fi
if [[ "$line" =~ ^type:[[:space:]]*([\"\'])(instructions|skill|hybrid|prompts)([\"\'])([[:space:]]|$) ]] \
&& [[ "${BASH_REMATCH[1]}" == "${BASH_REMATCH[3]}" ]]; then
return 0
fi
done < "$apm_yml"
return 1
}
PACKAGE_ROOT=""
current="$ROOT"
while true; do
if [[ -f "$current/apm.yml" ]] && is_apm_package_manifest "$current/apm.yml"; then
PACKAGE_ROOT="$current"
break
fi
if [[ -e "$current/.git" ]]; then
break
fi
parent="$(dirname "$current")"
[[ "$parent" == "$current" ]] && break
current="$parent"
done
if [[ -z "$PACKAGE_ROOT" ]]; then
echo "Error: no apm package found at or above '$ROOT'." >&2
echo " Instructions only deploy from a package's .apm/instructions/. Run" >&2
echo " /apm-workflow configure (apm plugin init) there first, then retry." >&2
exit 1
fi
DEST_DIR="$PACKAGE_ROOT/.apm/instructions"
DEST="$DEST_DIR/$NAME.instructions.md"
if [[ -f "$DEST" ]]; then
echo "Skipping '$DEST' — already exists." >&2
exit 0
fi
mkdir -p "$DEST_DIR"
cp "$TEMPLATE" "$DEST"
echo "Created: $DEST" >&2
echo "" >&2
echo "Next steps:" >&2
echo " 1. Fill in $DEST — replace every FILL IN: placeholder and delete the comments." >&2
echo " 2. Check '$NAME' does not collide with a hand-authored rule: install overwrites" >&2
echo " <target>/rules/$NAME.* on most targets without warning." >&2
echo " 3. Verify with a real compile, not --validate: apm compile --dry-run --target <target>" >&2
@@ -0,0 +1,15 @@
# tests/
- **`new-instructions.bats`** — covers `scripts/new-instructions.sh` (name validation, package walk-up, no-package refusal, no-op on an existing file, template placeholders) and the apm behaviour the skill's gotchas rest on, run against throwaway packages: a filled scaffold compiles into `CLAUDE.md` and installs into `.claude/rules/` with `description` dropped, compile writes nothing once rules are installed, an unquoted `applyTo` installs unscoped, and `--validate` hides the warnings a real compile prints.
## Dependencies
The test file loads `bats-support` and `bats-assert` from the repo root's `tests/test_helper/`, and runs on the repo's bats submodule at `tests/bats/`. The first `bash tests/run-bats.sh` initialises the submodules.
The apm tests need the `apm` CLI on `PATH` and skip when it is absent. They assert apm 0.28.0 behaviour, so a failure after an apm upgrade is a finding about the skill's gotchas, not a flaky test.
From the repo root:
```bash
tests/bats/bin/bats plugins/kyberforge/.apm/skills/instructions-author/tests/new-instructions.bats
```
@@ -0,0 +1,219 @@
#!/usr/bin/env bats
setup() {
REPO_ROOT="$(cd "$BATS_TEST_DIRNAME/../../../../../../" && pwd)"
load "$REPO_ROOT/tests/test_helper/bats-support/load"
load "$REPO_ROOT/tests/test_helper/bats-assert/load"
SCRIPT="$(cd "$BATS_TEST_DIRNAME/../scripts" && pwd)/new-instructions.sh"
ROOT="$(mktemp -d)"
}
teardown() {
rm -rf "$ROOT"
}
make_package() {
printf 'name: my-package\nversion: 0.1.0\ntype: instructions\ntargets:\n - claude\n' > "$ROOT/apm.yml"
}
# Replace every placeholder and drop the template's comments, leaving a valid file.
fill() {
sed -i -E \
-e '/^#/{/^# FILL IN/!d}' \
-e 's/^description: FILL IN.*/description: Python style rules/' \
-e 's/^applyTo: .*/applyTo: "**\/*.py"/' \
-e 's/^# FILL IN.*/# Python style/' \
-e 's/^- FILL IN.*/- Use type hints./' \
"$1"
}
need_apm() {
command -v apm >/dev/null 2>&1 || skip "apm CLI not installed"
}
# ---------------------------------------------------------------------------
# Help and arguments
# ---------------------------------------------------------------------------
@test "--help exits 0" {
run bash "$SCRIPT" --help
assert_success
assert_output --partial "Usage:"
}
@test "missing arguments exits 1" {
run bash "$SCRIPT"
assert_failure
assert_output --partial "name and root are required"
}
@test "nonexistent root exits 1" {
run bash "$SCRIPT" my-rule "$ROOT/missing"
assert_failure
assert_output --partial "does not exist"
}
@test "rejects names that are not kebab-case" {
make_package
for bad in My-Rule my_rule -leading trailing- double--hyphen; do
run bash "$SCRIPT" "$bad" "$ROOT"
assert_failure
assert_output --partial "lowercase letters"
done
assert [ ! -d "$ROOT/.apm" ]
}
# ---------------------------------------------------------------------------
# Package resolution
# ---------------------------------------------------------------------------
@test "creates <name>.instructions.md under .apm/instructions/" {
make_package
run bash "$SCRIPT" my-rule "$ROOT"
assert_success
assert [ -f "$ROOT/.apm/instructions/my-rule.instructions.md" ]
}
@test "walks up from a subdirectory to the package root" {
make_package
mkdir -p "$ROOT/deep/er"
run bash "$SCRIPT" my-rule "$ROOT/deep/er"
assert_success
assert [ -f "$ROOT/.apm/instructions/my-rule.instructions.md" ]
assert [ ! -d "$ROOT/deep/er/.apm" ]
}
@test "skips a type-less apm.yml and keeps walking up" {
make_package
mkdir -p "$ROOT/marketplace"
printf 'name: catalog\nmarketplace:\n packages: []\n' > "$ROOT/marketplace/apm.yml"
run bash "$SCRIPT" my-rule "$ROOT/marketplace"
assert_success
assert [ -f "$ROOT/.apm/instructions/my-rule.instructions.md" ]
assert [ ! -d "$ROOT/marketplace/.apm" ]
}
@test "accepts a quoted type value" {
printf 'name: p\ntype: "hybrid"\n' > "$ROOT/apm.yml"
run bash "$SCRIPT" my-rule "$ROOT"
assert_success
assert [ -f "$ROOT/.apm/instructions/my-rule.instructions.md" ]
}
@test "no package: exits 1, points at apm-workflow configure, writes nothing" {
mkdir -p "$ROOT/.git"
run bash "$SCRIPT" my-rule "$ROOT"
assert_failure
assert_output --partial "apm-workflow configure"
assert [ ! -d "$ROOT/.apm" ]
}
@test "does not walk above a .git boundary" {
make_package
mkdir -p "$ROOT/repo/.git"
run bash "$SCRIPT" my-rule "$ROOT/repo"
assert_failure
assert [ ! -d "$ROOT/.apm" ]
}
@test "no-op when the file already exists" {
make_package
mkdir -p "$ROOT/.apm/instructions"
echo "existing" > "$ROOT/.apm/instructions/my-rule.instructions.md"
run bash "$SCRIPT" my-rule "$ROOT"
assert_success
run cat "$ROOT/.apm/instructions/my-rule.instructions.md"
assert_output "existing"
}
# ---------------------------------------------------------------------------
# Template contents
# ---------------------------------------------------------------------------
@test "scaffold carries FILL IN placeholders and a quoted applyTo" {
make_package
bash "$SCRIPT" my-rule "$ROOT"
file="$ROOT/.apm/instructions/my-rule.instructions.md"
run grep -c 'FILL IN' "$file"
assert_success
run grep -E '^applyTo: "' "$file"
assert_success
}
# ---------------------------------------------------------------------------
# apm behaviour the skill's gotchas rest on (verified against apm 0.28.0)
# ---------------------------------------------------------------------------
@test "filled scaffold compiles for the Claude target into CLAUDE.md without the description" {
need_apm
make_package
bash "$SCRIPT" my-rule "$ROOT"
fill "$ROOT/.apm/instructions/my-rule.instructions.md"
cd "$ROOT"
run apm compile --target claude
assert_success
assert [ -f "$ROOT/CLAUDE.md" ]
run grep -F 'Use type hints.' "$ROOT/CLAUDE.md"
assert_success
run grep -F 'Python style rules' "$ROOT/CLAUDE.md"
assert_failure
}
@test "install deploys .claude/rules with paths: and drops the description" {
need_apm
make_package
bash "$SCRIPT" my-rule "$ROOT"
fill "$ROOT/.apm/instructions/my-rule.instructions.md"
cd "$ROOT"
run apm install
assert_success
rule="$ROOT/.claude/rules/my-rule.md"
assert [ -f "$rule" ]
run grep -F 'paths:' "$rule"
assert_success
run grep -F '**/*.py' "$rule"
assert_success
run grep -F 'Python style rules' "$rule"
assert_failure
}
@test "once rules are installed, compile --target claude writes no CLAUDE.md and exits 0" {
need_apm
make_package
bash "$SCRIPT" my-rule "$ROOT"
fill "$ROOT/.apm/instructions/my-rule.instructions.md"
cd "$ROOT"
apm install
run apm compile --target claude
assert_success
assert [ ! -f "$ROOT/CLAUDE.md" ]
}
@test "an unquoted applyTo still installs, as a rule with no paths:" {
need_apm
make_package
mkdir -p "$ROOT/.apm/instructions"
printf -- '---\ndescription: x\napplyTo: **/*.py\n---\n# T\n\n- a\n' > "$ROOT/.apm/instructions/bad.instructions.md"
cd "$ROOT"
run apm install
assert_success
assert [ -f "$ROOT/.claude/rules/bad.md" ]
run grep -F 'paths:' "$ROOT/.claude/rules/bad.md"
assert_failure
}
@test "compile --validate exits 0 and hides the warnings a real compile prints" {
need_apm
make_package
mkdir -p "$ROOT/.apm/instructions"
printf -- '---\napplyTo: "**/*.py"\n---\n' > "$ROOT/.apm/instructions/bare.instructions.md"
cd "$ROOT"
run apm compile --validate
assert_success
refute_output --partial "Missing 'description'"
run apm compile --dry-run --target claude
assert_success
assert_output --partial "Missing 'description'"
assert_output --partial "Empty content"
}
+1 -1
View File
@@ -1,5 +1,5 @@
name: kyberforge name: kyberforge
version: 2.0.2 version: 2.1.0
description: Skills and agents for creating, maintaining, and managing an apm plugin marketplace for Claude Code and GitHub Copilot. description: Skills and agents for creating, maintaining, and managing an apm plugin marketplace for Claude Code and GitHub Copilot.
author: author:
name: Defame1297 name: Defame1297