@vegastack/skills 0.6.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/README.md +4 -0
  2. package/package.json +1 -1
  3. package/skill/dev-implement/SKILL.md +62 -0
  4. package/skill/dev-implement/agents/openai.yaml +4 -0
  5. package/skill/dev-implement/refresh/REFRESH.md +3 -0
  6. package/skill/dev-implement/refresh/sources.json +6 -0
  7. package/skill/dev-intake/SKILL.md +62 -0
  8. package/skill/dev-intake/agents/openai.yaml +4 -0
  9. package/skill/dev-intake/references/brief-template.md +56 -0
  10. package/skill/dev-intake/refresh/REFRESH.md +3 -0
  11. package/skill/dev-intake/refresh/sources.json +6 -0
  12. package/skill/dev-setup/SKILL.md +65 -0
  13. package/skill/dev-setup/agents/openai.yaml +4 -0
  14. package/skill/dev-setup/assets/agents-section.md.template +9 -0
  15. package/skill/dev-setup/assets/dev-profile.md.template +24 -0
  16. package/skill/dev-setup/references/harness-facts.md +22 -0
  17. package/skill/dev-setup/refresh/REFRESH.md +13 -0
  18. package/skill/dev-setup/refresh/sources.json +152 -0
  19. package/skill/dev-ship/SKILL.md +33 -0
  20. package/skill/dev-ship/agents/openai.yaml +4 -0
  21. package/skill/dev-ship/refresh/REFRESH.md +3 -0
  22. package/skill/dev-ship/refresh/sources.json +6 -0
  23. package/skill/skill-maintainer/SKILL.md +9 -10
  24. package/skill/skill-maintainer/references/release-ops.md +1 -1
  25. package/skill/skill-maintainer/refresh/REFRESH.md +1 -1
  26. package/skill/skillify/SKILL.md +38 -48
  27. package/skill/skillify/assets/templates/REFRESH.md.template +5 -10
  28. package/skill/skillify/assets/templates/SKILL.md.template +1 -0
  29. package/skill/skillify/assets/templates/skill.test.ts.template +11 -24
  30. package/skill/skillify/assets/templates/sources.json.template +1 -1
  31. package/skill/skillify/assets/templates/trigger-queries.json.template +1 -0
  32. package/skill/skillify/references/authoring.md +20 -14
  33. package/skill/skillify/refresh/REFRESH.md +1 -1
  34. package/skill/skillify/scripts/scaffold-skill.mjs +75 -17
  35. package/skill-integrity.json +48 -11
@@ -9,7 +9,7 @@ Maintenance skill for this repository. Every change under `skills/` must satisfy
9
9
 
10
10
  ## Operating rules
11
11
 
12
- 1. Skill content lives only in `skills/<name>/`. Wiring lives outside it: the packaging allowlist in `packages/cli/scripts/sync-skill.mjs`, the root `README.md` skills table, and `CHANGELOG.md` (via changesets).
12
+ 1. Skill content lives only in `skills/<name>/`. Wiring lives outside it: the per-skill packaging allowlist in `packages/cli/packaging.json`, the root `README.md` skills table, and `CHANGELOG.md` (via changesets). The skillify scaffolder writes all three when creating a skill.
13
13
  2. Frontmatter is exactly two keys: `name` and `description`. The open spec also allows `license`, `compatibility`, and `metadata`, but this repo defaults to the minimal two; adding any other key is a policy exception needing maintainer sign-off.
14
14
  3. `name` must equal the directory name. Grammar: starts with a lowercase letter, then only lowercase letters, digits, and hyphens; no leading/trailing hyphen, no consecutive hyphens; at most 64 chars.
15
15
  4. `description`: at most 1024 chars, third person, states WHAT the skill does and WHEN to load it, trigger words front-loaded, no angle brackets. Never summarize the workflow in it — agents follow the summary and skip the body. Err slightly pushy: agents under-trigger.
@@ -23,29 +23,28 @@ Maintenance skill for this repository. Every change under `skills/` must satisfy
23
23
  | Need | Read |
24
24
  |---|---|
25
25
  | tri-harness standards: discovery paths, frontmatter rules, context budgets, install surfaces, portability rules, unverified items | [standards](references/standards.md) |
26
+ | new skill: should-it-exist gate, scaffolding, the 8-item contract audit, behavioral evals | the `skillify` skill |
26
27
  | release, rename, deprecate, rollback mechanics | [release ops](references/release-ops.md) |
27
28
  | this skill's own freshness contract | [REFRESH](refresh/REFRESH.md), [sources](refresh/sources.json) |
28
29
  | authoritative repo policy | `CONTRIBUTING.md`, `docs/policies/content-versioning.md`, `docs/policies/release-and-rollback.md` at the repo root |
29
30
 
30
31
  ## Workflow: scaffold a new skill
31
32
 
32
- 1. Create `skills/<name>/` with the full per-skill contract: `SKILL.md` (agent entry), `README.md` (repo-side walkthrough, never packaged), `references/`, `tests/` (bun tests, never packaged), `refresh/sources.json` + `refresh/REFRESH.md` (freshness contract), `agents/openai.yaml` (Codex interface metadata); `scripts/` and `assets/` only if the skill needs them.
33
- 2. Write frontmatter and body per operating rules 2–6; the body routes to references, it does not inline them.
34
- 3. Wire it in: add every packaged file to the allowlist in `packages/cli/scripts/sync-skill.mjs` (the build fails loudly on unlisted files); add a row to the root README skills table; add a changeset (a new skill is MINOR).
35
- 4. The installer is multi-skill: every authored skill needs a packaging allowlist entry in packages/cli/scripts/sync-skill.mjs (the build fails loudly on unlisted files).
36
- 5. Seed refresh baselines with the deterministic runner, never by hand — invocation in [REFRESH](refresh/REFRESH.md).
37
- 6. Run the checks in operating rule 8.
33
+ 1. Run skillify's `scripts/scaffold-skill.mjs` it creates the contract tree (`SKILL.md`, `README.md`, `refresh/`, `agents/openai.yaml`, tests and the trigger fixture) and performs the repo wiring itself: the `packages/cli/packaging.json` entry, the root README row, and the changeset (a new skill is MINOR). Fill in the README row description and changeset text it leaves as TODOs; files added after scaffolding go into the skill's `packaging.json` entry by hand (the build fails loudly on unlisted files).
34
+ 2. Write frontmatter and body per operating rules 2–6; the body routes to references, it does not inline them. `skillify` owns the full authoring and eval discipline.
35
+ 3. Seed refresh baselines with the deterministic runner, never by hand invocation in [REFRESH](refresh/REFRESH.md).
36
+ 4. Run the checks in operating rule 8.
38
37
 
39
38
  ## Workflow: update or maintain
40
39
 
41
- - **Stable IDs are permanent.** Never renumber or reuse a rule ID; removing or renaming one is a MAJOR content change (`docs/policies/content-versioning.md`). New rules/references are MINOR; weakening a MUST is MAJOR; factual refreshes (pins, URLs, checksums) are PATCH.
40
+ - **Content versioning.** New rules/references and new recorded decisions are MINOR; weakening a normative rule, removing or renaming a skill, or breaking a per-project profile format is MAJOR; factual refreshes (pins, URLs, checksums) are PATCH (`docs/policies/content-versioning.md`).
42
41
  - **Tag volatile claims.** Any sentence carrying a vendor version, mechanism name, or numeric budget gets a source marker comment mapping to an ID in that skill's `refresh/sources.json`, and the registry entry's `affected` list must name the reference it lives in. Untagged volatile facts rot silently.
43
42
  - **Description budgets.** Stay within 1024 chars and keep triggers in the first sentence: Codex truncates its skill list at 2% of the context window / 8,000 chars, Claude Code truncates a skill's always-loaded listing at 1,536 chars — the tail of a long description is the first thing lost.
44
- - **Packaged-file changes.** Any add/remove/rename of a packaged file must update the `sync-skill.mjs` allowlist in the same PR.
43
+ - **Packaged-file changes.** Any add/remove/rename of a packaged file must update that skill's `packages/cli/packaging.json` entry in the same PR.
45
44
 
46
45
  ## Workflow: rename, deprecate, or remove
47
46
 
48
- Full playbook in [release ops](references/release-ops.md). Short form: a rename changes the directory and the frontmatter `name` in the same commit (they must stay equal), updates allowlist + root README table + CHANGELOG, and is MAJOR. Deprecation is announced in README/CHANGELOG before removal. Removal deletes the tree, unwires it, and is MAJOR.
47
+ Full playbook in [release ops](references/release-ops.md). Short form: a rename changes the directory and the frontmatter `name` in the same commit (they must stay equal), updates the packaging.json entry + root README table + CHANGELOG, and is MAJOR. Deprecation is announced in README/CHANGELOG before removal. Removal deletes the tree, unwires it, and is MAJOR.
49
48
 
50
49
  ## Workflow: release
51
50
 
@@ -43,7 +43,7 @@ Contributors do not bump versions in PRs; releases are maintainer-driven.
43
43
  Skill names are consumer-facing identifiers — treat a rename as a stable-ID break:
44
44
 
45
45
  1. Rename the directory and the frontmatter `name` in the same commit — they must always stay equal.
46
- 2. Update every wiring point in the same PR: the packaging allowlist in `packages/cli/scripts/sync-skill.mjs`, the root README skills table row, and any cross-skill or docs links.
46
+ 2. Update every wiring point in the same PR: the skill's entry in `packages/cli/packaging.json`, the root README skills table row, and any cross-skill or docs links.
47
47
  3. Changeset: MAJOR. Note the migration in `CHANGELOG.md`: copies installed under the old name are orphaned, and installer operations addressed to the old name stop resolving once the shipped manifest no longer knows it.
48
48
  4. Registry source IDs inside `refresh/sources.json` are skill-internal and unaffected, but every `affected` ref must still resolve to a real reference in the renamed tree.
49
49
  5. Re-run `node packages/cli/scripts/validate-skill.mjs skills/<new-name>` and the skill's tests — name/directory equality is validated.
@@ -5,7 +5,7 @@ Instructions for the scheduled refresh agent (and any human running a manual ref
5
5
  ## What this skill claims
6
6
 
7
7
  - **Durable content** (`SKILL.md` workflows and operating rules, `references/release-ops.md`): repo process, derived from `docs/policies/*` — the refresh agent NEVER edits these. If a policy doc changes, that is a normal human PR, not a refresh.
8
- - **Mechanism-coupled claims**: sentences marked `<!-- source: SOURCE-ID -->` inside `references/standards.md`. These carry the tri-harness standards — discovery paths, frontmatter rules, numeric context budgets, install surfaces. They are the compliance basis of every skill in this repo, so **all standards changes are semantic drift requiring a human-reviewed PR**: the agent may propose edits to marked sentences only, in the same PR as the registry update that evidences the change, and a maintainer must review before merge. The hard-limits table in `SKILL.md` mirrors several marked sentences; update it in the same PR (this is the one sanctioned SKILL.md touch, and only when a mirrored number changed).
8
+ - **Volatile standards claims**: sentences marked `<!-- source: SOURCE-ID -->` inside `references/standards.md`. These carry the tri-harness standards — discovery paths, frontmatter rules, numeric context budgets, install surfaces. They are the compliance basis of every skill in this repo, so **all standards changes are semantic drift requiring a human-reviewed PR**: the agent may propose edits to marked sentences only, in the same PR as the registry update that evidences the change, and a maintainer must review before merge. The hard-limits table in `SKILL.md` mirrors several marked sentences; update it in the same PR (this is the one sanctioned SKILL.md touch, and only when a mirrored number changed).
9
9
  - **Volatile layer** (the only file the agent edits freely): `refresh/sources.json` — the source registry and staleness snapshot (checksums, retrieval times).
10
10
 
11
11
  ## How to refresh
@@ -5,27 +5,22 @@ description: Repo-local skill factory and auditor for the vegastack-skills monor
5
5
 
6
6
  # Skillify
7
7
 
8
- Turn a raw workflow into a properly-skilled unit of this monorepo, or audit an existing `skills/<name>/` tree against the full repo contract (CONTRIBUTING.md, "Adding a new skill"). Skillify is repo-only: it creates and scores skills in this repository, not anywhere else. Deep wiring/release mechanics belong to `skill-maintainer`; cross-reference it rather than restating it.
8
+ Turn a raw workflow into a properly-skilled unit of this monorepo, or audit an existing `skills/<name>/` tree against the repo contract (CONTRIBUTING.md, "Adding a new skill"). Skillify is repo-only: it creates and scores skills in this repository, not anywhere else. Deep wiring/release mechanics belong to `skill-maintainer`; cross-reference it rather than restating it.
9
9
 
10
- ## The completeness checklist
10
+ ## The contract checklist
11
11
 
12
- Score every item pass / fail / N/A. N/A requires a one-line rationale; N/A without one is a fail.
12
+ Score every item pass / fail / N/A. N/A requires a one-line rationale; N/A without one is a fail. Numbering is stable and additive — future items append, existing items never renumber — so other skills can cite "skillify item 6".
13
13
 
14
- 1. **SKILL.md spec-compliant** — frontmatter is exactly `name` + `description`; name matches the directory and the grammar (starts with a lowercase letter, `[a-z0-9-]`, no consecutive hyphens, max 64); description max 1024 chars with no angle brackets; body under 500 lines; no harness-specific body syntax. `bun run validate:skill` agrees.
15
- 2. **Description triggers well** — states triggering conditions only (never the workflow), third person, trigger words front-loaded, per [authoring](references/authoring.md); a should/should-not-trigger query set with near-miss negatives exists at `tests/fixtures/trigger-queries.json`.
16
- 3. **README walkthrough** — repo-side `README.md`: install, contents table, behavior contract.
17
- 4. **References routed** — `references/` for on-demand detail with a routing table in SKILL.md; or N/A for a self-contained skill.
18
- 5. **Scripts deterministic** — dependency-free Node under `scripts/`, `--json` for machine output, documented exit codes, atomic writes and symlink refusal for anything mutating; or N/A.
19
- 6. **Assets** — templates/schemas/examples under `assets/`; or N/A.
20
- 7. **Unit tests + fixtures** — `tests/*.test.ts` (bun test) covering every deterministic branch; fixtures under `tests/fixtures/`.
21
- 8. **Consistency test** — a test asserting prose, scripts, templates, and registry agree: relative links resolve, documented flags exist, template inventory matches the scaffolder.
22
- 9. **Behavioral eval evidence** — with-skill vs baseline subagent runs on 2–3 realistic prompts per the [eval playbook](references/eval-playbook.md); summary in the PR body; unresolved gaps in a KNOWN_GAPS section of the skill's README.
23
- 10. **Refresh contract** — `refresh/sources.json` + `refresh/REFRESH.md` tracking every volatile claim; or an explicit evergreen waiver in REFRESH.md stating why nothing in the skill decays.
24
- 11. **Codex metadata** — `agents/openai.yaml`.
25
- 12. **Repo wiring** — sync-skill allowlist entry, root README table row, CHANGELOG entry (`skill-maintainer` owns the detail).
26
- 13. **Green check** — `bun run check` passes.
14
+ 1. **SKILL.md spec-compliant** — frontmatter is exactly `name` + `description`; name matches the directory and the grammar (starts with a lowercase letter, `[a-z0-9-]`, no consecutive hyphens, max 64); description max 1024 chars with no angle brackets; body targets 50–150 lines with 300 as the ceiling; no harness-specific body syntax. `bun run validate:skill` agrees — it also verifies every relative link in the skill's prose resolves.
15
+ 2. **Description triggers well** — states triggering conditions only (never the workflow), third person, trigger words front-loaded, per [authoring](references/authoring.md); a query set of realistic positives and near-miss negatives exists at `tests/fixtures/trigger-queries.json`, with `ambiguous_with` naming the nearest competing skill where one exists.
16
+ 3. **Sharp boundary** — the SKILL.md body names its nearest-neighbor skill and the one-sentence axis of difference (or states it has no neighbor). Two skills answering the same trigger get merged, not shipped.
17
+ 4. **References routed** — `references/` holds detail only some invocations need, behind a routing table; SKILL.md keeps the workflow, one excellent example, and the routes. N/A for a self-contained skill.
18
+ 5. **Scripts deterministic and tested** — dependency-free Node under `scripts/`, `--json` for machine output, documented exit codes, atomic writes and symlink refusal for anything mutating, dry-run default; unit tests cover every deterministic branch. N/A when the skill ships no scripts — a prose-only skill needs no test theater; its quality bar is item 6.
19
+ 6. **Behavioral eval passed** — with-skill vs baseline subagent runs on 2–3 realistic prompts per the [eval playbook](references/eval-playbook.md), at most 3 improve cycles; pass, or ship with a KNOWN_GAPS section in the skill's README.
20
+ 7. **Freshness honest** — volatile facts (version pins, model names, numeric limits, dated claims) live in refresh-tracked files, never in SKILL.md; a skill with none states a one-line evergreen waiver in `refresh/REFRESH.md`.
21
+ 8. **Wired and green** — README walkthrough and `agents/openai.yaml` exist; packaging entry, root README row, and changeset are in place (the scaffolder performs these); `bun run check` passes.
27
22
 
28
- **Verdict:** all pass → `properly skilled`. At most three misses, none of items 1, 7, or 13 → `close — create: <missing items>`. Otherwise → `needs skillify — run skillify on <target>`. Always report the score as `<passed>/13` plus the verdict.
23
+ **Verdict:** all pass → `properly skilled`. At most two misses, neither item 1 nor item 6 → `close — create: <missing items>`. Otherwise → `needs skillify — run skillify on <target>`. Always report the score as `<passed>/8` plus the verdict.
29
24
 
30
25
  ## Phase 0 — Should this be a skill?
31
26
 
@@ -37,6 +32,8 @@ Before anything else, check:
37
32
 
38
33
  If any answer is no: it is a script or a doc, not a skill. Stop — do not scaffold, do not write a SKILL.md. Say why and move on.
39
34
 
35
+ Then search the existing skills. If one nearly covers the intent, **prefer merging into it** over creating a near-duplicate — a new neighbor that splits an existing trigger family makes both skills trigger worse.
36
+
40
37
  Scope upper bound: **one skill = one capability = one coherent trigger family.** If the target spans distinct intents users would invoke separately ("run the build" / "roll back the deploy" / "notify the team" are three intents), do not build one skill covering them all. Propose the split and ask which target to skillify first.
41
38
 
42
39
  ## Phase 1 — Audit
@@ -45,30 +42,22 @@ For an existing skill, score the checklist against the actual tree and stop with
45
42
 
46
43
  ```
47
44
  Skill: <name> Path: skills/<name>/
48
- Score: <passed>/13 Verdict: <verdict>
45
+ Score: <passed>/8 Verdict: <verdict>
49
46
  Missing: <item>: <one-line evidence> ...
50
47
  ```
51
48
 
52
- For a new skill the audit is trivially 0/13 — proceed. When the request was only "audit" / "is this complete", deliver the verdict and the shortest path to `properly skilled`; do not start editing unasked.
49
+ For a new skill the audit is trivially 0/8 — proceed. When the request was only "audit" / "is this complete", deliver the verdict and the shortest path to `properly skilled`; do not start editing unasked.
53
50
 
54
51
  ## Phase 2 — Elicit requirements
55
52
 
56
53
  Interview before writing (skip questions the conversation already answers):
57
54
 
58
- - **Triggers:** what exact phrases should invoke this? What near-miss requests should NOT? (These become the query set.)
55
+ - **Triggers:** what exact phrases should invoke this? What near-miss requests should NOT? Which existing skill is the nearest neighbor? (These become the query set and the boundary line.)
59
56
  - **Output:** what does done look like — files, report shape, side effects?
60
57
  - **Edge cases:** empty input, missing config, offline, partial state?
61
58
  - **Existing behavior:** is there code/prose to absorb? What did its author correct over time?
62
59
 
63
- Then classify every factual claim the skill will make:
64
-
65
- | Class | Meaning | Where it lives |
66
- |---|---|---|
67
- | Durable | versionless principle | references prose; refresh never edits |
68
- | Mechanism-coupled | durable intent through a vendor-named mechanism | marked `<!-- source: SOURCE-ID -->`; edited only with registry evidence |
69
- | Volatile | version pins, limits, model names, URLs | `refresh/sources.json` entries or refresh-tracked files only |
70
-
71
- Every mechanism-coupled or volatile claim needs a registry source — or the skill declares an evergreen waiver. No unclassified claims ship.
60
+ While eliciting, mark every volatile fact the skill will state — version pins, model names, numeric limits, dated claims. Those go to refresh-tracked files (item 7); everything else is plain prose. A skill with no volatile facts takes the one-line evergreen waiver.
72
61
 
73
62
  ## Phase 3 — Scaffold and write
74
63
 
@@ -79,13 +68,12 @@ node <skill-dir>/scripts/scaffold-skill.mjs <name> --dir <repo-root>
79
68
  node <skill-dir>/scripts/scaffold-skill.mjs <name> --dir <repo-root> --write
80
69
  ```
81
70
 
82
- The scaffolder validates the name grammar, refuses existing directories and symlinks, stages in a temp sibling then renames, and prints the manual wiring steps. Then write, in this order:
71
+ The scaffolder validates the name grammar, refuses existing directories and symlinks, stages in a temp sibling then renames, and performs the repo wiring itself (packaging entry, root README row, changeset) — fill in the README row's TODO description. Then write, in this order:
83
72
 
84
- 1. *Description + trigger query set* — engineer the description per [authoring](references/authoring.md); write 8–10 should-trigger and 8–10 near-miss should-not-trigger queries into `tests/fixtures/trigger-queries.json`.
85
- 2. *SKILL.md body* — workflow, one excellent example, routing table. Respect the token budgets in [authoring](references/authoring.md).
73
+ 1. *Description + trigger query set* — engineer the description per [authoring](references/authoring.md); write the query set with near-miss negatives into `tests/fixtures/trigger-queries.json`.
74
+ 2. *SKILL.md body* — workflow, one excellent example, nearest-neighbor boundary line, routing table. Respect the writing-style rules and token budgets in [authoring](references/authoring.md).
86
75
  3. *References* — detail that only some invocations need.
87
76
  4. *Scripts* — only for work that is deterministic and repeated (criteria in [authoring](references/authoring.md)).
88
- 5. *Draft tests* — but do not treat them as the quality bar yet; that is Phase 4's job.
89
77
 
90
78
  ## Phase 4 — Behavioral eval — the quality gate
91
79
 
@@ -99,10 +87,9 @@ Iterate at most 3 cycles: eval → apply the top improvements to the skill → r
99
87
 
100
88
  Now that quality is proven:
101
89
 
102
- 1. Finalize unit tests and fixtures locking in the eval-proven behavior; add the consistency test (item 8).
103
- 2. Write `refresh/sources.json` + `refresh/REFRESH.md` from the Phase 2 claim classification — or the evergreen waiver.
104
- 3. Finish `README.md` and `agents/openai.yaml`.
105
- 4. Wire the repo: sync-skill allowlist, root README row, CHANGELOG entry — as printed by the scaffolder; `skill-maintainer` documents each step in depth.
90
+ 1. Write unit tests for the deterministic branches of any bundled scripts, plus fixtures. A prose-only skill keeps just its trigger-query fixture.
91
+ 2. Write `refresh/sources.json` + `refresh/REFRESH.md` for the volatile facts marked in Phase 2 — or the one-line evergreen waiver.
92
+ 3. Finish `README.md` and `agents/openai.yaml`; fill in the wiring TODOs the scaffolder left (README row description, changeset text).
106
93
 
107
94
  ## Phase 6 — Verify
108
95
 
@@ -112,23 +99,24 @@ bun test skills/<name>
112
99
  bun run check
113
100
  ```
114
101
 
115
- Re-score the checklist and report `<passed>/13` with the verdict. Anything below `properly skilled` ships only with named gaps.
102
+ Re-score the checklist and report `<passed>/8` with the verdict. Anything below `properly skilled` ships only with named gaps.
116
103
 
117
104
  ## Worked example: skillifying a "release-notes" workflow
118
105
 
119
106
  ```
120
- Phase 0: yes — run at every release, ~80 lines of conventions, trigger "draft the release notes"
121
- Phase 1: 0/13 (new)
107
+ Phase 0: yes — run at every release, ~80 lines of conventions, trigger "draft the release notes";
108
+ nearest neighbor is skill-maintainer (release wiring) — different axis, proceed
109
+ Phase 1: 0/8 (new)
122
110
  Phase 2: triggers "draft/write the release notes", NOT "write a changelog entry for this PR";
123
- output = CHANGELOG section + npm summary; claims: changeset flow = durable,
124
- npm publish mechanics = mechanism-coupled (source NPM-PUBLISH), CLI version pin = volatile
125
- Phase 3: scaffold-skill.mjs release-notes --dir . --write; description + 18 trigger queries;
126
- body + references/format.md; no scripts (judgment-heavy, N/A with rationale)
111
+ output = CHANGELOG section + npm summary; volatile: none owned evergreen waiver
112
+ Phase 3: scaffold-skill.mjs release-notes --dir . --write (wiring done by the scaffolder);
113
+ description + 10 trigger queries incl. ambiguous_with skill-maintainer; body + references/format.md;
114
+ no scripts (judgment-heavy, item 5 N/A)
127
115
  Phase 4: eval cycle 1 — baseline subagent invents section headings, with-skill misses breaking-change
128
116
  callouts → add callout contract to SKILL.md; cycle 2 — with-skill clearly better, baseline still
129
117
  wrong shape → pass
130
- Phase 5: tests lock the section contract; sources.json gets NPM-PUBLISH; wiring done
131
- Phase 6: bun run check green; 13/13 → properly skilled
118
+ Phase 5: trigger fixture locked; evergreen waiver written; README row description filled in
119
+ Phase 6: bun run check green; 8/8 → properly skilled
132
120
  ```
133
121
 
134
122
  ## Anti-patterns
@@ -141,14 +129,16 @@ Phase 6: bun run check green; 13/13 → properly skilled
141
129
  - Version pins or model names in SKILL.md — volatile facts live in refresh-tracked locations.
142
130
  - Multi-intent skills spanning unrelated triggers — split them.
143
131
  - Two skills answering the same trigger — merge or kill one.
144
- - Prose and scripts disagreeing about flags or paths that is what the consistency test catches.
132
+ - Steering by prohibition a wall of NEVERs drags the forbidden behavior into context and grows forever; state the positive rule once and explain why it matters.
133
+ - Patching every observed agent failure with a new clause — that is sediment; rewrite the existing rule in place instead of appending case law.
134
+ - Unit tests for prose — a test asserting a markdown file contains a phrase proves nothing the eval didn't.
145
135
  - Building eval tooling — evals are instructions you run with subagents, not code you write.
146
136
 
147
137
  ## Routing
148
138
 
149
139
  | Need | Read |
150
140
  |---|---|
151
- | description engineering, numeric limits, token budgets, script-vs-instructions, claim classes | [authoring](references/authoring.md) |
141
+ | description engineering, writing style, numeric limits, token budgets, script-vs-instructions, volatile facts | [authoring](references/authoring.md) |
152
142
  | eval method, pass criteria, trigger-query doctrine, cycle protocol, KNOWN_GAPS, model guidance | [eval playbook](references/eval-playbook.md) |
153
143
  | the scaffolded starting points | `assets/templates/` |
154
144
  | skillify's own freshness stance | [refresh/REFRESH.md](refresh/REFRESH.md) |
@@ -4,18 +4,13 @@ Instructions for the scheduled refresh agent (and any human running a manual ref
4
4
 
5
5
  ## What this skill claims
6
6
 
7
- TODO: classify before shipping (see skillify references/authoring.md):
7
+ TODO: apply the volatile-facts rule (skillify references/authoring.md) before shipping:
8
8
 
9
9
  - **Durable rules**: versionless principles in this skill's prose. The refresh agent NEVER edits these; if a source change invalidates one, open an issue quoting the evidence.
10
- - **Mechanism-coupled claims**: sentences marked with a source comment naming a SOURCE-ID from `sources.json`. The agent may propose edits to these marked sentences only, in the same PR as the registry update that evidences the change.
11
- - **Volatile layer**: `refresh/sources.json` itself (checksums, versions, retrieval times) — the only file the agent edits freely.
10
+ - **Volatile facts**: version pins, model names, numeric vendor limits, URLs, dated claims. Each lives in a refresh-tracked file named here, evidenced by a `sources.json` entry. A sentence leaning on one vendor mechanism may be marked `<!-- source: SOURCE-ID -->`.
12
11
 
13
- ## Evergreen waiver
14
-
15
- TODO: if this skill has NO mechanism-coupled or volatile claims, replace the sources TODO in `sources.json` with this waiver, filled in: "This skill's content is evergreen: it asserts no version pins, vendor mechanism names, numeric vendor limits, or dated facts. Rationale: ... Revisit if a future edit introduces any." Then delete the refresh steps below. Otherwise delete this section.
12
+ TODO: name the refresh-tracked files and the sections the refresh agent may edit.
16
13
 
17
- ## How to refresh
14
+ ## Evergreen waiver
18
15
 
19
- 1. TODO: deterministic pass (which command, which registry).
20
- 2. TODO: what may be auto-accepted vs what needs human review.
21
- 3. One PR per refresh, evidence-linked; refresh branches touch only `refresh/` metadata.
16
+ TODO: if this skill states no volatile facts, replace everything above with one line — "Evergreen: this skill asserts no version pins, vendor mechanism names, numeric vendor limits, or dated facts. Revisit if a future edit introduces any." — and keep `sources: []`.
@@ -11,6 +11,7 @@ TODO: one-paragraph overview - what this skill guarantees and the single trigger
11
11
 
12
12
  - TODO: concrete situations and user phrasings
13
13
  - TODO: when NOT to use - the nearest adjacent intent and where it goes instead
14
+ - Nearest neighbor: TODO - name the closest existing skill and the one-sentence axis of difference (or state that none exists)
14
15
 
15
16
  ## Workflow
16
17
 
@@ -1,5 +1,5 @@
1
1
  import { describe, expect, test } from 'bun:test'
2
- import { existsSync, readFileSync } from 'node:fs'
2
+ import { readFileSync } from 'node:fs'
3
3
  import { join, resolve } from 'node:path'
4
4
  import { validateSkill } from '../../../packages/cli/scripts/validate-skill.mjs'
5
5
 
@@ -12,29 +12,16 @@ describe('{{name}} contract', () => {
12
12
  expect(result.ok).toBe(true)
13
13
  })
14
14
 
15
- test('frontmatter carries only name and description', () => {
16
- const content = readFileSync(join(skillRoot, 'SKILL.md'), 'utf8')
17
- const frontmatter = /^---\n([\s\S]*?)\n---/.exec(content)
18
- expect(frontmatter).not.toBeNull()
19
- const keys = (frontmatter as RegExpExecArray)[1]
20
- .split('\n')
21
- .filter(line => /^[A-Za-z]/.test(line))
22
- .map(line => line.split(':')[0])
23
- expect(keys.sort()).toEqual(['description', 'name'])
15
+ test('trigger query fixture is a small hard set with near-miss negatives', () => {
16
+ const queries = JSON.parse(readFileSync(join(skillRoot, 'tests/fixtures/trigger-queries.json'), 'utf8'))
17
+ const positives = queries.filter((entry: { should_trigger: boolean }) => entry.should_trigger)
18
+ const negatives = queries.filter((entry: { should_trigger: boolean }) => !entry.should_trigger)
19
+ expect(positives.length).toBeGreaterThanOrEqual(5)
20
+ expect(negatives.length).toBeGreaterThanOrEqual(4)
21
+ for (const entry of queries) expect(typeof entry.query).toBe('string')
24
22
  })
25
23
 
26
- test('relative markdown links resolve', () => {
27
- for (const file of ['SKILL.md', 'README.md']) {
28
- const body = readFileSync(join(skillRoot, file), 'utf8')
29
- for (const match of body.matchAll(/\]\(([^)#\s]+)\)/g)) {
30
- const target = match[1]
31
- if (/^[a-z][a-z0-9+.-]*:/.test(target)) continue
32
- expect(`${file} -> ${target}: ${existsSync(join(skillRoot, target))}`).toBe(`${file} -> ${target}: true`)
33
- }
34
- }
35
- })
36
-
37
- // TODO: add unit tests for every deterministic branch of scripts/, plus the
38
- // consistency test (prose, scripts, templates, and registry agree). Lock in
39
- // eval-proven behavior only AFTER the behavioral eval passes (skillify Phase 4).
24
+ // TODO: if this skill ships scripts/, add unit tests for every deterministic
25
+ // branch. A prose-only skill needs nothing more here - its quality bar is the
26
+ // behavioral eval of skillify Phase 4, which runs BEFORE tests lock anything in.
40
27
  })
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
3
  "retrievalBaseline": "{{date}}",
4
- "note": "TODO: add one entry per volatile or mechanism-coupled external claim (see skillify references/authoring.md, claim classification), or record an evergreen waiver in REFRESH.md and keep sources empty.",
4
+ "note": "TODO: add one entry per volatile external fact this skill states (see skillify references/authoring.md, volatile facts), or record the one-line evergreen waiver in REFRESH.md and keep sources empty.",
5
5
  "sources": []
6
6
  }
@@ -12,11 +12,22 @@ The description is the primary triggering mechanism: it is always in context, an
12
12
  - **Cover the ways users actually ask.** Exact phrases in quotes, symptoms, file types, adjacent phrasings, casual variants. Include the situations where the skill competes with a neighbor and should win.
13
13
  - **Name the boundary.** One clause on what the skill is NOT for prevents over-triggering ("Only for skills inside this repository...").
14
14
  - **No angle brackets** — the repo validator rejects them.
15
+ - **No `#` after a space.** The description is an unquoted YAML scalar, and real harness parsers treat ` #` as a comment start — everything after it silently vanishes from the skill listing. Write "issue 12", never "issue #12"; the repo validator rejects it.
15
16
 
16
17
  Bad: `Helps with skills.` (no triggers, no boundary)
17
- Bad: `Audits a skill by scoring 13 checklist items, then scaffolds, evals, and wires it.` (workflow summary — the body will be skipped)
18
+ Bad: `Audits a skill by scoring checklist items, then scaffolds, evals, and wires it.` (workflow summary — the body will be skipped)
18
19
  Good: `Use when asked to "skillify this", "make this a skill", audit a skill's completeness, or decide whether a workflow should become a skill. Only for skills inside this repository.`
19
20
 
21
+ ## Writing style
22
+
23
+ Rules for the prose itself — they exist because agents pay attention (and tokens) for every sentence, and because rule corpora rot in predictable ways:
24
+
25
+ - **Prompt the positive.** Steering by prohibition drags the forbidden behavior into context and invites a wall of NEVERs that only ever grows. State the rule you want followed, once, with the reason it matters. Reserve negation for genuinely dangerous acts.
26
+ - **Explain why, not ALL-CAPS what.** Modern models have good theory of mind; a rule with its reason generalizes, a bare MUST gets pattern-matched and misapplied. Reaching for caps or bold on every rule is a sign the rule needs a reason, not emphasis.
27
+ - **Hunt no-ops and sediment.** A no-op is an instruction the model already obeys by default — it pays load to say nothing. Sediment is case law: a clause added for one past failure that never gets removed. When a failure exposes a gap, rewrite the existing rule in place; do not append an exception. Prune by deleting whole sentences, not trimming words.
28
+ - **Budget the body.** Target 50–150 lines for a SKILL.md; treat 300 as the ceiling (the spec's 500 is a far bound, not a budget). One great example beats five mediocre ones. Anything only some invocations need goes behind a routed reference.
29
+ - **Name the nearest neighbor.** One sentence in the body stating which existing skill is closest and the axis of difference. It keeps trigger families sharp better than any linter, and forces the merge conversation before a near-duplicate ships.
30
+
20
31
  ## Numeric limits
21
32
 
22
33
  <!-- mirrored: these numbers are volatile and mirrored from the standards sources tracked by the skill-maintainer registry; on drift, fix them there first, then here. -->
@@ -32,10 +43,11 @@ Good: `Use when asked to "skillify this", "make this a skill", audit a skill's c
32
43
 
33
44
  ## Trigger query sets
34
45
 
35
- Every skill ships `tests/fixtures/trigger-queries.json`: an array of `{"query": "...", "should_trigger": true|false}`.
46
+ Every skill ships `tests/fixtures/trigger-queries.json`: an array of `{"query": "...", "should_trigger": true|false}`, with an optional `"ambiguous_with": ["skill-name"]` on entries where a neighboring skill competes for the query.
36
47
 
37
- - 8–10 should-trigger: different phrasings of the same intent — formal, casual, typo-ridden; cases that never name the skill but clearly need it; cases where a neighboring skill competes and this one should win.
38
- - 8–10 should-NOT-trigger: **near-misses only.** Queries sharing keywords or domain with the skill but needing something else adjacent intents, ambiguous phrasing a naive keyword match would catch. "Write a fibonacci function" as a negative for a PDF skill tests nothing.
48
+ - About 10 queries total: 5–6 should-trigger, 4–5 should-NOT-trigger. A small set of hard queries beats a long set of easy ones.
49
+ - Should-trigger: different phrasings of the same intent formal, casual, typo-ridden; cases that never name the skill but clearly need it; at least one `ambiguous_with` case this skill should win against its nearest neighbor.
50
+ - Should-NOT-trigger: **near-misses only.** Queries sharing keywords or domain with the skill but needing something else — adjacent intents, ambiguous phrasing a naive keyword match would catch. "Write a fibonacci function" as a negative for a PDF skill tests nothing.
39
51
  - Queries must be realistic: concrete detail, file paths, a little backstory — what a user would actually type, not abstract category labels.
40
52
 
41
53
  The set is both a design artifact (it forces the description to draw a real boundary) and eval input (see the [eval playbook](eval-playbook.md)).
@@ -48,7 +60,7 @@ Three loading levels; spend accordingly:
48
60
  2. **SKILL.md body** — loaded on every trigger. Keep the workflow, one excellent example, and a routing table; push everything else down a level. One great example beats five mediocre ones.
49
61
  3. **references/ / scripts/ / assets/** — loaded or executed on demand; effectively unlimited, but give each reference a clear routing condition so agents read only what the task needs.
50
62
 
51
- Cross-reference other skills by name (`skill-maintainer`) instead of restating their content. Don't document a script's flags in prose beyond what routing needs — the script's own `--help`/usage error is the source of truth, and the consistency test keeps the two honest.
63
+ Cross-reference other skills by name (`skill-maintainer`) instead of restating their content. Don't document a script's flags in prose beyond what routing needs — the script's own `--help`/usage error is the source of truth.
52
64
 
53
65
  ## Scripts vs instructions
54
66
 
@@ -59,14 +71,8 @@ Cross-reference other skills by name (`skill-maintainer`) instead of restating t
59
71
  - Repo conventions for bundled scripts: dependency-free Node (`.mjs`), runnable from the skill dir with plain relative paths, `--json` for machine-readable output, documented exit codes (0 ok / 1 finding-or-refusal / 2 usage), atomic writes (stage then rename), symlink refusal, and an explicit `--write` gate for anything mutating — dry-run is the default.
60
72
  - Do NOT script judgment: descriptions, verdicts, review prose. A script that fakes judgment produces confident garbage.
61
73
 
62
- ## Claim classification for refresh
63
-
64
- Classify every factual claim while writing, not after:
74
+ ## Volatile facts and refresh
65
75
 
66
- | Class | Test | Treatment |
67
- |---|---|---|
68
- | **Durable** | still true if every vendor renames everything | plain prose; refresh automation never touches it |
69
- | **Mechanism-coupled** | durable intent expressed through a vendor-named mechanism (an option name, an API shape) | mark the sentence `<!-- source: SOURCE-ID -->`; editable only alongside registry evidence for that source |
70
- | **Volatile** | version pins, numeric limits, model names, URLs, anything with a date | lives in `refresh/sources.json` entries or other refresh-tracked files — never inline in SKILL.md |
76
+ One rule: **version pins, model names, numeric limits, URLs, and anything with a date live in refresh-tracked files — never inline in SKILL.md.** Everything else is plain prose that refresh automation never touches.
71
77
 
72
- The refresh contract falls out of this table: each distinct SOURCE-ID becomes a registry entry with URLs, checksum, threshold; REFRESH.md states which files the refresh agent may edit (the volatile layer), which sentences it may propose edits to (marked ones), and which it must only flag (durable). A skill with no mechanism-coupled or volatile claims declares an **evergreen waiver** in REFRESH.md — one paragraph stating why nothing decays — and keeps `sources: []`.
78
+ A volatile fact gets a `refresh/sources.json` entry (URL, checksum, threshold) and lives in a section the REFRESH.md names as editable; marking an individual sentence `<!-- source: SOURCE-ID -->` remains available when a durable rule leans on one vendor-named mechanism. A skill with no volatile facts of its own declares an **evergreen waiver** in REFRESH.md — one line stating why nothing decays — and keeps `sources: []`. Facts whose source of truth already lives in another skill's registry are mirrored with a `<!-- mirrored -->` marker, not duplicated as a second registry entry.
@@ -4,7 +4,7 @@ Instructions for the scheduled refresh agent (and any human running a manual ref
4
4
 
5
5
  ## What this skill claims
6
6
 
7
- - **Durable rules** (SKILL.md, most of `references/`): the phase model, checklist structure, eval-before-tests ordering, description doctrine, claim-classification taxonomy. Versionless; the refresh agent NEVER edits these. If external evidence invalidates one, open an issue quoting the evidence — do not edit.
7
+ - **Durable rules** (SKILL.md, most of `references/`): the phase model, checklist structure, eval-before-tests ordering, description doctrine, the volatile-facts rule. Versionless; the refresh agent NEVER edits these. If external evidence invalidates one, open an issue quoting the evidence — do not edit.
8
8
  - **Mirrored volatile claims**: skillify does not track its own external sources. Its two volatile spots are marked in place:
9
9
  - the numeric limits and harness listing budgets table in `references/authoring.md` (marked `<!-- mirrored -->`);
10
10
  - the model guidance section in `references/eval-playbook.md` (marked `<!-- volatile -->`).
@@ -4,10 +4,11 @@
4
4
  // node scripts/scaffold-skill.mjs <skill-name> --dir <repo-root> [--write] [--json]
5
5
  //
6
6
  // Dry-run by default: prints the plan (files that would be created plus the
7
- // remaining manual wiring steps) and creates nothing. --write stages the tree
8
- // in a temporary sibling inside skills/ and renames it into place, refusing
9
- // existing directories and symlinks. Exit codes: 0 ok, 1 refusal or failure,
10
- // 2 usage error.
7
+ // wiring actions that would be performed) and creates nothing. --write stages
8
+ // the tree in a temporary sibling inside skills/ and renames it into place,
9
+ // refusing existing directories and symlinks, then performs the repo wiring
10
+ // itself: packaging.json entry, root README row, changeset. Exit codes: 0 ok,
11
+ // 1 refusal or failure, 2 usage error.
11
12
  import { lstat, mkdir, mkdtemp, readFile, rename, rm, writeFile } from 'node:fs/promises'
12
13
  import { dirname, join, resolve } from 'node:path'
13
14
  import { fileURLToPath } from 'node:url'
@@ -22,9 +23,14 @@ export const templateFiles = [
22
23
  ['sources.json.template', 'refresh/sources.json'],
23
24
  ['REFRESH.md.template', 'refresh/REFRESH.md'],
24
25
  ['openai.yaml.template', 'agents/openai.yaml'],
26
+ // Scaffolded empty so the shape test stays red until real queries are written.
27
+ ['trigger-queries.json.template', 'tests/fixtures/trigger-queries.json'],
25
28
  ['skill.test.ts.template', null],
26
29
  ]
27
30
 
31
+ // The scaffolded files that ship to installers (README and tests never package).
32
+ const defaultPackagedFiles = ['SKILL.md', 'agents/openai.yaml', 'refresh/REFRESH.md', 'refresh/sources.json']
33
+
28
34
  // Full grammar shared by the repo validator and every target harness: starts
29
35
  // with a lowercase letter, then lowercase letters/digits/hyphens, no
30
36
  // consecutive hyphens, no trailing hyphen, at most 64 characters.
@@ -38,14 +44,6 @@ export function validateName(name) {
38
44
  return null
39
45
  }
40
46
 
41
- export function wiringSteps(name) {
42
- return [
43
- `Add skills/${name}/ packaged files to the allowlist in packages/cli/scripts/sync-skill.mjs (the build fails loudly on unlisted files)`,
44
- `Add a ${name} row to the Skills table in the root README.md`,
45
- `Add a CHANGELOG entry (changeset) introducing ${name}`,
46
- ]
47
- }
48
-
49
47
  async function entryAt(path) {
50
48
  try {
51
49
  return await lstat(path)
@@ -54,6 +52,66 @@ async function entryAt(path) {
54
52
  }
55
53
  }
56
54
 
55
+ async function writeAtomic(path, body) {
56
+ const staging = `${path}.scaffold-tmp`
57
+ await writeFile(staging, body)
58
+ await rename(staging, path)
59
+ }
60
+
61
+ // Adds the new skill's entry to packages/cli/packaging.json with the default
62
+ // scaffolded runtime files. Extra authored files added later must be appended
63
+ // there by hand — sync-skill.mjs still fails loudly on anything unlisted.
64
+ async function wirePackaging(repoRoot, name, write) {
65
+ const path = join(repoRoot, 'packages/cli/packaging.json')
66
+ if (!(await entryAt(path))?.isFile()) return { step: 'packaging.json entry', status: 'skipped: packages/cli/packaging.json not found' }
67
+ const packaged = JSON.parse(await readFile(path, 'utf8'))
68
+ if (name in packaged) return { step: 'packaging.json entry', status: 'skipped: entry already exists' }
69
+ if (!write) return { step: 'packaging.json entry', status: 'planned' }
70
+ packaged[name] = defaultPackagedFiles
71
+ const sorted = Object.fromEntries(Object.keys(packaged).sort().map(key => [key, packaged[key]]))
72
+ await writeAtomic(path, `${JSON.stringify(sorted, null, 2)}\n`)
73
+ return { step: 'packaging.json entry', status: 'done' }
74
+ }
75
+
76
+ // Inserts a row for the new skill at the end of the root README's Skills table.
77
+ async function wireReadme(repoRoot, name, write) {
78
+ const path = join(repoRoot, 'README.md')
79
+ if (!(await entryAt(path))?.isFile()) return { step: 'root README row', status: 'skipped: README.md not found' }
80
+ const body = await readFile(path, 'utf8')
81
+ if (body.includes(`](skills/${name}/)`)) return { step: 'root README row', status: 'skipped: row already exists' }
82
+ const lines = body.split('\n')
83
+ const header = lines.findIndex(line => /^\| *Skill *\|/.test(line))
84
+ if (header < 0 || !/^\|[ -]*---/.test(lines[header + 1] ?? '')) {
85
+ return { step: 'root README row', status: 'skipped: Skills table not found' }
86
+ }
87
+ let last = header + 1
88
+ while (lines[last + 1]?.startsWith('|')) last += 1
89
+ if (!write) return { step: 'root README row', status: 'planned' }
90
+ const row = `| [${name}](skills/${name}/) | TODO: one-line description | [Walkthrough](skills/${name}/README.md) · [SKILL.md](skills/${name}/SKILL.md) |`
91
+ lines.splice(last + 1, 0, row)
92
+ await writeAtomic(path, lines.join('\n'))
93
+ return { step: 'root README row', status: 'done' }
94
+ }
95
+
96
+ // Writes the changeset introducing the skill (content versioning: new skill = minor).
97
+ async function wireChangeset(repoRoot, name, write) {
98
+ const directory = join(repoRoot, '.changeset')
99
+ if (!(await entryAt(directory))?.isDirectory()) return { step: 'changeset', status: 'skipped: .changeset/ not found' }
100
+ const path = join(directory, `add-${name}.md`)
101
+ if (await entryAt(path)) return { step: 'changeset', status: 'skipped: changeset already exists' }
102
+ if (!write) return { step: 'changeset', status: 'planned' }
103
+ await writeAtomic(path, `---\n"@vegastack/skills": minor\n---\n\nAdd the ${name} skill.\n`)
104
+ return { step: 'changeset', status: 'done' }
105
+ }
106
+
107
+ export async function wireSkill({ name, repoRoot, write = false }) {
108
+ return [
109
+ await wirePackaging(repoRoot, name, write),
110
+ await wireReadme(repoRoot, name, write),
111
+ await wireChangeset(repoRoot, name, write),
112
+ ]
113
+ }
114
+
57
115
  export async function scaffoldSkill({ name, dir, write = false, now = new Date() }) {
58
116
  const nameError = validateName(name)
59
117
  if (nameError) throw new Error(`Invalid skill name ${JSON.stringify(name ?? null)}: ${nameError}`)
@@ -69,8 +127,8 @@ export async function scaffoldSkill({ name, dir, write = false, now = new Date()
69
127
  if (await entryAt(target)) throw new Error(`Refusing to scaffold: ${target} already exists`)
70
128
 
71
129
  const outputs = templateFiles.map(([source, output]) => [source, output ?? `tests/${name}.test.ts`])
72
- const plan = { name, target, files: outputs.map(([, output]) => output), wiring: wiringSteps(name), wrote: false }
73
- if (!write) return plan
130
+ const plan = { name, target, files: outputs.map(([, output]) => output), wrote: false }
131
+ if (!write) return { ...plan, wiring: await wireSkill({ name, repoRoot }) }
74
132
 
75
133
  const date = now.toISOString().slice(0, 10)
76
134
  const staging = await mkdtemp(join(skillsRoot, `.${name}.scaffold-`))
@@ -88,7 +146,7 @@ export async function scaffoldSkill({ name, dir, write = false, now = new Date()
88
146
  await rm(staging, { recursive: true, force: true })
89
147
  throw error
90
148
  }
91
- return { ...plan, wrote: true }
149
+ return { ...plan, wrote: true, wiring: await wireSkill({ name, repoRoot, write: true }) }
92
150
  }
93
151
 
94
152
  function parseArguments(argv) {
@@ -128,8 +186,8 @@ if (invokedDirectly) {
128
186
  } else {
129
187
  console.log(result.wrote ? `Created ${result.target}` : `Dry run - pass --write to create ${result.target}`)
130
188
  for (const file of result.files) console.log(` ${file}`)
131
- console.log('Remaining manual wiring:')
132
- for (const step of result.wiring) console.log(` - ${step}`)
189
+ console.log('Wiring:')
190
+ for (const { step, status } of result.wiring) console.log(` - ${step}: ${status}`)
133
191
  }
134
192
  } catch (error) {
135
193
  console.error(String(error.message ?? error))