feat(kyberforge): add agent-author skill for Claude Code and Copilot CLI agents

## Why

skill-author explicitly excludes agent definition files ("Do not use to author
agent definition files"). No factory skill existed to create or improve the
.md / .agent.md files that define Claude Code subagents and Copilot CLI agents
in a plugin, project, or user scope. This fills that gap.

## Implementation Notes

Single-root scaffold convention: new-agent.sh <name> <root> derives both
provider file paths from the root by convention — plugin scope (plugin.json
present) writes both files into <root>/agents/; non-plugin scope writes
.claude/agents/<name>.md and .github/agents/<name>.agent.md. This keeps
input minimal while always generating both provider files. See ADR-0015.

Routing is file-level (not directory-level like skill-author): neither file
exists → create flow; at least one exists → improve flow; scaffold is a
file-by-file no-op so retries are safe.

No companion agent-audit skill — inline validation in the close step covers
the simpler agent field contract. agent-audit is tracked as a follow-on.

## Impact

Closes the skill-author gap for agent definitions. Follow-ons tracked in
Gitea #11: agent-audit skill and --copilot-dest override flag for non-standard
Copilot project paths.

---
ADR: docs/adr/0015-agent-author-dual-provider-scaffold.md
Refs: #10
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-27 15:07:33 +00:00
parent 83e1a50a51
commit ba68b09c87
13 changed files with 936 additions and 0 deletions

View File

@@ -0,0 +1,43 @@
# 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 (`.md` for Claude Code, `.agent.md` for Copilot CLI) at plugin, project, or user scope. Always generates both provider files in one pass. Also applies improvement signals — grill output, inline feedback, session context — to existing agent files.
## 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 plugins/kyberforge/
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 Claude Code and Copilot CLI agent files from templates |
| `references/deployment-modes.md` | Plugin vs project vs user scope: restrictions, scoped identifiers, path conventions |
| `references/sources.md` | Research provenance — sources that informed this skill |
| `assets/templates/claude-code.md` | Annotated Claude Code agent definition template |
| `assets/templates/copilot.agent.md` | Annotated Copilot CLI agent definition template |
| `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 |

View File

@@ -0,0 +1,181 @@
---
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 both Claude Code and
GitHub Copilot CLI agent formats, at plugin, project, and user scope — always
generating both provider files in one pass. 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
- Plugin agents silently ignore `hooks`, `mcpServers`, and `permissionMode` — these fields have no effect and produce no warning. They only work in `.claude/agents/` or `~/.claude/agents/`.
- `AskUserQuestion`, `EnterPlanMode`, `ExitPlanMode`, and `ScheduleWakeup` are never available to any subagent regardless of the `tools` field.
- 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.
- Copilot CLI agent files **must** use the `.agent.md` extension. A plain `.md` file is not picked up by Copilot.
- Copilot has no `permissionMode`, `maxTurns`, `isolation`, or `memory` fields — do not include them in the Copilot file.
- `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.
If the destination is a plugin directory, read `references/deployment-modes.md`.
## Route
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 (plugin root, 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.
### 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 plugins/kyberforge/
bash scripts/new-agent.sh deploy-assistant .
bash scripts/new-agent.sh security-reviewer ~
```
**Scope detection (script handles this automatically):**
- Root contains `plugin.json` → plugin scope → creates `<root>/agents/<name>.md` + `<root>/agents/<name>.agent.md`
- Root is a project directory (no `plugin.json`) → creates `<root>/.claude/agents/<name>.md` + `<root>/.github/agents/<name>.agent.md`
- Root is `~` → creates `~/.claude/agents/<name>.md` + `~/.copilot/agents/<name>.agent.md`
The script is file-by-file no-op — it skips any file that already exists.
### Step 2 — Fill in the Claude Code agent file (`<name>.md`)
Open the scaffolded Claude Code file. Replace every `FILL IN:` placeholder.
**`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..."
- Include "Use proactively" to trigger automatic invocation without explicit user direction
- Specific about the triggering condition and expertise domain
- Under 300 characters preferred
**`tools`** — 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:**
- `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
**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 CLI agent file (`<name>.agent.md`)
Open the scaffolded Copilot file. Replace every `FILL IN:` placeholder.
**Field differences from Claude Code:**
- `tools` uses Copilot aliases: `execute` (shell), `read`, `edit`, `search`, `agent`, `web`
- `target`: `vscode`, `github-copilot`, or omit for both (default: both)
- `user-invocable`: set `false` to hide from manual invocation (auto-select only)
- `disable-model-invocation`: set `true` to require explicit user invocation
**Do not include Claude Code-only fields**: `maxTurns`, `isolation`, `memory`, `permissionMode`, `effort`, `hooks`, `mcpServers`.
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 `agents/sources.md` (plugin scope only)
Skip this step at project and user scope.
If a research `sources.md` is present in the conversation context:
1. Filter to entries with `` `extracted` `` status only.
2. For each entry, note which agent files it contributed to.
3. Write `agents/sources.md` with those entries. Include `- **Research doc:** <path>` per entry pointing to the upstream research sources file.
If no research sources are in context, delete `agents/sources.md`.
### Step 5 — Validate and close
**Claude Code file (`<name>.md`):**
- [ ] `name` field present, kebab-case, unique in scope
- [ ] `description` field present, action-first
- [ ] If plugin scope: no `hooks`, `mcpServers`, or `permissionMode` (silently ignored at plugin scope — move agent to `.claude/agents/` to use them)
- [ ] System prompt body present and non-empty
**Copilot file (`<name>.agent.md`):**
- [ ] File extension is `.agent.md` (not `.md`)
- [ ] `description` field present
- [ ] No Claude Code-only fields (`maxTurns`, `isolation`, `memory`, `permissionMode`, `effort`)
- [ ] System prompt body present and non-empty
## 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."
**Partial state** — if one provider file exists but the other does not, scaffold the missing file first (run `bash scripts/new-agent.sh <name> <root>` — the file-by-file no-op means only the missing file is created), then continue with the improve flow on both files.
### 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 Copilot files, verify no Claude Code-only fields are introduced.
### Step 5 — Validate and close
Re-run the validation checklist from the create flow's Step 5 on any edited file.

View File

@@ -0,0 +1,8 @@
# 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. Includes all supported frontmatter fields (required and optional) with inline guidance comments and `FILL IN:` placeholders. Notes which fields are silently ignored for plugin agents.
- **`copilot.agent.md`** — Copilot CLI agent definition template. Uses Copilot-specific fields (`target`, `user-invocable`, `disable-model-invocation`, Copilot tool aliases). Explicitly excludes Claude Code-only fields.

View File

@@ -0,0 +1,66 @@
---
# Claude Code agent definition
# Fill in all FILL IN: placeholders. Remove or uncomment optional fields as needed.
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
# 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.
# 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.

View File

@@ -0,0 +1,58 @@
---
# GitHub Copilot CLI agent definition
# File extension MUST be .agent.md — a plain .md file is not picked up by Copilot.
# Fill in all FILL IN: placeholders. Remove or uncomment optional fields as needed.
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/*"
# target: github-copilot
# Optional. Which runtime loads this file.
# vscode — VS Code Copilot only
# github-copilot — Copilot CLI / cloud agents only
# both (default) — loaded by both runtimes
# user-invocable: true
# Optional. Set false to hide from manual invocation (auto-select only).
# disable-model-invocation: false
# Optional. Set true to require explicit user invocation; prevents auto-selection.
# model: claude-sonnet-4-5
# Optional. Model to run this agent on.
# 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.

View File

@@ -0,0 +1,9 @@
# 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.
## 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.

View File

@@ -0,0 +1,63 @@
---
source_keys:
- context7-websites-code-claude
- claude-code-plugins-docs
- claude-code-subagents-docs
- 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 | `<plugin>/agents/` | `<plugin>/agents/` | Sessions with the plugin 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 agents (agents shipped inside a plugin's `agents/` directory) have fewer capabilities than project or user agents:
**Claude Code — silently ignored for plugin agents:**
- `hooks` — per-agent event handlers; copy the agent to `.claude/agents/` to use these
- `mcpServers` — inline MCP server definitions; not available in plugin context
- `permissionMode` — silently ignored; the parent session's permission mode applies
**Copilot CLI — unsupported or ignored:**
- `mcp-servers` — cloud agents only; ignored in VS Code and most IDEs
- `metadata` — key-value annotations; ignored in VS Code
## 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 | `<plugin>/agents/<name>.agent.md` | Inside plugin root |
The `.agent.md` extension is **mandatory** — Copilot CLI does not pick up plain `.md` files in the `agents/` directory.

View File

@@ -0,0 +1,68 @@
---
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
---
# 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, assets/templates/claude-code.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, assets/templates/claude-code.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, assets/templates/copilot.agent.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, assets/templates/copilot.agent.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, scripts/new-agent.sh, 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, assets/templates/copilot.agent.md
- **Status:** `extracted`

View File

@@ -0,0 +1,13 @@
# scripts/
## new-agent.sh
Scaffolds agent definition files for both Claude Code and GitHub Copilot CLI from a single root directory input.
```
Usage: new-agent.sh <agent-name> <root>
```
Detects scope from the root: `plugin.json` present → plugin scope (both files in `<root>/agents/`); `~` → user scope (`~/.claude/agents/` + `~/.copilot/agents/`); otherwise project scope (`.claude/agents/` + `.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`).

View File

@@ -0,0 +1,169 @@
#!/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 files for Claude Code and GitHub Copilot CLI.
Arguments:
agent-name Kebab-case agent identifier (e.g. code-reviewer, deploy-assistant).
root Root directory — determines scope:
plugin scope : root contains plugin.json
→ creates <root>/agents/<name>.md
→ creates <root>/agents/<name>.agent.md
→ creates <root>/agents/sources.md (if absent)
project scope : root is a project directory (no plugin.json)
→ creates <root>/.claude/agents/<name>.md
→ creates <root>/.github/agents/<name>.agent.md
user scope : root is ~ (home directory)
→ 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
# Detect scope
if [[ -f "$ROOT/plugin.json" || -f "$ROOT/.claude-plugin/plugin.json" || -f "$ROOT/.plugin/plugin.json" || -f "$ROOT/.github/plugin/plugin.json" ]]; then
SCOPE="plugin"
elif [[ "$ROOT" == "$HOME" ]]; then
SCOPE="user"
else
SCOPE="project"
fi
# Determine file destinations
case "$SCOPE" in
plugin)
CC_DIR="$ROOT/agents"
CP_DIR="$ROOT/agents"
SOURCES_DIR="$ROOT/agents"
;;
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
CC_FILE="$CC_DIR/$AGENT_NAME.md"
CP_FILE="$CP_DIR/$AGENT_NAME.agent.md"
# Create directories
mkdir -p "$CC_DIR"
mkdir -p "$CP_DIR"
# Copy Claude Code template (no-op if exists)
created_any=false
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
# Create sources.md at plugin scope (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 directory.
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:** agents/<name>.md, 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
echo " 1. Fill in $CC_FILE — replace all FILL IN: placeholders" >&2
echo " 2. Fill in $CP_FILE — replace all FILL IN: placeholders" >&2
if [[ -n "$SOURCES_DIR" ]]; then
echo " 3. Populate $SOURCES_DIR/sources.md with research sources, or delete it" >&2
echo " 4. Validate: check required fields (name, description, system prompt) in both files" >&2
else
echo " 3. Validate: check required fields (name, description, system prompt) in both files" >&2
fi
fi

View File

@@ -0,0 +1,33 @@
# 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.

View File

@@ -0,0 +1,184 @@
#!/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 scope (plugin.json present at root)
# ---------------------------------------------------------------------------
@test "plugin scope: creates both agent files in agents/" {
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" ]
}
@test "plugin scope: creates agents/ directory if missing" {
touch "$ROOT/plugin.json"
run bash "$SCRIPT" my-agent "$ROOT"
assert_success
assert [ -d "$ROOT/agents" ]
}
@test "plugin scope: creates sources.md in agents/" {
touch "$ROOT/plugin.json"
run bash "$SCRIPT" my-agent "$ROOT"
assert_success
assert [ -f "$ROOT/agents/sources.md" ]
}
@test "plugin scope: no-op if claude code file already exists" {
touch "$ROOT/plugin.json"
mkdir -p "$ROOT/agents"
echo "existing" > "$ROOT/agents/my-agent.md"
run bash "$SCRIPT" my-agent "$ROOT"
assert_success
run grep "existing" "$ROOT/agents/my-agent.md"
assert_success
}
@test "plugin scope: no-op if copilot file already exists" {
touch "$ROOT/plugin.json"
mkdir -p "$ROOT/agents"
echo "existing" > "$ROOT/agents/my-agent.agent.md"
run bash "$SCRIPT" my-agent "$ROOT"
assert_success
run grep "existing" "$ROOT/agents/my-agent.agent.md"
assert_success
}
@test "plugin scope: still creates missing file when one already exists" {
touch "$ROOT/plugin.json"
mkdir -p "$ROOT/agents"
echo "existing" > "$ROOT/agents/my-agent.md"
run bash "$SCRIPT" my-agent "$ROOT"
assert_success
assert [ -f "$ROOT/agents/my-agent.agent.md" ]
}
# ---------------------------------------------------------------------------
# Non-plugin scope (no plugin.json)
# ---------------------------------------------------------------------------
@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
}
# ---------------------------------------------------------------------------
# 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
}
# ---------------------------------------------------------------------------
# Template content
# ---------------------------------------------------------------------------
@test "plugin scope: claude code template contains AGENT_NAME substituted" {
touch "$ROOT/plugin.json"
bash "$SCRIPT" my-agent "$ROOT"
run grep "my-agent" "$ROOT/agents/my-agent.md"
assert_success
}
@test "plugin scope: copilot template contains AGENT_NAME substituted" {
touch "$ROOT/plugin.json"
bash "$SCRIPT" my-agent "$ROOT"
run grep "my-agent" "$ROOT/agents/my-agent.agent.md"
assert_success
}