@hegemonart/get-design-done 1.59.3 → 1.59.4

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 (149) hide show
  1. package/.claude-plugin/marketplace.json +2 -2
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/CHANGELOG.md +31 -0
  4. package/SKILL.md +2 -0
  5. package/figma-plugin/README.md +61 -0
  6. package/figma-plugin/code.ts +36 -0
  7. package/figma-plugin/manifest.json +12 -0
  8. package/figma-plugin/package-lock.json +35 -0
  9. package/figma-plugin/package.json +12 -0
  10. package/figma-plugin/src/export-variables.ts +144 -0
  11. package/figma-plugin/src/payload-schema.ts +250 -0
  12. package/figma-plugin/tsconfig.json +16 -0
  13. package/figma-plugin/ui.html +44 -0
  14. package/hooks/gdd-intel-trigger.js +3 -3
  15. package/package.json +6 -1
  16. package/reference/DEPRECATIONS.md +3 -3
  17. package/reference/live-mode-integration.md +1 -1
  18. package/reference/registry.json +1 -1
  19. package/reference/skill-metadata.md +4 -4
  20. package/reference/skill-placeholders.md +2 -2
  21. package/scripts/build-skills.cjs +146 -0
  22. package/scripts/generate-skill-frontmatter.cjs +243 -0
  23. package/scripts/lib/manifest/scaffolder.cjs +1 -1
  24. package/scripts/lib/manifest/schemas/skills.schema.json +1 -1
  25. package/scripts/lib/manifest/skills.json +1 -1
  26. package/scripts/lib/new-addendum.cjs +1 -1
  27. package/scripts/skill-templates/README.md +90 -0
  28. package/scripts/skill-templates/add-backlog/SKILL.md +48 -0
  29. package/scripts/skill-templates/analyze-dependencies/SKILL.md +95 -0
  30. package/scripts/skill-templates/apply-reflections/SKILL.md +109 -0
  31. package/scripts/skill-templates/apply-reflections/apply-reflections-procedure.md +170 -0
  32. package/scripts/skill-templates/audit/SKILL.md +79 -0
  33. package/scripts/skill-templates/bandit-reset/SKILL.md +91 -0
  34. package/scripts/skill-templates/bandit-status/SKILL.md +94 -0
  35. package/scripts/skill-templates/benchmark/SKILL.md +65 -0
  36. package/scripts/skill-templates/bootstrap-ds/SKILL.md +43 -0
  37. package/scripts/skill-templates/brief/SKILL.md +145 -0
  38. package/scripts/skill-templates/budget/SKILL.md +45 -0
  39. package/scripts/skill-templates/cache-manager/SKILL.md +66 -0
  40. package/scripts/skill-templates/cache-manager/cache-policy.md +126 -0
  41. package/scripts/skill-templates/check-update/SKILL.md +98 -0
  42. package/scripts/skill-templates/compare/SKILL.md +82 -0
  43. package/scripts/skill-templates/compare/compare-rubric.md +171 -0
  44. package/scripts/skill-templates/complete-cycle/SKILL.md +81 -0
  45. package/scripts/skill-templates/connections/SKILL.md +71 -0
  46. package/scripts/skill-templates/connections/connections-onboarding.md +608 -0
  47. package/scripts/skill-templates/context/SKILL.md +137 -0
  48. package/scripts/skill-templates/continue/SKILL.md +24 -0
  49. package/scripts/skill-templates/darkmode/SKILL.md +76 -0
  50. package/scripts/skill-templates/darkmode/darkmode-audit-procedure.md +258 -0
  51. package/scripts/skill-templates/debug/SKILL.md +41 -0
  52. package/scripts/skill-templates/debug/debug-feedback-loops.md +119 -0
  53. package/scripts/skill-templates/design/SKILL.md +118 -0
  54. package/scripts/skill-templates/design/design-procedure.md +304 -0
  55. package/scripts/skill-templates/discuss/SKILL.md +96 -0
  56. package/scripts/skill-templates/do/SKILL.md +45 -0
  57. package/scripts/skill-templates/explore/SKILL.md +118 -0
  58. package/scripts/skill-templates/explore/explore-procedure.md +267 -0
  59. package/scripts/skill-templates/export/SKILL.md +30 -0
  60. package/scripts/skill-templates/extract-learnings/SKILL.md +114 -0
  61. package/scripts/skill-templates/fast/SKILL.md +91 -0
  62. package/scripts/skill-templates/figma-extract/SKILL.md +64 -0
  63. package/scripts/skill-templates/figma-write/SKILL.md +50 -0
  64. package/scripts/skill-templates/graphify/SKILL.md +49 -0
  65. package/scripts/skill-templates/health/SKILL.md +99 -0
  66. package/scripts/skill-templates/health/health-mcp-detection.md +44 -0
  67. package/scripts/skill-templates/health/health-skill-length-report.md +69 -0
  68. package/scripts/skill-templates/help/SKILL.md +60 -0
  69. package/scripts/skill-templates/instinct/SKILL.md +111 -0
  70. package/scripts/skill-templates/list-assumptions/SKILL.md +61 -0
  71. package/scripts/skill-templates/list-pins/SKILL.md +27 -0
  72. package/scripts/skill-templates/live/SKILL.md +98 -0
  73. package/scripts/skill-templates/locale/SKILL.md +51 -0
  74. package/scripts/skill-templates/map/SKILL.md +89 -0
  75. package/scripts/skill-templates/migrate/SKILL.md +70 -0
  76. package/scripts/skill-templates/migrate-context/SKILL.md +123 -0
  77. package/scripts/skill-templates/new-addendum/SKILL.md +81 -0
  78. package/scripts/skill-templates/new-cycle/SKILL.md +37 -0
  79. package/scripts/skill-templates/new-project/SKILL.md +53 -0
  80. package/scripts/skill-templates/new-skill/SKILL.md +90 -0
  81. package/scripts/skill-templates/next/SKILL.md +68 -0
  82. package/scripts/skill-templates/note/SKILL.md +48 -0
  83. package/scripts/skill-templates/openrouter-status/SKILL.md +86 -0
  84. package/scripts/skill-templates/optimize/SKILL.md +97 -0
  85. package/scripts/skill-templates/override/SKILL.md +86 -0
  86. package/scripts/skill-templates/paper-write/SKILL.md +54 -0
  87. package/scripts/skill-templates/pause/SKILL.md +77 -0
  88. package/scripts/skill-templates/peer-cli-add/SKILL.md +88 -0
  89. package/scripts/skill-templates/peer-cli-add/peer-cli-protocol.md +161 -0
  90. package/scripts/skill-templates/peer-cli-customize/SKILL.md +89 -0
  91. package/scripts/skill-templates/peers/SKILL.md +96 -0
  92. package/scripts/skill-templates/pencil-write/SKILL.md +54 -0
  93. package/scripts/skill-templates/pin/SKILL.md +37 -0
  94. package/scripts/skill-templates/plan/SKILL.md +105 -0
  95. package/scripts/skill-templates/plan/plan-procedure.md +278 -0
  96. package/scripts/skill-templates/plant-seed/SKILL.md +48 -0
  97. package/scripts/skill-templates/pr-branch/SKILL.md +32 -0
  98. package/scripts/skill-templates/progress/SKILL.md +107 -0
  99. package/scripts/skill-templates/quality-gate/SKILL.md +90 -0
  100. package/scripts/skill-templates/quality-gate/threat-modeling.md +101 -0
  101. package/scripts/skill-templates/quick/SKILL.md +44 -0
  102. package/scripts/skill-templates/reapply-patches/SKILL.md +32 -0
  103. package/scripts/skill-templates/recall/SKILL.md +75 -0
  104. package/scripts/skill-templates/reflect/SKILL.md +85 -0
  105. package/scripts/skill-templates/reflect/procedures/capability-gap-scan.md +119 -0
  106. package/scripts/skill-templates/report-issue/SKILL.md +53 -0
  107. package/scripts/skill-templates/report-issue/report-issue-procedure.md +119 -0
  108. package/scripts/skill-templates/resume/SKILL.md +93 -0
  109. package/scripts/skill-templates/review-backlog/SKILL.md +46 -0
  110. package/scripts/skill-templates/review-decisions/SKILL.md +42 -0
  111. package/scripts/skill-templates/roi/SKILL.md +54 -0
  112. package/scripts/skill-templates/rollout-status/SKILL.md +35 -0
  113. package/scripts/skill-templates/router/SKILL.md +89 -0
  114. package/scripts/skill-templates/router/capability-gap-emitter.md +65 -0
  115. package/scripts/skill-templates/router/router-pick-emitter.md +78 -0
  116. package/scripts/skill-templates/router/router-rules.md +84 -0
  117. package/scripts/skill-templates/settings/SKILL.md +87 -0
  118. package/scripts/skill-templates/ship/SKILL.md +48 -0
  119. package/scripts/skill-templates/sketch/SKILL.md +78 -0
  120. package/scripts/skill-templates/sketch-wrap-up/SKILL.md +92 -0
  121. package/scripts/skill-templates/skill-manifest/SKILL.md +79 -0
  122. package/scripts/skill-templates/spike/SKILL.md +67 -0
  123. package/scripts/skill-templates/spike-wrap-up/SKILL.md +86 -0
  124. package/scripts/skill-templates/start/SKILL.md +67 -0
  125. package/scripts/skill-templates/start/start-procedure.md +115 -0
  126. package/scripts/skill-templates/state/SKILL.md +106 -0
  127. package/scripts/skill-templates/stats/SKILL.md +51 -0
  128. package/scripts/skill-templates/style/SKILL.md +71 -0
  129. package/scripts/skill-templates/style/style-doc-procedure.md +150 -0
  130. package/scripts/skill-templates/synthesize/SKILL.md +94 -0
  131. package/scripts/skill-templates/timeline/SKILL.md +66 -0
  132. package/scripts/skill-templates/todo/SKILL.md +64 -0
  133. package/scripts/skill-templates/turn-closeout/SKILL.md +95 -0
  134. package/scripts/skill-templates/undo/SKILL.md +31 -0
  135. package/scripts/skill-templates/unlock-decision/SKILL.md +54 -0
  136. package/scripts/skill-templates/unpin/SKILL.md +31 -0
  137. package/scripts/skill-templates/update/SKILL.md +56 -0
  138. package/scripts/skill-templates/using-gdd/SKILL.md +78 -0
  139. package/scripts/skill-templates/verify/SKILL.md +113 -0
  140. package/scripts/skill-templates/verify/verify-procedure.md +511 -0
  141. package/scripts/skill-templates/warm-cache/SKILL.md +81 -0
  142. package/scripts/skill-templates/watch-authorities/SKILL.md +82 -0
  143. package/scripts/skill-templates/zoom-out/SKILL.md +26 -0
  144. package/sdk/cli/commands/build.ts +2 -2
  145. package/sdk/cli/index.js +2 -2
  146. package/sdk/cli/index.ts +1 -1
  147. package/skills/README.md +22 -14
  148. package/skills/help/SKILL.md +28 -55
  149. package/skills/new-skill/SKILL.md +5 -5
@@ -0,0 +1,44 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8" />
5
+ <style>
6
+ body {
7
+ font-family: Inter, system-ui, sans-serif;
8
+ margin: 0;
9
+ padding: 16px;
10
+ font-size: 12px;
11
+ color: #1a1a1a;
12
+ }
13
+ button {
14
+ width: 100%;
15
+ padding: 10px;
16
+ font-size: 13px;
17
+ font-weight: 600;
18
+ color: #fff;
19
+ background: #18a0fb;
20
+ border: none;
21
+ border-radius: 6px;
22
+ cursor: pointer;
23
+ }
24
+ button:hover {
25
+ background: #0d8de0;
26
+ }
27
+ #status {
28
+ margin-top: 12px;
29
+ min-height: 16px;
30
+ color: #555;
31
+ }
32
+ </style>
33
+ </head>
34
+ <body>
35
+ <button id="export">Export to GDD</button>
36
+ <p id="status">Reads local Figma variables and POSTs them to the GDD receiver on localhost:5179.</p>
37
+ <script>
38
+ document.getElementById('export').onclick = function () {
39
+ document.getElementById('status').textContent = 'Exporting…';
40
+ parent.postMessage({ pluginMessage: { type: 'export' } }, '*');
41
+ };
42
+ </script>
43
+ </body>
44
+ </html>
@@ -4,7 +4,7 @@
4
4
  * hooks/gdd-intel-trigger.js — D5 (PostToolUse on Edit|Write)
5
5
  *
6
6
  * On every Edit/Write that touches a design-authoritative surface
7
- * (skills/**, agents/**, reference/**, skill-templates/**), spawn a
7
+ * (skills/**, agents/**, reference/**, scripts/skill-templates/**), spawn a
8
8
  * background, detached refresh of the .design/intel/ store so downstream
9
9
  * consumers (router, planner, audits) see the latest extracts without the
10
10
  * user paying for a full rebuild on the next /gdd run.
@@ -14,7 +14,7 @@
14
14
  * camelCase field names (tool_name/toolName, tool_input/toolInput,
15
15
  * file_path/filePath/path).
16
16
  * 2. If the edited path matches
17
- * ^(skills|agents|reference|skill-templates)/.*\.(md|json)$
17
+ * ^(skills|agents|reference|scripts/skill-templates)/.*\.(md|json)$
18
18
  * (path-separator-agnostic), schedule a background refresh.
19
19
  * 3. Otherwise no-op — write {continue:true} and exit 0.
20
20
  * 4. Always exit 0. Never block. Never surface errors. Errors only ever
@@ -55,7 +55,7 @@ const path = require('node:path');
55
55
  const { spawn } = require('node:child_process');
56
56
 
57
57
  const LOCK_TTL_MS = 5 * 60 * 1000; // 5 minutes
58
- const TARGET_RE = /^(?:skills|agents|reference|source\/skills)\/.*\.(?:md|json)$/;
58
+ const TARGET_RE = /^(?:skills|agents|reference|scripts\/skill-templates)\/.*\.(?:md|json)$/;
59
59
 
60
60
  /**
61
61
  * Extract the edited file path + tool name from a PostToolUse payload.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hegemonart/get-design-done",
3
- "version": "1.59.3",
3
+ "version": "1.59.4",
4
4
  "description": "A design-quality pipeline for AI coding agents: brief, explore, plan, design, and verify UI work against your design system.",
5
5
  "author": "Hegemon",
6
6
  "homepage": "https://github.com/hegemonart/get-design-done",
@@ -25,9 +25,13 @@
25
25
  "docs/i18n/",
26
26
  "scripts/lib/",
27
27
  "scripts/cli/",
28
+ "scripts/skill-templates/",
28
29
  "scripts/install.cjs",
29
30
  "scripts/injection-patterns.cjs",
30
31
  "scripts/bootstrap.cjs",
32
+ "scripts/build-skills.cjs",
33
+ "scripts/generate-skill-frontmatter.cjs",
34
+ "figma-plugin/",
31
35
  "SKILL.md",
32
36
  "README.md",
33
37
  "CHANGELOG.md",
@@ -62,6 +66,7 @@
62
66
  "test:behavior": "node scripts/run-behavior-tests.cjs",
63
67
  "typecheck": "tsc --noEmit",
64
68
  "codegen:schemas": "node --experimental-strip-types scripts/codegen-schema-types.ts",
69
+ "codegen:schemas:check": "node --experimental-strip-types scripts/codegen-schema-types.ts --check",
65
70
  "lint:md": "npx --yes markdownlint-cli2 \"**/*.md\" \"#**/node_modules\" \"#.planning\" \"#.claude\" \"#test/fixtures/baselines\"",
66
71
  "lint:links": "npx --yes lychee --no-progress --accept 200,206,403,429 \"**/*.md\" || true",
67
72
  "lint:agentskills": "node scripts/lint-agentskills-spec.cjs",
@@ -31,7 +31,7 @@ the scanner pattern list in lockstep.
31
31
  ## Authoring surfaces
32
32
 
33
33
  - **`skills/` as the authoring source** → **`source/skills/`** (Phase 42, deprecated)
34
- → **`skill-templates/`** (v1.58.0, current). Two-step migration:
34
+ → **`skill-templates/`** (v1.58.0) → **`scripts/skill-templates/`** (v1.59, current). Migration:
35
35
 
36
36
  - **Phase 42** introduced multi-harness compilation. Skills moved to `source/skills/` with
37
37
  placeholders (`{{command_prefix}}` et al.; see `reference/skill-placeholders.md`) and the
@@ -43,10 +43,10 @@ the scanner pattern list in lockstep.
43
43
  tarball via `prepack`. Net: 232 tracked files dropped to 116, with no change to end-user
44
44
  install experience (the tarball still ships `skills/` pre-built).
45
45
 
46
- Migration for contributors: **edit `skill-templates/`, never `skills/` directly**. After edits,
46
+ Migration for contributors: **edit `scripts/skill-templates/`, never `skills/` directly**. After edits,
47
47
  `npm run build:skills` regenerates `skills/` locally (or `npm install` does it automatically).
48
48
  `.claude-plugin/plugin.json`'s `"skills": ["./skills/"]` is unchanged - the plugin still loads
49
- `skills/`, now produced from `skill-templates/`. The `detect-stale-refs.cjs` list is extended to
49
+ `skills/`, now produced from `scripts/skill-templates/`. The `detect-stale-refs.cjs` list is extended to
50
50
  emit warnings on lingering `source/skills/` path references.
51
51
 
52
52
  ## Scanner scope
@@ -9,7 +9,7 @@ last_updated: 2026-06-03
9
9
 
10
10
  # Live Mode Integration
11
11
 
12
- This file is the meta-rules companion to the `gdd-live` skill (`skill-templates/live/SKILL.md`). It describes how `/gdd:live` turns a running dev server into a live design surface: the user picks a DOM element, the agent generates N variants in one batch, the variants hot-swap in place, the user accepts or discards, and the whole session persists. For the SKILL.md structural contract (line cap, description budget, frontmatter), see `./skill-authoring-contract.md`. The variants themselves are grounded in the Phase 45 domain indexes (`./spatial.md`, `./interaction.md`, `./color.md`, `./typography.md`, `./motion.md`).
12
+ This file is the meta-rules companion to the `gdd-live` skill (`scripts/skill-templates/live/SKILL.md`). It describes how `/gdd:live` turns a running dev server into a live design surface: the user picks a DOM element, the agent generates N variants in one batch, the variants hot-swap in place, the user accepts or discards, and the whole session persists. For the SKILL.md structural contract (line cap, description budget, frontmatter), see `./skill-authoring-contract.md`. The variants themselves are grounded in the Phase 45 domain indexes (`./spatial.md`, `./interaction.md`, `./color.md`, `./typography.md`, `./motion.md`).
13
13
 
14
14
  There is NO bundled browser automation. The skill drives the Claude Preview MCP at runtime; the modules under `scripts/lib/live/` are pure and dependency-free.
15
15
 
@@ -1071,7 +1071,7 @@
1071
1071
  "path": "reference/skill-placeholders.md",
1072
1072
  "type": "meta-rules",
1073
1073
  "phase": 42,
1074
- "description": "Multi-harness skill-placeholder catalogue: the four placeholders ({{command_prefix}}/{{model}}/{{config_file}}/{{ask_instruction}}) + per-harness substitution table + the \\{{...}} escape + the <!-- harness-only: a,b --> block rule. Skills authored once in skill-templates/, compiled per-harness by scripts/build-skills.cjs via scripts/lib/build/factory.cjs reading scripts/lib/manifest/harnesses.json."
1074
+ "description": "Multi-harness skill-placeholder catalogue: the four placeholders ({{command_prefix}}/{{model}}/{{config_file}}/{{ask_instruction}}) + per-harness substitution table + the \\{{...}} escape + the <!-- harness-only: a,b --> block rule. Skills authored once in scripts/skill-templates/, compiled per-harness by scripts/build-skills.cjs via scripts/lib/build/factory.cjs reading scripts/lib/manifest/harnesses.json."
1075
1075
  },
1076
1076
  {
1077
1077
  "name": "color",
@@ -10,7 +10,7 @@ last_updated: 2026-06-02
10
10
  # Skill Metadata Single Source of Truth
11
11
 
12
12
  `scripts/lib/manifest/skills.json` is the one place skill frontmatter is authored. A
13
- generator projects it onto every `skill-templates/<id>/SKILL.md`, the build step compiles
13
+ generator projects it onto every `scripts/skill-templates/<id>/SKILL.md`, the build step compiles
14
14
  those into the shipped trees, and CI gates keep the three copies identical. This doc
15
15
  explains the file, the generator, and the description budget. For the structural rules a
16
16
  SKILL.md body must follow (line cap, progressive disclosure, frontmatter required fields),
@@ -20,7 +20,7 @@ see `./skill-authoring-contract.md`.
20
20
 
21
21
  The file is a JSON object: a `schema_version` integer and a `skills` array. Each array
22
22
  element is one skill record. Only `name` is required (it must equal the
23
- `skill-templates/<id>/` directory name); every other field is optional and omitted when it
23
+ `scripts/skill-templates/<id>/` directory name); every other field is optional and omitted when it
24
24
  has no value.
25
25
 
26
26
  ```json
@@ -50,7 +50,7 @@ Metadata flows in one direction, and a `*:check` gate guards each hop:
50
50
  ```text
51
51
  skills.json
52
52
  -> generate-skill-frontmatter (npm run generate:skill-frontmatter)
53
- -> skill-templates/<id>/SKILL.md
53
+ -> scripts/skill-templates/<id>/SKILL.md
54
54
  -> build:skills (npm run build:skills)
55
55
  -> skills/ + dist/claude-code/
56
56
  ```
@@ -64,7 +64,7 @@ skills.json
64
64
  - `--check`: the CI drift gate. It writes nothing and exits non-zero when any committed
65
65
  frontmatter differs from what the manifest would generate.
66
66
 
67
- `scripts/build-skills.cjs` then propagates `skill-templates/` into the committed `skills/`
67
+ `scripts/build-skills.cjs` then propagates `scripts/skill-templates/` into the committed `skills/`
68
68
  tree and `dist/claude-code/`; its own check mode asserts that the built output equals the
69
69
  committed output. So the contract is: edit `skills.json`, regenerate, build. Never
70
70
  hand-edit a managed frontmatter line in `SKILL.md`, because the drift gate will fail.
@@ -1,6 +1,6 @@
1
1
  # Skill Placeholders - Multi-Harness Source Compilation
2
2
 
3
- > Phase 42. Skills are authored once in `skill-templates/` with placeholders and compiled per-harness into
3
+ > Phase 42. Skills are authored once in `scripts/skill-templates/` with placeholders and compiled per-harness into
4
4
  > `dist/<bundle>/<config-dir>/skills/...` by `scripts/build-skills.cjs` (the pure transform lives in
5
5
  > `scripts/lib/build/factory.cjs`; the per-harness values in `scripts/lib/build/harness-configs.cjs`,
6
6
  > which reads the Phase 41.5 manifest root `scripts/lib/manifest/harnesses.json`). The Claude-Code
@@ -68,4 +68,4 @@ scope (a maintenance trap) - see the Phase 42 CONTEXT.
68
68
  ## Validation
69
69
 
70
70
  `test/suite/phase-42-placeholders.test.cjs` asserts that every placeholder actually used across
71
- `skill-templates/` is documented in this file, and that `skill-templates/` mirrors the `skills/` skill count.
71
+ `scripts/skill-templates/` is documented in this file, and that `scripts/skill-templates/` mirrors the `skills/` skill count.
@@ -0,0 +1,146 @@
1
+ 'use strict';
2
+ // Phase 42 — multi-harness skill build orchestrator.
3
+ // v1.58.0 renamed source/skills/ → skill-templates/ (the source/ wrapper held only skills/
4
+ // and added nothing). v1.58.0 ALSO gitignored skills/ as a pure build artifact — that broke
5
+ // the Claude Code marketplace install path (Claude Code git-clones the plugin without running
6
+ // `npm install`, so `./skills/` was absent post-clone). v1.58.1 reverts the gitignore:
7
+ // skills/ is committed again so git-clone-based installs work; scripts/skill-templates/ remains the
8
+ // canonical editable source; `prepare` still regenerates skills/ on contributor checkouts.
9
+ //
10
+ // node scripts/build-skills.cjs [--harness <id>] [--check] [--zip]
11
+ //
12
+ // Reads scripts/skill-templates/**/*.md, applies the pure factory per harness config, and writes:
13
+ // - skills/** (the committed Claude-Code surface, regenerated in place)
14
+ // - dist/<bundleSlug>/<configDir>/skills/** (per-harness bundles; build-only artifacts, gitignored)
15
+ //
16
+ // --check : no writes; verify the committed skills/ equals compile(scripts/skill-templates/),
17
+ // exit 1 on any byte drift. This is the CI drift gate.
18
+ // --harness <id> : restrict to one harness (skips the skills/ in-place regen unless id === claude).
19
+ // --zip : after building, tar -czf dist/<bundleSlug>.tgz each bundle (graceful skip if tar absent).
20
+ //
21
+ // Idempotent + byte-stable: file walk is sorted; bytes are written verbatim (line endings preserved).
22
+
23
+ const fs = require('fs');
24
+ const path = require('path');
25
+ const { spawnSync } = require('child_process');
26
+ const { compile } = require('./lib/build/factory.cjs');
27
+ const { CONFIGS, byId, claude } = require('./lib/build/harness-configs.cjs');
28
+
29
+ const ROOT = path.resolve(__dirname, '..');
30
+ const SRC = path.join(ROOT, 'scripts', 'skill-templates');
31
+ const SKILLS = path.join(ROOT, 'skills');
32
+ const DIST = path.join(ROOT, 'dist');
33
+
34
+ function parseArgs(argv) {
35
+ const out = { check: false, zip: false, harness: null };
36
+ for (let i = 0; i < argv.length; i++) {
37
+ const a = argv[i];
38
+ if (a === '--check') out.check = true;
39
+ else if (a === '--zip') out.zip = true;
40
+ else if (a === '--harness') out.harness = argv[++i];
41
+ else if (a.startsWith('--harness=')) out.harness = a.slice('--harness='.length);
42
+ }
43
+ return out;
44
+ }
45
+
46
+ function walkMd(dir) {
47
+ const out = [];
48
+ for (const e of fs.readdirSync(dir, { withFileTypes: true }).sort((x, y) => x.name.localeCompare(y.name))) {
49
+ const p = path.join(dir, e.name);
50
+ if (e.isDirectory()) out.push(...walkMd(p));
51
+ else if (e.isFile() && e.name.endsWith('.md')) out.push(p);
52
+ }
53
+ return out;
54
+ }
55
+
56
+ /** Compile every source file for one config -> Map<relPath, string>. */
57
+ function compileAll(config) {
58
+ const result = new Map();
59
+ for (const abs of walkMd(SRC)) {
60
+ const rel = path.relative(SRC, abs).split(path.sep).join('/');
61
+ result.set(rel, compile(fs.readFileSync(abs, 'utf8'), config));
62
+ }
63
+ return result;
64
+ }
65
+
66
+ function writeMap(map, destRoot) {
67
+ let written = 0;
68
+ for (const [rel, text] of map) {
69
+ const dst = path.join(destRoot, rel.split('/').join(path.sep));
70
+ fs.mkdirSync(path.dirname(dst), { recursive: true });
71
+ fs.writeFileSync(dst, text);
72
+ written++;
73
+ }
74
+ return written;
75
+ }
76
+
77
+ function bundleDir(config) {
78
+ return path.join(DIST, config.bundleSlug, config.configDir, 'skills');
79
+ }
80
+
81
+ /** Compare a compiled map against on-disk files under destRoot. Returns array of drifting rel paths. */
82
+ function diffMap(map, destRoot) {
83
+ const drift = [];
84
+ for (const [rel, text] of map) {
85
+ const dst = path.join(destRoot, rel.split('/').join(path.sep));
86
+ let cur = null;
87
+ try { cur = fs.readFileSync(dst, 'utf8'); } catch { /* missing */ }
88
+ if (cur !== text) drift.push(rel);
89
+ }
90
+ return drift;
91
+ }
92
+
93
+ function runCheck() {
94
+ // v1.58.1: skills/ is committed (reverts v1.58.0 gitignore — Claude Code marketplace
95
+ // git-clones the plugin without running npm install, so `./skills/` MUST exist post-clone).
96
+ // --check is back to its original Phase 42 semantics: gate that committed skills/ matches
97
+ // compile(scripts/skill-templates/). If contributors edit scripts/skill-templates/ without re-running
98
+ // `npm run build:skills`, this catches the drift.
99
+ const cfg = claude();
100
+ const map = compileAll(cfg);
101
+ const driftSkills = diffMap(map, SKILLS);
102
+ if (driftSkills.length) {
103
+ process.stderr.write('build-skills --check: DRIFT detected (run `npm run build:skills` and commit).\n');
104
+ for (const r of driftSkills.slice(0, 10)) process.stderr.write(` skills/${r}\n`);
105
+ if (driftSkills.length > 10) process.stderr.write(' ...\n');
106
+ return 1;
107
+ }
108
+ process.stderr.write(`build-skills --check: OK - skills/ matches scripts/skill-templates/ (${map.size} files).\n`);
109
+ return 0;
110
+ }
111
+
112
+ function tarBundle(config) {
113
+ const tgz = path.join(DIST, `${config.bundleSlug}.tgz`);
114
+ const r = spawnSync('tar', ['-czf', tgz, '-C', DIST, config.bundleSlug], { stdio: 'ignore' });
115
+ if (r.error || r.status !== 0) {
116
+ process.stderr.write(` (zip skipped for ${config.bundleSlug}: tar unavailable)\n`);
117
+ return false;
118
+ }
119
+ return true;
120
+ }
121
+
122
+ function runBuild(opts) {
123
+ const targets = opts.harness ? [byId(opts.harness)].filter(Boolean) : CONFIGS;
124
+ if (opts.harness && targets.length === 0) {
125
+ process.stderr.write(`build-skills: unknown harness '${opts.harness}'\n`);
126
+ return 1;
127
+ }
128
+ let total = 0;
129
+ for (const cfg of targets) {
130
+ const map = compileAll(cfg);
131
+ total += writeMap(map, bundleDir(cfg));
132
+ if (cfg.id === 'claude') writeMap(map, SKILLS); // regenerate the committed Claude surface in place
133
+ if (opts.zip) tarBundle(cfg);
134
+ process.stderr.write(` built ${cfg.bundleSlug} (${map.size} files)${cfg.id === 'claude' ? ' + skills/' : ''}\n`);
135
+ }
136
+ process.stderr.write(`build-skills: wrote ${total} files across ${targets.length} harness bundle(s).\n`);
137
+ return 0;
138
+ }
139
+
140
+ function main(argv) {
141
+ const opts = parseArgs(argv);
142
+ return opts.check ? runCheck() : runBuild(opts);
143
+ }
144
+
145
+ if (require.main === module) process.exit(main(process.argv.slice(2)));
146
+ module.exports = { main, parseArgs, compileAll, bundleDir };
@@ -0,0 +1,243 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+ /**
4
+ * generate-skill-frontmatter.cjs — Phase 46 (Skill UX Polish).
5
+ *
6
+ * scripts/lib/manifest/skills.json is the single source of truth for the
7
+ * universal skill frontmatter fields (description, argument-hint, tools,
8
+ * user-invocable, disable-model-invocation). This script regenerates the
9
+ * frontmatter block of every scripts/skill-templates/<name>/SKILL.md from that manifest,
10
+ * preserving the markdown body and any non-managed frontmatter lines verbatim.
11
+ *
12
+ * Direction is forward (manifest -> source frontmatter); build-skills.cjs then
13
+ * propagates scripts/skill-templates -> skills/ + dist/claude-code/. A CI drift gate
14
+ * (--check) keeps committed frontmatter == generated.
15
+ *
16
+ * Modes:
17
+ * (no flag) regenerate scripts/skill-templates/<name>/SKILL.md frontmatter from skills.json
18
+ * --check exit 1 if any committed frontmatter differs from generated (no writes)
19
+ * --extract reverse: read current source frontmatter -> rewrite skills.json
20
+ * (seed/refresh the SoT from ground truth; idempotent with forward)
21
+ *
22
+ * Managed keys (emitted in this canonical order, only when present):
23
+ * name, description, argument-hint, tools, user-invocable, disable-model-invocation
24
+ * Everything else (color, model, writes:, ...) is carried verbatim in the record's
25
+ * `extra_frontmatter` array and re-emitted after the managed block.
26
+ *
27
+ * Exit: 0 ok / 1 drift (--check) / 2 error.
28
+ */
29
+ const fs = require('fs');
30
+ const path = require('path');
31
+
32
+ const ROOT = path.resolve(__dirname, '..');
33
+ const SRC = path.join(ROOT, 'scripts', 'skill-templates');
34
+ const SKILLS_JSON = path.join(ROOT, 'scripts', 'lib', 'manifest', 'skills.json');
35
+
36
+ // Managed frontmatter keys <-> manifest record keys, in canonical emit order.
37
+ const MANAGED = [
38
+ { fm: 'name', rec: 'name', kind: 'name' },
39
+ { fm: 'description', rec: 'description', kind: 'qstr' },
40
+ { fm: 'argument-hint', rec: 'argument_hint', kind: 'qstr' },
41
+ { fm: 'tools', rec: 'tools', kind: 'bare' },
42
+ { fm: 'user-invocable', rec: 'user_invocable', kind: 'bool' },
43
+ { fm: 'disable-model-invocation', rec: 'disable_model_invocation', kind: 'bool' },
44
+ ];
45
+ const MANAGED_FM = new Set(MANAGED.map((m) => m.fm));
46
+
47
+ function fail(msg) {
48
+ process.stderr.write(`generate-skill-frontmatter: ${msg}\n`);
49
+ process.exit(2);
50
+ }
51
+
52
+ function listSkillDirs() {
53
+ if (!fs.existsSync(SRC)) fail(`source dir not found: ${SRC}`);
54
+ return fs
55
+ .readdirSync(SRC, { withFileTypes: true })
56
+ .filter((e) => e.isDirectory() && fs.existsSync(path.join(SRC, e.name, 'SKILL.md')))
57
+ .map((e) => e.name)
58
+ .sort((a, b) => a.localeCompare(b));
59
+ }
60
+
61
+ /** Split a SKILL.md into { fmLines, body }. fmLines excludes the --- fences. */
62
+ function splitFrontmatter(text, id) {
63
+ const norm = text.replace(/\r\n/g, '\n');
64
+ if (!norm.startsWith('---\n')) fail(`${id}: SKILL.md does not start with a --- frontmatter fence`);
65
+ const end = norm.indexOf('\n---\n', 4);
66
+ if (end === -1) fail(`${id}: unterminated frontmatter`);
67
+ const fmBlock = norm.slice(4, end + 1); // include trailing \n of last fm line
68
+ const body = norm.slice(end + 5); // after "\n---\n"
69
+ const fmLines = fmBlock.replace(/\n$/, '').split('\n');
70
+ return { fmLines, body };
71
+ }
72
+
73
+ function unquote(v) {
74
+ const t = v.trim();
75
+ if (t.length >= 2 && t.startsWith('"') && t.endsWith('"')) {
76
+ return t.slice(1, -1).replace(/\\"/g, '"').replace(/\\\\/g, '\\');
77
+ }
78
+ return t;
79
+ }
80
+ function quote(s) {
81
+ return '"' + String(s).replace(/\\/g, '\\\\').replace(/"/g, '\\"') + '"';
82
+ }
83
+
84
+ /** Parse one skill's frontmatter lines into an enriched record. */
85
+ function recordFromFrontmatter(id, fmLines) {
86
+ const rec = { name: id };
87
+ const extra = [];
88
+ let i = 0;
89
+ while (i < fmLines.length) {
90
+ const line = fmLines[i];
91
+ const m = /^([A-Za-z][\w-]*):(.*)$/.exec(line);
92
+ if (!m) {
93
+ // stray non-key line at top level — preserve verbatim
94
+ extra.push(line);
95
+ i += 1;
96
+ continue;
97
+ }
98
+ const key = m[1];
99
+ const rawVal = m[2];
100
+ // gather continuation lines (indented or list items or blank-within-block)
101
+ const block = [line];
102
+ let j = i + 1;
103
+ while (j < fmLines.length && /^(\s+\S|\s*-\s|\s*$)/.test(fmLines[j]) && !/^[A-Za-z][\w-]*:/.test(fmLines[j])) {
104
+ block.push(fmLines[j]);
105
+ j += 1;
106
+ }
107
+ const managed = MANAGED.find((mm) => mm.fm === key);
108
+ if (managed && block.length === 1) {
109
+ const v = rawVal.trim();
110
+ if (managed.kind === 'name') {
111
+ if (v !== `gdd-${id}`) rec.frontmatter_name = v;
112
+ } else if (managed.kind === 'bool') {
113
+ rec[managed.rec] = v === 'true';
114
+ } else if (managed.kind === 'qstr') {
115
+ rec[managed.rec] = unquote(v);
116
+ } else {
117
+ rec[managed.rec] = v; // bare (tools)
118
+ }
119
+ } else {
120
+ extra.push(...block);
121
+ }
122
+ i = j;
123
+ }
124
+ if (extra.length) rec.extra_frontmatter = extra;
125
+ return rec;
126
+ }
127
+
128
+ /**
129
+ * Emit the frontmatter block (without --- fences) for a record.
130
+ *
131
+ * Order-preserving: `name` always leads, then the managed keys are emitted in
132
+ * the record's own insertion order (which --extract captures from the original
133
+ * file order), then any non-managed lines verbatim. This keeps forward
134
+ * generation a byte-for-byte fixed point on the committed tree, so existing
135
+ * frontmatter-snapshot baselines never churn. New skills authored directly in
136
+ * skills.json get whatever key order their record uses.
137
+ */
138
+ function frontmatterFromRecord(rec) {
139
+ const out = [`name: ${rec.frontmatter_name || `gdd-${rec.name}`}`];
140
+ const byRec = new Map(MANAGED.filter((m) => m.kind !== 'name').map((m) => [m.rec, m]));
141
+ for (const key of Object.keys(rec)) {
142
+ const m = byRec.get(key);
143
+ if (!m) continue; // name / frontmatter_name / extra_frontmatter / registered_in_phase / aliases / ...
144
+ const v = rec[key];
145
+ if (v === undefined || v === null) continue;
146
+ if (m.kind === 'bool') out.push(`${m.fm}: ${v ? 'true' : 'false'}`);
147
+ else if (m.kind === 'qstr') out.push(`${m.fm}: ${quote(v)}`);
148
+ else out.push(`${m.fm}: ${v}`);
149
+ }
150
+ if (Array.isArray(rec.extra_frontmatter)) out.push(...rec.extra_frontmatter);
151
+ return out.join('\n');
152
+ }
153
+
154
+ function readSkillsJson() {
155
+ return JSON.parse(fs.readFileSync(SKILLS_JSON, 'utf8'));
156
+ }
157
+ function recordMap(json) {
158
+ const map = new Map();
159
+ for (const r of json.skills || []) map.set(r.name, r);
160
+ return map;
161
+ }
162
+
163
+ /** Build the regenerated SKILL.md text for one skill from its record. */
164
+ function renderSkill(id, rec) {
165
+ const abs = path.join(SRC, id, 'SKILL.md');
166
+ const { body } = splitFrontmatter(fs.readFileSync(abs, 'utf8'), id);
167
+ return `---\n${frontmatterFromRecord(rec)}\n---\n${body}`;
168
+ }
169
+
170
+ function modeForward(check) {
171
+ const json = readSkillsJson();
172
+ const map = recordMap(json);
173
+ const dirs = listSkillDirs();
174
+ const drift = [];
175
+ let written = 0;
176
+ for (const id of dirs) {
177
+ const rec = map.get(id);
178
+ if (!rec) {
179
+ if (check) { drift.push(`${id} (missing from skills.json)`); continue; }
180
+ fail(`${id}: present in scripts/skill-templates but missing from skills.json — add a record (run --extract)`);
181
+ }
182
+ const abs = path.join(SRC, id, 'SKILL.md');
183
+ const cur = fs.readFileSync(abs, 'utf8').replace(/\r\n/g, '\n');
184
+ const next = renderSkill(id, rec);
185
+ if (cur === next) continue;
186
+ if (check) drift.push(id);
187
+ else { fs.writeFileSync(abs, next); written += 1; }
188
+ }
189
+ // records in skills.json with no source dir (e.g. not yet authored) are tolerated
190
+ if (check) {
191
+ if (drift.length) {
192
+ process.stderr.write(
193
+ `generate-skill-frontmatter --check: ${drift.length} skill(s) drift from skills.json:\n ${drift.slice(0, 20).join('\n ')}\n` +
194
+ `Run \`npm run generate:skill-frontmatter\` then \`npm run build:skills\`.\n`,
195
+ );
196
+ process.exit(1);
197
+ }
198
+ process.stdout.write(`generate-skill-frontmatter --check: OK — ${dirs.length} skills match skills.json.\n`);
199
+ return;
200
+ }
201
+ process.stdout.write(`generate-skill-frontmatter: regenerated ${written}/${dirs.length} skill frontmatter block(s).\n`);
202
+ }
203
+
204
+ function modeExtract() {
205
+ const existing = fs.existsSync(SKILLS_JSON) ? readSkillsJson() : { schema_version: 1, skills: [] };
206
+ const prevMap = recordMap(existing);
207
+ const dirs = listSkillDirs();
208
+ const skills = [];
209
+ for (const id of dirs) {
210
+ const { fmLines } = splitFrontmatter(fs.readFileSync(path.join(SRC, id, 'SKILL.md'), 'utf8'), id);
211
+ const rec = recordFromFrontmatter(id, fmLines);
212
+ // preserve curated fields that live only in the manifest (not frontmatter)
213
+ const prev = prevMap.get(id);
214
+ if (prev && prev.registered_in_phase != null) rec.registered_in_phase = prev.registered_in_phase;
215
+ if (prev && prev.aliases != null) rec.aliases = prev.aliases;
216
+ skills.push(rec);
217
+ }
218
+ const out = { schema_version: existing.schema_version || 1 };
219
+ if (existing.note) out.note = existing.note;
220
+ out.skills = skills;
221
+ fs.writeFileSync(SKILLS_JSON, JSON.stringify(out, null, 2) + '\n');
222
+ process.stdout.write(`generate-skill-frontmatter --extract: wrote ${skills.length} enriched records to skills.json.\n`);
223
+ }
224
+
225
+ function main(argv) {
226
+ const args = argv.slice(2);
227
+ if (args.includes('--extract')) return modeExtract();
228
+ return modeForward(args.includes('--check'));
229
+ }
230
+
231
+ if (require.main === module) main(process.argv);
232
+
233
+ module.exports = {
234
+ splitFrontmatter,
235
+ recordFromFrontmatter,
236
+ frontmatterFromRecord,
237
+ renderSkill,
238
+ unquote,
239
+ quote,
240
+ MANAGED,
241
+ MANAGED_FM,
242
+ main,
243
+ };
@@ -3,7 +3,7 @@
3
3
  * scripts/lib/manifest/scaffolder.cjs — Phase 50 (Authoring Contract v3).
4
4
  *
5
5
  * Pure, dependency-free generator behind the `/gdd:new-skill` scaffolder skill.
6
- * The SKILL.md (skill-templates/new-skill/SKILL.md) drives the interactive
6
+ * The SKILL.md (scripts/skill-templates/new-skill/SKILL.md) drives the interactive
7
7
  * prompts; this module is the deterministic core it (and the test suite) call.
8
8
  *
9
9
  * Exports:
@@ -25,7 +25,7 @@
25
25
  "name": {
26
26
  "type": "string",
27
27
  "minLength": 1,
28
- "description": "Skill id = skill-templates/<name>/ directory name."
28
+ "description": "Skill id = scripts/skill-templates/<name>/ directory name."
29
29
  },
30
30
  "frontmatter_name": {
31
31
  "type": "string",
@@ -319,7 +319,7 @@
319
319
  },
320
320
  {
321
321
  "name": "new-skill",
322
- "description": "Scaffolds a new Phase-28.5 + Phase-50-compliant skill: gathers a name, a multi-paragraph v3 description, a lifecycle stage, an allowed-tools list, and optional composes_with neighbours, then writes skill-templates/<name>/SKILL.md from the pure generator. Use when adding a brand-new gdd skill and you want the frontmatter, length cap, and v3 description form correct from the first commit. Activates for requests involving authoring a skill, scaffolding a command, creating a new SKILL.md, or adding a slash command.",
322
+ "description": "Scaffolds a new Phase-28.5 + Phase-50-compliant skill: gathers a name, a multi-paragraph v3 description, a lifecycle stage, an allowed-tools list, and optional composes_with neighbours, then writes scripts/skill-templates/<name>/SKILL.md from the pure generator. Use when adding a brand-new gdd skill and you want the frontmatter, length cap, and v3 description form correct from the first commit. Activates for requests involving authoring a skill, scaffolding a command, creating a new SKILL.md, or adding a slash command.",
323
323
  "argument_hint": "<skill-name>",
324
324
  "tools": "Read, Write, Bash, AskUserQuestion",
325
325
  "user_invocable": true,
@@ -3,7 +3,7 @@
3
3
  * scripts/lib/new-addendum.cjs — Phase 54 (Composable Reference Addendums), REG-01.
4
4
  *
5
5
  * Pure, dependency-free generator behind the `/gdd:new-addendum <kind> <name>`
6
- * scaffolder skill (skill-templates/new-addendum/SKILL.md). The SKILL.md drives
6
+ * scaffolder skill (scripts/skill-templates/new-addendum/SKILL.md). The SKILL.md drives
7
7
  * the prompts; this module is the deterministic core it (and the test suite)
8
8
  * call. Mirrors scripts/lib/manifest/scaffolder.cjs (the Phase 50 skill
9
9
  * scaffolder): same ReDoS-safe NAME_RE, same throw-on-invalid contract, same