@hegemonart/get-design-done 1.59.3 → 1.59.5
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.
- package/.claude-plugin/marketplace.json +2 -2
- package/.claude-plugin/plugin.json +1 -1
- package/CHANGELOG.md +61 -0
- package/SKILL.md +2 -0
- package/figma-plugin/README.md +61 -0
- package/figma-plugin/code.ts +36 -0
- package/figma-plugin/manifest.json +12 -0
- package/figma-plugin/package-lock.json +35 -0
- package/figma-plugin/package.json +12 -0
- package/figma-plugin/src/export-variables.ts +144 -0
- package/figma-plugin/src/payload-schema.ts +250 -0
- package/figma-plugin/tsconfig.json +16 -0
- package/figma-plugin/ui.html +44 -0
- package/hooks/budget-enforcer.ts +134 -7
- package/hooks/gdd-intel-trigger.js +3 -3
- package/package.json +6 -1
- package/reference/DEPRECATIONS.md +3 -3
- package/reference/live-mode-integration.md +1 -1
- package/reference/registry.json +1 -1
- package/reference/runtime-models.md +15 -15
- package/reference/schemas/generated.d.ts +4 -0
- package/reference/schemas/runtime-models.schema.json +5 -0
- package/reference/skill-metadata.md +4 -4
- package/reference/skill-placeholders.md +2 -2
- package/scripts/build-skills.cjs +146 -0
- package/scripts/generate-skill-frontmatter.cjs +243 -0
- package/scripts/lib/bandit-router/integration.cjs +38 -0
- package/scripts/lib/install/installer.cjs +133 -1
- package/scripts/lib/manifest/scaffolder.cjs +1 -1
- package/scripts/lib/manifest/schemas/skills.schema.json +1 -1
- package/scripts/lib/manifest/skills.json +1 -1
- package/scripts/lib/new-addendum.cjs +1 -1
- package/scripts/skill-templates/README.md +90 -0
- package/scripts/skill-templates/add-backlog/SKILL.md +48 -0
- package/scripts/skill-templates/analyze-dependencies/SKILL.md +95 -0
- package/scripts/skill-templates/apply-reflections/SKILL.md +109 -0
- package/scripts/skill-templates/apply-reflections/apply-reflections-procedure.md +170 -0
- package/scripts/skill-templates/audit/SKILL.md +79 -0
- package/scripts/skill-templates/bandit-reset/SKILL.md +91 -0
- package/scripts/skill-templates/bandit-status/SKILL.md +94 -0
- package/scripts/skill-templates/benchmark/SKILL.md +65 -0
- package/scripts/skill-templates/bootstrap-ds/SKILL.md +43 -0
- package/scripts/skill-templates/brief/SKILL.md +145 -0
- package/scripts/skill-templates/budget/SKILL.md +45 -0
- package/scripts/skill-templates/cache-manager/SKILL.md +66 -0
- package/scripts/skill-templates/cache-manager/cache-policy.md +126 -0
- package/scripts/skill-templates/check-update/SKILL.md +98 -0
- package/scripts/skill-templates/compare/SKILL.md +82 -0
- package/scripts/skill-templates/compare/compare-rubric.md +171 -0
- package/scripts/skill-templates/complete-cycle/SKILL.md +81 -0
- package/scripts/skill-templates/connections/SKILL.md +71 -0
- package/scripts/skill-templates/connections/connections-onboarding.md +608 -0
- package/scripts/skill-templates/context/SKILL.md +137 -0
- package/scripts/skill-templates/continue/SKILL.md +24 -0
- package/scripts/skill-templates/darkmode/SKILL.md +76 -0
- package/scripts/skill-templates/darkmode/darkmode-audit-procedure.md +258 -0
- package/scripts/skill-templates/debug/SKILL.md +41 -0
- package/scripts/skill-templates/debug/debug-feedback-loops.md +119 -0
- package/scripts/skill-templates/design/SKILL.md +118 -0
- package/scripts/skill-templates/design/design-procedure.md +304 -0
- package/scripts/skill-templates/discuss/SKILL.md +96 -0
- package/scripts/skill-templates/do/SKILL.md +45 -0
- package/scripts/skill-templates/explore/SKILL.md +118 -0
- package/scripts/skill-templates/explore/explore-procedure.md +267 -0
- package/scripts/skill-templates/export/SKILL.md +30 -0
- package/scripts/skill-templates/extract-learnings/SKILL.md +114 -0
- package/scripts/skill-templates/fast/SKILL.md +91 -0
- package/scripts/skill-templates/figma-extract/SKILL.md +64 -0
- package/scripts/skill-templates/figma-write/SKILL.md +50 -0
- package/scripts/skill-templates/graphify/SKILL.md +49 -0
- package/scripts/skill-templates/health/SKILL.md +99 -0
- package/scripts/skill-templates/health/health-mcp-detection.md +44 -0
- package/scripts/skill-templates/health/health-skill-length-report.md +69 -0
- package/scripts/skill-templates/help/SKILL.md +60 -0
- package/scripts/skill-templates/instinct/SKILL.md +111 -0
- package/scripts/skill-templates/list-assumptions/SKILL.md +61 -0
- package/scripts/skill-templates/list-pins/SKILL.md +27 -0
- package/scripts/skill-templates/live/SKILL.md +98 -0
- package/scripts/skill-templates/locale/SKILL.md +51 -0
- package/scripts/skill-templates/map/SKILL.md +89 -0
- package/scripts/skill-templates/migrate/SKILL.md +70 -0
- package/scripts/skill-templates/migrate-context/SKILL.md +123 -0
- package/scripts/skill-templates/new-addendum/SKILL.md +81 -0
- package/scripts/skill-templates/new-cycle/SKILL.md +37 -0
- package/scripts/skill-templates/new-project/SKILL.md +53 -0
- package/scripts/skill-templates/new-skill/SKILL.md +90 -0
- package/scripts/skill-templates/next/SKILL.md +68 -0
- package/scripts/skill-templates/note/SKILL.md +48 -0
- package/scripts/skill-templates/openrouter-status/SKILL.md +86 -0
- package/scripts/skill-templates/optimize/SKILL.md +97 -0
- package/scripts/skill-templates/override/SKILL.md +86 -0
- package/scripts/skill-templates/paper-write/SKILL.md +54 -0
- package/scripts/skill-templates/pause/SKILL.md +77 -0
- package/scripts/skill-templates/peer-cli-add/SKILL.md +88 -0
- package/scripts/skill-templates/peer-cli-add/peer-cli-protocol.md +161 -0
- package/scripts/skill-templates/peer-cli-customize/SKILL.md +89 -0
- package/scripts/skill-templates/peers/SKILL.md +96 -0
- package/scripts/skill-templates/pencil-write/SKILL.md +54 -0
- package/scripts/skill-templates/pin/SKILL.md +37 -0
- package/scripts/skill-templates/plan/SKILL.md +105 -0
- package/scripts/skill-templates/plan/plan-procedure.md +278 -0
- package/scripts/skill-templates/plant-seed/SKILL.md +48 -0
- package/scripts/skill-templates/pr-branch/SKILL.md +32 -0
- package/scripts/skill-templates/progress/SKILL.md +107 -0
- package/scripts/skill-templates/quality-gate/SKILL.md +90 -0
- package/scripts/skill-templates/quality-gate/threat-modeling.md +101 -0
- package/scripts/skill-templates/quick/SKILL.md +44 -0
- package/scripts/skill-templates/reapply-patches/SKILL.md +32 -0
- package/scripts/skill-templates/recall/SKILL.md +75 -0
- package/scripts/skill-templates/reflect/SKILL.md +85 -0
- package/scripts/skill-templates/reflect/procedures/capability-gap-scan.md +119 -0
- package/scripts/skill-templates/report-issue/SKILL.md +53 -0
- package/scripts/skill-templates/report-issue/report-issue-procedure.md +119 -0
- package/scripts/skill-templates/resume/SKILL.md +93 -0
- package/scripts/skill-templates/review-backlog/SKILL.md +46 -0
- package/scripts/skill-templates/review-decisions/SKILL.md +42 -0
- package/scripts/skill-templates/roi/SKILL.md +54 -0
- package/scripts/skill-templates/rollout-status/SKILL.md +35 -0
- package/scripts/skill-templates/router/SKILL.md +89 -0
- package/scripts/skill-templates/router/capability-gap-emitter.md +65 -0
- package/scripts/skill-templates/router/router-pick-emitter.md +78 -0
- package/scripts/skill-templates/router/router-rules.md +84 -0
- package/scripts/skill-templates/settings/SKILL.md +87 -0
- package/scripts/skill-templates/ship/SKILL.md +48 -0
- package/scripts/skill-templates/sketch/SKILL.md +78 -0
- package/scripts/skill-templates/sketch-wrap-up/SKILL.md +92 -0
- package/scripts/skill-templates/skill-manifest/SKILL.md +79 -0
- package/scripts/skill-templates/spike/SKILL.md +67 -0
- package/scripts/skill-templates/spike-wrap-up/SKILL.md +86 -0
- package/scripts/skill-templates/start/SKILL.md +67 -0
- package/scripts/skill-templates/start/start-procedure.md +115 -0
- package/scripts/skill-templates/state/SKILL.md +106 -0
- package/scripts/skill-templates/stats/SKILL.md +51 -0
- package/scripts/skill-templates/style/SKILL.md +71 -0
- package/scripts/skill-templates/style/style-doc-procedure.md +150 -0
- package/scripts/skill-templates/synthesize/SKILL.md +94 -0
- package/scripts/skill-templates/timeline/SKILL.md +66 -0
- package/scripts/skill-templates/todo/SKILL.md +64 -0
- package/scripts/skill-templates/turn-closeout/SKILL.md +95 -0
- package/scripts/skill-templates/undo/SKILL.md +31 -0
- package/scripts/skill-templates/unlock-decision/SKILL.md +54 -0
- package/scripts/skill-templates/unpin/SKILL.md +31 -0
- package/scripts/skill-templates/update/SKILL.md +56 -0
- package/scripts/skill-templates/using-gdd/SKILL.md +78 -0
- package/scripts/skill-templates/verify/SKILL.md +113 -0
- package/scripts/skill-templates/verify/verify-procedure.md +511 -0
- package/scripts/skill-templates/warm-cache/SKILL.md +81 -0
- package/scripts/skill-templates/watch-authorities/SKILL.md +82 -0
- package/scripts/skill-templates/zoom-out/SKILL.md +26 -0
- package/sdk/cli/commands/build.ts +2 -2
- package/sdk/cli/index.js +2 -2
- package/sdk/cli/index.ts +1 -1
- package/skills/README.md +22 -14
- package/skills/help/SKILL.md +28 -55
- package/skills/new-skill/SKILL.md +5 -5
|
@@ -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
|
+
};
|
|
@@ -35,6 +35,12 @@
|
|
|
35
35
|
|
|
36
36
|
const banditRouter = require('../bandit-router.cjs');
|
|
37
37
|
const adaptiveModeLib = require('../adaptive-mode.cjs');
|
|
38
|
+
// Phase 56 (CAL-01) per-agent risk calibration. recordOutcome feeds the same
|
|
39
|
+
// {agent, status} signal it gives the bandit into this table so calibration
|
|
40
|
+
// learns from the post-spawn outcome too. Lazy-tolerant: the call is wrapped in
|
|
41
|
+
// its own best-effort try/catch (D-04) so a calibration write can never break
|
|
42
|
+
// the bandit path.
|
|
43
|
+
const calibration = require('../risk/calibration.cjs');
|
|
38
44
|
|
|
39
45
|
const DELEGATE_NONE = banditRouter.DELEGATE_NONE; // 'none'
|
|
40
46
|
const VALID_DELEGATES = banditRouter.DEFAULT_DELEGATES; // ['none','gemini','codex','cursor','copilot','qwen']
|
|
@@ -299,6 +305,38 @@ function recordOutcome(input) {
|
|
|
299
305
|
}
|
|
300
306
|
}
|
|
301
307
|
|
|
308
|
+
// CAL-01: also fold the same outcome into the per-agent risk calibration
|
|
309
|
+
// table so the calibration layer (compute-risk feedback) learns from the
|
|
310
|
+
// identical post-spawn signal the bandit just saw. Independent best-effort
|
|
311
|
+
// try/catch (D-04): a calibration write failure must NEVER throw into or
|
|
312
|
+
// break the bandit path above. The bandit signal carries no emitted risk
|
|
313
|
+
// score, so `risk` degrades to 0 via normalizeRecord; status drives the
|
|
314
|
+
// correctness axis (completed → applied-correct, anything else → not-correct).
|
|
315
|
+
// Writes to calibration.DEFAULT_CALIBRATION_PATH ('.design/telemetry/
|
|
316
|
+
// calibration.json') under baseDir — the module's own canonical location.
|
|
317
|
+
try {
|
|
318
|
+
calibration.updateCalibration(
|
|
319
|
+
input.agent,
|
|
320
|
+
{
|
|
321
|
+
accepted: true,
|
|
322
|
+
post_apply_correct: input.status === 'completed',
|
|
323
|
+
},
|
|
324
|
+
{ root: input.baseDir, baseDir: input.baseDir },
|
|
325
|
+
);
|
|
326
|
+
} catch (err) {
|
|
327
|
+
if (process.env.GDD_BANDIT_DEBUG === '1') {
|
|
328
|
+
try {
|
|
329
|
+
process.stderr.write(
|
|
330
|
+
'[bandit-integration] recordOutcome calibration swallowed: ' +
|
|
331
|
+
(err && err.message ? err.message : String(err)) +
|
|
332
|
+
'\n',
|
|
333
|
+
);
|
|
334
|
+
} catch {
|
|
335
|
+
/* swallow */
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
|
|
302
340
|
return undefined;
|
|
303
341
|
}
|
|
304
342
|
|
|
@@ -334,6 +334,58 @@ function listSourceSkills(skillsRoot) {
|
|
|
334
334
|
});
|
|
335
335
|
}
|
|
336
336
|
|
|
337
|
+
/**
|
|
338
|
+
* Enumerate co-located sibling `*.md` reference files for a skill.
|
|
339
|
+
*
|
|
340
|
+
* A skill source directory may ship reference files next to SKILL.md
|
|
341
|
+
* (e.g. `<name>-procedure.md`, `<name>-rules.md`, `cache-policy.md`).
|
|
342
|
+
* SKILL.md references these via relative links; if they are not installed
|
|
343
|
+
* the links resolve to nothing. This returns the top-level sibling `.md`
|
|
344
|
+
* files only (NOT SKILL.md itself, NOT files in nested subdirectories).
|
|
345
|
+
*
|
|
346
|
+
* Best-effort: any fs error yields an empty list (never throws). A single
|
|
347
|
+
* unreadable skill dir must not crash the whole install.
|
|
348
|
+
*
|
|
349
|
+
* @param {string} skillSrcDir absolute path to `<skillsRoot>/<name>`
|
|
350
|
+
* @returns {string[]} basenames of sibling `.md` files (excluding SKILL.md)
|
|
351
|
+
*/
|
|
352
|
+
function listSiblingRefFiles(skillSrcDir) {
|
|
353
|
+
let entries;
|
|
354
|
+
try {
|
|
355
|
+
entries = fs.readdirSync(skillSrcDir, { withFileTypes: true });
|
|
356
|
+
} catch {
|
|
357
|
+
return [];
|
|
358
|
+
}
|
|
359
|
+
return entries
|
|
360
|
+
.filter((ent) => {
|
|
361
|
+
if (!ent.isFile()) return false;
|
|
362
|
+
if (ent.name === 'SKILL.md') return false;
|
|
363
|
+
return ent.name.toLowerCase().endsWith('.md');
|
|
364
|
+
})
|
|
365
|
+
.map((ent) => ent.name);
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
/**
|
|
369
|
+
* Wrap a passthrough sibling reference file's content with a plugin
|
|
370
|
+
* fingerprint header so foreign-file protection + uninstall can recognize
|
|
371
|
+
* it as plugin-owned. Idempotent: re-wrapping a file that already carries
|
|
372
|
+
* the fingerprint returns it unchanged.
|
|
373
|
+
*
|
|
374
|
+
* The fingerprint matches `merge.cjs#GDD_ADAPTER_FINGERPRINT`, the same
|
|
375
|
+
* marker every SKILL converter injects via `shared.ensureAdapterHeader`,
|
|
376
|
+
* so `isPluginOwned` treats the sibling as owned.
|
|
377
|
+
*
|
|
378
|
+
* @param {string} raw source sibling file content
|
|
379
|
+
* @returns {string}
|
|
380
|
+
*/
|
|
381
|
+
function fingerprintSiblingRef(raw) {
|
|
382
|
+
const text = typeof raw === 'string' ? raw : '';
|
|
383
|
+
if (isPluginOwned(text)) return text;
|
|
384
|
+
const header =
|
|
385
|
+
'<!-- gdd: auto-generated from Claude SKILL.md. Reference adapter -->\n\n';
|
|
386
|
+
return header + text;
|
|
387
|
+
}
|
|
388
|
+
|
|
337
389
|
/**
|
|
338
390
|
* Install all artifacts for a `multi-artifact` runtime.
|
|
339
391
|
*
|
|
@@ -395,6 +447,48 @@ function installMultiArtifact(runtime, configDir, dryRun, opts) {
|
|
|
395
447
|
action: writeResult.action,
|
|
396
448
|
...(writeResult.reason ? { reason: writeResult.reason } : {}),
|
|
397
449
|
});
|
|
450
|
+
|
|
451
|
+
// Batch H6: carry co-located sibling `*.md` reference files alongside
|
|
452
|
+
// SKILL.md. The skills layout only stages SKILL.md per skill, so
|
|
453
|
+
// reference siblings (e.g. `<name>-procedure.md`) are otherwise lost.
|
|
454
|
+
// Scoped to cursor (the audited flat-layout runtime); other runtimes
|
|
455
|
+
// keep their prior single-SKILL.md behavior. Siblings are passthrough
|
|
456
|
+
// copies fingerprinted so foreign-file protection + uninstall treat
|
|
457
|
+
// them as plugin-owned. Broader skillsKind-runtime carry is deferred
|
|
458
|
+
// (see converters/cursor.cjs KNOWN LIMITATION).
|
|
459
|
+
if (kind.kind === 'skills' && runtime.id === 'cursor' && item.srcPath) {
|
|
460
|
+
const skillSrcDir = path.dirname(item.srcPath);
|
|
461
|
+
const skillDestDir = path.dirname(destPath);
|
|
462
|
+
for (const sibling of listSiblingRefFiles(skillSrcDir)) {
|
|
463
|
+
let rawSibling;
|
|
464
|
+
try {
|
|
465
|
+
rawSibling = fs.readFileSync(
|
|
466
|
+
path.join(skillSrcDir, sibling),
|
|
467
|
+
'utf8',
|
|
468
|
+
);
|
|
469
|
+
} catch (err) {
|
|
470
|
+
perFile.push({
|
|
471
|
+
kind: 'skill-ref',
|
|
472
|
+
path: path.join(skillDestDir, sibling),
|
|
473
|
+
action: 'skipped-foreign',
|
|
474
|
+
reason: `Could not read sibling ${sibling}: ${err.message}`,
|
|
475
|
+
});
|
|
476
|
+
continue;
|
|
477
|
+
}
|
|
478
|
+
const siblingDest = path.join(skillDestDir, sibling);
|
|
479
|
+
const siblingWrite = writeFingerprinted(
|
|
480
|
+
siblingDest,
|
|
481
|
+
fingerprintSiblingRef(rawSibling),
|
|
482
|
+
dryRun,
|
|
483
|
+
);
|
|
484
|
+
perFile.push({
|
|
485
|
+
kind: 'skill-ref',
|
|
486
|
+
path: siblingDest,
|
|
487
|
+
action: siblingWrite.action,
|
|
488
|
+
...(siblingWrite.reason ? { reason: siblingWrite.reason } : {}),
|
|
489
|
+
});
|
|
490
|
+
}
|
|
491
|
+
}
|
|
398
492
|
}
|
|
399
493
|
}
|
|
400
494
|
|
|
@@ -489,7 +583,45 @@ function uninstallMultiArtifact(runtime, configDir, dryRun, opts) {
|
|
|
489
583
|
|
|
490
584
|
// If we removed a SKILL.md, remember to trim its now-empty parent.
|
|
491
585
|
if (kind.kind === 'skills') {
|
|
492
|
-
|
|
586
|
+
const skillDestDir = path.dirname(destPath);
|
|
587
|
+
skillDirsToTrim.push(skillDestDir);
|
|
588
|
+
|
|
589
|
+
// Batch H6: symmetric cleanup for the sibling reference files the
|
|
590
|
+
// cursor install carries alongside SKILL.md. Remove only the
|
|
591
|
+
// plugin-owned siblings so a now-empty dir can be trimmed below;
|
|
592
|
+
// user-authored siblings are left in place (foreign-file discipline).
|
|
593
|
+
if (runtime.id === 'cursor') {
|
|
594
|
+
for (const sibling of listSiblingRefFiles(skillDestDir)) {
|
|
595
|
+
const siblingPath = path.join(skillDestDir, sibling);
|
|
596
|
+
let siblingContent;
|
|
597
|
+
try {
|
|
598
|
+
siblingContent = fs.readFileSync(siblingPath, 'utf8');
|
|
599
|
+
} catch (err) {
|
|
600
|
+
perFile.push({
|
|
601
|
+
kind: 'skill-ref',
|
|
602
|
+
path: siblingPath,
|
|
603
|
+
action: 'skipped-foreign',
|
|
604
|
+
reason: `Could not read sibling ${sibling}: ${err.message}`,
|
|
605
|
+
});
|
|
606
|
+
continue;
|
|
607
|
+
}
|
|
608
|
+
if (!isPluginOwned(siblingContent)) {
|
|
609
|
+
perFile.push({
|
|
610
|
+
kind: 'skill-ref',
|
|
611
|
+
path: siblingPath,
|
|
612
|
+
action: 'skipped-foreign',
|
|
613
|
+
reason: `Existing ${sibling} was not authored by this plugin; not removing.`,
|
|
614
|
+
});
|
|
615
|
+
continue;
|
|
616
|
+
}
|
|
617
|
+
if (!dryRun) fs.unlinkSync(siblingPath);
|
|
618
|
+
perFile.push({
|
|
619
|
+
kind: 'skill-ref',
|
|
620
|
+
path: siblingPath,
|
|
621
|
+
action: 'removed',
|
|
622
|
+
});
|
|
623
|
+
}
|
|
624
|
+
}
|
|
493
625
|
}
|
|
494
626
|
}
|
|
495
627
|
}
|
|
@@ -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:
|
|
@@ -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
|