feat(kyberforge): execute plugin-to-apm marketplace conversion
Why: ADR-0015 established that Microsoft APM (apm.yml + .apm/) should replace this repo's hand-authored plugin.json/marketplace.json model, with those files becoming compiled output of `apm pack` instead of files edited by hand via the (now-retired) plugin-author/marketplace-author skills. Issue #90 was the deferred execution of that decision, gated on #88 (apm tooling) and #89 (apm-native agent-author/skill-author routing). Implementation notes: - All six plugins (bin, core, git, gitea, kyberforge, lint) now carry apm.yml + .apm/{skills,agents,hooks} as their authoring source. Skills moved with a plain git mv (content-identical across targets). Agents were re-authored, not moved: per ADR-0016, .apm/agents/*.agent.md compiles verbatim to both Claude and Copilot, so plugin-scope agents now carry only name/description/model/source_keys -- no tools: field, no Claude-only knobs (isolation, maxTurns, effort, memory, permissionMode). - Root apm.yml registers all 7 marketplace packages (6 local plus mattpocock-skills as a remote entry) under versioning: per_package, matching this repo's existing independent-plugin-versioning practice. - .claude-plugin/marketplace.json and every plugin's plugin.json are now apm-pack-compiled output, verified against the prior hand-maintained content: same names/descriptions/versions/licenses/authors, only cosmetic serialization differences (JSON key order, owner email vs. url, Unicode escaping). - plugin-author and marketplace-author are retired now that apm-based authoring fully replaces their job; kyberforge bumped 1.3.1 -> 1.4.0 for that removal, and the root marketplace catalog bumped 0.3.1 -> 0.3.2 to match, per the version-bump convention now documented in apm-workflow's reference docs instead of a dedicated script (apm has no native version-bump automation). - Fixed hardcoded pre-.apm/ path assumptions across .pre-commit-config.yaml, .pre-commit-hooks.yaml, scripts/check-scope-walkup-sync.sh, scripts/sync-vale-styles.sh, scripts/check-vale-style-sync.sh, six plugins' root plugin.json (stale skills/hooks/agents pointer fields that check-manifests.sh validates), and several tests/*.bats and tests/*.sh fixtures -- including a bats REPO_ROOT relative-path depth bug (10 files, one extra .apm/ directory level to walk up) and a vale probe-path isolation regression introduced mid-fix. - Corrected empirically-wrong assumptions surfaced this session in apm-workflow/apm-install's own reference docs: `apm marketplace package add` does not accept local paths (only owner/repo remote shorthand -- local packages are registered by editing apm.yml's marketplace.packages[] directly); `apm compile` is a consumer-side AGENTS.md/CLAUDE.md generator, not the plugin.json producer, and hard-fails on skill/agent-only packages without --clean; `apm plugin init <name>` nests a stray subdirectory when run with a positional name arg from inside a same-named directory; no native Copilot marketplace output profile exists; .mcp.json is merged into the compiled plugin.json content-aware and target-scoped, with no dependencies.mcp entry needed for simple passthrough; pipx is the correct pip fallback on externally-managed Python environments. - Renamed agent-author's copilot.agent.md template asset to copilot.agent.md.template so apm compile's recursive *.agent.md glob stops misparsing the placeholder template as a real agent primitive. Impact: plugin.json and marketplace.json are compiled artifacts from here on -- editing them by hand is no longer the workflow; edit apm.yml/.apm/ and run apm pack. CONTEXT.md's Plugin/Plugin marketplace glossary entries reflect this. ADR-0001 is marked superseded, ADR-0006 moot, and ADR-0010 updated for the new .apm/agents/ path (project/user scope unaffected, per ADR-0016). Full local verification: claude plugin validate --strict on all 6 plugins, apm audit --ci, apm marketplace check, check-manifests.sh, and the full test suite (165/165 bats, 13/13 shell scripts) all pass clean. Fixes: #90 Refs: #88, #89 ADR: 0015 ADR: 0016 Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ub96PyaSRD9BHPktotj1pC
This commit is contained in:
@@ -1,45 +0,0 @@
|
||||
# agent-author
|
||||
|
||||
Creates and improves agent definition files for Claude Code and GitHub Copilot CLI.
|
||||
|
||||
## What it does
|
||||
|
||||
Scaffolds and fills in agent definition files at plugin/APM, project, or user scope. Project and user scope always generate a Claude Code + Copilot CLI file pair (`.md` + `.agent.md`) in one pass. Plugin/APM scope generates a single vendor-neutral `.apm/agents/<name>.agent.md` file instead — no separate Claude Code / Copilot split, since `apm compile` has no per-target field integrator (see ADR-0016). Also applies improvement signals — grill output, inline feedback, session context — to existing agent files. Bumps the version after every change: the resolved package's `apm.yml` at plugin/APM scope (minor for new agents, patch for improvements); project/user scope has no manifest to bump.
|
||||
|
||||
## Before you start
|
||||
|
||||
Have ready: the agent's name (kebab-case), the root directory (plugin root, project root, or `~`), a one-sentence purpose, and the triggering condition (when should the runtime delegate to this agent?).
|
||||
|
||||
## Usage
|
||||
|
||||
```
|
||||
/agent-author
|
||||
```
|
||||
|
||||
**Manual scaffold (human workflow):**
|
||||
```bash
|
||||
bash scripts/new-agent.sh <agent-name> <root>
|
||||
|
||||
# Examples:
|
||||
bash scripts/new-agent.sh code-reviewer packages/my-package/ # plugin/APM scope if packages/my-package/apm.yml has a type: field
|
||||
bash scripts/new-agent.sh deploy-assistant .
|
||||
bash scripts/new-agent.sh security-reviewer ~
|
||||
```
|
||||
|
||||
## Files
|
||||
|
||||
| File | Purpose |
|
||||
|------|---------|
|
||||
| `SKILL.md` | Skill instructions for agents |
|
||||
| `scripts/new-agent.sh` | Scaffolds agent definition file(s) from templates — a single `.apm/agents/<name>.agent.md` at plugin/APM scope, or a Claude Code + Copilot CLI pair at project/user scope |
|
||||
| `references/deployment-modes.md` | Plugin/APM vs project vs user scope: restrictions, scoped identifiers, path conventions |
|
||||
| `references/scripts.md` | Conventions for new-agent.sh and any future scripts: contract, template variables, file placement, error messages |
|
||||
| `references/sources.md` | Research provenance — sources that informed this skill |
|
||||
| `assets/templates/claude-code.md` | Annotated Claude Code agent definition template (project/user scope) |
|
||||
| `assets/templates/copilot.agent.md` | Annotated Copilot CLI agent definition template (project/user scope) |
|
||||
| `assets/templates/apm-agent.md` | Annotated vendor-neutral APM agent definition template (plugin/APM scope) |
|
||||
| `tests/new-agent.bats` | bats tests for `scripts/new-agent.sh` |
|
||||
| `assets/README.md` | Directory meta-documentation for assets/ |
|
||||
| `references/README.md` | Directory meta-documentation for references/ |
|
||||
| `scripts/README.md` | Directory meta-documentation for scripts/ |
|
||||
| `tests/README.md` | bats dependency instructions and run command |
|
||||
@@ -1,263 +0,0 @@
|
||||
---
|
||||
name: agent-author
|
||||
description: >
|
||||
Use when the user wants to create a new agent definition file from scratch
|
||||
("write an agent for X", "build a subagent that does Y", "create an agent
|
||||
definition for Z"), or improve an existing one. Handles agent definitions at
|
||||
plugin/APM, project, and user scope. Project and user scope always generate
|
||||
a Claude Code (`.md`) + Copilot CLI (`.agent.md`) file pair in one pass;
|
||||
plugin/APM scope generates a single vendor-neutral `.apm/agents/<name>.agent.md`
|
||||
file instead (no per-target Claude Code / Copilot split). Also use when the
|
||||
user provides inline feedback about an agent's behavior and wants it applied,
|
||||
or when a grill session has produced findings the user wants acted on — even
|
||||
if they don't say "improve" explicitly. Do not use for read-only review —
|
||||
examine agent files manually or run a grill session to generate improvement
|
||||
signals. Do not use to author skills — use /skill-author instead.
|
||||
allowed-tools: Bash Read Write Edit
|
||||
metadata:
|
||||
category: factory
|
||||
source_keys:
|
||||
- context7-websites-code-claude
|
||||
- claude-code-plugins-docs
|
||||
- claude-code-subagents-docs
|
||||
- context7-github-en-copilot
|
||||
- github-custom-agents-configuration
|
||||
- github-cli-plugin-reference
|
||||
- github-plugins-creating
|
||||
---
|
||||
|
||||
## Gotchas
|
||||
|
||||
- At plugin/APM scope, bump the resolved package's `apm.yml` `version` after every change — minor for a new agent, patch for a fix. Consumers compare this version to detect updates; skipping it hides the change.
|
||||
- At plugin/APM scope, `tools` and all Claude-only fields (`isolation`, `maxTurns`, `effort`, `memory`, `permissionMode`, `disallowedTools`, `skills`, `color`, `initialPrompt`, `background`, `hooks`, `mcpServers`) are omitted entirely, not merely restricted (ADR-0016: `apm compile` copies frontmatter verbatim to both harnesses with no per-target integrator, so a harness-specific value is wrong on at least one). Only project/user scope supports these fields.
|
||||
- An `apm.yml` with no top-level `type:` field is a marketplace-only manifest, not a package root — the walk-up skips it and keeps going.
|
||||
- `AskUserQuestion`, `EnterPlanMode`, `ExitPlanMode`, `ScheduleWakeup`, and `WaitForMcpServers` are never available to any subagent regardless of the `tools` field. Exception: `ExitPlanMode` is available when the parent session runs in `permissionMode: plan`.
|
||||
- Duplicate `name` values in the same scope: Claude Code silently discards one without warning. Always verify uniqueness before shipping.
|
||||
- Plugin agents in subdirectories get scoped identifiers (`plugin:folder:name`) — keep agents flat in `agents/` to avoid this. Applies to project/user-scope Claude Code agents only.
|
||||
- Copilot CLI agent files **must** use the `.agent.md` extension — a plain `.md` file isn't picked up. The plugin/APM-scope single file also ends in `.agent.md` by convention, but it's vendor-neutral, not Copilot-only — it compiles to Claude Code too.
|
||||
- Copilot has no `permissionMode`, `maxTurns`, `isolation`, or `memory` fields — do not include them in project/user-scope Copilot files.
|
||||
- `model` resolution order for Claude Code: `CLAUDE_CODE_SUBAGENT_MODEL` env var → per-invocation parameter → frontmatter `model` → main session model. The frontmatter value is a low-priority default, not a guarantee.
|
||||
|
||||
## Route
|
||||
|
||||
If the destination resolves to plugin/APM scope (scope detection in Step 1 finds a `type:`-bearing `apm.yml` at or above the root), read `references/deployment-modes.md`.
|
||||
|
||||
Determine which flow before touching the filesystem:
|
||||
|
||||
- **Neither `<name>.md` nor `<name>.agent.md` exist at the target paths** → follow **Creating a new agent**
|
||||
- **At least one file exists + improvement signals present** → follow **Improving an existing agent**
|
||||
- **At least one file exists + no signals** → ask: "No improvement signals found. Did you mean to create a new agent, or do you have feedback to apply?"
|
||||
|
||||
Signals: grill session output, inline user feedback, session context describing what went wrong.
|
||||
|
||||
## Creating a new agent
|
||||
|
||||
### Prerequisites
|
||||
|
||||
Before touching the filesystem, confirm you have:
|
||||
- [ ] Agent name (kebab-case, e.g. `code-reviewer`)
|
||||
- [ ] Root directory (a path inside a package for plugin/APM scope, project root, or `~` for user scope)
|
||||
- [ ] Agent purpose — one sentence describing the task this agent handles
|
||||
- [ ] Trigger condition — when should the runtime delegate to this agent?
|
||||
|
||||
If any are missing, stop and ask before proceeding. Then capture `git log --oneline -1` before touching the filesystem — Step 5 needs it to verify a real commit landed.
|
||||
|
||||
Verify `kyberforge:agent-audit` is available — it ships with the kyberforge plugin and is co-installed with this skill. If unavailable, stop and tell the user to install the kyberforge plugin before continuing.
|
||||
|
||||
### Step 1 — Scaffold
|
||||
|
||||
Run the scaffold script with the agent name and root directory:
|
||||
|
||||
```bash
|
||||
bash scripts/new-agent.sh <name> <root>
|
||||
```
|
||||
|
||||
Examples:
|
||||
```bash
|
||||
bash scripts/new-agent.sh code-reviewer packages/my-package/ # plugin/APM scope if packages/my-package/apm.yml has a type: field
|
||||
bash scripts/new-agent.sh deploy-assistant .
|
||||
bash scripts/new-agent.sh security-reviewer ~
|
||||
```
|
||||
|
||||
**Scope detection (script handles this automatically).** The script walks up from `<root>` for a package boundary — same shape `agent-audit`'s `validate.sh` uses:
|
||||
- Nearest ancestor `apm.yml` with a top-level `type:` field (`instructions`/`skill`/`hybrid`/`prompts`) → **plugin/APM scope** → `<package-root>/.apm/agents/<name>.agent.md` (single vendor-neutral file). A `type:`-less `apm.yml` is marketplace-only — skipped, walk continues upward.
|
||||
- No such `apm.yml`, `<root>` is a project directory → **project scope** (unchanged) → `<root>/.claude/agents/<name>.md` + `<root>/.github/agents/<name>.agent.md`
|
||||
- `<root>` is exactly `~` (checked directly, no walk-up) → **user scope** (unchanged) → `~/.claude/agents/<name>.md` + `~/.copilot/agents/<name>.agent.md`
|
||||
|
||||
A bare `plugin.json` with no `apm.yml` no longer signals plugin scope — that path is fully replaced, not dual-mode; it falls through to project scope.
|
||||
|
||||
The script is file-by-file no-op — it skips any file that already exists.
|
||||
|
||||
### Step 2 — Fill in the agent file(s)
|
||||
|
||||
**At plugin/APM scope**, there is exactly one file: `<package-root>/.apm/agents/<name>.agent.md`. Frontmatter carries ONLY `name`, `description`, optionally `model`, and optionally `source_keys` (provenance metadata, not a runtime field — see the template) — never `tools` or the other Claude-only fields listed in Gotchas (ADR-0016). Fill in `name`, `description`, `model`, and the system prompt body per the guidance below; the rest of this step's field-by-field guidance (tools, maxTurns, effort, memory, isolation, disallowedTools, skills, color, initialPrompt, background) is project/user scope only. Skip Step 3 and go to Step 4.
|
||||
|
||||
**At project/user scope**, continue below to fill in both provider files — this step covers the Claude Code file (`<name>.md`); Step 3 covers the Copilot file.
|
||||
|
||||
Open the scaffolded Claude Code file. Replace every `FILL IN:` placeholder. **Remove all template documentation comments from the YAML frontmatter after filling in required fields** — these are marked with `<!--` and `-->` and must be deleted before shipping.
|
||||
|
||||
**`name`** — lowercase letters and hyphens only. Must be unique within the scope.
|
||||
|
||||
**`description`** — the most important field for autonomous delegation:
|
||||
- Start with an action verb: "Reviews...", "Analyzes...", "Generates..."
|
||||
- If this agent should trigger without explicit user direction, include "Use proactively" in the description
|
||||
- Specific about the triggering condition and expertise domain
|
||||
- Under 300 characters preferred
|
||||
|
||||
**`tools`** (project/user scope only — never at plugin/APM scope) — restrict to what the agent actually needs. Omit to inherit all tools. Use `Agent(type1,type2)` to limit which subagent types this agent can spawn; omit `Agent` entirely to prevent spawning.
|
||||
|
||||
**Optional fields worth considering (project/user scope only — never at plugin/APM scope):**
|
||||
- `model`: set when this agent needs a different capability tier (`haiku` for fast tasks, `opus` for deep reasoning)
|
||||
- `maxTurns`: set a cap to prevent runaway agents on bounded tasks
|
||||
- `effort`: set to `low` for single-lookup tasks, `high` or above for deep reasoning or multi-file analysis — overrides session effort level; omit to inherit
|
||||
- `memory`: `user`, `project`, or `local` — only when cross-session state is genuinely needed
|
||||
- `isolation: worktree` — only when the agent modifies files and needs an isolated copy
|
||||
- `disallowedTools`: space-separated denylist applied before `tools`; supports `mcp__*` glob patterns (e.g. `disallowedTools: mcp__filesystem__*`)
|
||||
- `skills`: list of skill names preloaded at agent startup — different from the `source_keys` metadata field
|
||||
- `color`: UI color for the agent tile (`red`, `blue`, `green`, `yellow`, `purple`, `orange`, `pink`, `cyan`)
|
||||
- `initialPrompt`: auto-submitted as the first turn when this agent activates as the main session thread; only set when this agent is intended for main-thread activation
|
||||
- `background`: set `true` to force background execution
|
||||
|
||||
**`source_keys`** — top-level list of research source slugs that informed this agent. Add only when research sources were used (i.e. entries with `` `extracted` `` status are in context from a prior `/research` session). Each slug must match an H2 heading in `sources.md` — see Step 4 for where that file lives (plugin/APM scope only). Omit entirely when no research was used.
|
||||
|
||||
```yaml
|
||||
source_keys:
|
||||
- my-source-slug
|
||||
```
|
||||
|
||||
**System prompt body** — write as a direct role instruction:
|
||||
- Open with: "You are a [role]. When invoked, [primary action]."
|
||||
- Cover: inputs expected, process steps, output format, error handling
|
||||
- One job per agent
|
||||
|
||||
### Step 3 — Fill in the Copilot agent file (project/user scope only)
|
||||
|
||||
Skip this step entirely at plugin/APM scope — there is no separate Copilot file there. The single `.apm/agents/<name>.agent.md` file from Step 2 already compiles to both Claude Code and Copilot CLI via `apm compile`.
|
||||
|
||||
**Two distinct Copilot agent formats** exist, with different paths and field sets. Choose one based on the deployment target:
|
||||
|
||||
**CLI format** (default — what the scaffold creates):
|
||||
- Path: `.github/agents/<name>.agent.md` (project) or `~/.copilot/agents/<name>.agent.md` (user)
|
||||
- Extension: **must be `.agent.md`**
|
||||
- Supported fields: `name` (required), `description` (required), `tools` (optional)
|
||||
- `tools` uses Copilot aliases: `execute` (shell), `read`, `edit`, `search`, `agent`, `web`
|
||||
- Body length limit: **30,000 characters** — content beyond this is silently truncated
|
||||
|
||||
**Cloud/IDE format** (use when targeting Copilot Chat in VS Code or GitHub.com):
|
||||
- Path: `.github/copilot/agents/<name>.md` (note: plain `.md`, different directory)
|
||||
- Additional fields available: `target` (`vscode`, `github-copilot`, or omit for both), `user-invocable` (set `false` to hide from manual invocation), `disable-model-invocation` (set `true` to require explicit user invocation), `mcp-servers` (MCP server config — processed by cloud runtime, ignored in VS Code)
|
||||
- Body length limit: **30,000 characters** — silently truncated
|
||||
|
||||
**Do not include Claude Code-only fields in either format**: `maxTurns`, `isolation`, `memory`, `permissionMode`, `effort`, `hooks`, `mcpServers`.
|
||||
|
||||
**`source_keys`** — add the same top-level list as the CC file when research sources were used. Omit when no research was used.
|
||||
|
||||
**Remove all template documentation comments from the YAML frontmatter after filling in required fields** — these are marked with `<!--` and `-->` and must be deleted before shipping.
|
||||
|
||||
The system prompt body should match the Claude Code version — the agent's task definition is the same across providers.
|
||||
|
||||
### Step 4 — Populate or delete `sources.md` (plugin/APM scope only)
|
||||
|
||||
Skip at project/user scope. The file lives at the package root (alongside `apm.yml`), not inside `.apm/agents/` — otherwise tooling that scans that directory for agent definitions would treat it as an agent needing frontmatter (ADR-0010).
|
||||
|
||||
If a research `sources.md` is present in the conversation context:
|
||||
1. Filter to entries with `` `extracted` `` status only.
|
||||
2. For each entry, identify which agent file it contributed to.
|
||||
3. Write `sources.md` at the package root using the format below. Paths in `Contributing files:` are relative to the package root.
|
||||
|
||||
```markdown
|
||||
# Sources
|
||||
|
||||
## slug-name
|
||||
|
||||
- **URL:** <source URL>
|
||||
- **Research doc:** <path/to/research/sources.md relative to repo root>
|
||||
- **Description:** <what this source covers>
|
||||
- **Contributing files:** .apm/agents/<name>.agent.md
|
||||
- **Status:** `extracted`
|
||||
```
|
||||
|
||||
Each slug must match an H2 heading, and each slug must also appear in the `source_keys` list of the file listed under `Contributing files:`.
|
||||
|
||||
If no research sources are in context, delete `sources.md`.
|
||||
|
||||
### Step 5 — Validate and close
|
||||
|
||||
Run this checklist before invoking the audit:
|
||||
|
||||
**Plugin/APM scope — single file (`<name>.agent.md`):**
|
||||
- [ ] `name` field present, kebab-case, unique in scope
|
||||
- [ ] `description` field present and action-first
|
||||
- [ ] Frontmatter contains ONLY `name`, `description`, and optionally `model` (plus `source_keys` if research-sourced) — no `tools`, `isolation`, `maxTurns`, `effort`, `memory`, `permissionMode`, `disallowedTools`, `skills`, `color`, `initialPrompt`, `background`, `hooks`, or `mcpServers`
|
||||
- [ ] System prompt body present and non-empty
|
||||
- [ ] No `FILL IN:` placeholders remain
|
||||
- [ ] No `<!-- -->` template comments remain in frontmatter
|
||||
|
||||
**Project/user scope — Claude Code file (`<name>.md`):**
|
||||
- [ ] `name` field present, kebab-case, unique in scope
|
||||
- [ ] `description` field present and action-first
|
||||
- [ ] System prompt body present and non-empty
|
||||
- [ ] No `FILL IN:` placeholders remain
|
||||
- [ ] No `<!-- -->` template comments remain in frontmatter
|
||||
|
||||
**Project/user scope — Copilot CLI file (`<name>.agent.md`):**
|
||||
- [ ] File extension is `.agent.md` (not `.md`)
|
||||
- [ ] `name` field matches the filename stem (e.g. `name: my-agent` in `my-agent.agent.md`)
|
||||
- [ ] `description` field present
|
||||
- [ ] No Claude Code-only fields (`maxTurns`, `isolation`, `memory`, `permissionMode`, `effort`, `hooks`, `mcpServers`)
|
||||
- [ ] System prompt body present and non-empty
|
||||
- [ ] Body does not exceed 30,000 characters
|
||||
- [ ] No `<!-- -->` template comments remain in frontmatter
|
||||
|
||||
At plugin/APM scope, apply a **minor bump** to the resolved package's `apm.yml` `version` (single manifest, e.g. `1.0.4` → `1.1.0`).
|
||||
|
||||
Invoke `kyberforge:agent-audit` on the created file(s) before closing — validates the pair at project/user scope, the single file at plugin/APM scope.
|
||||
|
||||
**Commit verification.** Capture `git log --oneline -1` before Step 1 and keep it. Once the audit is clean, run `git add` and `git commit` for the new agent files — do not stop at staging. Then run `git log --oneline -1` again and confirm the hash changed from the one you captured at the start. A non-empty `git diff --stat` is not sufficient proof of completion: staged-but-uncommitted work isn't part of any commit and can be silently lost if the working tree is cleaned up before a commit lands. Only report the agent as done once the hash has actually changed.
|
||||
|
||||
## Improving an existing agent
|
||||
|
||||
### Step 1 — Verify inputs
|
||||
|
||||
Confirm the agent files exist and at least one improvement signal is present in the conversation or a referenced file.
|
||||
|
||||
If no signals: "This skill applies existing signals to an agent. For a blind review, examine the files manually or run a grill session first."
|
||||
|
||||
Verify `kyberforge:agent-audit` is available — it ships with the kyberforge plugin and is co-installed with this skill. If unavailable, stop and tell the user to install the kyberforge plugin before continuing.
|
||||
|
||||
Capture `git log --oneline -1` now, before making any edits — Step 5 needs it to verify a real commit landed.
|
||||
|
||||
**Partial state (project/user scope only)** — if one provider file exists but not the other, scaffold the missing one (`bash scripts/new-agent.sh <name> <root>`, file-by-file no-op) then continue. Doesn't apply at plugin/APM scope — single file, no partial-pair state.
|
||||
|
||||
### Step 2 — Gather and group signals
|
||||
|
||||
Read the current agent file(s). Collect all signals from the conversation.
|
||||
|
||||
Group by **root cause**, not symptom. One root cause → one fix.
|
||||
|
||||
```text
|
||||
Example:
|
||||
- User feedback: agent keeps trying to push to remote
|
||||
- Session context: no scope boundary in system prompt
|
||||
→ Root cause: system prompt lacks git scope constraint → fix: add explicit boundary
|
||||
```
|
||||
|
||||
### Step 3 — Announce planned changes
|
||||
|
||||
Before editing, state which root causes were identified, what evidence supports each, and which files will change. Then proceed — edits are reversible via git.
|
||||
|
||||
### Step 4 — Apply changes
|
||||
|
||||
Edit any file the signals point to. Generalize the fix — find the underlying gap, not the specific example that failed. For every sentence you add, ask: "Would the agent get this wrong without it?" A shorter, focused definition consistently outperforms an exhaustive one. For Copilot files, verify no Claude Code-only fields are introduced. For a plugin/APM-scope single file, verify no field beyond `name`, `description`, `model`, and `source_keys` is introduced.
|
||||
|
||||
If the edit adds or removes research-sourced content, update `source_keys` in the edited file(s) and the corresponding entry in `sources.md` per Create flow's Step 4.
|
||||
|
||||
### Step 5 — Validate and close
|
||||
|
||||
Re-run the validation checklist from the create flow's Step 5 on any edited file.
|
||||
|
||||
At plugin/APM scope, apply a **patch bump** to the resolved package's `apm.yml` `version` (e.g. `1.0.4` → `1.0.5`).
|
||||
|
||||
Invoke `kyberforge:agent-audit` on the edited file(s) to confirm no regressions — the pair at project/user scope, the single file at plugin/APM scope.
|
||||
|
||||
**Commit verification.** Capture `git log --oneline -1` at the start of Step 1 and keep it. Once the audit is clean, run `git add` and `git commit` for the changed files — do not stop at staging. Then run `git log --oneline -1` again and confirm the hash changed from the one you captured at the start. A non-empty `git diff --stat` is not sufficient proof of completion: staged-but-uncommitted work isn't part of any commit and can be silently lost if the working tree is cleaned up before a commit lands. Only report the improvement as done once the hash has actually changed.
|
||||
@@ -1,9 +0,0 @@
|
||||
# assets/
|
||||
|
||||
## templates/
|
||||
|
||||
Annotated agent definition templates copied by `scripts/new-agent.sh` when scaffolding a new agent.
|
||||
|
||||
- **`claude-code.md`** — Claude Code agent definition template (project/user scope). Includes all supported frontmatter fields (required and optional) with inline guidance comments and `FILL IN:` placeholders.
|
||||
- **`copilot.agent.md`** — Copilot CLI agent definition template (CLI format, project/user scope). Excludes cloud/IDE-only fields (`target`, `user-invocable`, `disable-model-invocation`, `mcp-servers`) and Claude Code-only fields. Uses Copilot tool aliases (`execute`, `read`, `edit`, `search`, `agent`, `web`).
|
||||
- **`apm-agent.md`** — Vendor-neutral APM agent definition template (plugin/APM scope). Only `name`, `description`, optional `model`, and optional `source_keys` (provenance metadata, not a runtime field) in frontmatter — no `tools` and no Claude-only fields, since `apm compile` copies frontmatter verbatim to both the Claude Code and Copilot CLI targets with no per-target integrator (ADR-0016).
|
||||
@@ -1,54 +0,0 @@
|
||||
---
|
||||
<!-- Vendor-neutral APM agent definition (plugin/APM scope).
|
||||
Path: <package-root>/.apm/agents/<name>.agent.md — one file, no counterpart.
|
||||
`apm compile` copies this frontmatter verbatim to BOTH the Claude Code and
|
||||
Copilot CLI targets — there is no per-target field integrator. Claude's
|
||||
`tools:` (space-separated string) and Copilot's `tools:` (alias list) are
|
||||
incompatible vocabularies, and Claude-only fields (isolation, maxTurns,
|
||||
effort, memory, permissionMode) have no Copilot equivalent. A value correct
|
||||
for one harness is guaranteed wrong on the other, so this scope carries
|
||||
ONLY the fields below — full stop (see ADR-0016). `source_keys` is
|
||||
provenance metadata, not a runtime field, and is exempt from that rule.
|
||||
|
||||
Do NOT add: tools, isolation, maxTurns, effort, memory, permissionMode,
|
||||
disallowedTools, skills, color, initialPrompt, background, hooks, or
|
||||
mcpServers. Omitting `tools` means inherit-all-tools on both harnesses,
|
||||
which is never wrong.
|
||||
|
||||
Fill in all FILL IN: placeholders. Delete template comments before shipping. -->
|
||||
|
||||
name: AGENT_NAME
|
||||
<!-- Required. Lowercase letters and hyphens only. Must be unique within the scope. -->
|
||||
|
||||
description: FILL IN: Action-first description of what this agent does and when to invoke it.
|
||||
<!-- Required. The primary signal for autonomous delegation.
|
||||
Start with a verb: "Reviews...", "Analyzes...", "Generates..."
|
||||
Be specific about the triggering condition and expertise domain.
|
||||
Example: "Reviews pull request diffs for security issues. Use proactively after code changes." -->
|
||||
|
||||
<!-- model: sonnet
|
||||
Optional. Aliases: sonnet, opus, haiku, fable. Or full model ID.
|
||||
Omit to inherit the runtime default on whichever harness compiles this file. -->
|
||||
|
||||
<!-- source_keys:
|
||||
- slug-name
|
||||
Development-only. Add when research sources informed this agent (slugs must match
|
||||
sources.md at the package root — same directory as apm.yml).
|
||||
Omit when no research was used. Not a runtime field — silently ignored by both harnesses. -->
|
||||
---
|
||||
|
||||
FILL IN: System prompt body. Write as a direct role instruction.
|
||||
|
||||
You are a FILL IN: role description. When invoked, FILL IN: primary action.
|
||||
|
||||
## Inputs
|
||||
|
||||
FILL IN: What inputs does this agent expect? (files, context, parameters)
|
||||
|
||||
## Process
|
||||
|
||||
FILL IN: Steps the agent takes. Be specific about ordering if it matters.
|
||||
|
||||
## Output
|
||||
|
||||
FILL IN: What does the agent produce? Format, location, structure.
|
||||
@@ -1,86 +0,0 @@
|
||||
---
|
||||
<!-- Claude Code agent definition
|
||||
Fill in all FILL IN: placeholders. Remove or uncomment optional fields as needed.
|
||||
Delete template comments before shipping. -->
|
||||
|
||||
name: AGENT_NAME
|
||||
<!-- Required. Lowercase letters and hyphens only. Must be unique within the scope.
|
||||
Duplicate names are silently discarded — no warning is emitted. -->
|
||||
|
||||
description: FILL IN: Action-first description of what this agent does and when to invoke it.
|
||||
<!-- Required. The primary signal for autonomous delegation.
|
||||
Start with a verb: "Reviews...", "Analyzes...", "Generates..."
|
||||
Include "Use proactively" to trigger automatic invocation.
|
||||
Be specific about the triggering condition and domain.
|
||||
Example: "Reviews pull request diffs for security issues. Use proactively after code changes." -->
|
||||
|
||||
<!-- tools: Read Bash Grep
|
||||
Optional. Space-separated allowlist. Omit to inherit all tools from parent.
|
||||
Use Agent(type1,type2) to restrict which subagent types this agent can spawn.
|
||||
Omit Agent entirely to prevent this agent from spawning subagents.
|
||||
Never available to subagents regardless of tools field:
|
||||
AskUserQuestion, EnterPlanMode, ExitPlanMode, ScheduleWakeup, WaitForMcpServers
|
||||
Exception: ExitPlanMode IS available when parent session runs in permissionMode: plan -->
|
||||
|
||||
<!-- model: sonnet
|
||||
Optional. Aliases: sonnet, opus, haiku, fable. Or full model ID.
|
||||
Omit to inherit from the main session.
|
||||
Resolution order: CLAUDE_CODE_SUBAGENT_MODEL env var → per-invocation param → this field → session model. -->
|
||||
|
||||
<!-- effort: medium
|
||||
Optional. low / medium / high / xhigh / max. Overrides session effort level for this agent. -->
|
||||
|
||||
<!-- maxTurns: 20
|
||||
Optional. Integer cap on agentic turns. Prevents runaway on bounded tasks. -->
|
||||
|
||||
<!-- memory: project
|
||||
Optional. user / project / local. Enables cross-session MEMORY.md (first 200 lines loaded at startup).
|
||||
Auto-enables Read/Write/Edit tools. -->
|
||||
|
||||
<!-- isolation: worktree
|
||||
Optional. Set to "worktree" to run in an isolated temporary git worktree.
|
||||
Auto-cleaned if no changes are made. -->
|
||||
|
||||
<!-- color: blue
|
||||
Optional. UI color: red, blue, green, yellow, purple, orange, pink, cyan. -->
|
||||
|
||||
<!-- background: false
|
||||
Optional. Set true to force background execution. -->
|
||||
|
||||
<!-- disallowedTools: mcp__filesystem__write_file
|
||||
Optional. Space-separated denylist, applied before the tools allowlist.
|
||||
Supports mcp__* glob patterns (e.g. mcp__filesystem__* to block all filesystem tools). -->
|
||||
|
||||
<!-- skills:
|
||||
- skill-name
|
||||
Optional. Skill names preloaded into this agent's context at startup.
|
||||
Different from the source_keys metadata field (which is provenance-only). -->
|
||||
|
||||
<!-- initialPrompt: "Start by reading the README."
|
||||
Optional. Auto-submitted as the first turn when this agent activates as the main session thread.
|
||||
Only set when this agent is intended for main-thread (not subagent) activation. -->
|
||||
|
||||
<!-- source_keys:
|
||||
- slug-name
|
||||
Development-only. Add when research sources informed this agent (slugs must match sources.md at the plugin root).
|
||||
Omit when no research was used. Not a runtime field — silently ignored by Claude Code. -->
|
||||
|
||||
<!-- NOTE: hooks, mcpServers, and permissionMode are silently ignored for plugin agents.
|
||||
Those fields only work in .claude/agents/ or ~/.claude/agents/. -->
|
||||
---
|
||||
|
||||
FILL IN: System prompt body. Write as a direct role instruction.
|
||||
|
||||
You are a FILL IN: role description. When invoked, FILL IN: primary action.
|
||||
|
||||
## Inputs
|
||||
|
||||
FILL IN: What inputs does this agent expect? (files, context, parameters)
|
||||
|
||||
## Process
|
||||
|
||||
FILL IN: Steps the agent takes. Be specific about ordering if it matters.
|
||||
|
||||
## Output
|
||||
|
||||
FILL IN: What does the agent produce? Format, location, structure.
|
||||
@@ -1,59 +0,0 @@
|
||||
---
|
||||
<!-- GitHub Copilot CLI agent definition (CLI format — path: .github/agents/<name>.agent.md)
|
||||
File extension MUST be .agent.md — a plain .md file is not picked up by Copilot CLI.
|
||||
Fill in all FILL IN: placeholders. Remove or uncomment optional fields as needed.
|
||||
Body length limit: 30,000 characters — content beyond this is silently truncated.
|
||||
Delete template comments before shipping.
|
||||
|
||||
NOTE: This template is for the CLI format. The cloud/IDE format (path: .github/copilot/agents/<name>.md,
|
||||
extension: .md) supports additional fields: target, user-invocable, disable-model-invocation, mcp-servers.
|
||||
Do not add those fields here — they are silently ignored by the CLI runtime. -->
|
||||
|
||||
name: AGENT_NAME
|
||||
<!-- Required. Kebab-case identifier. Home-directory version wins on name collision. -->
|
||||
|
||||
description: FILL IN: Action-first description of what this agent does and when to invoke it.
|
||||
<!-- Required. Used by the runtime for automatic agent selection — quality matters.
|
||||
Start with a verb: "Reviews...", "Analyzes...", "Generates..."
|
||||
Example: "Reviews pull request diffs for security issues." -->
|
||||
|
||||
<!-- tools: ["read", "search", "edit"]
|
||||
Optional. Array of tool names. Omit = all available tools. [] = no tools.
|
||||
Copilot tool aliases (use these, not Claude Code names):
|
||||
execute — run shell commands (aliases: shell, Bash, powershell)
|
||||
read — read file contents (aliases: Read, NotebookRead)
|
||||
edit — modify files (aliases: Edit, MultiEdit, Write, NotebookEdit)
|
||||
search — search files (aliases: Grep, Glob)
|
||||
agent — invoke sub-agents (aliases: custom-agent, Task)
|
||||
web — web search and fetch (aliases: WebSearch, WebFetch)
|
||||
For MCP tools: "server-name/tool-name" or "server-name/*" -->
|
||||
|
||||
<!-- model: claude-sonnet-4-5
|
||||
Optional. Model to run this agent on.
|
||||
Cloud/IDE-only fields (target, user-invocable, disable-model-invocation, mcp-servers)
|
||||
are not valid in this CLI format — use the .github/copilot/agents/<name>.md path for those. -->
|
||||
|
||||
<!-- source_keys:
|
||||
- slug-name
|
||||
Development-only. Add when research sources informed this agent (slugs must match sources.md at the plugin root).
|
||||
Omit when no research was used. Not a Copilot runtime field — silently ignored. -->
|
||||
|
||||
<!-- DO NOT include these Claude Code-only fields:
|
||||
maxTurns, isolation, memory, permissionMode, effort, hooks, mcpServers -->
|
||||
---
|
||||
|
||||
FILL IN: System prompt body. Should match the Claude Code version — the agent's task is the same across providers.
|
||||
|
||||
You are a FILL IN: role description. When invoked, FILL IN: primary action.
|
||||
|
||||
## Inputs
|
||||
|
||||
FILL IN: What inputs does this agent expect? (files, context, parameters)
|
||||
|
||||
## Process
|
||||
|
||||
FILL IN: Steps the agent takes. Be specific about ordering if it matters.
|
||||
|
||||
## Output
|
||||
|
||||
FILL IN: What does the agent produce? Format, location, structure.
|
||||
@@ -1,17 +0,0 @@
|
||||
---
|
||||
source_keys: []
|
||||
---
|
||||
|
||||
# references/
|
||||
|
||||
## deployment-modes.md
|
||||
|
||||
Agent scope hierarchy, precedence rules, and per-scope restrictions. Covers: which fields are silently ignored for plugin agents (Claude Code and Copilot CLI), scoped identifiers for plugin subdirectory agents, cache isolation behaviour, and Copilot CLI path conventions. Loaded conditionally from SKILL.md when the destination is a plugin directory.
|
||||
|
||||
## scripts.md
|
||||
|
||||
Conventions for the `new-agent.sh` scaffold script and any future scripts added to this skill. Covers: what scripts should and should not do, file placement, error handling, template variable conventions, and the no-interactive-prompts rule.
|
||||
|
||||
## sources.md
|
||||
|
||||
Research provenance record for this skill. Lists the upstream research sources (claude-code-plugins and github-copilot-plugins research docs) that informed SKILL.md, the templates, and the deployment-modes reference. Used by `skill-audit` to validate the provenance chain.
|
||||
@@ -1,57 +0,0 @@
|
||||
---
|
||||
source_keys:
|
||||
- context7-websites-code-claude
|
||||
- claude-code-plugins-docs
|
||||
- claude-code-subagents-docs
|
||||
- context7-github-en-copilot
|
||||
- github-custom-agents-configuration
|
||||
- github-cli-plugin-reference
|
||||
---
|
||||
|
||||
# Agent Deployment Modes
|
||||
|
||||
Agent definitions deploy at three scopes and behave differently at each. The scope determines which fields are honoured, where files must live, and what identifiers users invoke.
|
||||
|
||||
## Scope hierarchy and precedence
|
||||
|
||||
| Scope | Claude Code path | Copilot CLI path | Who it affects |
|
||||
|---|---|---|---|
|
||||
| User | `~/.claude/agents/` | `~/.copilot/agents/` | All sessions for this user |
|
||||
| Project | `.claude/agents/` | `.github/agents/` or `.copilot/agents/` | This repo only |
|
||||
| Plugin/APM | `<package-root>/.apm/agents/<name>.agent.md` — single vendor-neutral file, `apm compile` emits it to both targets | *(same file)* | Sessions with the plugin/package installed |
|
||||
|
||||
When the same agent `name` appears at multiple scopes, **user scope wins over project scope wins over plugin scope** in Claude Code. In Copilot CLI, repo-level agents override enterprise and org-level; home-directory (user) agents override repo-level on name collision.
|
||||
|
||||
## Plugin scope restrictions
|
||||
|
||||
Plugin/APM agents (`.apm/agents/<name>.agent.md`) carry only `name`, `description`, optionally `model`, and optionally `source_keys` (provenance metadata, not a runtime field — silently ignored by both harnesses) in frontmatter — full stop (see ADR-0016). `apm compile` copies this frontmatter verbatim to both the Claude Code and Copilot CLI compile targets with no per-target integrator: Claude's `tools:` (space-separated string) and Copilot's `tools:` (alias list) are incompatible vocabularies, and Claude-only fields have no Copilot equivalent, so any harness-specific value is guaranteed wrong on at least one target.
|
||||
|
||||
This makes the old "silently ignored at plugin scope" framing moot. It's not that `hooks`, `mcpServers`, `permissionMode`, `tools`, `isolation`, `maxTurns`, `effort`, `memory`, `disallowedTools`, `skills`, `color`, `initialPrompt`, or `background` are merely ignored at this scope — they are never written to the file at all. Copy the agent to `.claude/agents/` (project scope) or `~/.claude/agents/` (user scope) to use any of them.
|
||||
|
||||
## Scoped identifiers (Claude Code plugin agents only)
|
||||
|
||||
Plugin agents in **subdirectories** get compound identifiers:
|
||||
|
||||
```
|
||||
plugins/my-plugin/agents/review/security.md → my-plugin:review:security
|
||||
```
|
||||
|
||||
Users must invoke with `@agent-my-plugin:review:security`. **Keep agents flat in `agents/` to avoid this** — subdirectory nesting is rarely worth the UX cost.
|
||||
|
||||
At project and user scope, subdirectory path does not affect the agent's name.
|
||||
|
||||
## Cache isolation
|
||||
|
||||
When a plugin is installed, its directory is copied to a cache. **Any path that leaves the agent's plugin directory breaks post-install.** Agent definition files must be self-contained — they cannot reference scripts, templates, or shared files outside the plugin.
|
||||
|
||||
Agents at project or user scope are read directly from disk; cache isolation does not apply.
|
||||
|
||||
## Copilot CLI path conventions
|
||||
|
||||
| Scope | Expected path | Notes |
|
||||
|---|---|---|
|
||||
| User | `~/.copilot/agents/<name>.agent.md` | Home directory |
|
||||
| Project | `.github/agents/<name>.agent.md` | Standard; also `.copilot/agents/` |
|
||||
| Plugin/APM | `<package-root>/.apm/agents/<name>.agent.md` | Not a Copilot-only file — this is the single vendor-neutral source `apm compile` reads for the Copilot CLI target |
|
||||
|
||||
The `.agent.md` extension is **mandatory** for real Copilot CLI files (project/user scope) — Copilot CLI does not pick up plain `.md` files in the `agents/` directory. The plugin/APM source file also uses `.agent.md` by convention, since it compiles to Copilot CLI too, but it is not itself a Copilot file.
|
||||
@@ -1,42 +0,0 @@
|
||||
---
|
||||
source_keys: []
|
||||
---
|
||||
|
||||
# Scripts Reference
|
||||
|
||||
Conventions for `scripts/new-agent.sh` and any future scripts in this skill.
|
||||
|
||||
## Contract
|
||||
|
||||
All scripts in this skill must follow these rules:
|
||||
|
||||
- **No interactive prompts** — agents run non-interactive; blocking on TTY input hangs indefinitely. Accept all input via positional arguments, flags, or environment variables.
|
||||
- **Structured output** — file paths and status messages to stderr; nothing to stdout unless a downstream tool needs to consume it.
|
||||
- **Idempotent** — "create if not exists" per file. The scaffold script skips any file that already exists; agents may safely re-run it.
|
||||
- **Meaningful exit codes** — `0` success, `1` invalid arguments or precondition failure. Document in `--help`.
|
||||
- **Self-contained** — no external package installs at runtime. The script uses only bash builtins and POSIX tools (`sed`, `mkdir`, `cat`).
|
||||
|
||||
## Template variables
|
||||
|
||||
The scaffold script uses `sed "s/AGENT_NAME/$AGENT_NAME/g"` to substitute the agent name into templates. Template files must use `AGENT_NAME` (all caps, no delimiters) as the substitution token.
|
||||
|
||||
Do not add additional substitution tokens unless you update both the template files and the script in the same edit pass.
|
||||
|
||||
## File placement
|
||||
|
||||
The script creates files at paths determined by scope detection (plugin/APM / project / user). Scope is resolved by walking up from the root directory: a `type:`-bearing `apm.yml` at or above the root marks the package root (plugin/APM scope, single file); an `apm.yml` without a `type:` field is a marketplace-only manifest and is skipped, the walk continues upward. If no such `apm.yml` is found, the root resolving to exactly `$HOME` is user scope; anything else is project scope. If scope detection logic changes, update the `new-agent.sh` usage comment and `SKILL.md` Step 1 scope detection description in the same pass.
|
||||
|
||||
## Error messages
|
||||
|
||||
On failure, state: what went wrong, what was expected, what to try. Example:
|
||||
```
|
||||
Error: agent-name must use lowercase letters, numbers, and hyphens only.
|
||||
No leading, trailing, or consecutive hyphens.
|
||||
Received: 'My_Agent'
|
||||
```
|
||||
|
||||
Vague errors leave agents unable to self-correct.
|
||||
|
||||
## --help output
|
||||
|
||||
Keep `--help` concise — it may enter the agent's context window. Include: usage line, argument descriptions with scope detection table, exit codes. Omit prose explanations.
|
||||
@@ -1,95 +0,0 @@
|
||||
---
|
||||
source_keys:
|
||||
- context7-websites-code-claude
|
||||
- claude-code-plugins-docs
|
||||
- claude-code-subagents-docs
|
||||
- context7-github-en-copilot
|
||||
- github-custom-agents-configuration
|
||||
- github-cli-plugin-reference
|
||||
- github-plugins-creating
|
||||
- github-plugins-finding-installing
|
||||
- github-plugins-marketplace
|
||||
- github-sdk-custom-agents
|
||||
---
|
||||
|
||||
# Sources
|
||||
|
||||
## context7-websites-code-claude
|
||||
|
||||
- **URL:** context7:/websites/code_claude
|
||||
- **Research doc:** plugins/kyberforge/docs/research/docs/claude-code-plugins/sources.md
|
||||
- **Description:** Official Claude Code documentation site indexed by Context7 — plugin manifest schema, subagent definition types, marketplace JSON format, agent markdown file format
|
||||
- **Contributing files:** SKILL.md, references/deployment-modes.md
|
||||
- **Status:** `extracted`
|
||||
|
||||
## claude-code-plugins-docs
|
||||
|
||||
- **URL:** https://code.claude.com/docs/en/plugins
|
||||
- **Research doc:** plugins/kyberforge/docs/research/docs/claude-code-plugins/sources.md
|
||||
- **Description:** Official Claude Code plugin authoring guide — plugin structure, manifest fields, loading methods, skill namespacing, agent activation, marketplace submission
|
||||
- **Contributing files:** SKILL.md, references/deployment-modes.md
|
||||
- **Status:** `extracted`
|
||||
|
||||
## claude-code-subagents-docs
|
||||
|
||||
- **URL:** https://code.claude.com/docs/en/sub-agents
|
||||
- **Research doc:** plugins/kyberforge/docs/research/docs/claude-code-plugins/sources.md
|
||||
- **Description:** Official Claude Code subagent reference — definition format, all frontmatter fields, scope priority, built-in agents, CLI flags, environment variables, known limitations
|
||||
- **Contributing files:** SKILL.md, references/deployment-modes.md
|
||||
- **Status:** `extracted`
|
||||
|
||||
## context7-github-en-copilot
|
||||
|
||||
- **URL:** context7:/websites/github_en_copilot
|
||||
- **Research doc:** plugins/kyberforge/docs/research/docs/github-copilot-plugins/sources.md
|
||||
- **Description:** Official GitHub Copilot documentation indexed by Context7; covers CLI plugins, custom agents, SDK, and marketplace
|
||||
- **Contributing files:** SKILL.md, references/deployment-modes.md
|
||||
- **Status:** `extracted`
|
||||
|
||||
## github-custom-agents-configuration
|
||||
|
||||
- **URL:** https://docs.github.com/en/copilot/reference/custom-agents-configuration
|
||||
- **Research doc:** plugins/kyberforge/docs/research/docs/github-copilot-plugins/sources.md
|
||||
- **Description:** Reference for cloud and IDE custom agent definition format — frontmatter fields, tool aliases, MCP server config, secrets interpolation, scoping hierarchy
|
||||
- **Contributing files:** SKILL.md, references/deployment-modes.md
|
||||
- **Status:** `extracted`
|
||||
|
||||
## github-cli-plugin-reference
|
||||
|
||||
- **URL:** https://docs.github.com/en/copilot/reference/copilot-cli-reference/cli-plugin-reference
|
||||
- **Research doc:** plugins/kyberforge/docs/research/docs/github-copilot-plugins/sources.md
|
||||
- **Description:** Full CLI plugin reference — plugin.json schema, marketplace.json schema, all CLI commands and flags, install specification formats, loading precedence, env vars, LSP config
|
||||
- **Contributing files:** SKILL.md, references/deployment-modes.md
|
||||
- **Status:** `extracted`
|
||||
|
||||
## github-plugins-creating
|
||||
|
||||
- **URL:** https://docs.github.com/en/copilot/how-tos/copilot-cli/customize-copilot/plugins-creating
|
||||
- **Research doc:** plugins/kyberforge/docs/research/docs/github-copilot-plugins/sources.md
|
||||
- **Description:** How-to for creating Copilot CLI plugins — plugin structure, agent and skill authoring, hooks format, MCP config, development lifecycle
|
||||
- **Contributing files:** SKILL.md
|
||||
- **Status:** `extracted`
|
||||
|
||||
## github-plugins-finding-installing
|
||||
|
||||
- **URL:** https://docs.github.com/en/copilot/how-tos/copilot-cli/customize-copilot/plugins-finding-installing
|
||||
- **Research doc:** plugins/kyberforge/docs/research/docs/github-copilot-plugins/sources.md
|
||||
- **Description:** User-facing guide to discovering and installing CLI plugins — marketplace browsing commands, install/update/uninstall workflow
|
||||
- **Contributing files:** (none)
|
||||
- **Status:** `extracted`
|
||||
|
||||
## github-plugins-marketplace
|
||||
|
||||
- **URL:** https://docs.github.com/en/copilot/how-tos/copilot-cli/customize-copilot/plugins-marketplace
|
||||
- **Research doc:** plugins/kyberforge/docs/research/docs/github-copilot-plugins/sources.md
|
||||
- **Description:** How-to for creating and publishing a plugin marketplace — marketplace.json structure, hosting options, registration commands
|
||||
- **Contributing files:** (none)
|
||||
- **Status:** `extracted`
|
||||
|
||||
## github-sdk-custom-agents
|
||||
|
||||
- **URL:** https://docs.github.com/en/copilot/how-tos/copilot-sdk/features/custom-agents
|
||||
- **Research doc:** plugins/kyberforge/docs/research/docs/github-copilot-plugins/sources.md
|
||||
- **Description:** SDK custom agent API — CustomAgentConfig fields in all five languages, session config, sub-agent lifecycle events, tool scoping, permission handling
|
||||
- **Contributing files:** (none)
|
||||
- **Status:** `extracted`
|
||||
@@ -1,13 +0,0 @@
|
||||
# scripts/
|
||||
|
||||
## new-agent.sh
|
||||
|
||||
Scaffolds agent definition file(s) from a single root directory input.
|
||||
|
||||
```
|
||||
Usage: new-agent.sh <agent-name> <root>
|
||||
```
|
||||
|
||||
Resolves scope by walking up from `<root>`: a `type:`-bearing `apm.yml` found at or above `<root>` → plugin/APM scope (single file at `<package-root>/.apm/agents/<name>.agent.md`; an `apm.yml` without `type:` is a marketplace-only manifest and is skipped); `<root>` exactly `~` → user scope (`~/.claude/agents/` + `~/.copilot/agents/`); otherwise project scope (`<root>/.claude/agents/` + `<root>/.github/agents/`). Each file is a no-op if it already exists. See `--help` for full usage.
|
||||
|
||||
Tests: `tests/new-agent.bats` (requires `bats-support` and `bats-assert`).
|
||||
@@ -1,277 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
SKILL_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
SKILL_ROOT="$(cd "$SKILL_DIR/.." && pwd)"
|
||||
TEMPLATES_DIR="$SKILL_ROOT/assets/templates"
|
||||
|
||||
usage() {
|
||||
cat <<EOF
|
||||
Usage: new-agent.sh <agent-name> <root>
|
||||
|
||||
Scaffold agent definition file(s) for Claude Code, GitHub Copilot CLI, and/or
|
||||
vendor-neutral APM packages.
|
||||
|
||||
Arguments:
|
||||
agent-name Kebab-case agent identifier (e.g. code-reviewer, deploy-assistant).
|
||||
root Starting directory — scope is resolved by walking up from here:
|
||||
plugin/APM scope : nearest ancestor (at/above root) whose apm.yml
|
||||
has a top-level type: field (instructions,
|
||||
skill, hybrid, or prompts) — an apm.yml
|
||||
without type: is a marketplace-only manifest
|
||||
and is skipped, the walk continues upward
|
||||
→ creates <package-root>/.apm/agents/<name>.agent.md
|
||||
(single vendor-neutral file — no tools,
|
||||
isolation, maxTurns, effort, memory, or
|
||||
permissionMode; apm compile has no per-target
|
||||
field integrator, see ADR-0016)
|
||||
→ creates <package-root>/sources.md (if absent)
|
||||
project scope : no type:-bearing apm.yml found; root is a
|
||||
project directory
|
||||
→ creates <root>/.claude/agents/<name>.md
|
||||
→ creates <root>/.github/agents/<name>.agent.md
|
||||
user scope : root is exactly ~ (home directory; checked
|
||||
directly, no walk-up)
|
||||
→ creates ~/.claude/agents/<name>.md
|
||||
→ creates ~/.copilot/agents/<name>.agent.md
|
||||
|
||||
Each file is created only if it does not already exist (no-op per file).
|
||||
|
||||
Exit codes:
|
||||
0 Files created or already existed (no-op)
|
||||
1 Invalid arguments, missing root, or templates not found
|
||||
EOF
|
||||
}
|
||||
|
||||
if [[ "${1:-}" == "--help" || "${1:-}" == "-h" ]]; then
|
||||
usage
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [[ $# -lt 2 ]]; then
|
||||
echo "Error: agent-name and root are required." >&2
|
||||
echo "" >&2
|
||||
usage >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
AGENT_NAME="$1"
|
||||
ROOT="$2"
|
||||
|
||||
# Validate agent name format
|
||||
if ! echo "$AGENT_NAME" | grep -qE '^[a-z0-9]+(-[a-z0-9]+)*$'; then
|
||||
echo "Error: agent-name must use lowercase letters, numbers, and hyphens only." >&2
|
||||
echo " No leading, trailing, or consecutive hyphens." >&2
|
||||
echo " Received: '$AGENT_NAME'" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Validate templates directory
|
||||
if [[ ! -d "$TEMPLATES_DIR" ]]; then
|
||||
echo "Error: templates directory not found at '$TEMPLATES_DIR'." >&2
|
||||
echo " Run this script from its original location inside the agent-author skill." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Expand tilde
|
||||
ROOT="${ROOT/#\~/$HOME}"
|
||||
|
||||
# Validate root exists
|
||||
if [[ ! -d "$ROOT" ]]; then
|
||||
echo "Error: root directory '$ROOT' does not exist." >&2
|
||||
exit 1
|
||||
fi
|
||||
ROOT="$(cd "$ROOT" && pwd)"
|
||||
|
||||
# True if apm_yml's top-level `type:` line names one of the four APM package
|
||||
# types (instructions/skill/hybrid/prompts) — mirrors validate.sh's
|
||||
# APM_TYPE_RE: an optional quote around the value must be closed by the
|
||||
# *same* quote character (a mismatched or unterminated quote is rejected,
|
||||
# not silently stripped), and the value must be followed by whitespace or
|
||||
# end-of-line so `prompts-only` doesn't false-match on the `prompts` prefix.
|
||||
# `|| [[ -n "$line" ]]` in the read condition also processes a final line
|
||||
# that lacks a trailing newline, which `read` alone would otherwise skip.
|
||||
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
|
||||
}
|
||||
|
||||
# --- Walk-up package-root detection ---
|
||||
#
|
||||
# Mirrors agent-audit's validate.sh scope walk-up, with apm.yml + type: swapped
|
||||
# in for the old plugin.json marker. Starting at ROOT, walk upward:
|
||||
# - an apm.yml with a top-level `type:` field marks an APM package root
|
||||
# (plugin/APM scope) — stop and return it.
|
||||
# - an apm.yml with no `type:` field is a marketplace-only manifest — skip
|
||||
# it, keep walking up.
|
||||
# - user scope is checked directly at $HOME, no walk-up (see usage text
|
||||
# above): ROOT itself being $HOME resolves to user scope, even if $HOME
|
||||
# is itself a .git-tracked dotfiles directory (checked before the .git
|
||||
# test below, so a dotfiles repo at $HOME can't shadow user scope).
|
||||
# Walking *up into* $HOME from a nested directory with no apm.yml/.git
|
||||
# of its own does NOT promote to user scope — it resolves to project
|
||||
# scope instead, same as any other unmatched boundary, so a stray
|
||||
# directory under $HOME can't be silently redirected into the shared
|
||||
# global ~/.claude or ~/.copilot agent directories.
|
||||
# - a .git file or directory marks the project-scope boundary (a worktree's
|
||||
# .git is a file, not a directory) — stop.
|
||||
# - filesystem root reached with neither found — project scope, same as
|
||||
# any other unmatched boundary.
|
||||
find_package_root() {
|
||||
local root="$1" current="$1"
|
||||
while true; do
|
||||
if [[ -f "$current/apm.yml" ]] && is_apm_package_manifest "$current/apm.yml"; then
|
||||
echo "plugin $current"
|
||||
return
|
||||
fi
|
||||
if [[ "$current" == "$HOME" ]]; then
|
||||
if [[ "$current" == "$root" ]]; then
|
||||
echo "user $current"
|
||||
return
|
||||
fi
|
||||
echo "project $current"
|
||||
return
|
||||
fi
|
||||
if [[ -e "$current/.git" ]]; then
|
||||
echo "project $current"
|
||||
return
|
||||
fi
|
||||
local parent
|
||||
parent="$(dirname "$current")"
|
||||
if [[ "$parent" == "$current" ]]; then
|
||||
echo "project $current"
|
||||
return
|
||||
fi
|
||||
current="$parent"
|
||||
done
|
||||
}
|
||||
|
||||
# `read` consumes a single line, so kind and path are emitted on one
|
||||
# space-separated line rather than two `echo`s — kind first (never contains
|
||||
# spaces), path last (absorbs any spaces in the path safely).
|
||||
WALK_RESULT="$(find_package_root "$ROOT")"
|
||||
read -r WALK_KIND WALK_ROOT <<< "$WALK_RESULT"
|
||||
|
||||
PACKAGE_ROOT=""
|
||||
case "$WALK_KIND" in
|
||||
plugin)
|
||||
SCOPE="plugin"
|
||||
PACKAGE_ROOT="$WALK_ROOT"
|
||||
;;
|
||||
user)
|
||||
SCOPE="user"
|
||||
;;
|
||||
project)
|
||||
SCOPE="project"
|
||||
;;
|
||||
esac
|
||||
|
||||
# Determine file destinations
|
||||
case "$SCOPE" in
|
||||
plugin)
|
||||
APM_DIR="$PACKAGE_ROOT/.apm/agents"
|
||||
SOURCES_DIR="$PACKAGE_ROOT"
|
||||
;;
|
||||
project)
|
||||
CC_DIR="$ROOT/.claude/agents"
|
||||
CP_DIR="$ROOT/.github/agents"
|
||||
SOURCES_DIR=""
|
||||
;;
|
||||
user)
|
||||
CC_DIR="$HOME/.claude/agents"
|
||||
CP_DIR="$HOME/.copilot/agents"
|
||||
SOURCES_DIR=""
|
||||
;;
|
||||
esac
|
||||
|
||||
created_any=false
|
||||
|
||||
if [[ "$SCOPE" == "plugin" ]]; then
|
||||
APM_FILE="$APM_DIR/$AGENT_NAME.agent.md"
|
||||
|
||||
mkdir -p "$APM_DIR"
|
||||
|
||||
if [[ -f "$APM_FILE" ]]; then
|
||||
echo "Skipping '$APM_FILE' — already exists." >&2
|
||||
else
|
||||
sed "s/AGENT_NAME/$AGENT_NAME/g" "$TEMPLATES_DIR/apm-agent.md" > "$APM_FILE"
|
||||
echo "Created: $APM_FILE" >&2
|
||||
created_any=true
|
||||
fi
|
||||
else
|
||||
CC_FILE="$CC_DIR/$AGENT_NAME.md"
|
||||
CP_FILE="$CP_DIR/$AGENT_NAME.agent.md"
|
||||
|
||||
mkdir -p "$CC_DIR"
|
||||
mkdir -p "$CP_DIR"
|
||||
|
||||
# Copy Claude Code template (no-op if exists)
|
||||
if [[ -f "$CC_FILE" ]]; then
|
||||
echo "Skipping '$CC_FILE' — already exists." >&2
|
||||
else
|
||||
sed "s/AGENT_NAME/$AGENT_NAME/g" "$TEMPLATES_DIR/claude-code.md" > "$CC_FILE"
|
||||
echo "Created: $CC_FILE" >&2
|
||||
created_any=true
|
||||
fi
|
||||
|
||||
# Copy Copilot template (no-op if exists)
|
||||
if [[ -f "$CP_FILE" ]]; then
|
||||
echo "Skipping '$CP_FILE' — already exists." >&2
|
||||
else
|
||||
sed "s/AGENT_NAME/$AGENT_NAME/g" "$TEMPLATES_DIR/copilot.agent.md" > "$CP_FILE"
|
||||
echo "Created: $CP_FILE" >&2
|
||||
created_any=true
|
||||
fi
|
||||
fi
|
||||
|
||||
# Create sources.md at plugin/APM package root (no-op if exists)
|
||||
if [[ -n "$SOURCES_DIR" ]]; then
|
||||
SOURCES_FILE="$SOURCES_DIR/sources.md"
|
||||
if [[ -f "$SOURCES_FILE" ]]; then
|
||||
echo "Skipping '$SOURCES_FILE' — already exists." >&2
|
||||
else
|
||||
cat > "$SOURCES_FILE" <<'SOURCES'
|
||||
# Sources
|
||||
|
||||
<!-- List research sources that informed agents in this package.
|
||||
Follow the format below. Only include entries with `extracted` status.
|
||||
Delete this file if no research sources informed these agents. -->
|
||||
|
||||
<!-- ## source-slug
|
||||
- **URL:** <url>
|
||||
- **Research doc:** <relative-path-to-upstream-research-sources-file>
|
||||
- **Description:** <what this source covers>
|
||||
- **Contributing files:** .apm/agents/<name>.agent.md
|
||||
- **Status:** `extracted` -->
|
||||
SOURCES
|
||||
echo "Created: $SOURCES_FILE" >&2
|
||||
created_any=true
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ "$created_any" == false ]]; then
|
||||
echo "All files already exist — nothing to do." >&2
|
||||
else
|
||||
echo "" >&2
|
||||
echo "Scope: $SCOPE" >&2
|
||||
echo "" >&2
|
||||
echo "Next steps:" >&2
|
||||
if [[ "$SCOPE" == "plugin" ]]; then
|
||||
echo " 1. Fill in $APM_FILE — replace all FILL IN: placeholders (name, description, model, body only)" >&2
|
||||
echo " 2. Populate $SOURCES_DIR/sources.md with research sources, or delete it" >&2
|
||||
echo " 3. Validate: check required fields (name, description, system prompt) in the file" >&2
|
||||
else
|
||||
echo " 1. Fill in $CC_FILE — replace all FILL IN: placeholders" >&2
|
||||
echo " 2. Fill in $CP_FILE — replace all FILL IN: placeholders" >&2
|
||||
echo " 3. Validate: check required fields (name, description, system prompt) in both files" >&2
|
||||
fi
|
||||
fi
|
||||
@@ -1,33 +0,0 @@
|
||||
# tests/
|
||||
|
||||
Test files for scripts bundled with this skill.
|
||||
|
||||
## When to add tests
|
||||
|
||||
Add tests here when the skill has scripts in `scripts/` that are complex enough
|
||||
to break silently — validators, parsers, generators, anything with branching
|
||||
logic or edge cases. Test infrastructure (`.bats`, `*_test.*`, `test_*.sh`)
|
||||
belongs here, not in `scripts/`.
|
||||
|
||||
## Dependencies
|
||||
|
||||
Tests require [bats-support](https://github.com/bats-core/bats-support) and
|
||||
[bats-assert](https://github.com/bats-core/bats-assert). The test files load
|
||||
helpers from the repo root's `tests/test_helper/`.
|
||||
|
||||
From the repo root:
|
||||
|
||||
```bash
|
||||
git clone https://github.com/bats-core/bats-support tests/test_helper/bats-support
|
||||
git clone https://github.com/bats-core/bats-assert tests/test_helper/bats-assert
|
||||
```
|
||||
|
||||
Run all tests for this skill (from the repo root):
|
||||
|
||||
```bash
|
||||
bats plugins/kyberforge/skills/agent-author/tests/
|
||||
```
|
||||
|
||||
## If no tests are needed
|
||||
|
||||
Delete this README and the `tests/` directory entirely.
|
||||
@@ -1,301 +0,0 @@
|
||||
#!/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-agent.sh"
|
||||
ROOT="$(mktemp -d)"
|
||||
}
|
||||
|
||||
teardown() {
|
||||
rm -rf "$ROOT"
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Help
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
@test "--help exits 0" {
|
||||
run bash "$SCRIPT" --help
|
||||
assert_success
|
||||
assert_output --partial "Usage:"
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Plugin/APM scope (type:-bearing apm.yml at root)
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
@test "plugin/APM scope: creates single agent file in .apm/agents/" {
|
||||
printf 'name: my-package\ntype: skill\n' > "$ROOT/apm.yml"
|
||||
run bash "$SCRIPT" my-agent "$ROOT"
|
||||
assert_success
|
||||
assert [ -f "$ROOT/.apm/agents/my-agent.agent.md" ]
|
||||
}
|
||||
|
||||
@test "plugin/APM scope: does not create the old dual-file pair" {
|
||||
printf 'name: my-package\ntype: skill\n' > "$ROOT/apm.yml"
|
||||
run bash "$SCRIPT" my-agent "$ROOT"
|
||||
assert_success
|
||||
assert [ ! -f "$ROOT/agents/my-agent.md" ]
|
||||
assert [ ! -f "$ROOT/agents/my-agent.agent.md" ]
|
||||
}
|
||||
|
||||
@test "plugin/APM scope: creates .apm/agents/ directory if missing" {
|
||||
printf 'name: my-package\ntype: skill\n' > "$ROOT/apm.yml"
|
||||
run bash "$SCRIPT" my-agent "$ROOT"
|
||||
assert_success
|
||||
assert [ -d "$ROOT/.apm/agents" ]
|
||||
}
|
||||
|
||||
@test "plugin/APM scope: creates sources.md at package root" {
|
||||
printf 'name: my-package\ntype: skill\n' > "$ROOT/apm.yml"
|
||||
run bash "$SCRIPT" my-agent "$ROOT"
|
||||
assert_success
|
||||
assert [ -f "$ROOT/sources.md" ]
|
||||
}
|
||||
|
||||
@test "plugin/APM scope: no-op if agent file already exists" {
|
||||
printf 'name: my-package\ntype: skill\n' > "$ROOT/apm.yml"
|
||||
mkdir -p "$ROOT/.apm/agents"
|
||||
echo "existing" > "$ROOT/.apm/agents/my-agent.agent.md"
|
||||
run bash "$SCRIPT" my-agent "$ROOT"
|
||||
assert_success
|
||||
run grep "existing" "$ROOT/.apm/agents/my-agent.agent.md"
|
||||
assert_success
|
||||
}
|
||||
|
||||
@test "plugin/APM scope: frontmatter has no tools/isolation/maxTurns/effort/memory/permissionMode keys" {
|
||||
printf 'name: my-package\ntype: skill\n' > "$ROOT/apm.yml"
|
||||
bash "$SCRIPT" my-agent "$ROOT"
|
||||
file="$ROOT/.apm/agents/my-agent.agent.md"
|
||||
fm="$(sed -n '/^---$/,/^---$/p' "$file")"
|
||||
# Column-0 key lines only — comment bodies in the template are indented,
|
||||
# so this anchor naturally excludes commented-out example fields.
|
||||
run grep -E '^(tools|isolation|maxTurns|effort|memory|permissionMode|disallowedTools|skills|color|initialPrompt|background|hooks|mcpServers):' <<< "$fm"
|
||||
assert_failure
|
||||
}
|
||||
|
||||
@test "plugin/APM scope: frontmatter carries only name, description, model, source_keys fields" {
|
||||
printf 'name: my-package\ntype: skill\n' > "$ROOT/apm.yml"
|
||||
bash "$SCRIPT" my-agent "$ROOT"
|
||||
file="$ROOT/.apm/agents/my-agent.agent.md"
|
||||
fm="$(sed -n '/^---$/,/^---$/p' "$file")"
|
||||
keys="$(grep -oE '^[a-zA-Z][a-zA-Z0-9_-]*:' <<< "$fm" | sed 's/:$//' | sort -u)"
|
||||
for key in $keys; do
|
||||
if [[ "$key" != "name" && "$key" != "description" && "$key" != "model" && "$key" != "source_keys" ]]; then
|
||||
fail "unexpected frontmatter key: $key"
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
@test "plugin/APM scope: sources.md contributing-files template mentions the single-file path" {
|
||||
printf 'name: my-package\ntype: skill\n' > "$ROOT/apm.yml"
|
||||
bash "$SCRIPT" my-agent "$ROOT"
|
||||
run grep ".apm/agents/<name>.agent.md" "$ROOT/sources.md"
|
||||
assert_success
|
||||
}
|
||||
|
||||
@test "plugin/APM scope: template AGENT_NAME substituted" {
|
||||
printf 'name: my-package\ntype: skill\n' > "$ROOT/apm.yml"
|
||||
bash "$SCRIPT" my-agent "$ROOT"
|
||||
run grep "my-agent" "$ROOT/.apm/agents/my-agent.agent.md"
|
||||
assert_success
|
||||
}
|
||||
|
||||
@test "plugin/APM scope: walk-up finds apm.yml at an ancestor directory, not just root arg" {
|
||||
printf 'name: my-package\ntype: skill\n' > "$ROOT/apm.yml"
|
||||
mkdir -p "$ROOT/nested/subdir"
|
||||
run bash "$SCRIPT" my-agent "$ROOT/nested/subdir"
|
||||
assert_success
|
||||
assert [ -f "$ROOT/.apm/agents/my-agent.agent.md" ]
|
||||
}
|
||||
|
||||
@test "plugin/APM scope: apm.yml without type: is skipped (marketplace-only manifest)" {
|
||||
printf 'name: my-marketplace\n' > "$ROOT/apm.yml"
|
||||
mkdir -p "$ROOT/.git"
|
||||
run bash "$SCRIPT" my-agent "$ROOT"
|
||||
assert_success
|
||||
assert [ ! -f "$ROOT/.apm/agents/my-agent.agent.md" ]
|
||||
assert [ -f "$ROOT/.claude/agents/my-agent.md" ]
|
||||
}
|
||||
|
||||
@test "plugin/APM scope: type:-less apm.yml at leaf falls through to a type:-bearing apm.yml higher up" {
|
||||
printf 'name: outer-package\ntype: skill\n' > "$ROOT/apm.yml"
|
||||
mkdir -p "$ROOT/inner"
|
||||
printf 'name: inner-marketplace\n' > "$ROOT/inner/apm.yml"
|
||||
run bash "$SCRIPT" my-agent "$ROOT/inner"
|
||||
assert_success
|
||||
assert [ -f "$ROOT/.apm/agents/my-agent.agent.md" ]
|
||||
}
|
||||
|
||||
@test "plugin/APM scope: matched-quote type value ('skill') is recognized" {
|
||||
printf 'name: my-package\ntype: "skill"\n' > "$ROOT/apm.yml"
|
||||
run bash "$SCRIPT" my-agent "$ROOT"
|
||||
assert_success
|
||||
assert [ -f "$ROOT/.apm/agents/my-agent.agent.md" ]
|
||||
}
|
||||
|
||||
@test "plugin/APM scope: mismatched-quote type value is rejected, falls through to project scope" {
|
||||
mkdir -p "$ROOT/.git"
|
||||
printf "name: my-package\ntype: \"skill'\n" > "$ROOT/apm.yml"
|
||||
run bash "$SCRIPT" my-agent "$ROOT"
|
||||
assert_success
|
||||
assert [ ! -f "$ROOT/.apm/agents/my-agent.agent.md" ]
|
||||
assert [ -f "$ROOT/.claude/agents/my-agent.md" ]
|
||||
}
|
||||
|
||||
@test "plugin/APM scope: type: line is recognized even without a trailing newline on the file" {
|
||||
printf 'name: my-package\ntype: skill' > "$ROOT/apm.yml"
|
||||
run bash "$SCRIPT" my-agent "$ROOT"
|
||||
assert_success
|
||||
assert [ -f "$ROOT/.apm/agents/my-agent.agent.md" ]
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Old plugin.json marker is no longer recognized (full switch, no dual-mode)
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
@test "bare plugin.json (no apm.yml) is no longer detected as plugin scope — falls through to project scope" {
|
||||
touch "$ROOT/plugin.json"
|
||||
run bash "$SCRIPT" my-agent "$ROOT"
|
||||
assert_success
|
||||
assert [ ! -f "$ROOT/agents/my-agent.md" ]
|
||||
assert [ ! -f "$ROOT/agents/my-agent.agent.md" ]
|
||||
assert [ ! -f "$ROOT/.apm/agents/my-agent.agent.md" ]
|
||||
assert [ -f "$ROOT/.claude/agents/my-agent.md" ]
|
||||
assert [ -f "$ROOT/.github/agents/my-agent.agent.md" ]
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Non-plugin (project) scope
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
@test "non-plugin scope: creates claude code file in .claude/agents/" {
|
||||
run bash "$SCRIPT" my-agent "$ROOT"
|
||||
assert_success
|
||||
assert [ -f "$ROOT/.claude/agents/my-agent.md" ]
|
||||
}
|
||||
|
||||
@test "non-plugin scope: creates copilot file in .github/agents/" {
|
||||
run bash "$SCRIPT" my-agent "$ROOT"
|
||||
assert_success
|
||||
assert [ -f "$ROOT/.github/agents/my-agent.agent.md" ]
|
||||
}
|
||||
|
||||
@test "non-plugin scope: creates .claude/agents/ directory if missing" {
|
||||
run bash "$SCRIPT" my-agent "$ROOT"
|
||||
assert_success
|
||||
assert [ -d "$ROOT/.claude/agents" ]
|
||||
}
|
||||
|
||||
@test "non-plugin scope: creates .github/agents/ directory if missing" {
|
||||
run bash "$SCRIPT" my-agent "$ROOT"
|
||||
assert_success
|
||||
assert [ -d "$ROOT/.github/agents" ]
|
||||
}
|
||||
|
||||
@test "non-plugin scope: no sources.md created" {
|
||||
run bash "$SCRIPT" my-agent "$ROOT"
|
||||
assert_success
|
||||
assert [ ! -f "$ROOT/.claude/agents/sources.md" ]
|
||||
assert [ ! -f "$ROOT/.github/agents/sources.md" ]
|
||||
}
|
||||
|
||||
@test "non-plugin scope: no-op if claude code file already exists" {
|
||||
mkdir -p "$ROOT/.claude/agents"
|
||||
echo "existing" > "$ROOT/.claude/agents/my-agent.md"
|
||||
run bash "$SCRIPT" my-agent "$ROOT"
|
||||
assert_success
|
||||
run grep "existing" "$ROOT/.claude/agents/my-agent.md"
|
||||
assert_success
|
||||
}
|
||||
|
||||
@test "project scope: detected via .git present above root, output still relative to root arg" {
|
||||
mkdir -p "$ROOT/repo/.git"
|
||||
mkdir -p "$ROOT/repo/pkg"
|
||||
run bash "$SCRIPT" my-agent "$ROOT/repo/pkg"
|
||||
assert_success
|
||||
assert [ -f "$ROOT/repo/pkg/.claude/agents/my-agent.md" ]
|
||||
assert [ -f "$ROOT/repo/pkg/.github/agents/my-agent.agent.md" ]
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# User scope
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
@test "user scope: root exactly \$HOME creates files under ~/.claude and ~/.copilot" {
|
||||
FAKE_HOME="$(mktemp -d)"
|
||||
run env HOME="$FAKE_HOME" bash "$SCRIPT" my-agent "~"
|
||||
assert_success
|
||||
assert [ -f "$FAKE_HOME/.claude/agents/my-agent.md" ]
|
||||
assert [ -f "$FAKE_HOME/.copilot/agents/my-agent.agent.md" ]
|
||||
rm -rf "$FAKE_HOME"
|
||||
}
|
||||
|
||||
@test "user scope: \$HOME being a dotfiles .git repo does not shadow user scope" {
|
||||
FAKE_HOME="$(mktemp -d)"
|
||||
mkdir "$FAKE_HOME/.git"
|
||||
run env HOME="$FAKE_HOME" bash "$SCRIPT" my-agent "~"
|
||||
assert_success
|
||||
assert [ -f "$FAKE_HOME/.claude/agents/my-agent.md" ]
|
||||
assert [ -f "$FAKE_HOME/.copilot/agents/my-agent.agent.md" ]
|
||||
refute [ -d "$FAKE_HOME/.github" ]
|
||||
rm -rf "$FAKE_HOME"
|
||||
}
|
||||
|
||||
@test "user scope is checked directly at \$HOME, no walk-up: a marker-less subdir under \$HOME resolves to project scope, not user scope" {
|
||||
FAKE_HOME="$(mktemp -d)"
|
||||
mkdir -p "$FAKE_HOME/scratch/testdir"
|
||||
run env HOME="$FAKE_HOME" bash "$SCRIPT" my-agent "$FAKE_HOME/scratch/testdir"
|
||||
assert_success
|
||||
assert [ -f "$FAKE_HOME/scratch/testdir/.claude/agents/my-agent.md" ]
|
||||
assert [ -f "$FAKE_HOME/scratch/testdir/.github/agents/my-agent.agent.md" ]
|
||||
refute [ -f "$FAKE_HOME/.claude/agents/my-agent.md" ]
|
||||
refute [ -f "$FAKE_HOME/.copilot/agents/my-agent.agent.md" ]
|
||||
rm -rf "$FAKE_HOME"
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Name validation
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
@test "fails when no arguments given" {
|
||||
run bash "$SCRIPT"
|
||||
assert_failure
|
||||
}
|
||||
|
||||
@test "fails when agent name contains uppercase" {
|
||||
run bash "$SCRIPT" MyAgent "$ROOT"
|
||||
assert_failure
|
||||
}
|
||||
|
||||
@test "fails when agent name has consecutive hyphens" {
|
||||
run bash "$SCRIPT" my--agent "$ROOT"
|
||||
assert_failure
|
||||
}
|
||||
|
||||
@test "fails when agent name has a leading hyphen" {
|
||||
run bash "$SCRIPT" -my-agent "$ROOT"
|
||||
assert_failure
|
||||
}
|
||||
|
||||
@test "fails when agent name has a trailing hyphen" {
|
||||
run bash "$SCRIPT" my-agent- "$ROOT"
|
||||
assert_failure
|
||||
}
|
||||
|
||||
@test "agent name with numbers is valid" {
|
||||
run bash "$SCRIPT" agent-v2 "$ROOT"
|
||||
assert_success
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Root validation
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
@test "fails when root directory does not exist" {
|
||||
run bash "$SCRIPT" my-agent "/nonexistent/path"
|
||||
assert_failure
|
||||
}
|
||||
Reference in New Issue
Block a user