@hegemonart/get-design-done 1.14.5 → 1.14.7

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.
@@ -0,0 +1,140 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+ /**
4
+ * hooks/gdd-mcp-circuit-breaker.js — PostToolUse counter for mutation-side
5
+ * MCP calls (use_figma / use_paper / use_pencil).
6
+ *
7
+ * Responsibilities:
8
+ * - Parse tool outcome: success | timeout | error
9
+ * - Append one JSONL row to .design/telemetry/mcp-budget.jsonl:
10
+ * { ts, tool, outcome, consecutive_timeouts, total_calls }
11
+ * - After the append, if consecutive_timeouts ≥ max OR total_calls > max_calls_per_task,
12
+ * emit {continue:false, stopReason:"..."} and append a STATE.md blocker line.
13
+ *
14
+ * Defaults live in reference/mcp-budget.default.json; overrides merge from
15
+ * .design/config.json.mcp_budget.
16
+ *
17
+ * Exit code always 0 (advisory + JSON-on-stdout pattern).
18
+ */
19
+
20
+ const fs = require('fs');
21
+ const path = require('path');
22
+
23
+ const REPO_ROOT = path.resolve(__dirname, '..');
24
+ const DEFAULT_FILE = path.join(REPO_ROOT, 'reference', 'mcp-budget.default.json');
25
+
26
+ const TRACKED_TOOL_RE = /^mcp__.*use_(figma|paper|pencil)$/;
27
+
28
+ function loadBudget(cwd) {
29
+ let defaults = { max_calls_per_task: 30, max_consecutive_timeouts: 3, reset_on_success: true };
30
+ try {
31
+ const d = JSON.parse(fs.readFileSync(DEFAULT_FILE, 'utf8'));
32
+ defaults = { ...defaults, ...d };
33
+ } catch { /* fall back */ }
34
+ try {
35
+ const cfg = JSON.parse(fs.readFileSync(path.join(cwd, '.design', 'config.json'), 'utf8'));
36
+ if (cfg && typeof cfg.mcp_budget === 'object') {
37
+ return { ...defaults, ...cfg.mcp_budget };
38
+ }
39
+ } catch { /* no user overrides */ }
40
+ return defaults;
41
+ }
42
+
43
+ function classifyOutcome(toolResponse) {
44
+ if (!toolResponse || typeof toolResponse !== 'object') return 'error';
45
+ const text = JSON.stringify(toolResponse).slice(0, 4000).toLowerCase();
46
+ // Check timeout FIRST — a timed-out call may also set is_error, but we want
47
+ // to classify it as "timeout" so consecutive_timeouts advances correctly.
48
+ if (text.includes('timeout') || text.includes('timed out') || text.includes('deadline exceeded')) return 'timeout';
49
+ if (toolResponse.is_error) return 'error';
50
+ if (text.includes('"error"') || text.includes('failed')) return 'error';
51
+ return 'success';
52
+ }
53
+
54
+ function readJsonlTail(filePath) {
55
+ if (!fs.existsSync(filePath)) return { lastRow: null, total_calls: 0, consecutive_timeouts: 0 };
56
+ let total = 0;
57
+ let lastTimeoutsChain = 0;
58
+ let lastRow = null;
59
+ try {
60
+ const text = fs.readFileSync(filePath, 'utf8');
61
+ for (const line of text.split(/\r?\n/)) {
62
+ const t = line.trim();
63
+ if (!t) continue;
64
+ let row;
65
+ try { row = JSON.parse(t); } catch { continue; }
66
+ total++;
67
+ if (row.outcome === 'timeout') lastTimeoutsChain++;
68
+ else lastTimeoutsChain = 0;
69
+ lastRow = row;
70
+ }
71
+ } catch { /* unreadable ledger → start fresh */ }
72
+ return { lastRow, total_calls: total, consecutive_timeouts: lastTimeoutsChain };
73
+ }
74
+
75
+ function appendJsonl(filePath, row) {
76
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
77
+ fs.appendFileSync(filePath, JSON.stringify(row) + '\n', 'utf8');
78
+ }
79
+
80
+ function appendStateBlocker(cwd, message) {
81
+ const statePath = path.join(cwd, '.design', 'STATE.md');
82
+ if (!fs.existsSync(statePath)) return; // silent if STATE missing
83
+ const line = `\n<!-- mcp-circuit-breaker: ${new Date().toISOString()} --> 🛑 BLOCKER: ${message}\n`;
84
+ try { fs.appendFileSync(statePath, line, 'utf8'); } catch { /* best-effort */ }
85
+ }
86
+
87
+ async function main() {
88
+ let buf = '';
89
+ for await (const chunk of process.stdin) buf += chunk;
90
+ let payload;
91
+ try { payload = JSON.parse(buf || '{}'); } catch {
92
+ process.stdout.write(JSON.stringify({ continue: true }));
93
+ return;
94
+ }
95
+
96
+ const tool = payload?.tool_name || '';
97
+ if (!TRACKED_TOOL_RE.test(tool)) {
98
+ process.stdout.write(JSON.stringify({ continue: true }));
99
+ return;
100
+ }
101
+
102
+ const cwd = payload?.cwd || process.cwd();
103
+ const budget = loadBudget(cwd);
104
+ const ledgerPath = path.join(cwd, '.design', 'telemetry', 'mcp-budget.jsonl');
105
+
106
+ const prior = readJsonlTail(ledgerPath);
107
+ const outcome = classifyOutcome(payload?.tool_response);
108
+ const total_calls = prior.total_calls + 1;
109
+ const consecutive_timeouts = outcome === 'timeout'
110
+ ? prior.consecutive_timeouts + 1
111
+ : (budget.reset_on_success && outcome === 'success' ? 0 : prior.consecutive_timeouts);
112
+
113
+ const row = {
114
+ ts: new Date().toISOString(),
115
+ tool,
116
+ outcome,
117
+ consecutive_timeouts,
118
+ total_calls,
119
+ };
120
+ appendJsonl(ledgerPath, row);
121
+
122
+ const timeoutBreak = consecutive_timeouts >= budget.max_consecutive_timeouts;
123
+ const volumeBreak = budget.max_calls_per_task > 0 && total_calls > budget.max_calls_per_task;
124
+
125
+ if (timeoutBreak || volumeBreak) {
126
+ const reason = timeoutBreak
127
+ ? `${consecutive_timeouts} consecutive MCP timeouts on ${tool} (≥${budget.max_consecutive_timeouts}). Likely the sandbox hill-climb failure mode. Stop and redirect.`
128
+ : `MCP call count for this task is ${total_calls}, above max_calls_per_task=${budget.max_calls_per_task}. Stop and redirect.`;
129
+ const msg = `${reason} For authoring new Figma content, use figma:figma-generate-design. For decision-writing, use /gdd:figma-write. See reference/figma-sandbox.md.`;
130
+ appendStateBlocker(cwd, msg);
131
+ process.stdout.write(JSON.stringify({ continue: false, stopReason: `gdd-mcp-circuit-breaker: ${msg}` }));
132
+ return;
133
+ }
134
+
135
+ process.stdout.write(JSON.stringify({ continue: true }));
136
+ }
137
+
138
+ main().catch(() => {
139
+ process.stdout.write(JSON.stringify({ continue: true }));
140
+ });
@@ -0,0 +1,114 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+ /**
4
+ * hooks/gdd-protected-paths.js — PreToolUse:Edit|Write|Bash guard
5
+ *
6
+ * Blocks Edit/Write on file paths matching the merged protected-paths glob list,
7
+ * and blocks destructive Bash targeting the same paths (rm/mv/cp/tee/sed -i/git rm).
8
+ *
9
+ * Defaults live in reference/protected-paths.default.json.
10
+ * User additions at .design/config.json.protected_paths are MERGED into the default
11
+ * list; users cannot reduce the default set by shipping an empty override.
12
+ */
13
+
14
+ const fs = require('fs');
15
+ const path = require('path');
16
+ const { matches } = require(path.join(__dirname, '..', 'scripts', 'lib', 'glob-match.cjs'));
17
+
18
+ const REPO_ROOT = path.resolve(__dirname, '..');
19
+
20
+ function loadProtectedPaths(cwd) {
21
+ const defaultFile = path.join(REPO_ROOT, 'reference', 'protected-paths.default.json');
22
+ let defaults = [];
23
+ try {
24
+ const parsed = JSON.parse(fs.readFileSync(defaultFile, 'utf8'));
25
+ defaults = Array.isArray(parsed.protected_paths) ? parsed.protected_paths : [];
26
+ } catch { /* fall back to an empty list; caller decides */ }
27
+
28
+ const userFile = path.join(cwd || process.cwd(), '.design', 'config.json');
29
+ let userList = [];
30
+ try {
31
+ const cfg = JSON.parse(fs.readFileSync(userFile, 'utf8'));
32
+ if (Array.isArray(cfg.protected_paths)) userList = cfg.protected_paths;
33
+ } catch { /* missing or invalid user config → defaults only */ }
34
+
35
+ return Array.from(new Set([...defaults, ...userList]));
36
+ }
37
+
38
+ /**
39
+ * Extract a target path from a Bash command, best-effort.
40
+ * Returns an array of candidate paths; empty if none parsed.
41
+ */
42
+ function extractBashTargets(command) {
43
+ if (!command) return [];
44
+ const targets = [];
45
+ // rm / cp / mv / mkdir trailing arg(s)
46
+ const rmMatch = command.match(/\b(rm|cp|mv|mkdir|touch|rmdir|chmod|chown)\s+(?:-[A-Za-z]+\s+)*([^\s|;&>]+)/);
47
+ if (rmMatch) targets.push(rmMatch[2]);
48
+ // redirect / tee
49
+ const redirectMatch = command.match(/[>|]\s*(?:tee\s+)?([^\s|;&]+)$/);
50
+ if (redirectMatch) targets.push(redirectMatch[1]);
51
+ // sed -i <path> (BSD and GNU variants)
52
+ const sedMatch = command.match(/\bsed\s+-i(?:\s*['"][^'"]*['"])?\s+(?:-[A-Za-z]+\s+)*(?:['"][^'"]*['"]\s+)?([^\s|;&]+)/);
53
+ if (sedMatch) targets.push(sedMatch[1]);
54
+ // git rm / git mv
55
+ const gitMatch = command.match(/\bgit\s+(rm|mv|restore|checkout)\s+(?:-[A-Za-z]+\s+)*([^\s|;&]+)/);
56
+ if (gitMatch) targets.push(gitMatch[2]);
57
+
58
+ return targets
59
+ .filter(Boolean)
60
+ .map(p => p.replace(/^['"]|['"]$/g, ''));
61
+ }
62
+
63
+ async function main() {
64
+ let buf = '';
65
+ for await (const chunk of process.stdin) buf += chunk;
66
+
67
+ let payload;
68
+ try { payload = JSON.parse(buf || '{}'); } catch {
69
+ process.stdout.write(JSON.stringify({ continue: true }));
70
+ return;
71
+ }
72
+
73
+ const tool = payload?.tool_name || '';
74
+ if (!['Edit', 'Write', 'MultiEdit', 'Bash'].includes(tool)) {
75
+ process.stdout.write(JSON.stringify({ continue: true }));
76
+ return;
77
+ }
78
+
79
+ const cwd = payload?.cwd || process.cwd();
80
+ const protectedPaths = loadProtectedPaths(cwd);
81
+ if (protectedPaths.length === 0) {
82
+ process.stdout.write(JSON.stringify({ continue: true }));
83
+ return;
84
+ }
85
+
86
+ const candidates = [];
87
+ if (tool === 'Edit' || tool === 'Write' || tool === 'MultiEdit') {
88
+ const fp = payload?.tool_input?.file_path;
89
+ if (fp) candidates.push(fp);
90
+ } else if (tool === 'Bash') {
91
+ candidates.push(...extractBashTargets(payload?.tool_input?.command || ''));
92
+ }
93
+
94
+ for (const cand of candidates) {
95
+ if (!cand) continue;
96
+ const rel = cand.startsWith('/') || /^[A-Z]:\\/i.test(cand)
97
+ ? path.relative(cwd, cand).replace(/\\/g, '/')
98
+ : cand.replace(/\\/g, '/');
99
+ const r = matches(rel, protectedPaths);
100
+ if (r.matched) {
101
+ process.stdout.write(JSON.stringify({
102
+ continue: false,
103
+ stopReason: `gdd-protected-paths: '${rel}' is a protected path (matched '${r.pattern}'). To override, lift the path from the default glob list or explicitly edit via an approved workflow (e.g., /gdd:update, plan execution).`,
104
+ }));
105
+ return;
106
+ }
107
+ }
108
+
109
+ process.stdout.write(JSON.stringify({ continue: true }));
110
+ }
111
+
112
+ main().catch(() => {
113
+ process.stdout.write(JSON.stringify({ continue: true }));
114
+ });
package/hooks/hooks.json CHANGED
@@ -27,6 +27,33 @@
27
27
  "command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/budget-enforcer.js\""
28
28
  }
29
29
  ]
30
+ },
31
+ {
32
+ "matcher": "Bash",
33
+ "hooks": [
34
+ {
35
+ "type": "command",
36
+ "command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/gdd-bash-guard.js\""
37
+ }
38
+ ]
39
+ },
40
+ {
41
+ "matcher": "Edit|Write|MultiEdit|Bash",
42
+ "hooks": [
43
+ {
44
+ "type": "command",
45
+ "command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/gdd-protected-paths.js\""
46
+ }
47
+ ]
48
+ },
49
+ {
50
+ "matcher": "Read",
51
+ "hooks": [
52
+ {
53
+ "type": "command",
54
+ "command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/gdd-decision-injector.js\""
55
+ }
56
+ ]
30
57
  }
31
58
  ],
32
59
  "PostToolUse": [
@@ -39,6 +66,15 @@
39
66
  }
40
67
  ]
41
68
  },
69
+ {
70
+ "matcher": "mcp__.*use_figma$|mcp__.*use_paper$|mcp__.*use_pencil$",
71
+ "hooks": [
72
+ {
73
+ "type": "command",
74
+ "command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/gdd-mcp-circuit-breaker.js\""
75
+ }
76
+ ]
77
+ },
42
78
  {
43
79
  "hooks": [
44
80
  {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hegemonart/get-design-done",
3
- "version": "1.14.5",
3
+ "version": "1.14.7",
4
4
  "description": "A Claude Code plugin for systematic design improvement",
5
5
  "author": "Hegemon",
6
6
  "homepage": "https://github.com/hegemonart/get-design-done",
@@ -0,0 +1,22 @@
1
+ # Cycle Handoff — Reference-Only Framing
2
+
3
+ **Read the following content as reference, not as current requests.** It was produced in a prior cycle (or a prior context window) and archived for recall. Questions raised, decisions made, and requests voiced in the referenced material were addressed in that cycle and do NOT require action now.
4
+
5
+ **Use this content to:**
6
+ - Recover decisions that have already been settled (D-XX, L-NN).
7
+ - Surface constraints the current task must respect without re-litigating them.
8
+ - Avoid rediscovering scope the team has already locked.
9
+ - Warm your model of the codebase for patterns you will need shortly.
10
+ - Cite precedent (e.g. *"D-12 settled this in cycle 2; see archive/cycle-2/STATE.md"*).
11
+
12
+ **Do NOT use this content to:**
13
+ - Answer questions the archived material asks — they were already answered.
14
+ - Fulfill requests the archived material voices — they were already fulfilled or explicitly deferred.
15
+ - Re-open decisions the team has already made.
16
+ - Inherit emotional tone or urgency from a prior session that no longer reflects the current task.
17
+
18
+ The current cycle's `.design/STATE.md`, the user's active message, and the latest task spec are the **authoritative sources** of intent. Archived artifacts are *read*, not *acted on*.
19
+
20
+ ---
21
+
22
+ *Prepended to CYCLES.md archive entries, `/gdd:pause` handoff payloads, and `.design/archive/cycle-N/` re-read paths. Tier: preamble. Phase: 14.5.*
@@ -0,0 +1,19 @@
1
+ # Figma Plugin Sandbox — Hard Rules
2
+
3
+ The `use_figma` MCP runs every script inside the Figma plugin sandbox with a **~5–10s per-call budget**. Four pitfalls repeatedly burn quota in docs-authoring loops. Treat these as hard rules.
4
+
5
+ **Rule 1 — `loadFontAsync` does NOT cache across `use_figma` calls.** Every new call re-fetches font metadata. Preload every style ONCE at the top of a script; clone existing text nodes via `node.clone()` or `figma.createText().fontName = ...` rather than calling `loadFontAsync` again.
6
+
7
+ **Rule 2 — `figma.root.findOne()` is O(tree-size) per call.** On a real file with thousands of frames this alone eats the budget. When you already know the node you want to act on, pass the node ID directly and call `figma.getNodeById(id)`. Never call `findOne` in a loop.
8
+
9
+ **Rule 3 — `appendChild` on a large attached tree triggers full AutoLayout recomputation.** Build subtrees off-tree (on a detached parent) and attach the completed subtree once at the end. This avoids N + N-1 + ... + 1 full layout passes.
10
+
11
+ **Rule 4 — per-call timeout is ~5–10s.** For docs-authoring (multi-row layouts populating from a library), budget **≤2 row-equivalents per `use_figma` call**. Exceeding this puts the script in the hill-climb-against-timeout failure mode: you retry with less content per call, each retry wastes another 5–10s, and MCP quota vanishes.
12
+
13
+ ---
14
+
15
+ ## When to skip `use_figma` entirely
16
+
17
+ For **authoring new content** — creating pages, populating with library components, building documentation layouts from scratch — prefer `figma:figma-generate-design` from the Figma plugin. It runs outside the sandbox and has no per-call timeout.
18
+
19
+ `use_figma` (and `/gdd:figma-write`) remain the right tools for **decision-writing**: attaching annotations, binding local-style tokens, registering Code Connect mappings, writing back implementation-status. Small, bounded, read-then-write operations where the four pitfalls don't apply.
@@ -0,0 +1,13 @@
1
+ {
2
+ "$schema": "./schemas/mcp-budget.schema.json",
3
+ "version": 1,
4
+ "description": "Default per-task MCP ceilings enforced by hooks/gdd-mcp-circuit-breaker.js. User overrides merge from .design/config.json.mcp_budget.",
5
+ "max_calls_per_task": 30,
6
+ "max_consecutive_timeouts": 3,
7
+ "reset_on_success": true,
8
+ "tracked_tools": [
9
+ "mcp__.*use_figma$",
10
+ "mcp__.*use_paper$",
11
+ "mcp__.*use_pencil$"
12
+ ]
13
+ }
@@ -0,0 +1,66 @@
1
+ # Meta-Rules (L0)
2
+
3
+ These rules are framework-invariant across the GDD pipeline. They do not change between cycles, phases, or tasks. Every agent imports `reference/shared-preamble.md`, which aggregates `reference/meta-rules.md` first.
4
+
5
+ **Tier: L0** (frozen prefix; stabilizes the Anthropic 5-min prompt-cache prefix across agent spawns — the churning L2 body below does NOT invalidate this).
6
+
7
+ ---
8
+
9
+ ## Required Reading Discipline
10
+
11
+ When the orchestrator's prompt contains a `<required_reading>` block, you MUST read every file it lists with the `Read` tool before taking any other action. Paths prefixed with `@` are file paths — pass them directly to `Read`. Skipping required reading is a hard violation: you will produce stale output that the downstream verifier catches, wasting a full spawn cycle.
12
+
13
+ ## Writes Protocol
14
+
15
+ Only write files declared in your frontmatter `writes:` list. Agents with `reads-only: true` must never call `Write` or `Edit` on any file. If the task appears to require writing outside your declared scope, stop and return a `<blocker>` in STATE.md rather than expanding your write surface.
16
+
17
+ If your agent runs in a phase that enforces atomic commits (most do), commit only files in your declared `writes:` list. Use the repo commit convention: `docs(phase-N-P): short imperative description` for documentation-class changes, `feat(phase-N-P): ...` for new capability, `fix(phase-N-P): ...` for bug fixes. Phase and plan numbers come from `.design/STATE.md` `phase:` and the invoking plan's frontmatter.
18
+
19
+ ## Deviation Handling
20
+
21
+ If an expected file is missing, a required reading entry fails to load, or the prompt references an artifact that contradicts STATE.md, **stop** before taking any destructive action. Return a structured blocker to STATE.md and terminate your response with your completion marker:
22
+
23
+ ```markdown
24
+ <blocker>
25
+ type: missing-artifact | stale-state | contract-violation
26
+ detail: <one sentence>
27
+ suggested-fix: <one sentence or leave blank>
28
+ </blocker>
29
+
30
+ ## {STAGE} COMPLETE
31
+ ```
32
+
33
+ ## Completion Markers
34
+
35
+ Valid completion markers per agent class (from `agents/README.md` §Completion Markers):
36
+ - Research agent → `## RESEARCH COMPLETE`
37
+ - Planning agent → `## PLANNING COMPLETE`
38
+ - Execution agent → `## EXECUTION COMPLETE`
39
+ - Verification agent → `## VERIFICATION COMPLETE`
40
+ - Stage-specific agents → the stage name: `## SCAN COMPLETE`, `## DISCOVER COMPLETE`, `## PLAN COMPLETE`, `## DESIGN COMPLETE`, `## VERIFY COMPLETE`.
41
+
42
+ The orchestrator detects failure by reading STATE.md for a `<blocker>`, not by the absence of a marker. Always emit the marker.
43
+
44
+ ## Context-Exhaustion & Budget Awareness
45
+
46
+ A PostToolUse hook at `hooks/context-exhaustion.js` watches your tool output for the string `<context-exhaustion>` in your response. If you determine you cannot finish the task in the remaining context, emit:
47
+
48
+ ```xml
49
+ <context-exhaustion>
50
+ reason: <one-sentence cause — e.g., "required_reading totals 47KB exceeding remaining context">
51
+ resume-hint: <one-sentence instruction for a resumption spawn>
52
+ </context-exhaustion>
53
+ ```
54
+
55
+ …before your completion marker. The hook captures this into STATE.md so the orchestrator can re-spawn you with a narrower scope. Do not guess when you're near exhaustion — only emit when a concrete obstacle (file too large to read, required diff too wide) forced the call.
56
+
57
+ A PreToolUse hook at `hooks/budget-enforcer.js` intercepts every `Task` spawn (including the one that invoked you). The hook may:
58
+ - **Short-circuit** your spawn with a cached result from `.design/cache-manifest.json` (transparent — you never run).
59
+ - **Downgrade** your tier to Haiku at the 80% per-task cap soft-threshold, silently (`auto_downgrade_on_cap: true` in `.design/budget.json`, D-03).
60
+ - **Hard-block** your spawn at the 100% per-task or per-phase cap with an actionable error (D-02).
61
+
62
+ Implication for you as the agent: **do not assume a specific model tier is live.** Your output must be correct whether you run on Haiku, Sonnet, or Opus. If a task genuinely requires reasoning density beyond Haiku, the `size_budget` + `default-tier` combination should have been set at authoring time so the router routes it correctly — the remedy is a frontmatter update (a Phase 11 reflector proposal), not a mid-run assumption.
63
+
64
+ ---
65
+
66
+ *Framework-invariant meta-rules. Aggregated by `reference/shared-preamble.md`.*
@@ -0,0 +1,18 @@
1
+ {
2
+ "$schema": "./schemas/protected-paths.schema.json",
3
+ "version": 1,
4
+ "description": "Paths the plugin refuses to Edit/Write or mutate via Bash without an explicit user override. User additions in .design/config.json.protected_paths MERGE into this list — they can extend but cannot reduce the default set.",
5
+ "protected_paths": [
6
+ "reference/**",
7
+ ".design/archive/**",
8
+ ".design/config.json",
9
+ ".design/telemetry/**",
10
+ "skills/**",
11
+ "commands/**",
12
+ "hooks/**",
13
+ ".git/**",
14
+ ".claude/settings.json",
15
+ ".claude-plugin/plugin.json",
16
+ ".claude-plugin/marketplace.json"
17
+ ]
18
+ }
@@ -0,0 +1,34 @@
1
+ {
2
+ "$schema": "./schemas/registry.schema.json",
3
+ "version": 1,
4
+ "generated_at": "2026-04-24T00:00:00.000Z",
5
+ "entries": [
6
+ { "name": "shared-preamble", "path": "reference/shared-preamble.md", "type": "preamble", "tier": "L0", "description": "L0 aggregator — imports meta-rules first and exposes Framework Identity + Ordering Convention + Pre-Warming" },
7
+ { "name": "meta-rules", "path": "reference/meta-rules.md", "type": "meta-rules", "tier": "L0", "description": "Framework-invariant rules: Required Reading Discipline, Writes Protocol, Deviation Handling, Completion Markers, Context-Exhaustion & Budget awareness" },
8
+ { "name": "cycle-handoff-preamble", "path": "reference/cycle-handoff-preamble.md", "type": "preamble", "description": "Framing prose for archived cycle material — read as reference, not as current request" },
9
+ { "name": "retrieval-contract", "path": "reference/retrieval-contract.md", "type": "preamble", "description": "3-layer search → metadata → full-doc protocol with per-row token-cost labels" },
10
+ { "name": "heuristics", "path": "reference/heuristics.md", "type": "heuristic", "tier": "L2", "description": "NNG heuristics + GDD-specific rubrics used by auditor / verifier" },
11
+ { "name": "anti-patterns", "path": "reference/anti-patterns.md", "type": "heuristic", "tier": "L2", "description": "Catalog of design anti-patterns surfaced during audit" },
12
+ { "name": "checklists", "path": "reference/checklists.md", "type": "heuristic", "tier": "L2", "description": "Per-category gate checklists" },
13
+ { "name": "accessibility", "path": "reference/accessibility.md", "type": "heuristic", "description": "WCAG + focus + keyboard-nav heuristics referenced by a11y-mapper" },
14
+ { "name": "debugger-philosophy", "path": "reference/debugger-philosophy.md", "type": "heuristic", "description": "Philosophy guide for systematic-debugging agents" },
15
+ { "name": "parallelism-rules", "path": "reference/parallelism-rules.md", "type": "heuristic", "description": "Rules for serial/parallel agent dispatch and Touches conflict detection" },
16
+ { "name": "priority-matrix", "path": "reference/priority-matrix.md", "type": "heuristic", "description": "Severity × Effort priority-score formula used by gap prioritizers" },
17
+ { "name": "project-skills-guide", "path": "reference/project-skills-guide.md", "type": "heuristic", "description": "Guide for authoring project-skill artifacts (sketch/spike wrap-ups)" },
18
+ { "name": "audit-scoring", "path": "reference/audit-scoring.md", "type": "output-contract", "description": "6-pillar + 7-category audit score rubric and output schema" },
19
+ { "name": "review-format", "path": "reference/review-format.md", "type": "output-contract", "description": "Format of code-review output documents" },
20
+ { "name": "authority-feeds", "path": "reference/authority-feeds.md", "type": "authority-feed", "description": "Whitelist of design-authority feed sources for the watcher" },
21
+ { "name": "motion", "path": "reference/motion.md", "type": "motion", "description": "Motion vocabulary seed (extended in Phase 18)" },
22
+ { "name": "typography", "path": "reference/typography.md", "type": "heuristic", "description": "Typography-system heuristics used by visual-hierarchy-mapper" },
23
+ { "name": "ai-native-tool-interface","path": "reference/ai-native-tool-interface.md","type": "surfaces", "description": "AI-native tool interface contract used across connections" },
24
+ { "name": "intel-schema", "path": "reference/intel-schema.md", "type": "schema", "description": "Schema documentation for .design/intel/ files" },
25
+ { "name": "config-schema", "path": "reference/config-schema.md", "type": "schema", "description": "Schema documentation for .design/config.json" },
26
+ { "name": "DEPRECATIONS", "path": "reference/DEPRECATIONS.md", "type": "schema", "description": "Deprecation records and redirect mappings" },
27
+ { "name": "STATE-TEMPLATE", "path": "reference/STATE-TEMPLATE.md", "type": "schema", "description": "Template for .design/STATE.md scaffolded by /gdd:scan" },
28
+ { "name": "model-prices", "path": "reference/model-prices.md", "type": "data", "description": "Anthropic model pricing table used by the telemetry aggregator" },
29
+ { "name": "model-tiers", "path": "reference/model-tiers.md", "type": "data", "description": "Per-agent default tier map and rationale" },
30
+ { "name": "figma-sandbox", "path": "reference/figma-sandbox.md", "type": "defaults", "description": "Four Figma plugin-sandbox pitfalls encoded as hard rules" },
31
+ { "name": "mcp-budget.default", "path": "reference/mcp-budget.default.json", "type": "defaults", "description": "Default MCP per-task budget (calls + consecutive-timeout thresholds)" },
32
+ { "name": "protected-paths.default", "path": "reference/protected-paths.default.json","type": "defaults", "description": "Default glob list the plugin refuses to Edit/Write/mutate-via-Bash without override" }
33
+ ]
34
+ }
@@ -0,0 +1,52 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "$id": "https://github.com/hegemonart/get-design-done/reference/schemas/registry.schema.json",
4
+ "title": "Reference Registry",
5
+ "description": "Typed index of every reference/*.md (and reference/*.json default) in the plugin. Enables agents to query by type instead of grep-hunting import strings. Round-trip enforced: every reference/*.md must appear in entries[], every entry must resolve to an existing file.",
6
+ "type": "object",
7
+ "required": ["version", "generated_at", "entries"],
8
+ "properties": {
9
+ "$schema": { "type": "string" },
10
+ "version": { "const": 1 },
11
+ "generated_at": { "type": "string", "format": "date-time" },
12
+ "entries": {
13
+ "type": "array",
14
+ "items": {
15
+ "type": "object",
16
+ "required": ["name", "path", "type"],
17
+ "properties": {
18
+ "name": { "type": "string", "minLength": 1, "pattern": "^[a-z0-9][a-z0-9-._]*$" },
19
+ "path": { "type": "string", "minLength": 1 },
20
+ "type": {
21
+ "type": "string",
22
+ "enum": [
23
+ "easing",
24
+ "taxonomy",
25
+ "preamble",
26
+ "output-contract",
27
+ "heuristic",
28
+ "schema",
29
+ "defaults",
30
+ "surfaces",
31
+ "motion",
32
+ "influences",
33
+ "meta-rules",
34
+ "data",
35
+ "authority-feed",
36
+ "principles",
37
+ "emotional-design",
38
+ "experience",
39
+ "palette",
40
+ "style-vocabulary"
41
+ ]
42
+ },
43
+ "tier": { "enum": ["L0", "L1", "L2"] },
44
+ "description": { "type": "string" },
45
+ "registered_at": { "type": "string", "format": "date-time" }
46
+ },
47
+ "additionalProperties": false
48
+ }
49
+ }
50
+ },
51
+ "additionalProperties": false
52
+ }
@@ -0,0 +1,30 @@
1
+ # Retrieval Contract — 3-Layer Search
2
+
3
+ When an agent or skill needs information from `.design/` artifacts or the `reference/` library, apply this protocol in order. Token economy matters — ladder from cheapest to most expensive.
4
+
5
+ ## Layer 1 — Search (~50–100 tokens per hit)
6
+
7
+ Open `reference/registry.json` or the intel index at `.design/intel/`. Read one row per candidate: `{name, path, type, tier, description}`. This is enough to decide whether to descend further.
8
+
9
+ - `list({type})` — everything tagged with a given type (e.g., `"heuristic"`, `"motion"`, `"preamble"`).
10
+ - `find(name)` — direct lookup for a specific reference file by short name.
11
+
12
+ ## Layer 2 — Metadata (~200–300 tokens per hit)
13
+
14
+ Open the candidate file's frontmatter + first 20 lines + heading outline. Decide if the full body is on the critical path.
15
+
16
+ ## Layer 3 — Full document (~500–1000+ tokens)
17
+
18
+ Read the file with the `Read` tool. Only do this when layers 1 and 2 confirmed the doc is load-bearing for the current task.
19
+
20
+ ## Token economy
21
+
22
+ A `/gdd:recall "term"` query that returns 5 Layer-1 hits ≈ 400 tokens. Opening all 5 full docs blind ≈ 4000 tokens. **Always ladder.** Skipping to Layer 3 is the #1 cause of context exhaustion in long pipeline runs.
23
+
24
+ ## FTS5 backend (Phase 19.5)
25
+
26
+ Layer 1 becomes `scripts/lib/design-search.cjs` — same protocol, same output shape, but backed by `.design/search.db` instead of grep. Agents do not need to change anything; the backend swap is transparent.
27
+
28
+ ---
29
+
30
+ *Imported by every skill that reads `.design/` artifacts: `/gdd:progress`, `/gdd:resume`, `/gdd:reflect`, `/gdd:pause`, `/gdd:recall` (Phase 19.5+), `/gdd:timeline` (Phase 19.5+). Tier: preamble. Phase: 14.5.*
@@ -0,0 +1,21 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "$id": "https://github.com/hegemonart/get-design-done/reference/schemas/mcp-budget.schema.json",
4
+ "title": "MCP Budget",
5
+ "description": "Thresholds for the MCP circuit-breaker hook. Applies to use_figma / use_paper / use_pencil and any tracked_tools entry the user extends.",
6
+ "type": "object",
7
+ "required": ["version", "max_calls_per_task", "max_consecutive_timeouts"],
8
+ "properties": {
9
+ "$schema": { "type": "string" },
10
+ "version": { "const": 1 },
11
+ "description": { "type": "string" },
12
+ "max_calls_per_task": { "type": "integer", "minimum": 0, "maximum": 10000 },
13
+ "max_consecutive_timeouts": { "type": "integer", "minimum": 0, "maximum": 1000 },
14
+ "reset_on_success": { "type": "boolean" },
15
+ "tracked_tools": {
16
+ "type": "array",
17
+ "items": { "type": "string", "minLength": 1 }
18
+ }
19
+ },
20
+ "additionalProperties": false
21
+ }
@@ -0,0 +1,19 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "$id": "https://github.com/hegemonart/get-design-done/reference/schemas/protected-paths.schema.json",
4
+ "title": "Protected Paths",
5
+ "description": "Glob list describing paths the plugin refuses to Edit/Write or mutate via destructive Bash. User additions MERGE with this default list; users cannot reduce the default set.",
6
+ "type": "object",
7
+ "required": ["version", "protected_paths"],
8
+ "properties": {
9
+ "$schema": { "type": "string" },
10
+ "version": { "const": 1 },
11
+ "description": { "type": "string" },
12
+ "protected_paths": {
13
+ "type": "array",
14
+ "minItems": 1,
15
+ "items": { "type": "string", "minLength": 1 }
16
+ }
17
+ },
18
+ "additionalProperties": false
19
+ }