@hegemonart/get-design-done 1.59.6 → 1.59.8
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 +55 -0
- package/README.md +4 -13
- package/SKILL.md +1 -1
- package/agents/design-authority-watcher.md +24 -5
- package/bin/gdd-graph +4 -1
- package/docs/i18n/README.de.md +210 -527
- package/docs/i18n/README.fr.md +201 -518
- package/docs/i18n/README.it.md +209 -526
- package/docs/i18n/README.ja.md +207 -524
- package/docs/i18n/README.ko.md +208 -525
- package/docs/i18n/README.zh-CN.md +213 -551
- package/hooks/_hook-emit.js +113 -29
- package/hooks/budget-enforcer.ts +44 -5
- package/hooks/gdd-mcp-circuit-breaker.js +72 -3
- package/hooks/gdd-sessionstart-recap.js +23 -14
- package/hooks/hooks.json +2 -2
- package/package.json +2 -2
- package/reference/bandit-integration.md +13 -2
- package/scripts/bootstrap.cjs +40 -8
- package/scripts/install.cjs +23 -1
- package/scripts/lib/bandit-router.cjs +47 -5
- package/scripts/lib/detect/cli.cjs +13 -3
- package/scripts/lib/install/converters/cursor.cjs +11 -19
- package/scripts/lib/install/doctor-codex-plugin.cjs +1 -1
- package/scripts/lib/install/doctor-cursor-marketplace.cjs +2 -2
- package/scripts/lib/install/installer.cjs +72 -21
- package/scripts/lib/install/merge.cjs +31 -3
- package/scripts/lib/install/runtime-artifact-layout.cjs +42 -8
- package/scripts/lib/manifest/harnesses.json +29 -1
- package/scripts/lib/manifest/skills.json +1 -1
- package/scripts/skill-templates/bandit-reset/SKILL.md +2 -0
- package/scripts/skill-templates/bandit-status/SKILL.md +4 -1
- package/scripts/skill-templates/darkmode/SKILL.md +1 -1
- package/scripts/skill-templates/graphify/SKILL.md +6 -6
- package/scripts/skill-templates/quick/SKILL.md +3 -1
- package/scripts/skill-templates/reflect/SKILL.md +1 -1
- package/scripts/skill-templates/router/SKILL.md +4 -2
- package/sdk/cli/index.js +114 -47
- package/sdk/dashboard/data/source.cjs +50 -4
- package/sdk/event-stream/writer.ts +112 -30
- package/sdk/mcp/gdd-mcp/server.js +49 -36
- package/sdk/mcp/gdd-mcp/tools/shared.ts +20 -2
- package/sdk/mcp/gdd-state/server.js +107 -41
- package/sdk/primitives/lockfile.cjs +26 -5
- package/sdk/state/index.ts +91 -17
- package/sdk/state/lockfile.ts +47 -8
- package/skills/bandit-reset/SKILL.md +2 -0
- package/skills/bandit-status/SKILL.md +4 -1
- package/skills/darkmode/SKILL.md +1 -1
- package/skills/graphify/SKILL.md +6 -6
- package/skills/quick/SKILL.md +3 -1
- package/skills/reflect/SKILL.md +1 -1
- package/skills/router/SKILL.md +4 -2
|
@@ -38,7 +38,9 @@
|
|
|
38
38
|
* - The `prior_class` value is persisted on the arm so subsequent
|
|
39
39
|
* reads + decay calculations preserve it (forward-compat).
|
|
40
40
|
*
|
|
41
|
-
* Atomic .tmp + rename
|
|
41
|
+
* Atomic per-pid-unique .tmp + rename (Phase 59-8 C2: unique tmp name per
|
|
42
|
+
* process so parallel waves never interleave writes on one scratch file).
|
|
43
|
+
* Discounted Thompson via per-arm time-decay
|
|
42
44
|
* factor `rho^days_since_last_use` applied at sample time, not stored.
|
|
43
45
|
*
|
|
44
46
|
* Reward computation (D-06): two-stage lexicographic — UNCHANGED.
|
|
@@ -57,6 +59,17 @@ const path = require('node:path');
|
|
|
57
59
|
const DEFAULT_POSTERIOR_PATH = '.design/telemetry/posterior.json';
|
|
58
60
|
const SCHEMA_VERSION = '1.0.0';
|
|
59
61
|
|
|
62
|
+
// C2 fix (Phase 59-8): monotonic per-process counter for tmp-file naming.
|
|
63
|
+
// Combined with process.pid it guarantees that two concurrent writers — even
|
|
64
|
+
// within the same process, even firing in the same millisecond — never target
|
|
65
|
+
// the same `.tmp` path. The old fixed `p + '.tmp'` name let parallel agent
|
|
66
|
+
// waves interleave partial writes on one tmp file, producing truncated JSON
|
|
67
|
+
// that loadPosterior() then silently reset to an empty posterior (losing all
|
|
68
|
+
// learned arms). Unique tmp + atomic rename makes a half-written file
|
|
69
|
+
// invisible to readers: rename is atomic on the same filesystem, so a reader
|
|
70
|
+
// sees either the old complete file or the new complete file, never a partial.
|
|
71
|
+
let _tmpCounter = 0;
|
|
72
|
+
|
|
60
73
|
// Decay factor — 60-day half-life.
|
|
61
74
|
const DEFAULT_DECAY = 0.988;
|
|
62
75
|
|
|
@@ -136,6 +149,12 @@ function loadPosterior(opts = {}) {
|
|
|
136
149
|
}
|
|
137
150
|
return data;
|
|
138
151
|
} catch {
|
|
152
|
+
// Corrupt-JSON recovery (preserved, Phase 59-8 C2): fall back to an empty
|
|
153
|
+
// posterior. With the per-pid unique-tmp + atomic-rename write discipline
|
|
154
|
+
// (see savePosterior), a reader can no longer observe a half-written file
|
|
155
|
+
// — rename publishes the complete file in one step — so this branch should
|
|
156
|
+
// now only fire on genuine on-disk corruption (e.g. external truncation),
|
|
157
|
+
// not on a write/read race during a parallel agent wave.
|
|
139
158
|
return { schema_version: SCHEMA_VERSION, generated_at: new Date().toISOString(), arms: [] };
|
|
140
159
|
}
|
|
141
160
|
}
|
|
@@ -159,9 +178,19 @@ function savePosterior(posterior, opts = {}) {
|
|
|
159
178
|
const p = resolvePath(opts);
|
|
160
179
|
fs.mkdirSync(path.dirname(p), { recursive: true });
|
|
161
180
|
posterior.generated_at = new Date().toISOString();
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
181
|
+
// C2 fix (Phase 59-8): per-process-unique tmp name (pid + monotonic
|
|
182
|
+
// counter) so concurrent writers never collide on the same scratch file.
|
|
183
|
+
// The atomic rename then publishes the fully-written file in one step.
|
|
184
|
+
const tmp = `${p}.${process.pid}.${_tmpCounter++}.tmp`;
|
|
185
|
+
try {
|
|
186
|
+
fs.writeFileSync(tmp, JSON.stringify(posterior, null, 2));
|
|
187
|
+
fs.renameSync(tmp, p);
|
|
188
|
+
} catch (err) {
|
|
189
|
+
// Best-effort cleanup of the orphaned tmp on failure so a crashed
|
|
190
|
+
// write never leaves stale scratch files behind. ENOENT is fine.
|
|
191
|
+
try { fs.unlinkSync(tmp); } catch { /* already gone */ }
|
|
192
|
+
throw err;
|
|
193
|
+
}
|
|
165
194
|
return p;
|
|
166
195
|
}
|
|
167
196
|
|
|
@@ -347,7 +376,20 @@ function decayArm(arm, opts = {}) {
|
|
|
347
376
|
const factor = Math.pow(decay, days);
|
|
348
377
|
// Decay shrinks both α and β toward the prior. We never go below the
|
|
349
378
|
// initial prior strength — caller can rebuild a fresh prior via reset().
|
|
350
|
-
|
|
379
|
+
//
|
|
380
|
+
// C1 fix (Phase 59-8): decay MUST target the SAME prior the arm was
|
|
381
|
+
// bootstrapped with. The arm persists `prior_class` (Phase 29 Plan 06 /
|
|
382
|
+
// D-04), so pass it through to priorFor — otherwise a promoted-incubator
|
|
383
|
+
// arm (Beta(2,8)) would drift back toward the informed TIER_PRIOR while
|
|
384
|
+
// idle, undoing the D-04 preferential-selection suppression. Default-class
|
|
385
|
+
// arms have no `prior_class` field, so `arm.prior_class` is undefined and
|
|
386
|
+
// priorFor falls through to the Phase 23.5 informed prior (byte-for-byte
|
|
387
|
+
// unchanged).
|
|
388
|
+
const { alpha: pa, beta: pb } = priorFor(
|
|
389
|
+
arm.tier,
|
|
390
|
+
opts.strength ?? PRIOR_STRENGTH,
|
|
391
|
+
arm.prior_class,
|
|
392
|
+
);
|
|
351
393
|
return {
|
|
352
394
|
alpha: pa + factor * Math.max(0, arm.alpha - pa),
|
|
353
395
|
beta: pb + factor * Math.max(0, arm.beta - pb),
|
|
@@ -45,13 +45,23 @@ function isUrl(p) {
|
|
|
45
45
|
return /^https?:\/\//i.test(String(p || ''));
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
-
/**
|
|
48
|
+
/**
|
|
49
|
+
* Select the detection engine. Returns { mode, warning }.
|
|
50
|
+
*
|
|
51
|
+
* There is exactly one engine path: regex over file text (see engine.cjs#run, which takes no
|
|
52
|
+
* jsdom/DOM parameter and is byte-identical whether or not jsdom is installed). So the truthful
|
|
53
|
+
* mode is always 'regex-fast'. We still probe jsdom (unless --fast) to surface a one-line hint
|
|
54
|
+
* that a DOM-aware path is not wired in this build — but we no longer claim a 'dom-aware' mode the
|
|
55
|
+
* engine does not have.
|
|
56
|
+
*/
|
|
49
57
|
function selectEngine(opts, requireFn) {
|
|
50
58
|
if (opts.fast) return { mode: 'regex-fast', warning: null };
|
|
51
59
|
let hasJsdom = false;
|
|
52
60
|
try { requireFn('jsdom'); hasJsdom = true; } catch { hasJsdom = false; }
|
|
53
|
-
|
|
54
|
-
|
|
61
|
+
// jsdom presence does not change the engine — only emit a hint when it's absent, and never
|
|
62
|
+
// promise a mode we can't deliver.
|
|
63
|
+
const warning = hasJsdom ? null : 'jsdom not installed — running regex-fast (the only wired mode; a DOM-aware path is not implemented). Pass --fast to silence this.';
|
|
64
|
+
return { mode: 'regex-fast', warning };
|
|
55
65
|
}
|
|
56
66
|
|
|
57
67
|
function renderHuman(result, mode) {
|
|
@@ -25,25 +25,17 @@
|
|
|
25
25
|
* Pure / side-effect-free: no fs, no env, no path. `convert` is a
|
|
26
26
|
* deterministic string → string transform.
|
|
27
27
|
*
|
|
28
|
-
*
|
|
29
|
-
* The
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
* `
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
* uses `skillsKind` (claude global, cursor, codex, copilot, antigravity,
|
|
40
|
-
* windsurf, augment, trae, qwen, codebuddy). Fix requires extending the
|
|
41
|
-
* StagedArtifact contract to emit multiple files per skill (one
|
|
42
|
-
* SKILL.md + N siblings), updating `computeDestPath`, the foreign-file
|
|
43
|
-
* detection in `detectMultiArtifactInstalled`, and the uninstall
|
|
44
|
-
* enumeration in `uninstallMultiArtifact`. Tracked as a follow-up
|
|
45
|
-
* beyond batch H6 scope. See `connections/cursor.md` for user-facing
|
|
46
|
-
* guidance.
|
|
28
|
+
* SIBLING .md FILES — RESOLVED (audit AR6, Phase 59.8):
|
|
29
|
+
* The install now carries co-located sibling `*.md` reference files
|
|
30
|
+
* (e.g. `discover-procedure.md`, `cache-policy.md`) alongside SKILL.md
|
|
31
|
+
* for EVERY skillsKind runtime, not just Cursor. The carry happens in
|
|
32
|
+
* `installer.cjs#installMultiArtifact` (gated on `kind.kind === 'skills'`
|
|
33
|
+
* plus `item.srcPath`), with symmetric removal in `uninstallMultiArtifact`.
|
|
34
|
+
* Siblings are passthrough copies fingerprinted via `fingerprintSiblingRef`
|
|
35
|
+
* so foreign-file protection + uninstall treat them as plugin-owned. Only
|
|
36
|
+
* top-level `*.md` siblings are carried; nested subdirectories are out of
|
|
37
|
+
* scope. Previously this was a Batch-H6 cursor-only patch (the audit AR6
|
|
38
|
+
* finding); it is now generalized across all skillsKind runtimes.
|
|
47
39
|
*/
|
|
48
40
|
|
|
49
41
|
const shared = require('./shared.cjs');
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
* path is COMPUTED (pure string composition via `os.homedir()`), NOT
|
|
21
21
|
* verified. The maintainer verifies the cache after running the field-
|
|
22
22
|
* test command on a Codex-installed machine (see
|
|
23
|
-
*
|
|
23
|
+
* the maintainer field-test notes).
|
|
24
24
|
*
|
|
25
25
|
* Phase 28.8 D-14: the `.claude-plugin/marketplace.json` catalog file is
|
|
26
26
|
* reused from Claude Code's marketplace per Codex's legacy-compatible
|
|
@@ -137,14 +137,14 @@ function readJsonFileSafe(filePath) {
|
|
|
137
137
|
function buildGuidance(r) {
|
|
138
138
|
switch (r.state) {
|
|
139
139
|
case MARKETPLACE_STATES.NOT_SUBMITTED:
|
|
140
|
-
return 'submit publisher application at cursor.com/marketplace/publish
|
|
140
|
+
return 'submit publisher application at cursor.com/marketplace/publish';
|
|
141
141
|
case MARKETPLACE_STATES.SUBMITTED_PENDING:
|
|
142
142
|
return 'await Cursor team review approval; no published SLA per D-16';
|
|
143
143
|
case MARKETPLACE_STATES.APPROVED_PUBLISHED:
|
|
144
144
|
return 'plugin is live at ' + (r.marketplaceUrl || '<marketplace-url>');
|
|
145
145
|
case MARKETPLACE_STATES.REJECTED:
|
|
146
146
|
return 'address rejection reason: ' + (r.rejectionReason || '<unspecified>')
|
|
147
|
-
+ '; re-submit
|
|
147
|
+
+ '; re-submit at cursor.com/marketplace/publish';
|
|
148
148
|
default:
|
|
149
149
|
return '';
|
|
150
150
|
}
|
|
@@ -163,12 +163,17 @@ function installClaudeMarketplace(runtime, configDir, dryRun) {
|
|
|
163
163
|
dryRun,
|
|
164
164
|
};
|
|
165
165
|
}
|
|
166
|
+
// B1 fix (Phase 59.8): decide created-vs-updated BEFORE the write. The
|
|
167
|
+
// settings.json file is written by atomicWrite below, so testing
|
|
168
|
+
// `existsSync(settingsPath)` afterwards always returned 'updated' (the file
|
|
169
|
+
// we just wrote exists). Capture the pre-write existence instead.
|
|
170
|
+
const existedBefore = fs.existsSync(settingsPath);
|
|
166
171
|
const formatted = `${JSON.stringify(next, null, 2)}\n`;
|
|
167
172
|
if (!dryRun) atomicWrite(settingsPath, formatted);
|
|
168
173
|
return {
|
|
169
174
|
runtime: runtime.id,
|
|
170
175
|
path: settingsPath,
|
|
171
|
-
action:
|
|
176
|
+
action: existedBefore ? 'updated' : 'created',
|
|
172
177
|
dryRun,
|
|
173
178
|
};
|
|
174
179
|
}
|
|
@@ -439,6 +444,20 @@ function installMultiArtifact(runtime, configDir, dryRun, opts) {
|
|
|
439
444
|
continue;
|
|
440
445
|
}
|
|
441
446
|
for (const item of staged) {
|
|
447
|
+
// AR7 fix (Phase 59.8): never write a 0-byte / empty artifact. The old
|
|
448
|
+
// agents path staged `content: ''` for every skill name that had no
|
|
449
|
+
// matching agent file, producing empty `gdd-<name>.md` placeholders.
|
|
450
|
+
// Even with the layout-side enumeration fix, guard defensively here so
|
|
451
|
+
// no converter/kind can ever emit an empty file to disk.
|
|
452
|
+
if (!item.content || !String(item.content).trim()) {
|
|
453
|
+
perFile.push({
|
|
454
|
+
kind: kind.kind,
|
|
455
|
+
path: computeDestPath(configDir, kind, item.name),
|
|
456
|
+
action: 'skipped-empty',
|
|
457
|
+
reason: `Refusing to write empty artifact ${item.name}`,
|
|
458
|
+
});
|
|
459
|
+
continue;
|
|
460
|
+
}
|
|
442
461
|
const destPath = computeDestPath(configDir, kind, item.name);
|
|
443
462
|
const writeResult = writeFingerprinted(destPath, item.content, dryRun);
|
|
444
463
|
perFile.push({
|
|
@@ -448,15 +467,16 @@ function installMultiArtifact(runtime, configDir, dryRun, opts) {
|
|
|
448
467
|
...(writeResult.reason ? { reason: writeResult.reason } : {}),
|
|
449
468
|
});
|
|
450
469
|
|
|
451
|
-
//
|
|
452
|
-
// SKILL.md
|
|
453
|
-
//
|
|
454
|
-
//
|
|
455
|
-
//
|
|
456
|
-
//
|
|
457
|
-
//
|
|
458
|
-
//
|
|
459
|
-
|
|
470
|
+
// AR6 fix (Phase 59.8): carry co-located sibling `*.md` reference files
|
|
471
|
+
// alongside SKILL.md for EVERY skillsKind runtime (cursor, codex,
|
|
472
|
+
// copilot, antigravity, windsurf, augment, trae, qwen, codebuddy, and
|
|
473
|
+
// claude global). The skills layout only stages SKILL.md per skill, so
|
|
474
|
+
// reference siblings (e.g. `<name>-procedure.md`) were otherwise lost on
|
|
475
|
+
// every runtime except cursor — shipping dead relative links. Siblings
|
|
476
|
+
// are passthrough copies fingerprinted so foreign-file protection +
|
|
477
|
+
// uninstall treat them as plugin-owned. Was previously scoped to cursor
|
|
478
|
+
// only (Batch H6); see converters/cursor.cjs former KNOWN LIMITATION.
|
|
479
|
+
if (kind.kind === 'skills' && item.srcPath) {
|
|
460
480
|
const skillSrcDir = path.dirname(item.srcPath);
|
|
461
481
|
const skillDestDir = path.dirname(destPath);
|
|
462
482
|
for (const sibling of listSiblingRefFiles(skillSrcDir)) {
|
|
@@ -550,8 +570,27 @@ function uninstallMultiArtifact(runtime, configDir, dryRun, opts) {
|
|
|
550
570
|
const skillDirsToTrim = [];
|
|
551
571
|
|
|
552
572
|
for (const kind of layout.kinds) {
|
|
553
|
-
|
|
554
|
-
|
|
573
|
+
// AR7 fix (Phase 59.8): derive the artifact names from the SAME staging
|
|
574
|
+
// pass install uses, so uninstall stays symmetric. The `agents` kind
|
|
575
|
+
// enumerates `agents/*.md` (real agent role names), NOT skill names — the
|
|
576
|
+
// old `gdd-<skillName>.md` derivation never matched any installed agent
|
|
577
|
+
// file and left every real agent on disk after `--uninstall`.
|
|
578
|
+
let stagedNames;
|
|
579
|
+
try {
|
|
580
|
+
const staged = kind.stage({
|
|
581
|
+
skillsRoot,
|
|
582
|
+
skillNames,
|
|
583
|
+
scope,
|
|
584
|
+
runtime: runtime.id,
|
|
585
|
+
configDir,
|
|
586
|
+
});
|
|
587
|
+
stagedNames = staged.map((item) => item.name);
|
|
588
|
+
} catch {
|
|
589
|
+
// Fall back to the prior skill-name derivation if staging fails (e.g.
|
|
590
|
+
// a converter throws); skills/commands kinds match this shape exactly.
|
|
591
|
+
stagedNames = skillNames.map((n) => (kind.prefix || '') + n);
|
|
592
|
+
}
|
|
593
|
+
for (const itemName of stagedNames) {
|
|
555
594
|
const destPath = computeDestPath(configDir, kind, itemName);
|
|
556
595
|
if (!fs.existsSync(destPath)) {
|
|
557
596
|
perFile.push({ kind: kind.kind, path: destPath, action: 'unchanged' });
|
|
@@ -586,11 +625,12 @@ function uninstallMultiArtifact(runtime, configDir, dryRun, opts) {
|
|
|
586
625
|
const skillDestDir = path.dirname(destPath);
|
|
587
626
|
skillDirsToTrim.push(skillDestDir);
|
|
588
627
|
|
|
589
|
-
//
|
|
590
|
-
//
|
|
591
|
-
// plugin-owned siblings so a now-empty dir can be trimmed
|
|
592
|
-
// user-authored siblings are left in place (foreign-file
|
|
593
|
-
|
|
628
|
+
// AR6 fix (Phase 59.8): symmetric cleanup for the sibling reference
|
|
629
|
+
// files every skillsKind install carries alongside SKILL.md. Remove
|
|
630
|
+
// only the plugin-owned siblings so a now-empty dir can be trimmed
|
|
631
|
+
// below; user-authored siblings are left in place (foreign-file
|
|
632
|
+
// discipline). Was previously scoped to cursor only (Batch H6).
|
|
633
|
+
if (kind.kind === 'skills') {
|
|
594
634
|
for (const sibling of listSiblingRefFiles(skillDestDir)) {
|
|
595
635
|
const siblingPath = path.join(skillDestDir, sibling);
|
|
596
636
|
let siblingContent;
|
|
@@ -682,11 +722,22 @@ function installCline(runtime, configDir, skillsRoot, skillNames, dryRun) {
|
|
|
682
722
|
const cline = require('./converters/cline.cjs');
|
|
683
723
|
ensureDir(configDir, dryRun);
|
|
684
724
|
|
|
685
|
-
|
|
725
|
+
// B2 fix (Phase 59.8): wrap the per-skill read in try/catch. Previously a
|
|
726
|
+
// single unreadable SKILL.md threw out of `installCline`, aborting the
|
|
727
|
+
// entire cline install (and, when cline is one runtime in a multi-runtime
|
|
728
|
+
// batch, every runtime queued after it). Skip the unreadable skill and keep
|
|
729
|
+
// going, mirroring the best-effort sibling reads elsewhere in this file.
|
|
730
|
+
const blocks = [];
|
|
731
|
+
for (const name of skillNames) {
|
|
686
732
|
const srcPath = path.join(skillsRoot, name, 'SKILL.md');
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
733
|
+
let raw;
|
|
734
|
+
try {
|
|
735
|
+
raw = fs.readFileSync(srcPath, 'utf8');
|
|
736
|
+
} catch {
|
|
737
|
+
continue; // unreadable skill — skip, don't abort the whole install
|
|
738
|
+
}
|
|
739
|
+
blocks.push({ name, block: cline.convert(raw, name, { runtime: 'cline' }) });
|
|
740
|
+
}
|
|
690
741
|
|
|
691
742
|
const desired = cline.buildClinerulesFile(blocks);
|
|
692
743
|
const target = path.join(configDir, '.clinerules');
|
|
@@ -111,11 +111,39 @@ function buildAgentsFileContent(runtime, payloadHeader) {
|
|
|
111
111
|
const GDD_ADAPTER_FINGERPRINT = 'gdd: auto-generated from Claude SKILL.md';
|
|
112
112
|
const CLINERULES_HEADER_FINGERPRINT = '# get-design-done rules';
|
|
113
113
|
|
|
114
|
+
// B5/S4 fix (Phase 59.8): ownership detection is WHOLE-LINE anchored, not a
|
|
115
|
+
// loose `String.includes` substring scan. The old substring match treated any
|
|
116
|
+
// user-authored file that merely *mentioned* a marker string (e.g. a doc that
|
|
117
|
+
// quotes "get-design-done plugin instructions", or a code fence containing
|
|
118
|
+
// "gdd: auto-generated from Claude SKILL.md") as plugin-owned — so install
|
|
119
|
+
// would overwrite it and uninstall would delete it. We now require the marker
|
|
120
|
+
// to appear on a recognized GENERATED line:
|
|
121
|
+
//
|
|
122
|
+
// - `<!-- ... <fingerprint> ... -->` HTML-comment marker line. Both the
|
|
123
|
+
// Phase-24 plugin fingerprint and the per-runtime/sibling adapter header
|
|
124
|
+
// are emitted as a standalone HTML comment line; we accept the marker only
|
|
125
|
+
// when it sits inside an HTML comment that occupies the whole (trimmed)
|
|
126
|
+
// line. A bare prose mention of the same words no longer qualifies.
|
|
127
|
+
// - `# get-design-done rules` cline rules header — must be the exact, whole
|
|
128
|
+
// trimmed line (a Markdown H1), matching converters/cline.cjs.
|
|
129
|
+
//
|
|
130
|
+
// Scanning line-by-line keeps detection of genuinely plugin-owned files intact
|
|
131
|
+
// (the generated marker line is always present near the top) while refusing to
|
|
132
|
+
// claim ownership of user files that merely contain the words somewhere.
|
|
133
|
+
function isHtmlCommentMarkerLine(line, fingerprint) {
|
|
134
|
+
const t = line.trim();
|
|
135
|
+
if (!t.startsWith('<!--') || !t.endsWith('-->')) return false;
|
|
136
|
+
return t.includes(fingerprint);
|
|
137
|
+
}
|
|
138
|
+
|
|
114
139
|
function isPluginOwned(content) {
|
|
115
140
|
if (!content || typeof content !== 'string') return false;
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
141
|
+
const lines = content.split(/\r?\n/);
|
|
142
|
+
for (const line of lines) {
|
|
143
|
+
if (isHtmlCommentMarkerLine(line, PLUGIN_FINGERPRINT)) return true;
|
|
144
|
+
if (isHtmlCommentMarkerLine(line, GDD_ADAPTER_FINGERPRINT)) return true;
|
|
145
|
+
if (line.trim() === CLINERULES_HEADER_FINGERPRINT) return true;
|
|
146
|
+
}
|
|
119
147
|
return false;
|
|
120
148
|
}
|
|
121
149
|
|
|
@@ -226,9 +226,21 @@ function commandsKind(destSubpath, prefix, converterPath, runtime) {
|
|
|
226
226
|
/**
|
|
227
227
|
* Build an `agents` artifact-kind descriptor.
|
|
228
228
|
*
|
|
229
|
-
* claude local only — passthrough copy from `<repo>/agents
|
|
229
|
+
* claude local only — passthrough copy from `<repo>/agents/*.md` into
|
|
230
230
|
* `<configDir>/agents/`. No converter.
|
|
231
231
|
*
|
|
232
|
+
* AR7 fix (Phase 59.8): the agent set is ENUMERATED from the `agents/`
|
|
233
|
+
* directory on disk — NOT derived from `ctx.skillNames`. Real agent files
|
|
234
|
+
* are named after agent roles (`design-planner.md`, `a11y-mapper.md`, …),
|
|
235
|
+
* which never coincide with skill directory names. The old skill-name-derived
|
|
236
|
+
* path read `agents/<skillName>.md`, found nothing for any skill, and staged
|
|
237
|
+
* ~96 empty `gdd-<skillName>.md` artifacts while installing ZERO real agents.
|
|
238
|
+
*
|
|
239
|
+
* Enumeration rules:
|
|
240
|
+
* - top-level `*.md` files in `agents/` only (no nested dirs),
|
|
241
|
+
* - `README.md` is excluded (it is documentation, not an agent),
|
|
242
|
+
* - empty / unreadable files are skipped (best-effort; never throws).
|
|
243
|
+
*
|
|
232
244
|
* @param {string} destSubpath
|
|
233
245
|
* @param {string} prefix
|
|
234
246
|
* @returns {ArtifactKind}
|
|
@@ -243,13 +255,35 @@ function agentsKind(destSubpath, prefix) {
|
|
|
243
255
|
path.dirname(ctx.skillsRoot),
|
|
244
256
|
'agents'
|
|
245
257
|
);
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
return
|
|
252
|
-
}
|
|
258
|
+
let entries;
|
|
259
|
+
try {
|
|
260
|
+
entries = fs.readdirSync(agentsRoot, { withFileTypes: true });
|
|
261
|
+
} catch {
|
|
262
|
+
// No agents/ dir on disk — stage nothing (never throw).
|
|
263
|
+
return [];
|
|
264
|
+
}
|
|
265
|
+
const staged = [];
|
|
266
|
+
for (const ent of entries) {
|
|
267
|
+
if (!ent.isFile()) continue;
|
|
268
|
+
if (!ent.name.toLowerCase().endsWith('.md')) continue;
|
|
269
|
+
if (ent.name.toLowerCase() === 'readme.md') continue;
|
|
270
|
+
// Strip any pre-existing gdd-/gsd- prefix on the agent filename before
|
|
271
|
+
// re-applying `prefix`, so an agent already named `gdd-foo.md` does not
|
|
272
|
+
// become `gdd-gdd-foo.md`. Real agents ship un-prefixed
|
|
273
|
+
// (`a11y-mapper.md`); this guard keeps both shapes correct.
|
|
274
|
+
const fileBase = ent.name.slice(0, -'.md'.length);
|
|
275
|
+
const bareName = fileBase.replace(/^(gdd-|gsd-)/i, '');
|
|
276
|
+
const srcPath = path.join(agentsRoot, ent.name);
|
|
277
|
+
let raw = '';
|
|
278
|
+
try {
|
|
279
|
+
raw = fs.readFileSync(srcPath, 'utf8');
|
|
280
|
+
} catch {
|
|
281
|
+
continue;
|
|
282
|
+
}
|
|
283
|
+
if (!raw.trim()) continue; // skip empty agent files
|
|
284
|
+
staged.push({ srcPath, content: raw, name: prefix + bareName });
|
|
285
|
+
}
|
|
286
|
+
return staged;
|
|
253
287
|
},
|
|
254
288
|
};
|
|
255
289
|
}
|
|
@@ -20,11 +20,13 @@
|
|
|
20
20
|
"command_syntax": "/gdd:<skill>",
|
|
21
21
|
"mcp_support": true,
|
|
22
22
|
"placeholder_substitution": true,
|
|
23
|
+
"agents_support": true,
|
|
24
|
+
"hooks_support": true,
|
|
23
25
|
"install_path": "dist/claude-code/.claude/skills/",
|
|
24
26
|
"status": "tested"
|
|
25
27
|
},
|
|
26
28
|
"last_verified": "2026-06-02",
|
|
27
|
-
"capability_notes": "Host runtime. Marketplace-registered, end-to-end documented, Phase 42 golden baseline.",
|
|
29
|
+
"capability_notes": "Host runtime. Marketplace-registered, end-to-end documented, Phase 42 golden baseline. Sole runtime that receives the 64 sub-agents (claude --local installs agents/) and the hook layer (SessionStart / PostToolUse / statusLine).",
|
|
28
30
|
"fragment_links": [
|
|
29
31
|
"reference/runtime-models.md#claude---claude-code"
|
|
30
32
|
]
|
|
@@ -44,6 +46,8 @@
|
|
|
44
46
|
"command_syntax": "/gdd-<skill>",
|
|
45
47
|
"mcp_support": true,
|
|
46
48
|
"placeholder_substitution": true,
|
|
49
|
+
"agents_support": false,
|
|
50
|
+
"hooks_support": false,
|
|
47
51
|
"install_path": "dist/codex/.codex/skills/",
|
|
48
52
|
"status": "experimental"
|
|
49
53
|
},
|
|
@@ -71,6 +75,8 @@
|
|
|
71
75
|
"command_syntax": "/gdd:<skill>",
|
|
72
76
|
"mcp_support": true,
|
|
73
77
|
"placeholder_substitution": true,
|
|
78
|
+
"agents_support": false,
|
|
79
|
+
"hooks_support": false,
|
|
74
80
|
"install_path": "dist/gemini/.gemini/skills/",
|
|
75
81
|
"status": "experimental"
|
|
76
82
|
},
|
|
@@ -98,6 +104,8 @@
|
|
|
98
104
|
"command_syntax": "/gdd:<skill>",
|
|
99
105
|
"mcp_support": false,
|
|
100
106
|
"placeholder_substitution": true,
|
|
107
|
+
"agents_support": false,
|
|
108
|
+
"hooks_support": false,
|
|
101
109
|
"install_path": "dist/qwen/.qwen/skills/",
|
|
102
110
|
"status": "experimental"
|
|
103
111
|
},
|
|
@@ -124,6 +132,8 @@
|
|
|
124
132
|
"command_syntax": "/gdd:<skill>",
|
|
125
133
|
"mcp_support": false,
|
|
126
134
|
"placeholder_substitution": true,
|
|
135
|
+
"agents_support": false,
|
|
136
|
+
"hooks_support": false,
|
|
127
137
|
"install_path": "dist/kilo/.kilo/skills/",
|
|
128
138
|
"status": "untested"
|
|
129
139
|
},
|
|
@@ -148,6 +158,8 @@
|
|
|
148
158
|
"command_syntax": "/gdd:<skill>",
|
|
149
159
|
"mcp_support": false,
|
|
150
160
|
"placeholder_substitution": true,
|
|
161
|
+
"agents_support": false,
|
|
162
|
+
"hooks_support": false,
|
|
151
163
|
"install_path": "dist/copilot/.copilot/skills/",
|
|
152
164
|
"status": "experimental"
|
|
153
165
|
},
|
|
@@ -174,6 +186,8 @@
|
|
|
174
186
|
"command_syntax": "/gdd:<skill>",
|
|
175
187
|
"mcp_support": false,
|
|
176
188
|
"placeholder_substitution": true,
|
|
189
|
+
"agents_support": false,
|
|
190
|
+
"hooks_support": false,
|
|
177
191
|
"install_path": "dist/cursor/.cursor/skills/",
|
|
178
192
|
"status": "experimental"
|
|
179
193
|
},
|
|
@@ -200,6 +214,8 @@
|
|
|
200
214
|
"command_syntax": "/gdd:<skill>",
|
|
201
215
|
"mcp_support": false,
|
|
202
216
|
"placeholder_substitution": true,
|
|
217
|
+
"agents_support": false,
|
|
218
|
+
"hooks_support": false,
|
|
203
219
|
"install_path": "dist/windsurf/.windsurf/skills/",
|
|
204
220
|
"status": "untested"
|
|
205
221
|
},
|
|
@@ -224,6 +240,8 @@
|
|
|
224
240
|
"command_syntax": "/gdd:<skill>",
|
|
225
241
|
"mcp_support": false,
|
|
226
242
|
"placeholder_substitution": true,
|
|
243
|
+
"agents_support": false,
|
|
244
|
+
"hooks_support": false,
|
|
227
245
|
"install_path": "dist/antigravity/.antigravity/skills/",
|
|
228
246
|
"status": "untested"
|
|
229
247
|
},
|
|
@@ -248,6 +266,8 @@
|
|
|
248
266
|
"command_syntax": "/gdd:<skill>",
|
|
249
267
|
"mcp_support": false,
|
|
250
268
|
"placeholder_substitution": true,
|
|
269
|
+
"agents_support": false,
|
|
270
|
+
"hooks_support": false,
|
|
251
271
|
"install_path": "dist/augment/.augment/skills/",
|
|
252
272
|
"status": "untested"
|
|
253
273
|
},
|
|
@@ -272,6 +292,8 @@
|
|
|
272
292
|
"command_syntax": "/gdd:<skill>",
|
|
273
293
|
"mcp_support": false,
|
|
274
294
|
"placeholder_substitution": true,
|
|
295
|
+
"agents_support": false,
|
|
296
|
+
"hooks_support": false,
|
|
275
297
|
"install_path": "dist/trae/.trae/skills/",
|
|
276
298
|
"status": "untested"
|
|
277
299
|
},
|
|
@@ -296,6 +318,8 @@
|
|
|
296
318
|
"command_syntax": "/gdd:<skill>",
|
|
297
319
|
"mcp_support": false,
|
|
298
320
|
"placeholder_substitution": true,
|
|
321
|
+
"agents_support": false,
|
|
322
|
+
"hooks_support": false,
|
|
299
323
|
"install_path": "dist/codebuddy/.codebuddy/skills/",
|
|
300
324
|
"status": "untested"
|
|
301
325
|
},
|
|
@@ -320,6 +344,8 @@
|
|
|
320
344
|
"command_syntax": "/gdd:<skill>",
|
|
321
345
|
"mcp_support": false,
|
|
322
346
|
"placeholder_substitution": true,
|
|
347
|
+
"agents_support": false,
|
|
348
|
+
"hooks_support": false,
|
|
323
349
|
"install_path": "dist/cline/.cline/skills/",
|
|
324
350
|
"status": "untested"
|
|
325
351
|
},
|
|
@@ -344,6 +370,8 @@
|
|
|
344
370
|
"command_syntax": "/gdd:<skill>",
|
|
345
371
|
"mcp_support": false,
|
|
346
372
|
"placeholder_substitution": true,
|
|
373
|
+
"agents_support": false,
|
|
374
|
+
"hooks_support": false,
|
|
347
375
|
"install_path": "dist/opencode/.opencode/skills/",
|
|
348
376
|
"status": "untested"
|
|
349
377
|
},
|
|
@@ -541,7 +541,7 @@
|
|
|
541
541
|
},
|
|
542
542
|
{
|
|
543
543
|
"name": "router",
|
|
544
|
-
"description": "Routes a /gdd command to fast|quick|full path + S|M|L|XL complexity_class and returns {path, complexity_class, model_tier_overrides, resolved_models, estimated_cost_usd, cache_hits}.
|
|
544
|
+
"description": "Routes a /gdd command to fast|quick|full path + S|M|L|XL complexity_class and returns {path, complexity_class, model_tier_overrides, resolved_models, estimated_cost_usd, cache_hits}. A SKILL.md prompt the model executes to emit a routing-decision JSON from rule tables (no separate agent spawn). Optional/advisory - invoked only by the skills that opt into routing; the budget-enforcer hook tolerates its absence. Read by hooks/budget-enforcer.ts.",
|
|
545
545
|
"argument_hint": "<intent-string> [<target-artifacts-csv>]",
|
|
546
546
|
"tools": "Read, Bash, Grep"
|
|
547
547
|
},
|
|
@@ -31,6 +31,8 @@ No posterior file found at `.design/telemetry/posterior.json` — nothing to res
|
|
|
31
31
|
The next bandit pull with `adaptive_mode: full` will bootstrap a fresh posterior from informed priors. See `reference/bandit-integration.md`.
|
|
32
32
|
```
|
|
33
33
|
|
|
34
|
+
> Note: the posterior only learns (updates from outcomes) on the SDK / headless `session-runner` path. In interactive Claude Code with `adaptive_mode: full`, the bandit samples from the configured priors but does not currently update them in-session. A reset therefore re-bootstraps the priors the SDK path will subsequently learn from. See `reference/bandit-integration.md` ("Where adaptive routing actually learns").
|
|
35
|
+
|
|
34
36
|
If present, count the arms (`arms.length`, treating a missing/non-array `arms` as `0`) so the confirmation and receipt can report what will be cleared. A corrupted/unparseable file is still resettable - report `arms: unknown (file unparseable)` and continue.
|
|
35
37
|
|
|
36
38
|
### 2. Require explicit confirmation
|
|
@@ -33,10 +33,13 @@ Possible reasons:
|
|
|
33
33
|
- `adaptive_mode` is `static` or `hedge` (bandit silent — see `.design/budget.json`).
|
|
34
34
|
- No spawns have fired since Phase 27.5 wiring landed.
|
|
35
35
|
- Posterior was cleared via `{{command_prefix}}bandit-reset`.
|
|
36
|
+
- You are running in interactive Claude Code: the posterior is updated (learns) only on the SDK / headless `session-runner` path. In interactive `adaptive_mode: full` the bandit samples from configured priors but does not learn from in-session outcomes.
|
|
36
37
|
|
|
37
|
-
See `reference/bandit-integration.md` for setup guidance.
|
|
38
|
+
See `reference/bandit-integration.md` ("Where adaptive routing actually learns") for setup guidance.
|
|
38
39
|
```
|
|
39
40
|
|
|
41
|
+
> Note: the posterior only moves (learns) on the SDK / headless `session-runner` path. In interactive Claude Code with `adaptive_mode: full`, the bandit samples from the configured priors but does not currently update them in-session. See `reference/bandit-integration.md`.
|
|
42
|
+
|
|
40
43
|
Skip to Section 4 (Record). Parse failure (truncated/corrupted) → emit `Posterior file exists but is unparseable. Run {{command_prefix}}bandit-reset to start fresh, or restore from a backup.`
|
|
41
44
|
|
|
42
45
|
### 2. Parse the posterior
|
|
@@ -29,7 +29,7 @@ Output artifact prefix `DARKMODE-AUDIT` is distinct from the pipeline namespace
|
|
|
29
29
|
|
|
30
30
|
## Pre-Flight
|
|
31
31
|
|
|
32
|
-
Confirm source root exists. Try in order: `src/` (preferred), `app/` (Next.js App Router), `lib/` (libraries), `pages/` (Next.js Pages Router). Set `SRC_ROOT` to the first that exists. If none exist, abort: `"No source directory detected. Run /get-design-done
|
|
32
|
+
Confirm source root exists. Try in order: `src/` (preferred), `app/` (Next.js App Router), `lib/` (libraries), `pages/` (Next.js Pages Router). Set `SRC_ROOT` to the first that exists. If none exist, abort: `"No source directory detected. Run /get-design-done explore first."`
|
|
33
33
|
|
|
34
34
|
Confirm `.design/` exists (create if absent: `mkdir -p .design/`).
|
|
35
35
|
|
|
@@ -5,7 +5,7 @@ description: "Manage the Graphify knowledge graph for the current project. Build
|
|
|
5
5
|
|
|
6
6
|
# gdd-graphify
|
|
7
7
|
|
|
8
|
-
Thin command wrapper around the
|
|
8
|
+
Thin command wrapper around the get-design-done (GDD) graphify tools integration.
|
|
9
9
|
|
|
10
10
|
## Usage
|
|
11
11
|
|
|
@@ -30,10 +30,10 @@ Thin command wrapper around the GSD graphify tools integration.
|
|
|
30
30
|
```
|
|
31
31
|
STOP.
|
|
32
32
|
4. Execute the requested subcommand via the native CLI:
|
|
33
|
-
- build: `node bin/gdd-graph build`
|
|
34
|
-
- query: `node bin/gdd-graph query "<term>" --budget 2000`
|
|
35
|
-
- status: `node bin/gdd-graph status`
|
|
36
|
-
- diff: `node bin/gdd-graph diff`
|
|
33
|
+
- build: `node "${CLAUDE_PLUGIN_ROOT}/bin/gdd-graph" build`
|
|
34
|
+
- query: `node "${CLAUDE_PLUGIN_ROOT}/bin/gdd-graph" query "<term>" --budget 2000`
|
|
35
|
+
- status: `node "${CLAUDE_PLUGIN_ROOT}/bin/gdd-graph" status`
|
|
36
|
+
- diff: `node "${CLAUDE_PLUGIN_ROOT}/bin/gdd-graph" diff`
|
|
37
37
|
5. After `build` completes, update `.design/STATE.md` `<connections>`: `graphify: available`
|
|
38
38
|
|
|
39
39
|
## Required Reading
|
|
@@ -43,7 +43,7 @@ Thin command wrapper around the GSD graphify tools integration.
|
|
|
43
43
|
|
|
44
44
|
## Notes
|
|
45
45
|
|
|
46
|
-
- Graphify is optional. The native CLI ships
|
|
46
|
+
- Graphify is optional. The native CLI ships with the plugin at `${CLAUDE_PLUGIN_ROOT}/bin/gdd-graph` (no external install - Node only).
|
|
47
47
|
- Graph is stored at `.design/graph/graph.json` (Ajv-validated against `scripts/lib/graph/schema.json`).
|
|
48
48
|
- Graph covers source code (`src/`, `components/`). It does NOT index `.design/` artifacts by default.
|
|
49
49
|
- Use `query` with node IDs from the graph schema: `component:<name>`, `token:color/<name>`, `decision:D-<nn>`, etc.
|