Skip to main content

generation-standards

House style for every file plugin-forge emits into a generated plugin. Load before writing or reviewing any generated SKILL.md, agent, hooks.json, hook script, .mcp.json, plugin manifest, marketplace entry, README, or settings snippet — and before ship runs the freshness-guard sweep. Covers description discipline, invocation-control decisions, paired-path allowed-tools, persistent-state placement, hook output discipline, degrees-of-freedom mapping, and the deprecated-shape greps.

インストールへ移動

ソース情報

リポジトリ
Aznatkoiny/claude-dev-toolkit
ソースの最終更新活動
2026年8月2日 20:26
検出された SKILL.md の言語
英語
スター
0
フォーク
0

インストール方法

デフォルトでは、最初にソースを確認する Prompt が選択されています。直接コマンドに切り替えるか、ローカルコピーをダウンロードすることもできます。

ソースファイルを確認

インストールを決める前に、SKILL.md と SkillsMP に表示されている付属ファイルをお読みください。

ファイルエクスプローラー
10 ファイル

SKILL.md を表示中

SKILL.md
ソースの指示 · 読み取り専用プレビュー
name
generation-standards
description
House style for every file plugin-forge emits into a generated plugin. Load before writing or reviewing any generated SKILL.md, agent, hooks.json, hook script, .mcp.json, plugin manifest, marketplace entry, README, or settings snippet — and before ship runs the freshness-guard sweep. Covers description discipline, invocation-control decisions, paired-path allowed-tools, persistent-state placement, hook output discipline, degrees-of-freedom mapping, and the deprecated-shape greps.
user-invocable
false
# Generation standards — plugin-forge house style These rules govern every file the build loop writes into a target plugin and every file plugin-forge itself ships. Apply them at generation time: plugin skills cannot be fixed post-install via `skillOverrides`, and a stale shape emitted today is a silently dead component on the user's machine tomorrow. Fill from the templates in `templates/` (index at the end of this file); the FRESHNESS GUARD greps below are the exact block ship runs before packaging. ## 1. Description discipline - Write `description` as **trigger conditions only** — when to invoke, never a summary of the workflow. A description that summarizes the steps makes Claude follow the description and skip the body (empirically observed by the superpowers CSO testing). - Put the key use case in the **first 200 characters**. The listing may be cut early under budget pressure; the front of the string does all the routing work. - `description` + `when_to_use` combined must be **≤ 1,536 characters** — the listing truncates there. Include the natural keywords users actually type (error messages, symptoms, task nouns), not internal jargon. - The skill listing budget is ~1% of the context window shared across all installed skills. Every generated description competes with every other plugin on the user's machine: shorter is safer; only `disable-model-invocation: true` skills cost nothing in the listing (`user-invocable: false` skills still carry their full description — the flag only hides them from the `/` menu). - Third-person imperative everywhere: "Use when...", "Covers...", never "I will..." or "This skill helps you...". ## 2. SKILL.md size and structure - **< 500 lines.** The body is a recurring token cost; it persists for the whole session once invoked. - Write **standing instructions** (rules that hold every turn), not narration or one-time setup steps. - Critical guidance goes in the **first 5,000 tokens**: auto-compaction re-attaches only the first 5,000 tokens of each invoked skill, inside a 25,000-token combined budget, most-recently-invoked first. Tails of long skills vanish. - Bulk material goes to `references/` (one level deep). SKILL.md must name each supporting file with what it contains and when to load it, e.g. `- For the full grader taxonomy, see references/<topic>.md — load when choosing graders.` - Never use `@`-links to files (they force-load content into context). Never put README/CHANGELOG inside a skill directory (plugin-level README is separate and required). - Scripts live in `scripts/` under the skill and are **executed, not loaded**. ## 3. Paths and portability ### Paired-path allowed-tools (the zero-prompt bundled-script pattern) Every bundled script a generated skill runs gets an `allowed-tools` rule whose string is **IDENTICAL** to the invocation string in the body. Make the script executable with a shebang and invoke it by path (no `python3 ` / `bash ` prefix — a prefix breaks the rule match): ```yaml allowed-tools: Bash(${CLAUDE_SKILL_DIR}/scripts/render.py *) ``` and in the body: ``` ${CLAUDE_SKILL_DIR}/scripts/render.py --input data.csv ``` - Skill-local assets → `${CLAUDE_SKILL_DIR}/…`. Plugin-level assets (hooks/, bin/, shared scripts/) → `${CLAUDE_PLUGIN_ROOT}/…`. Never relative paths, never `../` escapes (the marketplace cache copy breaks them; symlinks outside the marketplace are skipped). - `${CLAUDE_SKILL_DIR}` inside `allowed-tools` requires Claude Code ≥ 2.1.129 (older versions treat it as a literal string that never matches, so every run prompts). State the floor in the generated README. - `allowed-tools` grants last **one turn** — they clear on the user's next message even though skill content persists. Multi-turn workflows need a `permissions.allow` rule in the consumer settings snippet instead (see `templates/settings-snippet.tmpl.json`). ### Persistent state → ${CLAUDE_PLUGIN_DATA}, never ROOT - `${CLAUDE_PLUGIN_ROOT}` is the install directory: it **changes on every update** and old copies are garbage-collected (~14 days). Anything written there is lost. - Durable data — traces, run artifacts, caches, learned state — goes to `${CLAUDE_PLUGIN_DATA}` (survives updates, created on first reference, removed on last-scope uninstall unless `--keep-data`). - Both are substituted in skill/agent content, hook and monitor commands, and MCP/LSP config fields, and exported as env vars to hook/MCP/LSP subprocesses. ## 4. Invocation control — decide per skill, record the rationale | Situation | Frontmatter | Effect | |---|---|---| | Side-effectful, user-gated workflow (deploy, publish, ship, commit) | `disable-model-invocation: true` | Only the user invokes. Description never enters context (costs nothing). ALSO blocks Skill-tool programmatic invocation, subagent preloading, and scheduled-task invocation. | | Pure background knowledge (schemas, conventions, house style) | `user-invocable: false` | Hidden from the `/` menu; the model loads it on demand. Does NOT block Skill-tool access. | | Conventions relevant only to certain files | `paths:` globs | Auto-activation limited to matching files; cuts false triggers and listing cost. | | Skill that must be BOTH user-callable and chainable by a conductor | neither flag (default) | Both can invoke. Guard misuse inside the body (state checks), not with invocation flags. | - **The D2 worked example (record it in every PDR):** a conductor cannot Skill-tool invoke a skill marked `disable-model-invocation: true`. Pipelines that chain phase skills must leave those skills on default invocation and self-guard by reading their state file, reserving `disable-model-invocation: true` for the entry and exit points only. plugin-forge itself does exactly this: only `forge` and `ship` carry the flag. - Write one line of rationale per skill into the PDR slot ledger. "Default because nobody decided" is a finding, not a rationale. - Booleans are `true`/`false` only — `yes`/`on`/`1` need ≥ 2.1.218 and silently degrade on older versions. ## 5. Hook output discipline - **Exec-form commands only**: `"command": "${CLAUDE_PLUGIN_ROOT}/hooks/scripts/x.sh"` (plus optional `"args": []`). `${user_config.*}` is rejected in shell-form fields — pass it via exec-form args or read `CLAUDE_PLUGIN_OPTION_<KEY>` from the env. - Every hook script: `chmod +x`, `set -euo pipefail`, read stdin exactly once with `INPUT="$(cat)"`, parse JSON with `python3 -c` (stdlib), and take a **fast no-op path before reading stdin** when the guard cannot apply (< 10ms exit 0). - **Exit-code contract**: exit 0 = no objection (it does NOT force-approve PreToolUse; normal permission flow continues). Exit 2 = block, with the reason on **stderr** (fed back to Claude). Any other exit code = action proceeds with a logged error. - **Never mix exit 2 with JSON output.** Stdout JSON is IGNORED when the script exits 2. Choose per hook: stderr-then-exit-2, OR exit 0 + structured JSON. Not both. - Per-event output schemas differ — emit the right one: - `PreToolUse`: `{"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"allow|deny|ask","permissionDecisionReason":"…"}}`. Never the deprecated `{"decision":"approve"}` shape. - `PostToolUse`, `Stop`: top-level `{"decision":"block","reason":"…"}` (blocking only; exit 0 silent otherwise). Stop scripts must honor `stop_hook_active` to avoid loops. - `UserPromptSubmit` / `SessionStart`: stdout enters context; structured additions go in `hookSpecificOutput.additionalContext` (a top-level `additionalContext` key is silently ignored). - Matchers: event names are case-sensitive; tool matchers are regex (`Edit|Write`). Matchers and permission rules that reference a **bundled** MCP server's tools must use the plugin-scoped form `mcp__plugin_<plugin>_<server>__<tool>` — a bare `mcp__<server>__<tool>` never fires for bundled servers. - A malformed `hooks/hooks.json` prevents the ENTIRE plugin from loading. Validate the JSON before shipping. - Every generated hook ships a fixture test: `echo '<recorded event JSON>' | script.sh` with asserted exit code and output channel. Hooks are code; untested hooks are prose. ## 6. Degrees-of-freedom mapping Match the freedom level of each capability to its fragility, and let it drive the whole composition (this is the C3/C5 backbone of the PDR): | Freedom | Use for | Generate | Enforce with | Grade with | |---|---|---|---|---| | **Low** | Fragile/regulated operations: lab protocols, finance postings, destructive migrations, data-integrity rules | Locked, deterministic bundled scripts (stdlib-only), invoked via paired-path allowed-tools | PreToolUse/PostToolUse hooks (mechanical denial, not instructions) | Deterministic graders: workspace/file state, exit codes, `state_check`; numeric checks REQUIRE a `tolerance` | | **Medium** | Repeatable procedures with judgment at the edges | Stepwise checklists / pseudocode in skill bodies | Prose + spot-check hooks | Transcript graders (tool_called, budgets) + judges for the edges | | **High** | Judgment work: analysis, synthesis, review, writing | Prose skills with principles and worked examples | Prose (justify "prose" in the PDR C3 column) | Calibrated LLM judges — one rubric dimension per judge call, Unknown verdict allowed | If a capability's enforcement column says "prose" but its failure cost is high, the composition is wrong: push it down a row (script + hook + deterministic grader). ## 7. Scripts, manifests, and layout - Python: stdlib only, `#!/usr/bin/env python3`, no third-party deps, no install step. Bash: `#!/usr/bin/env bash`, `set -euo pipefail`, shellcheck-clean. - Manifest (`.claude-plugin/plugin.json`): include `"$schema": "https://json.schemastore.org/claude-code-plugin-manifest.json"`, kebab-case `name`, full metadata. **No `version` field during development** — omitted means the git SHA is the version and every commit reaches users; a pinned semver that never gets bumped strands users on a stale cache. Pin semver at release only (ship's job). - Component directories (`skills/`, `agents/`, `hooks/`, `.mcp.json`, `bin/`, …) live at the **plugin root**, never inside `.claude-plugin/` (only `plugin.json` goes there — the #1 documented mistake). - Never scaffold a `commands/` directory: it is the legacy form. Every entry point is `skills/<name>/SKILL.md`. - Always set frontmatter `name` on plugin skills (it becomes the command's last segment; without it, marketplace installs can surface a version-string name). - Live-state skills (dynamic `!`-backtick injection headers) need a documented fallback for environments with `disableSkillShellExecution` — the injected line degrades to a policy notice, so the body must say what to do when the state read is absent. ## 8. FRESHNESS GUARD — the greps ship runs Deprecated shapes rot silently: the host toolkit's own hook-builder emitted the old `{"decision":"approve"}` JSON and skill-forge referenced dead `/mnt/skills` paths. Before packaging, run this exact block against the generated plugin directory. Any FG hit is a blocking finding — fix the emitter, not just the output. ```bash PLUGIN_DIR="${1:?usage: freshness-guard <generated-plugin-dir>}" FAIL=0 # FG-1: deprecated hook decision JSON — modern PreToolUse uses # hookSpecificOutput.permissionDecision, never {"decision":"approve"}. grep -rnE '"decision"[[:space:]]*:[[:space:]]*"approve"' "$PLUGIN_DIR" && FAIL=1 # FG-2: stale sandbox doc paths — /mnt/skills does not exist in Claude Code. grep -rn '/mnt/skills' "$PLUGIN_DIR" && FAIL=1 # FG-3: bare MCP tool names — bundled-server tools are # mcp__plugin_<plugin>_<server>__<tool>; bare mcp__<server>__<tool> never fires. grep -rnE 'mcp__[A-Za-z0-9_-]+__[A-Za-z0-9_-]+' "$PLUGIN_DIR" | grep -v 'mcp__plugin_' && FAIL=1 # FG-4: legacy commands/ scaffolding — entry points are skills/<name>/SKILL.md. [ -d "$PLUGIN_DIR/commands" ] && { echo "$PLUGIN_DIR/commands: legacy commands/ directory"; FAIL=1; } grep -rn '"commands"' "$PLUGIN_DIR/.claude-plugin/plugin.json" 2>/dev/null && FAIL=1 exit "$FAIL" ``` Reading the results: - **FG-1** has no legitimate hits. `{"decision":"block"}` remains valid for PostToolUse/Stop and is deliberately NOT flagged. - **FG-3** allows one reviewed exception: references to servers the plugin does *not* bundle (user-level or external MCP servers) legitimately use unscoped names. Confirm each hit is external before waiving it; note the pipeline can miss a bare name that shares a line with a scoped one, so scan multi-reference lines manually. - **FG-4**: `"commands"` in the manifest is only valid when deliberately remapping the legacy dir — for generated plugins, treat any hit as a failure. - **Self-scans**: this file necessarily contains the patterns it hunts. When running the guard over plugin-forge itself (dogfood lint), exclude `skills/generation-standards/SKILL.md`; generated plugins never contain it, so ship runs the block verbatim. Advisory greps (warn, do not fail): `grep -rn 'streamable-http' "$PLUGIN_DIR"` (`"streamable-http"` is an accepted alias for `"http"`; prefer the canonical `"type": "http"` in generated `.mcp.json` for consistency), and `grep -rn 'dangerously-skip-permissions' "$PLUGIN_DIR"` (generated harnesses use a sandbox settings profile instead — see `templates/settings-snippet.tmpl.json`). ## 9. Templates index All templates carry `{{PLACEHOLDER}}` markers and a header note naming the filler (the build loop). JSON templates hold their notes in `"//"` keys — **delete every `"//"` key and template comment after filling**. Load a template only when emitting that file type. | Template | Emits | Load when | |---|---|---| | [templates/skill.tmpl.md](templates/skill.tmpl.md) | `skills/<name>/SKILL.md` | Writing any generated skill | | [templates/agent.tmpl.md](templates/agent.tmpl.md) | `agents/<name>.md` | Writing any generated subagent | | [templates/hooks.tmpl.json](templates/hooks.tmpl.json) | `hooks/hooks.json` | Wiring generated hook events | | [templates/hook-script.tmpl.sh](templates/hook-script.tmpl.sh) | `hooks/scripts/<name>.sh` | Writing any hook handler | | [templates/mcp.tmpl.json](templates/mcp.tmpl.json) | `.mcp.json` | Bundling an MCP server | | [templates/plugin-manifest.tmpl.json](templates/plugin-manifest.tmpl.json) | `.claude-plugin/plugin.json` | Creating the manifest | | [templates/marketplace-entry.tmpl.json](templates/marketplace-entry.tmpl.json) | entry in `marketplace.json` `plugins[]` | Registering in a marketplace | | [templates/readme.tmpl.md](templates/readme.tmpl.md) | plugin `README.md` | Writing the plugin's README (ship phase) | | [templates/settings-snippet.tmpl.json](templates/settings-snippet.tmpl.json) | consumer `.claude/settings.json` snippet | Emitting the team-distribution / permission story |
GitHubで見る