@hegemonart/get-design-done 1.19.0 → 1.19.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 (62) hide show
  1. package/.claude-plugin/marketplace.json +2 -2
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/CHANGELOG.md +54 -0
  4. package/SKILL.md +10 -4
  5. package/agents/README.md +53 -0
  6. package/agents/a11y-mapper.md +10 -0
  7. package/agents/component-benchmark-harvester.md +11 -0
  8. package/agents/component-benchmark-synthesizer.md +11 -0
  9. package/agents/component-taxonomy-mapper.md +10 -0
  10. package/agents/design-advisor.md +10 -0
  11. package/agents/design-assumptions-analyzer.md +10 -0
  12. package/agents/design-auditor.md +22 -0
  13. package/agents/design-authority-watcher.md +10 -0
  14. package/agents/design-component-generator.md +10 -0
  15. package/agents/design-context-checker-gate.md +10 -0
  16. package/agents/design-context-checker.md +10 -0
  17. package/agents/design-discussant.md +24 -0
  18. package/agents/design-doc-writer.md +12 -0
  19. package/agents/design-executor.md +10 -0
  20. package/agents/design-figma-writer.md +10 -0
  21. package/agents/design-fixer.md +10 -0
  22. package/agents/design-integration-checker-gate.md +10 -0
  23. package/agents/design-integration-checker.md +10 -0
  24. package/agents/design-paper-writer.md +10 -0
  25. package/agents/design-pattern-mapper.md +10 -0
  26. package/agents/design-pencil-writer.md +10 -0
  27. package/agents/design-phase-researcher.md +10 -0
  28. package/agents/design-plan-checker.md +10 -0
  29. package/agents/design-planner.md +10 -0
  30. package/agents/design-reflector.md +20 -0
  31. package/agents/design-research-synthesizer.md +10 -0
  32. package/agents/design-start-writer.md +10 -0
  33. package/agents/design-update-checker.md +10 -0
  34. package/agents/design-verifier-gate.md +10 -0
  35. package/agents/design-verifier.md +11 -0
  36. package/agents/gdd-graphify-sync.md +10 -0
  37. package/agents/gdd-intel-updater.md +10 -0
  38. package/agents/gdd-learnings-extractor.md +10 -0
  39. package/agents/motion-mapper.md +10 -0
  40. package/agents/token-mapper.md +10 -0
  41. package/agents/visual-hierarchy-mapper.md +10 -0
  42. package/hooks/gdd-decision-injector.js +30 -8
  43. package/package.json +18 -2
  44. package/reference/authority-feeds.md +4 -2
  45. package/reference/checklists.md +30 -0
  46. package/reference/component-authoring.md +184 -0
  47. package/reference/emotional-design.md +124 -0
  48. package/reference/first-principles.md +89 -0
  49. package/reference/heuristics.md +70 -0
  50. package/reference/motion-advanced.md +192 -3
  51. package/reference/registry.json +28 -0
  52. package/reference/schemas/insight-line.schema.json +37 -0
  53. package/reference/shared-preamble.md +10 -0
  54. package/scripts/lib/design-search.cjs +206 -0
  55. package/scripts/lib/probe-optional.cjs +29 -0
  56. package/scripts/lib/relevance-counter.cjs +121 -0
  57. package/skills/complete-cycle/SKILL.md +40 -2
  58. package/skills/continue/SKILL.md +23 -0
  59. package/skills/pause/SKILL.md +40 -14
  60. package/skills/recall/SKILL.md +74 -0
  61. package/skills/resume/SKILL.md +34 -16
  62. package/skills/timeline/SKILL.md +65 -0
@@ -0,0 +1,206 @@
1
+ 'use strict';
2
+ /**
3
+ * design-search.cjs — cross-cycle recall search backend.
4
+ *
5
+ * Priority chain:
6
+ * 1. FTS5 via better-sqlite3 (fast, ranked) — when module is available
7
+ * 2. ripgrep — when rg is on PATH
8
+ * 3. Node fs line scan — universal fallback
9
+ *
10
+ * Public API:
11
+ * search(query, projectRoot, opts?) → [{file, line, text}]
12
+ * reindex(projectRoot) → void (rebuilds FTS5 DB; no-op on grep path)
13
+ * backendName() → 'fts5' | 'ripgrep' | 'node-grep'
14
+ */
15
+
16
+ const fs = require('fs');
17
+ const path = require('path');
18
+ const { spawnSync } = require('child_process');
19
+ const { probeOptional } = require('./probe-optional.cjs');
20
+
21
+ // ---------------------------------------------------------------------------
22
+ // Backend selection (evaluated once at module load)
23
+ // ---------------------------------------------------------------------------
24
+
25
+ const Database = probeOptional('better-sqlite3');
26
+
27
+ let _fts5Supported = false;
28
+ if (Database) {
29
+ try {
30
+ const probe = new Database(':memory:');
31
+ probe.exec('CREATE VIRTUAL TABLE _p USING fts5(t)');
32
+ probe.close();
33
+ _fts5Supported = true;
34
+ } catch { /* fts5 extension not compiled in */ }
35
+ }
36
+
37
+ function _rgAvailable() {
38
+ try {
39
+ const r = spawnSync('rg', ['--version'], { encoding: 'utf8', windowsHide: true });
40
+ return r.status === 0;
41
+ } catch { return false; }
42
+ }
43
+
44
+ const _hasRg = _rgAvailable();
45
+
46
+ function backendName() {
47
+ if (_fts5Supported) return 'fts5';
48
+ if (_hasRg) return 'ripgrep';
49
+ return 'node-grep';
50
+ }
51
+
52
+ // ---------------------------------------------------------------------------
53
+ // Index paths
54
+ // ---------------------------------------------------------------------------
55
+
56
+ const INDEXED_GLOBS = [
57
+ '.design/archive/**/*.md',
58
+ '.design/learnings/LEARNINGS.md',
59
+ '.design/CYCLES.md',
60
+ ];
61
+
62
+ function _dbPath(projectRoot) {
63
+ return path.join(projectRoot, '.design', 'search.db');
64
+ }
65
+
66
+ function _collectFiles(projectRoot) {
67
+ const results = [];
68
+ function walk(dir) {
69
+ let entries;
70
+ try { entries = fs.readdirSync(dir, { withFileTypes: true }); } catch { return; }
71
+ for (const e of entries) {
72
+ const full = path.join(dir, e.name);
73
+ if (e.isDirectory()) { walk(full); continue; }
74
+ if (e.name.endsWith('.md')) results.push(full);
75
+ }
76
+ }
77
+ walk(path.join(projectRoot, '.design', 'archive'));
78
+ for (const rel of [
79
+ path.join('.design', 'learnings', 'LEARNINGS.md'),
80
+ path.join('.design', 'CYCLES.md'),
81
+ ]) {
82
+ const full = path.join(projectRoot, rel);
83
+ if (fs.existsSync(full)) results.push(full);
84
+ }
85
+ // STATE.md decision blocks
86
+ const state = path.join(projectRoot, '.design', 'STATE.md');
87
+ if (fs.existsSync(state)) results.push(state);
88
+ return results;
89
+ }
90
+
91
+ // ---------------------------------------------------------------------------
92
+ // FTS5 backend
93
+ // ---------------------------------------------------------------------------
94
+
95
+ function _openDb(projectRoot) {
96
+ const dbPath = _dbPath(projectRoot);
97
+ fs.mkdirSync(path.dirname(dbPath), { recursive: true });
98
+ const db = new Database(dbPath);
99
+ db.exec(`
100
+ CREATE VIRTUAL TABLE IF NOT EXISTS docs USING fts5(
101
+ file UNINDEXED, line UNINDEXED, text, tokenize='trigram'
102
+ );
103
+ `);
104
+ return db;
105
+ }
106
+
107
+ function reindex(projectRoot) {
108
+ if (!_fts5Supported) return;
109
+ const db = _openDb(projectRoot);
110
+ db.exec('DELETE FROM docs');
111
+ const insert = db.prepare('INSERT INTO docs(file, line, text) VALUES (?,?,?)');
112
+ const txn = db.transaction((files) => {
113
+ for (const file of files) {
114
+ let content;
115
+ try { content = fs.readFileSync(file, 'utf8'); } catch { continue; }
116
+ const lines = content.split(/\r?\n/);
117
+ for (let i = 0; i < lines.length; i++) {
118
+ const t = lines[i].trim();
119
+ if (t) insert.run(file, i + 1, t);
120
+ }
121
+ }
122
+ });
123
+ txn(_collectFiles(projectRoot));
124
+ db.close();
125
+ }
126
+
127
+ function _searchFts5(query, projectRoot, limit) {
128
+ const dbPath = _dbPath(projectRoot);
129
+ if (!fs.existsSync(dbPath)) reindex(projectRoot);
130
+ const db = new Database(dbPath, { readonly: true });
131
+ try {
132
+ const rows = db.prepare(
133
+ `SELECT file, line, text FROM docs WHERE docs MATCH ? ORDER BY rank LIMIT ?`
134
+ ).all(query, limit);
135
+ return rows.map(r => ({ file: r.file, line: r.line, text: r.text }));
136
+ } finally {
137
+ db.close();
138
+ }
139
+ }
140
+
141
+ // ---------------------------------------------------------------------------
142
+ // Ripgrep backend
143
+ // ---------------------------------------------------------------------------
144
+
145
+ function _escapeRe(s) {
146
+ return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
147
+ }
148
+
149
+ function _searchRg(query, projectRoot, limit) {
150
+ const terms = query.split(/\s+/).filter(Boolean);
151
+ const pattern = terms.map(_escapeRe).join('|');
152
+ const targets = _collectFiles(projectRoot);
153
+ if (!targets.length || !pattern) return [];
154
+ const r = spawnSync('rg', ['-n', '--no-heading', '-i', '-S', pattern, ...targets], {
155
+ encoding: 'utf8', windowsHide: true,
156
+ });
157
+ const results = [];
158
+ for (const line of (r.stdout || '').split(/\r?\n/)) {
159
+ const m = line.match(/^(.+?):(\d+):(.*)$/);
160
+ if (m) results.push({ file: m[1], line: Number(m[2]), text: m[3].trim() });
161
+ if (results.length >= limit) break;
162
+ }
163
+ return results;
164
+ }
165
+
166
+ // ---------------------------------------------------------------------------
167
+ // Node fs fallback backend
168
+ // ---------------------------------------------------------------------------
169
+
170
+ function _searchNode(query, projectRoot, limit) {
171
+ const terms = query.toLowerCase().split(/\s+/).filter(Boolean);
172
+ const files = _collectFiles(projectRoot);
173
+ const results = [];
174
+ for (const file of files) {
175
+ let content;
176
+ try { content = fs.readFileSync(file, 'utf8'); } catch { continue; }
177
+ const lines = content.split(/\r?\n/);
178
+ for (let i = 0; i < lines.length; i++) {
179
+ const lower = lines[i].toLowerCase();
180
+ if (terms.every(t => lower.includes(t))) {
181
+ results.push({ file, line: i + 1, text: lines[i].trim() });
182
+ if (results.length >= limit) return results;
183
+ }
184
+ }
185
+ }
186
+ return results;
187
+ }
188
+
189
+ // ---------------------------------------------------------------------------
190
+ // Public search
191
+ // ---------------------------------------------------------------------------
192
+
193
+ /**
194
+ * @param {string} query
195
+ * @param {string} projectRoot absolute path to the project (contains .design/)
196
+ * @param {{ limit?: number }} [opts]
197
+ * @returns {{ file: string, line: number, text: string }[]}
198
+ */
199
+ function search(query, projectRoot, opts = {}) {
200
+ const limit = opts.limit ?? 20;
201
+ if (_fts5Supported) return _searchFts5(query, projectRoot, limit);
202
+ if (_hasRg) return _searchRg(query, projectRoot, limit);
203
+ return _searchNode(query, projectRoot, limit);
204
+ }
205
+
206
+ module.exports = { search, reindex, backendName };
@@ -0,0 +1,29 @@
1
+ 'use strict';
2
+ /**
3
+ * probe-optional.cjs — safely require optional native dependencies.
4
+ *
5
+ * Usage:
6
+ * const { probeOptional } = require('./probe-optional.cjs');
7
+ * const Database = probeOptional('better-sqlite3');
8
+ * if (Database) { ... } else { // fallback }
9
+ *
10
+ * Returns the module if available and natively compatible, null otherwise.
11
+ * Swallows MODULE_NOT_FOUND and native binding errors silently — callers
12
+ * must implement their own fallback path.
13
+ */
14
+ function probeOptional(name) {
15
+ try {
16
+ return require(name);
17
+ } catch (e) {
18
+ if (
19
+ e.code === 'MODULE_NOT_FOUND' ||
20
+ e.message?.includes('was compiled against a different Node.js version') ||
21
+ e.message?.includes('NODE_MODULE_VERSION')
22
+ ) {
23
+ return null;
24
+ }
25
+ throw e;
26
+ }
27
+ }
28
+
29
+ module.exports = { probeOptional };
@@ -0,0 +1,121 @@
1
+ 'use strict';
2
+ /**
3
+ * relevance-counter.cjs — tracks per-learning signal counts.
4
+ *
5
+ * Signals: 'cited' | 'surfaced' | 'dismissed'
6
+ *
7
+ * Writes to `.design/learnings/.relevance.json` under an atomic-write
8
+ * pattern (write tmp → rename) guarded by a `.relevance.lock` file so
9
+ * concurrent agents don't clobber each other.
10
+ *
11
+ * Public API:
12
+ * record(id, signal, designDir) → void
13
+ * load(designDir) → { [id]: { cited, surfaced, dismissed, last_used } }
14
+ * shouldPromote(id, designDir) → boolean (cited >= 8)
15
+ * shouldPrune(id, age, designDir) → boolean (cited === 0 and age >= 4 cycles)
16
+ */
17
+
18
+ const fs = require('fs');
19
+ const path = require('path');
20
+ const os = require('os');
21
+
22
+ const SIGNALS = new Set(['cited', 'surfaced', 'dismissed']);
23
+ const PROMOTE_THRESHOLD = 8;
24
+
25
+ function _counterPath(designDir) {
26
+ return path.join(designDir, 'learnings', '.relevance.json');
27
+ }
28
+
29
+ function _lockPath(designDir) {
30
+ return path.join(designDir, 'learnings', '.relevance.lock');
31
+ }
32
+
33
+ function _acquireLock(lockPath, timeout = 5000) {
34
+ const deadline = Date.now() + timeout;
35
+ while (Date.now() < deadline) {
36
+ try {
37
+ fs.writeFileSync(lockPath, String(process.pid), { flag: 'wx' });
38
+ return true;
39
+ } catch (e) {
40
+ if (e.code !== 'EEXIST') throw e;
41
+ // Check for stale lock (> 30s old)
42
+ try {
43
+ const stat = fs.statSync(lockPath);
44
+ if (Date.now() - stat.mtimeMs > 30000) {
45
+ fs.unlinkSync(lockPath);
46
+ continue;
47
+ }
48
+ } catch { /* lock was removed between our check and unlink */ }
49
+ // Brief spin
50
+ const end = Date.now() + 50;
51
+ while (Date.now() < end) { /* spin */ }
52
+ }
53
+ }
54
+ return false;
55
+ }
56
+
57
+ function _releaseLock(lockPath) {
58
+ try { fs.unlinkSync(lockPath); } catch { /* already gone */ }
59
+ }
60
+
61
+ function load(designDir) {
62
+ const p = _counterPath(designDir);
63
+ try {
64
+ return JSON.parse(fs.readFileSync(p, 'utf8'));
65
+ } catch {
66
+ return {};
67
+ }
68
+ }
69
+
70
+ /**
71
+ * Record a signal for a learning entry id.
72
+ * @param {string} id Learning ID (e.g. "L-01")
73
+ * @param {'cited'|'surfaced'|'dismissed'} signal
74
+ * @param {string} designDir Path to the .design/ directory
75
+ */
76
+ function record(id, signal, designDir) {
77
+ if (!SIGNALS.has(signal)) throw new Error(`Unknown signal: ${signal}`);
78
+ fs.mkdirSync(path.join(designDir, 'learnings'), { recursive: true });
79
+
80
+ const lockPath = _lockPath(designDir);
81
+ if (!_acquireLock(lockPath)) {
82
+ // Non-fatal — skip the update rather than corrupting the store
83
+ return;
84
+ }
85
+
86
+ try {
87
+ const data = load(designDir);
88
+ if (!data[id]) {
89
+ data[id] = { cited: 0, surfaced: 0, dismissed: 0, last_used: null };
90
+ }
91
+ data[id][signal]++;
92
+ data[id].last_used = new Date().toISOString();
93
+
94
+ // Atomic write: tmp → rename
95
+ const counterPath = _counterPath(designDir);
96
+ const tmp = counterPath + '.tmp.' + process.pid;
97
+ fs.writeFileSync(tmp, JSON.stringify(data, null, 2) + '\n', 'utf8');
98
+ fs.renameSync(tmp, counterPath);
99
+ } finally {
100
+ _releaseLock(lockPath);
101
+ }
102
+ }
103
+
104
+ function shouldPromote(id, designDir) {
105
+ const data = load(designDir);
106
+ return (data[id]?.cited ?? 0) >= PROMOTE_THRESHOLD;
107
+ }
108
+
109
+ /**
110
+ * @param {string} id
111
+ * @param {number} ageCycles number of cycles since last_used (or since created)
112
+ * @param {string} designDir
113
+ */
114
+ function shouldPrune(id, ageCycles, designDir) {
115
+ const data = load(designDir);
116
+ const entry = data[id];
117
+ if (!entry) return false;
118
+ return entry.cited === 0 && ageCycles >= 4;
119
+ }
120
+
121
+ module.exports = { record, load, shouldPromote, shouldPrune };
@@ -22,8 +22,46 @@ Closes the current cycle: marks CYCLES.md entry complete, archives pipeline arti
22
22
  - `DESIGN-AUDIT.md`
23
23
  - `DESIGN-SUMMARY.md`
24
24
  Mark originals with a `<!-- archived to .design/archive/cycle-N/ -->` note at top (do not delete — next cycle will overwrite).
25
- 5. **Clear STATE.md**: Set `cycle:` to empty string, reset `<decisions>` to a fresh empty section, reset `stage:` to `brief`.
26
- 6. Print: "Cycle <N> archived. Run `/gdd:new-cycle` to start the next cycle."
25
+ 5. **Generate EXPERIENCE.md** (Haiku-tier writer step): Read cycle STATE decisions + `.design/DESIGN-VERIFICATION.md` (if present) + any `.design/reflections/*.md` from this cycle. Write `.design/archive/cycle-N/EXPERIENCE.md` using this structure (~100–200 lines, declarative-fact framing, prepend `reference/cycle-handoff-preamble.md` content):
26
+
27
+ ```markdown
28
+ <!-- archived cycle experience — reference, not current requests -->
29
+ # Cycle N — Experience
30
+
31
+ **Goal**: <one-line goal from STATE.md or CYCLES.md>
32
+ **Opened**: <start date>
33
+ **Ended**: <today>
34
+
35
+ ## Decisions Made
36
+
37
+ <list all D-XX decisions from STATE.md <decisions> block — one per line>
38
+
39
+ ## Learnings Graduated
40
+
41
+ <list all L-NN learnings surfaced or promoted this cycle>
42
+
43
+ ## What Died
44
+
45
+ <designs, approaches, or tasks that were started but abandoned — and why>
46
+
47
+ ## Surprises
48
+
49
+ <what was unexpected during this cycle>
50
+
51
+ ## Handoff to Next Cycle
52
+
53
+ <what the next cycle should know: active sketches, deferred tasks, unresolved questions>
54
+ ```
55
+
56
+ This file becomes the highest-priority source for the decision-injector hook in future sessions.
57
+
58
+ 6. **Trigger reindex**: if `.design/search.db` exists, rebuild the search index:
59
+ ```bash
60
+ node -e "require('./scripts/lib/design-search.cjs').reindex(process.cwd())" 2>/dev/null || true
61
+ ```
62
+
63
+ 7. **Clear STATE.md**: Set `cycle:` to empty string, reset `<decisions>` to a fresh empty section, reset `stage:` to `brief`.
64
+ 8. Print: "Cycle <N> archived. Run `/gdd:new-cycle` to start the next cycle."
27
65
 
28
66
  ## Do Not
29
67
 
@@ -0,0 +1,23 @@
1
+ ---
2
+ name: gdd-continue
3
+ description: "Alias for /gdd:resume — restore session context from the most recent checkpoint."
4
+ argument-hint: "[<checkpoint-N>]"
5
+ tools: Read, Write, Bash, Glob
6
+ ---
7
+
8
+ @reference/retrieval-contract.md
9
+
10
+ # /gdd:continue
11
+
12
+ Alias for `/gdd:resume`. Delegates immediately to the resume skill with the same argument.
13
+
14
+ This alias exists for discoverability — users familiar with `git continue` or similar conventions find `/gdd:continue` more intuitive than `/gdd:resume` after a pause.
15
+
16
+ ## Steps
17
+
18
+ 1. Forward the argument (if any) to the `/gdd:resume` skill logic.
19
+ 2. Execute all `/gdd:resume` steps exactly as documented in `skills/resume/SKILL.md`.
20
+
21
+ The two commands are functionally identical. `/gdd:resume` is the canonical form; `/gdd:continue` is the convenience alias.
22
+
23
+ ## CONTINUE COMPLETE
@@ -1,8 +1,8 @@
1
1
  ---
2
2
  name: gdd-pause
3
- description: "Write session handoff so work can resume in a new session without re-running completed stages."
3
+ description: "Write a numbered checkpoint so work can resume in a new session without re-running completed stages."
4
4
  argument-hint: "[context note]"
5
- tools: Read, Write, AskUserQuestion
5
+ tools: Read, Write, Bash, AskUserQuestion
6
6
  ---
7
7
 
8
8
  @reference/retrieval-contract.md
@@ -10,35 +10,61 @@ tools: Read, Write, AskUserQuestion
10
10
 
11
11
  # /gdd:pause
12
12
 
13
- Captures enough state that a killed or stopped session can resume cleanly via `/gdd:resume`.
13
+ Captures enough state that a killed or stopped session can resume cleanly via `/gdd:resume` or `/gdd:continue`.
14
+
15
+ Each invocation writes an **immutable numbered checkpoint** — it does not overwrite previous pauses. This enables branched cycles: you can pause, take a detour via `/gdd:sketch`, compare, and resume an older snapshot via `/gdd:resume N`.
14
16
 
15
17
  ## Steps
16
18
 
17
- 1. Read `.design/STATE.md`. Extract:
18
- - Current `stage:` and `cycle:`
19
+ 1. **Read `.design/STATE.md`**. Extract:
20
+ - `stage:` and `cycle:`
19
21
  - Last activity timestamp
20
22
  - Completed tasks in the current pipeline run
21
- - Open todos (from `.design/TODO.md` if present)
23
+ - Open todos (scan `.design/TODO.md` if present)
22
24
  - Active sketch/spike directories (scan `.design/sketches/` and `.design/spikes/` for in-progress markers)
23
- 2. If no context argument was passed, ask (AskUserQuestion): "What are you in the middle of? (optional context to capture)"
24
- 3. Write `.design/HANDOFF.md`:
25
+
26
+ 2. **Determine checkpoint number**: run
27
+ ```bash
28
+ ls .design/checkpoints/ 2>/dev/null | grep -E '^[0-9]+' | sort -n | tail -1
29
+ ```
30
+ Next checkpoint = last number + 1 (or `01` if none exist).
31
+
32
+ 3. **Collect context**: if no context argument was passed, ask (AskUserQuestion):
33
+ "What are you in the middle of? (optional context to capture)"
34
+
35
+ 4. **Write numbered checkpoint**: create `.design/checkpoints/` with `mkdir -p`, then write:
36
+ ```
37
+ .design/checkpoints/NN-<stage>-<ISO-date>.md
38
+ ```
39
+ e.g. `01-design-2026-04-24.md`
40
+
41
+ Content:
25
42
  ```markdown
26
- # Session Handoff
27
- **Paused**: <ISO timestamp>
43
+ # Checkpoint NN
44
+ **Saved**: <ISO timestamp>
28
45
  **Stage**: <stage>
29
- **Cycle**: <cycle-N>
46
+ **Cycle**: <cycle>
30
47
  **In progress**: <task description + wave/index>
31
48
  **Next**: <next step>
32
- **Context**: <user note>
49
+ **Context**: <user note or "none">
33
50
  **Active sketch**: <path or none>
34
51
  **Open todos**: <N items (see .design/TODO.md)>
35
52
  **Completed this session**: <list>
36
53
  ```
37
- 4. Print: "Session paused. Run `/gdd:resume` to pick back up."
54
+
55
+ 5. **Update HANDOFF.md pointer**: write `.design/HANDOFF.md` with:
56
+ ```markdown
57
+ # Session Handoff (pointer)
58
+ Latest checkpoint: `.design/checkpoints/NN-<stage>-<ISO-date>.md`
59
+ Run `/gdd:resume` to restore, or `/gdd:resume N` for a specific checkpoint.
60
+ ```
61
+
62
+ 6. Print: "Checkpoint NN saved. Run `/gdd:resume` or `/gdd:continue` to pick back up."
38
63
 
39
64
  ## Do Not
40
65
 
41
- - Do not modify STATE.md itself HANDOFF.md is the only write.
66
+ - Do not modify STATE.md — checkpoints are the only write.
42
67
  - Do not abort in-progress sketches; just record them.
68
+ - Do not delete previous checkpoint files.
43
69
 
44
70
  ## PAUSE COMPLETE
@@ -0,0 +1,74 @@
1
+ ---
2
+ name: gdd-recall
3
+ description: "Search cross-cycle memory: decisions, learnings, experience archives. Returns ranked matches."
4
+ argument-hint: "<query> [--reindex]"
5
+ tools: Read, Write, Bash
6
+ ---
7
+
8
+ @reference/retrieval-contract.md
9
+
10
+ # /gdd:recall
11
+
12
+ Searches `.design/archive/`, `.design/learnings/LEARNINGS.md`, `.design/CYCLES.md`, and `STATE.md` decision blocks for the given query and returns ranked matches. Uses FTS5 when `better-sqlite3` is available, ripgrep next, Node fs scan as universal fallback.
13
+
14
+ ## Steps
15
+
16
+ 1. **Parse argument**: extract `<query>` and optional `--reindex` flag.
17
+ - If no query and no `--reindex`: print usage and exit.
18
+ - If `--reindex` only: rebuild the index (see step 3), then exit.
19
+
20
+ 2. **Check backend**: run
21
+ ```bash
22
+ node -e "const {backendName}=require('./scripts/lib/design-search.cjs');console.log(backendName())"
23
+ ```
24
+ Print a one-line notice: `Search backend: fts5 | ripgrep | node-grep`.
25
+
26
+ 3. **Reindex if requested**: run
27
+ ```bash
28
+ node -e "require('./scripts/lib/design-search.cjs').reindex(process.cwd())"
29
+ ```
30
+ Print: "Index rebuilt."
31
+
32
+ 4. **Search**: run
33
+ ```bash
34
+ node -e "
35
+ const s=require('./scripts/lib/design-search.cjs');
36
+ const r=s.search(process.argv[1], process.cwd(), {limit:20});
37
+ console.log(JSON.stringify(r));
38
+ " -- "<query>"
39
+ ```
40
+ Parse the JSON array of `{file, line, text}` objects.
41
+
42
+ 5. **Format results**: print in this shape:
43
+ ```
44
+ ## Recall: "<query>" (N matches, backend: fts5)
45
+
46
+ ### 1. .design/archive/cycle-2/EXPERIENCE.md:42
47
+ > Decided to skip dark-mode token layer in cycle 2 — cost–benefit did not clear threshold
48
+
49
+ ### 2. .design/learnings/LEARNINGS.md:18
50
+ > L-03 · color · cited:4 · Color contrast failures cluster around interactive states, not surfaces
51
+ ```
52
+ Group by file. Truncate text at 120 chars. Cap at 20 results.
53
+
54
+ 6. **Signal relevance**: for every result whose text contains an `L-NN` learning ID, record a `surfaced` signal:
55
+ ```bash
56
+ node -e "
57
+ const rc=require('./scripts/lib/relevance-counter.cjs');
58
+ rc.record('L-NN','surfaced','.design');
59
+ "
60
+ ```
61
+
62
+ 7. **Empty result**: if no matches, print:
63
+ ```
64
+ No matches for "<query>" in cross-cycle memory.
65
+ Tip: run /gdd:recall --reindex if the archive has been updated since last index build.
66
+ ```
67
+
68
+ ## Do Not
69
+
70
+ - Do not modify any source file.
71
+ - Do not run git commands.
72
+ - Do not write `.design/STATE.md`.
73
+
74
+ ## RECALL COMPLETE
@@ -1,8 +1,8 @@
1
1
  ---
2
2
  name: gdd-resume
3
- description: "Restore session context from .design/HANDOFF.md and route to where work left off."
4
- argument-hint: ""
5
- tools: Read, Write, Bash, Glob
3
+ description: "Restore session context from a numbered checkpoint. Lists available checkpoints when no argument given."
4
+ argument-hint: "[<N>]"
5
+ tools: Read, Write, Bash, Glob, AskUserQuestion
6
6
  ---
7
7
 
8
8
  @reference/retrieval-contract.md
@@ -10,31 +10,49 @@ tools: Read, Write, Bash, Glob
10
10
 
11
11
  # /gdd:resume
12
12
 
13
- Inverse of `/gdd:pause`. Reads the handoff file, prints a clear "you were here" summary, and routes to the next command.
13
+ Inverse of `/gdd:pause`. Reads a checkpoint file, prints a clear "you were here" summary, and routes to the next command.
14
14
 
15
15
  ## Steps
16
16
 
17
- 1. Try to read `.design/HANDOFF.md`. If missing, read `.design/STATE.md` and infer position from the `stage:` and `cycle:` fields.
18
- 2. Print a summary in this exact shape:
17
+ 1. **Parse argument**:
18
+ - If argument is a number N restore checkpoint N.
19
+ - If no argument → list available checkpoints and ask which to restore (see step 2).
20
+
21
+ 2. **List mode** (no argument):
22
+ ```bash
23
+ ls .design/checkpoints/ 2>/dev/null | sort -n
24
+ ```
25
+ If empty, fall back to reading `.design/HANDOFF.md` (legacy single-slot format).
26
+ If checkpoints exist, present the list and ask (AskUserQuestion):
27
+ "Which checkpoint would you like to restore? (enter number, or press Enter for the latest)"
28
+ Use the answer (or latest if Enter pressed) as N.
29
+
30
+ 3. **Read checkpoint**: load `.design/checkpoints/NN-*.md`. If not found, try `.design/HANDOFF.md` as legacy fallback.
31
+
32
+ 4. **Print summary** in this exact shape:
19
33
  ```
20
- Last paused: <timestamp>
34
+ Checkpoint NN restored.
35
+ Saved: <timestamp>
21
36
  You were: <in-progress description>
22
37
  Next step: <next>
23
38
  Active sketch: <path or none>
24
39
  Open todos: <N>
25
40
  ```
26
- 3. **Staleness check** — compare mtime of `.design/` artifacts vs `src/` (via Glob + Bash `stat` when available). If `src/` has commits/changes newer than the last pipeline artifact, warn: "Source has changed since last pipeline run — consider re-running explore or verify."
27
- 4. **Route recommendation** based on stage:
28
- - `brief` "Run `@get-design-done brief`"
29
- - `explore` → "Run `@get-design-done explore`"
30
- - `plan` "Run `@get-design-done plan`"
31
- - `design` → "Run `@get-design-done design` to continue"
32
- - `verify` → "Run `@get-design-done verify`"
33
- 5. Do not auto-execute the next command — just recommend.
41
+
42
+ 5. **Staleness check**: compare mtime of `.design/` artifacts vs `src/` via Bash `stat` when available. If `src/` has commits newer than the checkpoint timestamp, warn:
43
+ "Source has changed since checkpoint NN — consider re-running explore or verify."
44
+
45
+ 6. **Route recommendation** based on checkpoint `Stage:` field:
46
+ - `brief` → "Run `/gdd:brief`"
47
+ - `explore` → "Run `/gdd:explore`"
48
+ - `plan` "Run `/gdd:plan`"
49
+ - `design` → "Run `/gdd:design` to continue"
50
+ - `verify` → "Run `/gdd:verify`"
34
51
 
35
52
  ## Do Not
36
53
 
37
- - Do not delete HANDOFF.md (leave it; next `/gdd:pause` overwrites it).
54
+ - Do not delete checkpoint files.
38
55
  - Do not modify STATE.md.
56
+ - Do not auto-execute the next command — just recommend.
39
57
 
40
58
  ## RESUME COMPLETE