@hegemonart/get-design-done 1.30.0 → 1.30.6

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 (49) hide show
  1. package/.claude-plugin/marketplace.json +2 -2
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/CHANGELOG.md +103 -0
  4. package/README.de.md +2 -0
  5. package/README.fr.md +2 -0
  6. package/README.it.md +2 -0
  7. package/README.ja.md +2 -0
  8. package/README.ko.md +2 -0
  9. package/README.md +3 -1
  10. package/README.zh-CN.md +2 -0
  11. package/agents/design-authority-watcher.md +42 -1
  12. package/agents/design-integration-checker.md +1 -1
  13. package/agents/design-planner.md +1 -1
  14. package/agents/gdd-graph-refresh.md +90 -0
  15. package/bin/gdd-graph +261 -0
  16. package/connections/connections.md +10 -9
  17. package/connections/graphify.md +65 -54
  18. package/package.json +4 -2
  19. package/reference/capability-gap-stage-gate.md +7 -4
  20. package/reference/known-failure-modes.md +337 -1
  21. package/reference/model-tiers.md +2 -2
  22. package/reference/schemas/events.schema.json +61 -0
  23. package/reference/start-interview.md +1 -1
  24. package/scripts/detect-stale-refs.cjs +6 -0
  25. package/scripts/lib/apply-reflections/incubator-proposals.cjs +10 -3
  26. package/scripts/lib/authority-watcher/index.cjs +201 -0
  27. package/scripts/lib/failure-mode-matcher.cjs +460 -0
  28. package/scripts/lib/graph/atomic-write.mjs +68 -0
  29. package/scripts/lib/graph/build.mjs +124 -0
  30. package/scripts/lib/graph/diff.mjs +90 -0
  31. package/scripts/lib/graph/index.mjs +14 -0
  32. package/scripts/lib/graph/query.mjs +155 -0
  33. package/scripts/lib/graph/schema.json +69 -0
  34. package/scripts/lib/graph/schema.mjs +47 -0
  35. package/scripts/lib/graph/status.mjs +88 -0
  36. package/scripts/lib/graph/token-estimate.mjs +27 -0
  37. package/scripts/lib/graph/upsert.mjs +210 -0
  38. package/scripts/lib/{gsd-health-mirror → health-mirror}/index.cjs +1 -1
  39. package/scripts/lib/install/interactive.cjs +27 -2
  40. package/scripts/lib/reflector-capability-gap-aggregator.cjs +32 -0
  41. package/scripts/lib/reflector-kfm-proposer.cjs +468 -0
  42. package/scripts/mcp-servers/gdd-mcp/tools/gdd_health.ts +3 -3
  43. package/skills/apply-reflections/SKILL.md +4 -0
  44. package/skills/apply-reflections/apply-reflections-procedure.md +38 -4
  45. package/skills/connections/connections-onboarding.md +6 -6
  46. package/skills/graphify/SKILL.md +11 -10
  47. package/skills/scan/scan-procedure.md +9 -8
  48. package/agents/gdd-graphify-sync.md +0 -110
  49. /package/scripts/lib/{gsd-health-mirror → health-mirror}/index.d.cts +0 -0
package/bin/gdd-graph ADDED
@@ -0,0 +1,261 @@
1
+ #!/usr/bin/env node
2
+ // bin/gdd-graph — Plan 30.6-02 Task 3
3
+ //
4
+ // Native CLI replacing every `gsd-tools.cjs graphify *` dispatch in our
5
+ // tree. This bin is a thin CJS dispatcher that dynamic-imports the ESM
6
+ // handler modules (package.json has no "type":"module", so the no-ext
7
+ // bin file is resolved as CJS by Node — we bridge with `await import()`).
8
+ //
9
+ // Subcommands shipped in 30.6-02:
10
+ // build — read .design/intel/graph.json, write .design/graph/graph.json
11
+ // status — report graph file state (configured/exists/counts/stale)
12
+ // diff <a> <b> — emit added/removed/changed nodes + edges
13
+ //
14
+ // Subcommands shipped in 30.6-03 (this plan):
15
+ // query <term> [--budget N] — search graph, return ranked matches +
16
+ // 1-hop neighbors, budget-truncated per D-04
17
+ // upsert-node --json '<json>' — create/update a node by id (idempotent)
18
+ // upsert-edge --json '<json>' — create/update an edge by (from,to,kind);
19
+ // fails if either endpoint node is absent
20
+
21
+ 'use strict';
22
+
23
+ const path = require('node:path');
24
+
25
+ const SUBCOMMANDS = new Set([
26
+ 'build', 'status', 'diff', 'query', 'upsert-node', 'upsert-edge',
27
+ ]);
28
+
29
+ function printUsage(stream) {
30
+ stream.write([
31
+ 'Usage: gdd-graph <subcommand> [options]',
32
+ '',
33
+ 'Subcommands:',
34
+ ' build [--intel <path>] [--out <path>] [--now <iso>]',
35
+ ' Build .design/graph/graph.json from .design/intel/graph.json.',
36
+ ' status [--graph <path>] [--intel <path>] [--format json|text]',
37
+ ' Report graph store state (configured/exists/counts/stale).',
38
+ ' diff <from> <to> [--format json|text]',
39
+ ' Diff two graph.json files; emit added/removed/changed nodes+edges.',
40
+ ' query <term> [--budget N] [--graph <path>]',
41
+ ' Search graph; return ranked matches + 1-hop neighbors,',
42
+ ' budget-truncated per D-04 (chars/4 heuristic, default 8000).',
43
+ ' upsert-node {--json \'<obj>\' | --id <id> --type <type> [--label <l>] [--source <s>] [--attrs <json>]}',
44
+ ' Idempotent node upsert by id; bootstraps graph file if absent.',
45
+ ' upsert-edge {--json \'<obj>\' | --from <id> --to <id> --kind <kind> [--weight N] [--source <s>] [--attrs <json>]}',
46
+ ' Idempotent edge upsert by (from,to,kind); requires both endpoint',
47
+ ' nodes to exist — emits GDD_GRAPH_MISSING_ENDPOINT otherwise.',
48
+ '',
49
+ 'Env:',
50
+ ' GDD_GRAPH_DEBUG=1 include stack traces in error JSON',
51
+ ' GDD_GRAPH_TOKEN_FACTOR <int> override chars-per-token divisor (D-04)',
52
+ '',
53
+ 'Exit codes:',
54
+ ' 0 success / 1 failure (structured-error JSON on stderr)',
55
+ '',
56
+ ].join('\n'));
57
+ }
58
+
59
+ function parseArgs(rest) {
60
+ // Minimal hand-rolled parser. Positional args land in `_`; flags with
61
+ // `--name value` (and `--name=value`) land in `opts`.
62
+ const opts = {};
63
+ const positional = [];
64
+ for (let i = 0; i < rest.length; i++) {
65
+ const arg = rest[i];
66
+ if (arg === '--' ) { positional.push(...rest.slice(i + 1)); break; }
67
+ if (arg.startsWith('--')) {
68
+ const eq = arg.indexOf('=');
69
+ if (eq !== -1) {
70
+ opts[arg.slice(2, eq)] = arg.slice(eq + 1);
71
+ } else {
72
+ const next = rest[i + 1];
73
+ if (next === undefined || next.startsWith('--')) {
74
+ opts[arg.slice(2)] = true;
75
+ } else {
76
+ opts[arg.slice(2)] = next;
77
+ i++;
78
+ }
79
+ }
80
+ } else {
81
+ positional.push(arg);
82
+ }
83
+ }
84
+ return { _: positional, opts };
85
+ }
86
+
87
+ function emitError(subcommand, err, exitCode = 1) {
88
+ const payload = {
89
+ ok: false,
90
+ subcommand,
91
+ message: err && err.message ? err.message : String(err),
92
+ };
93
+ if (err && err.code) payload.code = err.code;
94
+ if (err && err.schemaErrors) payload.schemaErrors = err.schemaErrors;
95
+ if (err && err.missingEndpoints) payload.missingEndpoints = err.missingEndpoints;
96
+ if (process.env.GDD_GRAPH_DEBUG && err && err.stack) payload.stack = err.stack;
97
+ process.stderr.write(JSON.stringify(payload) + '\n');
98
+ process.exit(exitCode);
99
+ }
100
+
101
+ async function dispatch(subcommand, parsed) {
102
+ const lib = await import(
103
+ // Resolve via relative require root — bin/ is sibling of scripts/.
104
+ 'file://' + path.resolve(__dirname, '..', 'scripts', 'lib', 'graph', 'index.mjs').replace(/\\/g, '/')
105
+ );
106
+
107
+ if (subcommand === 'build') {
108
+ const r = lib.buildGraph({
109
+ intelPath: parsed.opts.intel,
110
+ outPath: parsed.opts.out,
111
+ now: parsed.opts.now,
112
+ });
113
+ process.stdout.write(JSON.stringify(r) + '\n');
114
+ return;
115
+ }
116
+
117
+ if (subcommand === 'status') {
118
+ const r = lib.statusGraph({
119
+ graphPath: parsed.opts.graph,
120
+ intelPath: parsed.opts.intel,
121
+ });
122
+ process.stdout.write(JSON.stringify(r) + '\n');
123
+ return;
124
+ }
125
+
126
+ if (subcommand === 'diff') {
127
+ const [fromPath, toPath] = parsed._;
128
+ if (!fromPath || !toPath) {
129
+ const err = new Error('diff requires two positional args: <from> <to>');
130
+ err.code = 'DIFF_ARGS_MISSING';
131
+ throw err;
132
+ }
133
+ const r = lib.diffGraph({ fromPath, toPath });
134
+ process.stdout.write(JSON.stringify(r) + '\n');
135
+ return;
136
+ }
137
+
138
+ if (subcommand === 'query') {
139
+ const [term] = parsed._;
140
+ if (!term) {
141
+ const err = new Error('query requires a positional argument: <term>');
142
+ err.code = 'QUERY_TERM_MISSING';
143
+ throw err;
144
+ }
145
+ const budget = parsed.opts.budget !== undefined
146
+ ? Number.parseInt(parsed.opts.budget, 10)
147
+ : undefined;
148
+ if (parsed.opts.budget !== undefined && !Number.isFinite(budget)) {
149
+ const err = new Error(`query --budget must be an integer (got: ${parsed.opts.budget})`);
150
+ err.code = 'QUERY_BUDGET_INVALID';
151
+ throw err;
152
+ }
153
+ const r = lib.queryGraph({
154
+ graphPath: parsed.opts.graph,
155
+ query: term,
156
+ budget,
157
+ });
158
+ process.stdout.write(JSON.stringify(r) + '\n');
159
+ return;
160
+ }
161
+
162
+ if (subcommand === 'upsert-node') {
163
+ const node = parseUpsertInput(parsed, 'upsert-node');
164
+ const r = lib.upsertNode({
165
+ graphPath: parsed.opts.graph,
166
+ node,
167
+ });
168
+ process.stdout.write(JSON.stringify(r) + '\n');
169
+ return;
170
+ }
171
+
172
+ if (subcommand === 'upsert-edge') {
173
+ const edge = parseUpsertInput(parsed, 'upsert-edge');
174
+ const r = lib.upsertEdge({
175
+ graphPath: parsed.opts.graph,
176
+ edge,
177
+ });
178
+ process.stdout.write(JSON.stringify(r) + '\n');
179
+ return;
180
+ }
181
+ }
182
+
183
+ /**
184
+ * Parse the upsert payload from argv. Supports two shapes:
185
+ * - `--json '<json-string>'` (preferred — single source of truth for batch
186
+ * callers in agents/gdd-graph-refresh.md and skills/graphify)
187
+ * - flat flags `--id ... --type ... --label ... --from ... --to ... --kind ...
188
+ * --weight N --source ... --attrs '<json>'` (convenience for shell users
189
+ * + matches the upstream gsd-tools.cjs graphify flag surface verbatim
190
+ * per RESEARCH.md §8-callsite inventory)
191
+ *
192
+ * Conflict: `--json` wins when both are provided.
193
+ */
194
+ function parseUpsertInput(parsed, subcommand) {
195
+ if (parsed.opts.json !== undefined) {
196
+ try {
197
+ const obj = JSON.parse(parsed.opts.json);
198
+ if (!obj || typeof obj !== 'object') {
199
+ throw new Error(`${subcommand} --json must decode to an object`);
200
+ }
201
+ return obj;
202
+ } catch (e) {
203
+ const err = new Error(`${subcommand} --json parse failed: ${e.message}`);
204
+ err.code = 'UPSERT_JSON_PARSE_FAILED';
205
+ throw err;
206
+ }
207
+ }
208
+ // Flat-flag mode — assemble from individual --field flags.
209
+ const obj = {};
210
+ for (const key of ['id', 'type', 'label', 'from', 'to', 'kind', 'source']) {
211
+ if (parsed.opts[key] !== undefined && parsed.opts[key] !== true) {
212
+ obj[key] = parsed.opts[key];
213
+ }
214
+ }
215
+ if (parsed.opts.weight !== undefined) {
216
+ const w = Number.parseFloat(parsed.opts.weight);
217
+ if (!Number.isFinite(w)) {
218
+ const err = new Error(`${subcommand} --weight must be a number (got: ${parsed.opts.weight})`);
219
+ err.code = 'UPSERT_WEIGHT_INVALID';
220
+ throw err;
221
+ }
222
+ obj.weight = w;
223
+ }
224
+ if (parsed.opts.attrs !== undefined) {
225
+ try {
226
+ obj.attrs = JSON.parse(parsed.opts.attrs);
227
+ } catch (e) {
228
+ const err = new Error(`${subcommand} --attrs parse failed: ${e.message}`);
229
+ err.code = 'UPSERT_ATTRS_PARSE_FAILED';
230
+ throw err;
231
+ }
232
+ }
233
+ return obj;
234
+ }
235
+
236
+ (async () => {
237
+ const argv = process.argv.slice(2);
238
+ if (argv.length === 0 || argv[0] === '-h' || argv[0] === '--help') {
239
+ printUsage(process.stderr);
240
+ process.exit(argv.length === 0 ? 1 : 0);
241
+ }
242
+ const subcommand = argv[0];
243
+ if (!SUBCOMMANDS.has(subcommand)) {
244
+ process.stderr.write(JSON.stringify({
245
+ ok: false,
246
+ message: `unknown subcommand: ${subcommand}`,
247
+ }) + '\n');
248
+ process.exit(1);
249
+ }
250
+ const parsed = parseArgs(argv.slice(1));
251
+ try {
252
+ await dispatch(subcommand, parsed);
253
+ process.exit(0);
254
+ } catch (err) {
255
+ emitError(subcommand, err, 1);
256
+ }
257
+ })().catch((err) => {
258
+ // Last-resort guard. dispatch() already routes via emitError; this only
259
+ // fires for bugs in argv parsing or the dispatch glue itself.
260
+ emitError('<bootstrap>', err, 1);
261
+ });
@@ -185,15 +185,16 @@ Note: First Chromatic run has no baseline — all stories become new snapshots.
185
185
  **Graphify probe (execute at agent entry, before using graph):**
186
186
 
187
187
  ```
188
- Step G1 — Config check:
189
- node "$HOME/.claude/get-shit-done/bin/gsd-tools.cjs" graphify status
190
- Error or { enabled: false } → graphify: not_configured
191
- { enabled: true } → proceed to Step G2
192
-
193
- Step G2 — Graph file check:
194
- Check if graphify-out/graph.json exists in project root
195
- Absent → graphify: unavailable (graph not built yet)
196
- Present → graphify: available
188
+ Step G1 — Config check (per D-09 direct read, no CLI subcommand):
189
+ Bash: node -e "try{const c=JSON.parse(require('fs').readFileSync('.design/config.json','utf8'));process.stdout.write(String(c.graphify?.enabled===true))}catch{process.stdout.write('false')}"
190
+ → false → graphify: not_configured
191
+ → true → proceed to Step G2
192
+
193
+ Step G2 — Graph file check (native CLI):
194
+ Bash: node bin/gdd-graph status --format json
195
+ { configured: true, exists: true } graphify: available
196
+ { configured: true, exists: false } → graphify: unavailable (graph not built yet)
197
+ → { configured: false, exists: ... } → graphify: not_configured (mirrors G1)
197
198
 
198
199
  Write graphify status to STATE.md <connections>.
199
200
  ```
@@ -1,46 +1,43 @@
1
1
  # Graphify — Connection Specification
2
2
 
3
- This file is the connection specification for Graphify within the get-design-done pipeline. Graphify builds a queryable knowledge graph over the codebase — mapping component↔token↔decision relationships via Tree-sitter static analysis and LLM semantic extraction. See `connections/connections.md` for the full connection index and capability matrix.
3
+ This file is the connection specification for **Graphify** within the get-design-done pipeline. Graphify builds a queryable knowledge graph over the codebase and design intel — mapping component↔token↔decision relationships from `.design/intel/` slices. See `connections/connections.md` for the full connection index and capability matrix.
4
+
5
+ > **Native, no external dependency.** Phase 30.6 (v1.30.6) replaced the previous runtime dispatch to `~/.claude/get-shit-done/bin/gsd-tools.cjs graphify *` with a native CLI shipped in this repo at `bin/gdd-graph`. No Python, no separate install — just Node ≥22.
4
6
 
5
7
  ---
6
8
 
7
9
  ## Setup
8
10
 
9
11
  **Prerequisites:**
10
- - Python 3.9+ available on PATH
11
- - GSD framework with `graphify.enabled = true` in `.planning/config.json`
12
+ - Node ≥22 (per `package.json` engines)
13
+ - get-design-done installed (provides `bin/gdd-graph`)
12
14
 
13
- **Install:**
14
- ```
15
- pip install graphifyy
16
- graphify install # installs skill files into ~/.claude/skills/graphify/
17
- ```
15
+ **Enable in project config** (per D-09 — direct file edit, no CLI subcommand):
18
16
 
19
- **Enable in GSD config:**
20
- ```
21
- node "$HOME/.claude/get-shit-done/bin/gsd-tools.cjs" config-set graphify.enabled true
17
+ Edit `.design/config.json` and set:
18
+ ```json
19
+ {
20
+ "graphify": {
21
+ "enabled": true
22
+ }
23
+ }
22
24
  ```
23
25
 
24
- **Build the graph (initial):**
25
- ```
26
- graphify . # run in project root; produces graphify-out/graph.json
27
- # or via GSD tools:
28
- node "$HOME/.claude/get-shit-done/bin/gsd-tools.cjs" graphify build
29
- ```
26
+ If `.design/config.json` does not exist, create it. Missing file = disabled.
30
27
 
31
- **Recommended: auto-rebuild after commits:**
28
+ **Build the graph (initial):**
32
29
  ```
33
- graphify hook install
30
+ node bin/gdd-graph build
34
31
  ```
32
+ Produces `.design/graph/graph.json` (Ajv-validated against `scripts/lib/graph/schema.json`).
35
33
 
36
34
  **Verification:**
37
- After building, run:
38
35
  ```
39
- node "$HOME/.claude/get-shit-done/bin/gsd-tools.cjs" graphify status
36
+ node bin/gdd-graph status --format json
40
37
  ```
41
- Expect: `{ enabled: true, graph_path: "...", node_count: N, edge_count: N, stale: false }`
38
+ Expect: `{ configured: true, exists: true, node_count: N, edge_count: M, built_at: "<iso>", schema_version: "1.0" }`.
42
39
 
43
- **Note:** Graphify is an optional external dependency. It requires Python and takes 1-5 minutes to build on a large codebase. Do NOT add to plugin bootstrap users opt in manually.
40
+ **Note:** Graphify is optional. It is a pre-search oracle for planner and verifier agents, never a hard requirement. All stages MUST degrade gracefully when graphify is `unavailable` or `not_configured`.
44
41
 
45
42
  ---
46
43
 
@@ -50,7 +47,7 @@ Expect: `{ enabled: true, graph_path: "...", node_count: N, edge_count: N, stale
50
47
 
51
48
  | Node type | Source | ID pattern |
52
49
  |-----------|--------|-----------|
53
- | component | `.stories.tsx` / `src/components/*.tsx` | `component:<name>` |
50
+ | component | `.stories.tsx` / `src/components/*.tsx` (via intel slices) | `component:<name>` |
54
51
  | token:color | CSS custom properties / Figma variables | `token:color/<name>` |
55
52
  | token:spacing | CSS custom properties | `token:spacing/<name>` |
56
53
  | token:typography | CSS custom properties | `token:typography/<name>` |
@@ -65,7 +62,7 @@ Expect: `{ enabled: true, graph_path: "...", node_count: N, edge_count: N, stale
65
62
 
66
63
  ### Edge Types
67
64
 
68
- | Edge | From | To | Meaning |
65
+ | Edge kind | From | To | Meaning |
69
66
  |------|------|-----|---------|
70
67
  | `uses` | component | token | Component references this token |
71
68
  | `renders` | page | component | Page renders this component |
@@ -74,22 +71,32 @@ Expect: `{ enabled: true, graph_path: "...", node_count: N, edge_count: N, stale
74
71
  | `maps-to` | figma-variable | token | Figma variable corresponds to CSS token |
75
72
  | `detected-at` | anti-pattern | component | Anti-pattern found in this component |
76
73
 
77
- ### graph.json structure
74
+ ### graph.json structure (schema v1.0)
78
75
 
79
76
  ```json
80
77
  {
78
+ "schema_version": "1.0",
79
+ "built_at": "2026-05-28T12:00:00.000Z",
81
80
  "nodes": [
82
- { "id": "component:Button", "label": "Button", "type": "component",
83
- "description": "Primary interactive element", "source": "src/components/Button.tsx" }
81
+ {
82
+ "id": "component:Button",
83
+ "type": "component",
84
+ "label": "Button",
85
+ "attrs": { "source": "src/components/Button.tsx" }
86
+ }
84
87
  ],
85
88
  "edges": [
86
- { "source": "component:Button", "target": "token:color/primary/500",
87
- "label": "uses", "confidence": "EXTRACTED", "confidence_score": 0.95 }
89
+ {
90
+ "from": "component:Button",
91
+ "to": "token:color/primary/500",
92
+ "kind": "uses",
93
+ "weight": 0.95
94
+ }
88
95
  ]
89
96
  }
90
97
  ```
91
98
 
92
- Edge confidence tiers: EXTRACTED (found in source), INFERRED (semantic inference), AMBIGUOUS (flagged for review).
99
+ Edges use the `{from, to, kind, weight?}` shape (per D-03.b — matches intel-store schema verbatim, no translation layer). The optional `weight: number` replaces the upstream three-tier confidence enum (per D-03.c).
93
100
 
94
101
  ---
95
102
 
@@ -110,18 +117,19 @@ Unlike MCP connections, Graphify has no ToolSearch check. The probe is file-exis
110
117
 
111
118
  **Graphify probe sequence (execute at agent entry, before using graph):**
112
119
 
113
- Step G1 — Config check:
120
+ Step G1 — Config check (per D-09 — direct read, no CLI subcommand):
114
121
  ```
115
- node "$HOME/.claude/get-shit-done/bin/gsd-tools.cjs" graphify status
116
- Error or { enabled: false } → graphify: not_configured (skip all graph steps)
117
- { enabled: true } → proceed to Step G2
122
+ node -e "try{const c=JSON.parse(require('fs').readFileSync('.design/config.json','utf8'));process.stdout.write(String(c.graphify?.enabled===true))}catch{process.stdout.write('false')}"
123
+ → false → graphify: not_configured (skip all graph steps)
124
+ → true → proceed to Step G2
118
125
  ```
119
126
 
120
- Step G2 — Graph file check:
127
+ Step G2 — Graph status check (native CLI):
121
128
  ```
122
- Check if graphify-out/graph.json exists in project root
123
- Absent → graphify: unavailable (graph not built yet)
124
- Present → graphify: available
129
+ node bin/gdd-graph status --format json
130
+ { configured: true, exists: true } graphify: available
131
+ { configured: true, exists: false } → graphify: unavailable (graph not built yet)
132
+ → { configured: false, ... } → graphify: not_configured (mirrors G1; defensive)
125
133
  ```
126
134
 
127
135
  Write graphify status to `.design/STATE.md` `<connections>`.
@@ -138,8 +146,8 @@ This is the canonical pre-search pattern for agents. Copy inline — SKILL.md an
138
146
 
139
147
  Step 1: Query graph for decision node and its neighbors
140
148
  ```
141
- node "$HOME/.claude/get-shit-done/bin/gsd-tools.cjs" graphify query "decision:D-<nn>" --budget 1500
142
- → Returns: connected components + tokens as JSON
149
+ node bin/gdd-graph query "decision:D-<nn>" --budget 1500
150
+ → Returns: ranked match list — connected components + tokens as JSON
143
151
  → Use returned component IDs as grep seed list (reduces false-negative "not found")
144
152
  ```
145
153
 
@@ -150,15 +158,15 @@ Step 2: Grep each returned component for the decision pattern
150
158
 
151
159
  Step 1: Query graph for token node and its neighbors
152
160
  ```
153
- node "$HOME/.claude/get-shit-done/bin/gsd-tools.cjs" graphify query "<token-name>" --budget 1500
154
- → Returns: all components that reference this token
161
+ node bin/gdd-graph query "<token-name>" --budget 1500
162
+ → Returns: all components that reference this token (ranked by D-04.a score)
155
163
  → Annotate planned task with "N components affected" before scoping
156
164
  ```
157
165
 
158
166
  Step 2: Include component list in the task description
159
167
  (then continue standard planning behavior)
160
168
 
161
- **Budget note:** Use `--budget 1500` for pre-search queries. High confidence_score edges (>= 0.8) are more reliable; AMBIGUOUS edges are hints only.
169
+ **Budget note:** Use `--budget 1500` for pre-search queries. Higher-weight edges (`weight >= 0.8`) are more reliable; lower-weight edges are hints only.
162
170
 
163
171
  ---
164
172
 
@@ -177,21 +185,24 @@ The graph is a performance optimization and accuracy enhancer. It is never a har
177
185
  ## Anti-Patterns
178
186
 
179
187
  - **Do NOT use graphify to replace grep.** The graph is a seed list, not a complete index. Always grep after querying the graph.
180
- - **Do NOT embed graph.json contents in agent context.** Query specific nodes via gsd-tools; never read graph.json directly.
188
+ - **Do NOT embed `graph.json` contents in agent context.** Query specific nodes via `bin/gdd-graph query`; never read `graph.json` directly.
181
189
  - **Do NOT query the graph during scan or design stages.** The graph is read-only and only useful when decisions already exist (plan, verify).
182
- - **Do NOT block on graph build time.** If `graphify build` takes >30 seconds mid-session, log "graphify build deferred — run /gdd:graphify build manually" and continue without graph.
183
- - **Do NOT assume graph covers .design/ artifacts.** Graphify analyzes source code (src/, components/). DESIGN-CONTEXT.md and DESIGN-PLAN.md are not graph nodes unless explicitly indexed.
190
+ - **Do NOT block on graph build time.** If `gdd-graph build` takes >30 seconds mid-session, log "graphify build deferred — run /gdd:graphify build manually" and continue without graph.
191
+ - **Do NOT assume graph covers `.design/` artifacts.** The build walks `.design/intel/` slices and project source; arbitrary planning docs are not graph nodes unless explicitly indexed.
184
192
 
185
193
  ---
186
194
 
187
195
  ## /gdd:graphify Commands
188
196
 
189
- | Subcommand | GSD tools call | Purpose |
197
+ | Subcommand | Native CLI call | Purpose |
190
198
  |------------|----------------|---------|
191
- | `build` | `gsd-tools graphify build` | Build or rebuild the knowledge graph |
192
- | `query <term>` | `gsd-tools graphify query "<term>" --budget 2000` | Query the graph for a node and its neighbors |
193
- | `status` | `gsd-tools graphify status` | Check graph age, node count, enabled status |
194
- | `diff` | `gsd-tools graphify diff` | Show topology changes since last build |
199
+ | `build` | `node bin/gdd-graph build` | Build or rebuild the knowledge graph |
200
+ | `query <term>` | `node bin/gdd-graph query "<term>" --budget 2000` | Query the graph for a node and its neighbors |
201
+ | `status` | `node bin/gdd-graph status` | Check graph age, node count, enabled status |
202
+ | `diff` | `node bin/gdd-graph diff` | Show topology changes since last build |
203
+ | `upsert-node` | `node bin/gdd-graph upsert-node --id X --type T --label L` | Programmatic single-node insert (used by gdd-graph-refresh agent) |
204
+ | `upsert-edge` | `node bin/gdd-graph upsert-edge --from A --to B --kind R` | Programmatic single-edge insert (used by gdd-graph-refresh agent) |
205
+
206
+ If `graphify.enabled` is `false` (or `.design/config.json` is missing), the `bin/gdd-graph` subcommands graceful-degrade — `status` returns `{ configured: false, exists: false }`, other subcommands no-op with exit 0 and a one-line stderr notice.
195
207
 
196
- If `graphify.enabled = false` in `.planning/config.json`, the skill prompts:
197
- "Graphify is not enabled. Enable with: gsd-tools config-set graphify.enabled true — then run /gdd:graphify build."
208
+ To enable: edit `.design/config.json` and set `graphify.enabled: true`, then `node bin/gdd-graph build`.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hegemonart/get-design-done",
3
- "version": "1.30.0",
3
+ "version": "1.30.6",
4
4
  "description": "A design-quality pipeline for AI coding agents: brief, plan, implement, and verify UI work against your design system.",
5
5
  "author": "Hegemon",
6
6
  "homepage": "https://github.com/hegemonart/get-design-done",
@@ -28,6 +28,7 @@
28
28
  ],
29
29
  "bin": {
30
30
  "gdd-events": "./scripts/cli/gdd-events.mjs",
31
+ "gdd-graph": "./bin/gdd-graph",
31
32
  "gdd-mcp": "./scripts/mcp-servers/gdd-mcp/server.ts",
32
33
  "gdd-sdk": "./bin/gdd-sdk",
33
34
  "gdd-state-mcp": "./scripts/mcp-servers/gdd-state/server.ts",
@@ -88,7 +89,8 @@
88
89
  "dependencies": {
89
90
  "@anthropic-ai/claude-agent-sdk": "^0.3.143",
90
91
  "@clack/prompts": "^1.2.0",
91
- "@modelcontextprotocol/sdk": "^1.0.0"
92
+ "@modelcontextprotocol/sdk": "^1.0.0",
93
+ "ajv": "^8.18.0"
92
94
  },
93
95
  "optionalDependencies": {
94
96
  "pngjs": "^7.0.0",
@@ -159,10 +159,13 @@ appends the following verbatim block to the cycle markdown:
159
159
  > opt in with the project-local command below. You can always opt out
160
160
  > later by deleting the timestamps from `.design/config.json` (§ 7).
161
161
  >
162
- > <!-- TODO: confirm opt-in command likely
163
- > `node "$HOME/.claude/get-shit-done/bin/gsd-tools.cjs" config set capability_gap_gate.opted_in_at "$(date -Iseconds)"`
164
- > or a project-local equivalent. Plan 29-05 (apply-reflections
165
- > extension) will land the canonical command. -->
162
+ > <!-- Phase 30.6 (D-09) update: per the convention adopted across the
163
+ > knowledge layer in v1.30.6, project-local config flips are direct
164
+ > edits to .design/config.json — no CLI subcommand. The canonical
165
+ > opt-in is therefore: edit .design/config.json to add
166
+ > { "capability_gap_gate": { "opted_in_at": "<ISO date>" } }.
167
+ > Plan 29-05 (apply-reflections extension) surfaces this as a guided
168
+ > prompt rather than a CLI invocation. -->
166
169
  >
167
170
  > This prompt is emitted at most once per project. If you ignore it,
168
171
  > the gate continues to evaluate every cycle but does not re-prompt