@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.
- package/.claude-plugin/marketplace.json +2 -2
- package/.claude-plugin/plugin.json +1 -1
- package/CHANGELOG.md +103 -0
- package/README.de.md +2 -0
- package/README.fr.md +2 -0
- package/README.it.md +2 -0
- package/README.ja.md +2 -0
- package/README.ko.md +2 -0
- package/README.md +3 -1
- package/README.zh-CN.md +2 -0
- package/agents/design-authority-watcher.md +42 -1
- package/agents/design-integration-checker.md +1 -1
- package/agents/design-planner.md +1 -1
- package/agents/gdd-graph-refresh.md +90 -0
- package/bin/gdd-graph +261 -0
- package/connections/connections.md +10 -9
- package/connections/graphify.md +65 -54
- package/package.json +4 -2
- package/reference/capability-gap-stage-gate.md +7 -4
- package/reference/known-failure-modes.md +337 -1
- package/reference/model-tiers.md +2 -2
- package/reference/schemas/events.schema.json +61 -0
- package/reference/start-interview.md +1 -1
- package/scripts/detect-stale-refs.cjs +6 -0
- package/scripts/lib/apply-reflections/incubator-proposals.cjs +10 -3
- package/scripts/lib/authority-watcher/index.cjs +201 -0
- package/scripts/lib/failure-mode-matcher.cjs +460 -0
- package/scripts/lib/graph/atomic-write.mjs +68 -0
- package/scripts/lib/graph/build.mjs +124 -0
- package/scripts/lib/graph/diff.mjs +90 -0
- package/scripts/lib/graph/index.mjs +14 -0
- package/scripts/lib/graph/query.mjs +155 -0
- package/scripts/lib/graph/schema.json +69 -0
- package/scripts/lib/graph/schema.mjs +47 -0
- package/scripts/lib/graph/status.mjs +88 -0
- package/scripts/lib/graph/token-estimate.mjs +27 -0
- package/scripts/lib/graph/upsert.mjs +210 -0
- package/scripts/lib/{gsd-health-mirror → health-mirror}/index.cjs +1 -1
- package/scripts/lib/install/interactive.cjs +27 -2
- package/scripts/lib/reflector-capability-gap-aggregator.cjs +32 -0
- package/scripts/lib/reflector-kfm-proposer.cjs +468 -0
- package/scripts/mcp-servers/gdd-mcp/tools/gdd_health.ts +3 -3
- package/skills/apply-reflections/SKILL.md +4 -0
- package/skills/apply-reflections/apply-reflections-procedure.md +38 -4
- package/skills/connections/connections-onboarding.md +6 -6
- package/skills/graphify/SKILL.md +11 -10
- package/skills/scan/scan-procedure.md +9 -8
- package/agents/gdd-graphify-sync.md +0 -110
- /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 "
|
|
190
|
-
→
|
|
191
|
-
→
|
|
192
|
-
|
|
193
|
-
Step G2 — Graph file check:
|
|
194
|
-
|
|
195
|
-
→
|
|
196
|
-
→
|
|
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
|
```
|
package/connections/graphify.md
CHANGED
|
@@ -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
|
|
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
|
-
-
|
|
11
|
-
-
|
|
12
|
+
- Node ≥22 (per `package.json` engines)
|
|
13
|
+
- get-design-done installed (provides `bin/gdd-graph`)
|
|
12
14
|
|
|
13
|
-
**
|
|
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
|
-
|
|
20
|
-
```
|
|
21
|
-
|
|
17
|
+
Edit `.design/config.json` and set:
|
|
18
|
+
```json
|
|
19
|
+
{
|
|
20
|
+
"graphify": {
|
|
21
|
+
"enabled": true
|
|
22
|
+
}
|
|
23
|
+
}
|
|
22
24
|
```
|
|
23
25
|
|
|
24
|
-
|
|
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
|
-
**
|
|
28
|
+
**Build the graph (initial):**
|
|
32
29
|
```
|
|
33
|
-
|
|
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
|
|
36
|
+
node bin/gdd-graph status --format json
|
|
40
37
|
```
|
|
41
|
-
Expect: `{
|
|
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
|
|
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
|
-
{
|
|
83
|
-
"
|
|
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
|
-
{
|
|
87
|
-
"
|
|
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
|
-
|
|
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 "
|
|
116
|
-
→
|
|
117
|
-
→
|
|
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
|
|
127
|
+
Step G2 — Graph status check (native CLI):
|
|
121
128
|
```
|
|
122
|
-
|
|
123
|
-
→
|
|
124
|
-
→
|
|
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
|
|
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
|
|
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.
|
|
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
|
|
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 `
|
|
183
|
-
- **Do NOT assume graph covers
|
|
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 |
|
|
197
|
+
| Subcommand | Native CLI call | Purpose |
|
|
190
198
|
|------------|----------------|---------|
|
|
191
|
-
| `build` | `
|
|
192
|
-
| `query <term>` | `
|
|
193
|
-
| `status` | `
|
|
194
|
-
| `diff` | `
|
|
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
|
-
|
|
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.
|
|
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
|
-
> <!--
|
|
163
|
-
>
|
|
164
|
-
>
|
|
165
|
-
>
|
|
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
|