Plugin Development¶
The kk plugin lives at klaude-plugin/. This section covers the practical rules for authoring each component. For the full specification, see CLAUDE.md.
Skills¶
Each skill is a directory under klaude-plugin/skills/<name>/ containing at minimum a SKILL.md entry point.
SKILL.md structure¶
---
name: skill-name
description: |
Trigger-first description. Front-load the key use case.
---
# Skill Title
## Conventions
## Workflow
Naming rules¶
- Imperative verbs over noun phrases:
/kk:designnot/kk:analysis-process. - Family prefixes for grouped skills:
/kk:review-code,/kk:review-design,/kk:review-spec. - Always use
/kk:prefix when referencing skills: write/kk:review-codenotreview-code. The codex generation tool rewrites/kk:→$kk:for Codex output.
Description budget¶
Claude Code truncates skill descriptions at 1,536 characters per entry. OpenCode's limit is 1,024. Lead with trigger keywords — truncation happens at the tail. Detailed rules, cascades, and examples belong in the SKILL.md body, not the description.
Workflow ordering (ADR 0004)¶
Every skill MUST fully load its instructions before taking any action on its subject matter. This is the single most critical rule for skill authoring.
- "Instructions" = SKILL.md + referenced process files + shared protocols + resolved profile content.
- "Action on subject matter" = reading diff content, editing code, engaging with idea prose, running tests, producing findings.
- A narrow early scope is permitted (e.g.,
git diff --statfor filenames) to drive profile detection. - Content-level read instructions appear exactly once in the workflow, after instruction loading.
Every skill's Workflow section must carry a mandatory-order directive at the top naming this rule by intent. The failure mode: once an LLM has diff content loaded, it has enough to pattern-match findings without methodology, and its efficiency bias favors the shortcut. The methodology becomes ceremony the agent optimizes away.
Shared Instructions¶
Instructions consumed by multiple skills live at klaude-plugin/skills/_shared/<name>.md. Each consuming skill gets a symlink:
Reference in skill prose as [shared-<name>.md](shared-<name>.md). The shared- prefix makes it obvious which files are shared vs. skill-specific. Only symlink into skills that actually reference the file.
Symlinks must stay inside the skills/ tree — cross-boundary symlinks break under some plugin installers (see ADR 0003).
Commands¶
Commands live under klaude-plugin/commands/<name>/. For skills with standard + isolated modes:
default.md— standard variant, invoked as/kk:<name>:defaultisolated.md— isolated sub-agent variant, invoked as/kk:<name>:isolated
Agents¶
Agent definitions live at klaude-plugin/agents/<name>.md with frontmatter specifying name, description, and tools (an allowlist of tools the agent can use):
---
name: code-reviewer
description: |
Independent code reviewer with no authorship attachment.
tools:
- Read
- Grep
- Glob
- mcp__capy__capy_search
---
Agent names describe the role (code-reviewer, design-reviewer), not the skill that invokes them. Don't rename agent files when renaming skills.
Agents inherit the instruction-before-action rule — they must read provided checklists before analyzing subject matter, regardless of payload delivery order from the spawning skill.
Hooks¶
Hook definitions in klaude-plugin/hooks/hooks.json:
{
"hooks": {
"PreToolUse": [{
"matcher": "Bash",
"hooks": [{
"type": "command",
"command": "${CLAUDE_PLUGIN_ROOT}/scripts/validate-bash.sh"
}]
}]
}
}
Hook scripts read JSON from stdin (the tool_input object), return structured JSON for deny decisions. Always exit 0 — use permissionDecision: "deny" in the JSON output to block a tool call. See klaude-plugin/scripts/validate-bash.sh for the pattern.
Profiles¶
Profiles at klaude-plugin/profiles/<name>/ provide domain-specific content to every workflow phase. See the Profile Conventions section of CLAUDE.md for the full specification.
Required files:
DETECTION.md— three mandatory section headings:## Path signals,## Filename signals,## Content signals. All three must be present even if the body is empty. Optional:## Design signalsfor pre-code detection.overview.md— human-readable summary and dependency-lookup targets.
Phase subdirectories: review-code/, design/, implement/, test/, document/, review-spec/. Not every profile populates every phase. Each populated phase must have an index.md with always-load and conditional entries. Conditional entries need explicit Load if: clauses naming concrete diff properties — two agents evaluating the same diff must reach the same conclusion.
Bidirectional index invariant (enforced by test/test-plugin-structure.sh):
- Forward: every markdown link in
index.mdresolves to a file on disk. - Reverse: every
.mdin the phase directory (exceptindex.md) is referenced by the index.
An unreferenced .md inside a phase subdirectory is always a bug. Authoring notes belong in overview.md at the profile root, not inside phase subdirectories.
Adding a new profile checklist:
- Create the profile directory by copying an existing one as a template.
- Write
DETECTION.mdandoverview.md. - Populate the phase subdirectories the profile needs (each with
index.md). - Append the profile name to
EXPECTED_PROFILESintest/test-plugin-structure.sh. - Append the profile name to the Known Profiles list in
klaude-plugin/skills/_shared/profile-detection.md. - Run
bash test/test-plugin-structure.shand confirm green.
Evaluations¶
Skills with non-trivial decision logic should ship evaluation scenarios under klaude-plugin/skills/<skill>/evals/. One directory per eval:
eval.json schema:
{
"id": 1,
"name": "eval-name-kebab-case",
"description": "What this eval tests.",
"skills": ["skill-name"],
"prompt": "The user prompt that triggers the skill.",
"trap": "The failure hypothesis — what a model is likely to get wrong.",
"files": ["test-files/foo.yaml"],
"assertions": [
{ "id": "1.1", "text": "Specific, gradable behavior." }
]
}
Use real filesystem fixtures in test-files/, not inline JSON strings. Include at least one regression eval proving the skill does NOT activate when it shouldn't.
${CLAUDE_PLUGIN_ROOT} — Substitution Rules¶
The harness provides ${CLAUDE_PLUGIN_ROOT} resolving to the installed plugin's root. Understanding its substitution boundary is critical (ADR 0003):
Substituted at plugin-load time (safe to use freely):
SKILL.mdfilesagents/*.mdfileshooks/*.jsoncommand strings- MCP config files
NOT substituted by the Read tool (the literal token reaches the agent):
- Everything in
skills/_shared/ - Everything in
profiles/ - Any file an agent reads at runtime
For runtime-read files, prefer explicit content (e.g., hard-coded profile name lists) over the token. If the file must describe a plugin-root path, instruct the agent to construct it using the resolved prefix it already knows from SKILL.md.
Other rules:
- Brace form required:
${CLAUDE_PLUGIN_ROOT}works, bare$CLAUDE_PLUGIN_ROOTdoes NOT get substituted. Globwon't work against these paths — it's cwd-scoped and returns 0 matches for outside-cwd absolute paths. UseReadwith the resolved path instead.- To reference the variable name literally in prose, use bare
$CLAUDE_PLUGIN_ROOTor${CLAUDE_PLUGIN_ROOT}.
Capy Knowledge Protocol¶
Skills that interact with capy MCP tools use kk: namespaced source labels:
| Label | Contents |
|---|---|
kk:arch-decisions | Architecture decisions, design rationale |
kk:review-findings | Code review patterns, recurring issues |
kk:lang-idioms | Language best practices from external sources |
kk:project-conventions | Discovered project patterns |
kk:test-patterns | Testing approaches, edge cases |
kk:debug-context | Root causes, tricky bugs |
Only index non-obvious learnings not derivable from reading the code or git history. Empty results are normal for new projects — proceed with standard guidelines.
Common Pitfalls¶
-
Workflow ordering is the #1 failure mode. If the agent sees subject matter before methodology, it shortcuts the methodology. Structure every workflow so instructions load first. See ADR 0004.
-
Forgetting
make generate-kodex. After editing anything inklaude-plugin/, the Codex variant drifts. CI checks this withmake generate-kodex && git diff --exit-code kodex-plugin/ .codex/agents/. -
Orphan files in profile phase directories. Every
.mdfile (exceptindex.md) must be referenced by the phase'sindex.md. The bidirectional invariant test catches this. -
Symlinks outside
skills/. Per-skill symlinks must point within theskills/tree (../_shared/<name>.md). Cross-boundary symlinks break under some installers. Profile content uses${CLAUDE_PLUGIN_ROOT}references instead. -
Skill description truncation. Descriptions are truncated from the tail. If your trigger keywords are at the end, the skill won't be matched. Lead with the use case.
-
Stale Known Profiles list. When adding a profile, you must update both
EXPECTED_PROFILESin the test file and the Known Profiles list inklaude-plugin/skills/_shared/profile-detection.md. The list is the runtime enumeration — consumers iterate it rather than walking the filesystem. -
Renaming skills. Update
EXPECTED_SKILLS/EXPECTED_COMMANDSin tests. Don't rename agent files. Don't touchrun_plugin_migration'sdirs_to_removein.claude/toolbox/scripts/template-sync.sh(historical cleanup paths). Don't touchdocs/done/(frozen history). Watch for substring collisions in sed operations. -
Vague
Load if:clauses. Conditional entries in profileindex.mdmust name concrete diff properties (field values, filenames, directory names) — not vague category labels. Two agents evaluating the same diff must reach the same conclusion.