@hegemonart/get-design-done 1.0.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.
Files changed (144) hide show
  1. package/.claude-plugin/marketplace.json +63 -0
  2. package/.claude-plugin/plugin.json +54 -0
  3. package/CHANGELOG.md +221 -0
  4. package/LICENSE +21 -0
  5. package/README.md +724 -0
  6. package/SKILL.md +232 -0
  7. package/agents/README.md +226 -0
  8. package/agents/a11y-mapper.md +118 -0
  9. package/agents/component-taxonomy-mapper.md +88 -0
  10. package/agents/design-advisor.md +139 -0
  11. package/agents/design-assumptions-analyzer.md +171 -0
  12. package/agents/design-auditor.md +383 -0
  13. package/agents/design-context-builder.md +544 -0
  14. package/agents/design-context-checker-gate.md +90 -0
  15. package/agents/design-context-checker.md +260 -0
  16. package/agents/design-discussant.md +98 -0
  17. package/agents/design-doc-writer.md +229 -0
  18. package/agents/design-executor.md +452 -0
  19. package/agents/design-figma-writer.md +302 -0
  20. package/agents/design-fixer.md +180 -0
  21. package/agents/design-integration-checker-gate.md +93 -0
  22. package/agents/design-integration-checker.md +326 -0
  23. package/agents/design-pattern-mapper.md +206 -0
  24. package/agents/design-phase-researcher.md +229 -0
  25. package/agents/design-plan-checker.md +164 -0
  26. package/agents/design-planner.md +352 -0
  27. package/agents/design-reflector.md +175 -0
  28. package/agents/design-research-synthesizer.md +127 -0
  29. package/agents/design-verifier-gate.md +97 -0
  30. package/agents/design-verifier.md +605 -0
  31. package/agents/gdd-graphify-sync.md +100 -0
  32. package/agents/gdd-intel-updater.md +88 -0
  33. package/agents/gdd-learnings-extractor.md +85 -0
  34. package/agents/motion-mapper.md +103 -0
  35. package/agents/token-mapper.md +103 -0
  36. package/agents/visual-hierarchy-mapper.md +95 -0
  37. package/connections/chromatic.md +247 -0
  38. package/connections/claude-design.md +190 -0
  39. package/connections/connections.md +218 -0
  40. package/connections/figma-writer.md +139 -0
  41. package/connections/figma.md +146 -0
  42. package/connections/graphify.md +197 -0
  43. package/connections/pinterest.md +153 -0
  44. package/connections/preview.md +173 -0
  45. package/connections/refero.md +189 -0
  46. package/connections/storybook.md +280 -0
  47. package/hooks/budget-enforcer.js +318 -0
  48. package/hooks/context-exhaustion.js +127 -0
  49. package/hooks/gdd-read-injection-scanner.js +44 -0
  50. package/hooks/hooks.json +44 -0
  51. package/package.json +60 -0
  52. package/reference/BRANCH-PROTECTION.md +65 -0
  53. package/reference/DEPRECATIONS.md +41 -0
  54. package/reference/STATE-TEMPLATE.md +200 -0
  55. package/reference/accessibility.md +190 -0
  56. package/reference/anti-patterns.md +336 -0
  57. package/reference/audit-scoring.md +205 -0
  58. package/reference/checklists.md +137 -0
  59. package/reference/config-schema.md +319 -0
  60. package/reference/debugger-philosophy.md +32 -0
  61. package/reference/heuristics.md +201 -0
  62. package/reference/intel-schema.md +266 -0
  63. package/reference/model-prices.md +37 -0
  64. package/reference/model-tiers.md +118 -0
  65. package/reference/motion.md +285 -0
  66. package/reference/parallelism-rules.md +108 -0
  67. package/reference/priority-matrix.md +31 -0
  68. package/reference/project-skills-guide.md +42 -0
  69. package/reference/review-format.md +107 -0
  70. package/reference/schemas/config.schema.json +41 -0
  71. package/reference/schemas/hooks.schema.json +55 -0
  72. package/reference/schemas/intel.schema.json +191 -0
  73. package/reference/schemas/marketplace.schema.json +72 -0
  74. package/reference/schemas/plugin.schema.json +59 -0
  75. package/reference/shared-preamble.md +82 -0
  76. package/reference/typography.md +229 -0
  77. package/scripts/aggregate-agent-metrics.js +144 -0
  78. package/scripts/apply-branch-protection.sh +75 -0
  79. package/scripts/bootstrap-manifest.txt +3 -0
  80. package/scripts/bootstrap.sh +80 -0
  81. package/scripts/build-intel.cjs +458 -0
  82. package/scripts/detect-stale-refs.cjs +101 -0
  83. package/scripts/extract-changelog-section.cjs +57 -0
  84. package/scripts/release-smoke-test.cjs +169 -0
  85. package/scripts/rollback-release.sh +42 -0
  86. package/scripts/run-injection-scanner-ci.cjs +92 -0
  87. package/scripts/validate-frontmatter.cjs +68 -0
  88. package/scripts/validate-schemas.cjs +225 -0
  89. package/scripts/verify-version-sync.cjs +30 -0
  90. package/skills/add-backlog/SKILL.md +47 -0
  91. package/skills/analyze-dependencies/SKILL.md +184 -0
  92. package/skills/apply-reflections/SKILL.md +112 -0
  93. package/skills/audit/SKILL.md +54 -0
  94. package/skills/brief/SKILL.md +75 -0
  95. package/skills/cache-manager/SKILL.md +120 -0
  96. package/skills/compare/SKILL.md +322 -0
  97. package/skills/complete-cycle/SKILL.md +33 -0
  98. package/skills/darkmode/SKILL.md +331 -0
  99. package/skills/debug/SKILL.md +38 -0
  100. package/skills/design/SKILL.md +281 -0
  101. package/skills/discover/SKILL.md +172 -0
  102. package/skills/discuss/SKILL.md +67 -0
  103. package/skills/do/SKILL.md +45 -0
  104. package/skills/explore/SKILL.md +109 -0
  105. package/skills/extract-learnings/SKILL.md +98 -0
  106. package/skills/fast/SKILL.md +44 -0
  107. package/skills/figma-write/SKILL.md +40 -0
  108. package/skills/graphify/SKILL.md +48 -0
  109. package/skills/health/SKILL.md +48 -0
  110. package/skills/help/SKILL.md +76 -0
  111. package/skills/list-assumptions/SKILL.md +60 -0
  112. package/skills/map/SKILL.md +112 -0
  113. package/skills/new-cycle/SKILL.md +35 -0
  114. package/skills/new-project/SKILL.md +53 -0
  115. package/skills/next/SKILL.md +42 -0
  116. package/skills/note/SKILL.md +47 -0
  117. package/skills/optimize/SKILL.md +120 -0
  118. package/skills/pause/SKILL.md +41 -0
  119. package/skills/plan/SKILL.md +251 -0
  120. package/skills/plant-seed/SKILL.md +47 -0
  121. package/skills/pr-branch/SKILL.md +31 -0
  122. package/skills/progress/SKILL.md +60 -0
  123. package/skills/quick/SKILL.md +43 -0
  124. package/skills/reapply-patches/SKILL.md +31 -0
  125. package/skills/reflect/SKILL.md +73 -0
  126. package/skills/resume/SKILL.md +37 -0
  127. package/skills/review-backlog/SKILL.md +45 -0
  128. package/skills/router/SKILL.md +67 -0
  129. package/skills/scan/SKILL.md +721 -0
  130. package/skills/settings/SKILL.md +78 -0
  131. package/skills/ship/SKILL.md +31 -0
  132. package/skills/sketch/SKILL.md +78 -0
  133. package/skills/sketch-wrap-up/SKILL.md +88 -0
  134. package/skills/skill-manifest/SKILL.md +79 -0
  135. package/skills/spike/SKILL.md +67 -0
  136. package/skills/spike-wrap-up/SKILL.md +81 -0
  137. package/skills/stats/SKILL.md +50 -0
  138. package/skills/style/SKILL.md +193 -0
  139. package/skills/synthesize/SKILL.md +93 -0
  140. package/skills/todo/SKILL.md +54 -0
  141. package/skills/undo/SKILL.md +30 -0
  142. package/skills/update/SKILL.md +36 -0
  143. package/skills/verify/SKILL.md +452 -0
  144. package/skills/warm-cache/SKILL.md +113 -0
@@ -0,0 +1,318 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * budget-enforcer.js — PreToolUse hook (matcher: Agent)
4
+ *
5
+ * Intercepts every Agent tool spawn. Consults:
6
+ * (a) router decision (from tool_input.context.router_decision if supplied)
7
+ * (b) .design/cache-manifest.json for short-circuit cached answers (D-05)
8
+ * (c) .design/budget.json for tier_overrides + caps (D-01, D-04, D-10)
9
+ *
10
+ * Enforcement (D-02, D-03, D-11):
11
+ * - enforcement_mode: "enforce" + 100% cap → block with actionable error
12
+ * - enforcement_mode: "enforce" + 80% soft-threshold + auto_downgrade_on_cap → rewrite tier to haiku
13
+ * - enforcement_mode: "warn" → log warning, allow spawn
14
+ * - enforcement_mode: "log" → advisory only
15
+ *
16
+ * Logs every decision to .design/telemetry/costs.jsonl (OPT-09 schema).
17
+ * Every telemetry write fires a detached child aggregator (scripts/aggregate-agent-metrics.js)
18
+ * that rebuilds .design/agent-metrics.json incrementally.
19
+ *
20
+ * Hook type: PreToolUse
21
+ * Input: JSON on stdin { tool_name, tool_input }
22
+ * Output: JSON on stdout { continue, suppressOutput, message, modified_tool_input? }
23
+ */
24
+
25
+ 'use strict';
26
+
27
+ const fs = require('fs');
28
+ const path = require('path');
29
+ const readline = require('readline');
30
+ const { spawn } = require('child_process');
31
+
32
+ const BUDGET_PATH = path.join(process.cwd(), '.design', 'budget.json');
33
+ const MANIFEST_PATH = path.join(process.cwd(), '.design', 'cache-manifest.json');
34
+ const TELEMETRY_PATH = path.join(process.cwd(), '.design', 'telemetry', 'costs.jsonl');
35
+ const STATE_PATH = path.join(process.cwd(), '.design', 'STATE.md');
36
+
37
+ // ---- budget.json loader with defaults per D-12 ----
38
+ function loadBudget() {
39
+ const defaults = {
40
+ per_task_cap_usd: 2.00,
41
+ per_phase_cap_usd: 20.00,
42
+ tier_overrides: {},
43
+ auto_downgrade_on_cap: true,
44
+ cache_ttl_seconds: 3600,
45
+ enforcement_mode: 'enforce'
46
+ };
47
+ if (!fs.existsSync(BUDGET_PATH)) return defaults;
48
+ try { return { ...defaults, ...JSON.parse(fs.readFileSync(BUDGET_PATH, 'utf8')) }; }
49
+ catch { return defaults; }
50
+ }
51
+
52
+ // ---- cumulative phase spend from telemetry (D-02 per_phase_cap_usd check) ----
53
+ function currentPhaseSpend(phase) {
54
+ if (!fs.existsSync(TELEMETRY_PATH)) return 0;
55
+ const lines = fs.readFileSync(TELEMETRY_PATH, 'utf8').split(/\r?\n/).filter(Boolean);
56
+ let sum = 0;
57
+ for (const line of lines) {
58
+ try {
59
+ const row = JSON.parse(line);
60
+ if (row.phase === phase) sum += Number(row.est_cost_usd || 0);
61
+ } catch { /* tolerant */ }
62
+ }
63
+ return sum;
64
+ }
65
+
66
+ // ---- cycle + phase reader (STATE.md frontmatter) ----
67
+ function readCycleAndPhase() {
68
+ const defaults = { cycle: 'unknown', phase: 'unknown' };
69
+ if (!fs.existsSync(STATE_PATH)) return defaults;
70
+ try {
71
+ const content = fs.readFileSync(STATE_PATH, 'utf8');
72
+ // Match the first frontmatter block: between opening '---' and next '---'
73
+ const fm = content.match(/^---\s*\n([\s\S]*?)\n---/);
74
+ const body = fm ? fm[1] : content;
75
+ const cycleMatch = body.match(/^cycle:\s*"?([^"\n]+)"?/m);
76
+ const phaseMatch = body.match(/^phase:\s*"?([^"\n]+)"?/m);
77
+ return {
78
+ cycle: cycleMatch ? cycleMatch[1].trim() : 'unknown',
79
+ phase: phaseMatch ? phaseMatch[1].trim() : 'unknown',
80
+ };
81
+ } catch {
82
+ return defaults;
83
+ }
84
+ }
85
+
86
+ // Deprecated alias for plan 01 callers (and any other hook/script that imports this).
87
+ // Returns only the phase string; prefer readCycleAndPhase() for new code.
88
+ function currentPhase() {
89
+ return readCycleAndPhase().phase;
90
+ }
91
+
92
+ // ---- cache short-circuit (D-05) ----
93
+ function cacheLookup(agent, inputHash) {
94
+ if (!fs.existsSync(MANIFEST_PATH)) return null;
95
+ try {
96
+ const manifest = JSON.parse(fs.readFileSync(MANIFEST_PATH, 'utf8'));
97
+ const entry = manifest.entries?.[`${agent}:${inputHash}`];
98
+ if (!entry) return null;
99
+ const age = Date.now() / 1000 - entry.ts_unix;
100
+ if (age > (manifest.ttl_seconds || 3600)) return null;
101
+ return entry.result; // cached blob
102
+ } catch { return null; }
103
+ }
104
+
105
+ // ---- tier resolution (D-04) ----
106
+ function resolveTier(agent, agentDefaultTier, overrides) {
107
+ return overrides?.[agent] || agentDefaultTier || 'sonnet';
108
+ }
109
+
110
+ // ---- detached aggregator invocation ----
111
+ // Fire-and-forget: do not block the hook. The aggregator reads costs.jsonl tail
112
+ // and rewrites .design/agent-metrics.json atomically.
113
+ function spawnAggregator() {
114
+ try {
115
+ const aggregatorPath = path.join(process.cwd(), 'scripts', 'aggregate-agent-metrics.js');
116
+ if (!fs.existsSync(aggregatorPath)) return; // script not installed — fail open
117
+ const child = spawn('node', [aggregatorPath], {
118
+ cwd: process.cwd(),
119
+ detached: true,
120
+ stdio: 'ignore',
121
+ env: process.env,
122
+ });
123
+ child.unref();
124
+ } catch {
125
+ // Aggregator failures are non-fatal to the hook.
126
+ }
127
+ }
128
+
129
+ // ---- locked-schema row builder (OPT-09) ----
130
+ function buildTelemetryRow(partial) {
131
+ const { cycle, phase } = partial._cyclePhase || readCycleAndPhase();
132
+ // The nine mandatory fields per OPT-09, always in this order.
133
+ const row = {
134
+ ts: partial.ts || new Date().toISOString(),
135
+ agent: String(partial.agent || 'unknown'),
136
+ tier: String(partial.tier || 'unknown'),
137
+ tokens_in: Number(partial.tokens_in || 0),
138
+ tokens_out: Number(partial.tokens_out || 0),
139
+ cache_hit: Boolean(partial.cache_hit),
140
+ est_cost_usd: Number(partial.est_cost_usd || 0),
141
+ cycle: partial.cycle || cycle,
142
+ phase: partial.phase || phase,
143
+ };
144
+ // Optional diagnostic fields (Phase 11 reflector ignores unknown fields gracefully).
145
+ if (partial.tier_downgraded !== undefined) row.tier_downgraded = Boolean(partial.tier_downgraded);
146
+ if (partial.enforcement_mode !== undefined) row.enforcement_mode = String(partial.enforcement_mode);
147
+ if (partial.lazy_skipped !== undefined) row.lazy_skipped = Boolean(partial.lazy_skipped);
148
+ if (partial.block_reason !== undefined) row.block_reason = String(partial.block_reason);
149
+ return row;
150
+ }
151
+
152
+ // ---- telemetry writer: append one JSON row to costs.jsonl ----
153
+ function writeTelemetry(partial) {
154
+ const dir = path.dirname(TELEMETRY_PATH);
155
+ try {
156
+ if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
157
+ const row = buildTelemetryRow(partial);
158
+ fs.appendFileSync(TELEMETRY_PATH, JSON.stringify(row) + '\n', 'utf8');
159
+ // Fire-and-forget aggregator — rebuilds .design/agent-metrics.json incrementally.
160
+ spawnAggregator();
161
+ } catch {
162
+ // Fail open: telemetry must never block the hook.
163
+ }
164
+ }
165
+
166
+ // Backward-compat alias (plan 01 called it appendTelemetry; keep it working).
167
+ const appendTelemetry = writeTelemetry;
168
+
169
+ // ---- main ----
170
+ async function main() {
171
+ const rl = readline.createInterface({ input: process.stdin });
172
+ let inputData = '';
173
+ for await (const line of rl) inputData += line + '\n';
174
+
175
+ let parsed;
176
+ try { parsed = JSON.parse(inputData); } catch { process.exit(0); }
177
+
178
+ if (parsed.tool_name !== 'Agent') process.exit(0); // only guard Agent spawns
179
+
180
+ const toolInput = parsed.tool_input || {};
181
+ const agent = toolInput.subagent_type || toolInput.agent || 'unknown';
182
+ const inputHash = toolInput._input_hash || null; // supplied by orchestrator if cache-manager pre-computed it
183
+
184
+ // Resolve cycle + phase once so every branch can stamp consistent values.
185
+ const { cycle, phase } = readCycleAndPhase();
186
+ const cyclePhase = { cycle, phase };
187
+
188
+ // Branch A: lazy-gate signal from plan 10.1-04 agents (design-verifier-gate, etc.).
189
+ // Gate agents set tool_input.lazy_skipped === true when the heuristic declines
190
+ // to spawn the full checker. We log a zero-cost row and pass through.
191
+ if (toolInput.lazy_skipped === true) {
192
+ writeTelemetry({
193
+ agent,
194
+ tier: 'gate',
195
+ tokens_in: 0,
196
+ tokens_out: 0,
197
+ cache_hit: false,
198
+ est_cost_usd: 0,
199
+ lazy_skipped: true,
200
+ _cyclePhase: cyclePhase,
201
+ });
202
+ const response = { continue: true, suppressOutput: true };
203
+ process.stdout.write(JSON.stringify(response));
204
+ return;
205
+ }
206
+
207
+ const budget = loadBudget();
208
+
209
+ // Branch B: cache short-circuit (D-05)
210
+ if (inputHash) {
211
+ const cached = cacheLookup(agent, inputHash);
212
+ if (cached !== null) {
213
+ writeTelemetry({
214
+ agent,
215
+ tier: 'cache',
216
+ tokens_in: 0,
217
+ tokens_out: 0,
218
+ cache_hit: true,
219
+ est_cost_usd: 0,
220
+ _cyclePhase: cyclePhase,
221
+ });
222
+ const response = {
223
+ continue: false, // block the real spawn; orchestrator reads suppressOutput.message for cached blob
224
+ suppressOutput: false,
225
+ message: `gdd-budget-enforcer: SkippedCached — returning cached result for ${agent}:${inputHash}`,
226
+ cached_result: cached,
227
+ };
228
+ process.stdout.write(JSON.stringify(response));
229
+ return;
230
+ }
231
+ }
232
+
233
+ // Layer B: cap checks (D-02)
234
+ const estCost = Number(toolInput._est_cost_usd || 0);
235
+ const phaseSpend = currentPhaseSpend(phase);
236
+
237
+ if (budget.enforcement_mode === 'enforce') {
238
+ // Branch C: 100% per_task cap (hard block)
239
+ if (estCost >= budget.per_task_cap_usd) {
240
+ writeTelemetry({
241
+ agent,
242
+ tier: toolInput._tier_override || toolInput._default_tier || 'sonnet',
243
+ tokens_in: Number(toolInput._tokens_in_est || 0),
244
+ tokens_out: Number(toolInput._tokens_out_est || 0),
245
+ cache_hit: false,
246
+ est_cost_usd: estCost,
247
+ enforcement_mode: budget.enforcement_mode,
248
+ block_reason: 'per_task_cap',
249
+ _cyclePhase: cyclePhase,
250
+ });
251
+ const response = {
252
+ continue: false,
253
+ suppressOutput: false,
254
+ message: `Budget cap reached for per-task. Estimated: $${estCost.toFixed(4)}, cap: $${budget.per_task_cap_usd.toFixed(2)}. Raise cap in .design/budget.json or retry after next task.`,
255
+ };
256
+ process.stdout.write(JSON.stringify(response));
257
+ return;
258
+ }
259
+ // Branch D: 100% per_phase cap (hard block)
260
+ if (phaseSpend + estCost >= budget.per_phase_cap_usd) {
261
+ writeTelemetry({
262
+ agent,
263
+ tier: toolInput._tier_override || toolInput._default_tier || 'sonnet',
264
+ tokens_in: Number(toolInput._tokens_in_est || 0),
265
+ tokens_out: Number(toolInput._tokens_out_est || 0),
266
+ cache_hit: false,
267
+ est_cost_usd: estCost,
268
+ enforcement_mode: budget.enforcement_mode,
269
+ block_reason: 'per_phase_cap',
270
+ _cyclePhase: cyclePhase,
271
+ });
272
+ const response = {
273
+ continue: false,
274
+ suppressOutput: false,
275
+ message: `Budget cap reached for per-phase (${phase}). Cumulative: $${(phaseSpend + estCost).toFixed(4)}, cap: $${budget.per_phase_cap_usd.toFixed(2)}. Raise cap in .design/budget.json or retry after next phase.`,
276
+ };
277
+ process.stdout.write(JSON.stringify(response));
278
+ return;
279
+ }
280
+ // 80% soft-threshold downgrade (D-03)
281
+ if (budget.auto_downgrade_on_cap && (phaseSpend + estCost) >= (0.80 * budget.per_task_cap_usd)) {
282
+ toolInput._tier_override = 'haiku';
283
+ toolInput._tier_downgraded = true;
284
+ }
285
+ } else if (budget.enforcement_mode === 'warn') {
286
+ if (estCost >= budget.per_task_cap_usd) {
287
+ process.stderr.write(`gdd-budget-enforcer WARN: per-task cap will be exceeded ($${estCost.toFixed(4)} >= $${budget.per_task_cap_usd})\n`);
288
+ }
289
+ }
290
+ // enforcement_mode === 'log': no blocking, just telemetry
291
+
292
+ // D-04: tier_overrides rewrite
293
+ if (budget.tier_overrides[agent]) {
294
+ toolInput._tier_override = budget.tier_overrides[agent];
295
+ }
296
+
297
+ // Branch E: standard spawn-allowed (includes tier-downgraded path D-03)
298
+ writeTelemetry({
299
+ agent,
300
+ tier: toolInput._tier_override || toolInput._default_tier || 'sonnet',
301
+ tokens_in: Number(toolInput._tokens_in_est || 0),
302
+ tokens_out: Number(toolInput._tokens_out_est || 0),
303
+ cache_hit: false,
304
+ est_cost_usd: estCost,
305
+ tier_downgraded: !!toolInput._tier_downgraded,
306
+ enforcement_mode: budget.enforcement_mode,
307
+ _cyclePhase: cyclePhase,
308
+ });
309
+
310
+ const response = {
311
+ continue: true,
312
+ suppressOutput: true,
313
+ modified_tool_input: toolInput
314
+ };
315
+ process.stdout.write(JSON.stringify(response));
316
+ }
317
+
318
+ main().catch(err => { console.error('budget-enforcer hook error:', err); process.exit(0); });
@@ -0,0 +1,127 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * context-exhaustion.js — PostToolUse hook
4
+ *
5
+ * Monitors context usage reported in the tool_response. When usage exceeds
6
+ * THRESHOLD (default 85%), writes a <paused> resumption block to
7
+ * .design/STATE.md so the next session can resume without losing context.
8
+ *
9
+ * Hook type: PostToolUse (any tool)
10
+ * Input: JSON on stdin { tool_name, tool_input, tool_response }
11
+ * Output: JSON on stdout { continue, suppressOutput, message } or nothing
12
+ */
13
+
14
+ 'use strict';
15
+
16
+ const fs = require('fs');
17
+ const path = require('path');
18
+ const readline = require('readline');
19
+
20
+ const THRESHOLD = parseFloat(process.env.GDD_CONTEXT_THRESHOLD || '0.85');
21
+ const STATE_PATH = path.join(process.cwd(), '.design', 'STATE.md');
22
+
23
+ function now() {
24
+ return new Date().toISOString();
25
+ }
26
+
27
+ function extractContextUsage(toolResponse) {
28
+ // Claude Code injects context usage as a field in the tool response envelope.
29
+ // Try common field names used across Claude Code versions.
30
+ if (typeof toolResponse !== 'object' || toolResponse === null) return null;
31
+
32
+ // Direct field
33
+ if (typeof toolResponse.context_usage === 'number') return toolResponse.context_usage;
34
+ if (typeof toolResponse.contextUsage === 'number') return toolResponse.contextUsage;
35
+
36
+ // Nested under metadata
37
+ const meta = toolResponse.metadata || toolResponse.meta || {};
38
+ if (typeof meta.context_usage === 'number') return meta.context_usage;
39
+ if (typeof meta.contextUsage === 'number') return meta.contextUsage;
40
+
41
+ // Fraction string "0.87" or percentage "87%"
42
+ const raw = toolResponse.context_usage || toolResponse.contextUsage
43
+ || meta.context_usage || meta.contextUsage;
44
+ if (typeof raw === 'string') {
45
+ if (raw.endsWith('%')) return parseFloat(raw) / 100;
46
+ const n = parseFloat(raw);
47
+ if (!isNaN(n)) return n > 1 ? n / 100 : n;
48
+ }
49
+
50
+ return null;
51
+ }
52
+
53
+ function buildPausedBlock(toolName, usage) {
54
+ const pct = Math.round(usage * 100);
55
+ return `
56
+ <paused>
57
+ recorded: ${now()}
58
+ trigger: context-exhaustion-hook
59
+ context_usage: ${pct}%
60
+ last_tool: ${toolName}
61
+
62
+ ## Resumption instructions
63
+
64
+ Context reached ${pct}% during the previous session (threshold: ${Math.round(THRESHOLD * 100)}%).
65
+ The session was auto-paused to preserve quality.
66
+
67
+ To resume:
68
+ 1. Run \`/gdd:resume\` — it will read this block and restore working context
69
+ 2. If mid-plan: check .design/STATE.md for the last completed task
70
+ 3. Re-read the active PLAN.md to orient before continuing
71
+
72
+ Intel store status at pause time:
73
+ ls .design/intel/files.json 2>/dev/null && echo "present" || echo "missing"
74
+ </paused>
75
+ `;
76
+ }
77
+
78
+ function stateFileHasPausedBlock() {
79
+ if (!fs.existsSync(STATE_PATH)) return false;
80
+ const content = fs.readFileSync(STATE_PATH, 'utf8');
81
+ return content.includes('<paused>') && content.includes('context-exhaustion-hook');
82
+ }
83
+
84
+ function appendPausedBlock(block) {
85
+ if (!fs.existsSync(path.dirname(STATE_PATH))) {
86
+ fs.mkdirSync(path.dirname(STATE_PATH), { recursive: true });
87
+ }
88
+ if (!fs.existsSync(STATE_PATH)) {
89
+ fs.writeFileSync(STATE_PATH, '# Design State\n\n', 'utf8');
90
+ }
91
+ fs.appendFileSync(STATE_PATH, block, 'utf8');
92
+ }
93
+
94
+ async function main() {
95
+ const rl = readline.createInterface({ input: process.stdin });
96
+ let inputData = '';
97
+ for await (const line of rl) inputData += line + '\n';
98
+
99
+ let parsed;
100
+ try { parsed = JSON.parse(inputData); } catch { process.exit(0); }
101
+
102
+ const toolName = parsed?.tool_name || 'unknown';
103
+ const toolResponse = parsed?.tool_response || {};
104
+
105
+ const usage = extractContextUsage(toolResponse);
106
+
107
+ // No usage data — cannot act
108
+ if (usage === null) process.exit(0);
109
+
110
+ // Below threshold — do nothing
111
+ if (usage < THRESHOLD) process.exit(0);
112
+
113
+ // At or above threshold — write paused block (only once per session)
114
+ if (stateFileHasPausedBlock()) process.exit(0);
115
+
116
+ const block = buildPausedBlock(toolName, usage);
117
+ appendPausedBlock(block);
118
+
119
+ const response = {
120
+ continue: true,
121
+ suppressOutput: false,
122
+ message: `gdd-context-exhaustion: Context at ${Math.round(usage * 100)}% — auto-recorded <paused> block in .design/STATE.md. Run /gdd:resume in the next session to continue.`,
123
+ };
124
+ process.stdout.write(JSON.stringify(response));
125
+ }
126
+
127
+ main().catch(err => { console.error('context-exhaustion hook error:', err); process.exit(0); });
@@ -0,0 +1,44 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * gdd-read-injection-scanner — PostToolUse hook
4
+ * Scans Read tool output for common prompt-injection patterns and warns
5
+ * (does not block) when suspicious content is found in a read file.
6
+ */
7
+
8
+ const readline = require('readline');
9
+
10
+ const INJECTION_PATTERNS = [
11
+ /ignore\s+(all\s+)?(previous|prior|above)\s+instructions?/i,
12
+ /disregard\s+(all\s+)?(previous|prior|above)\s+instructions?/i,
13
+ /you\s+are\s+now\s+a\s+different/i,
14
+ /system\s*:\s*you\s+are/i,
15
+ /<\s*\/?\s*(system|assistant|human)\s*>/i,
16
+ /\[INST\]/i,
17
+ /###\s*instruction/i,
18
+ ];
19
+
20
+ async function main() {
21
+ const rl = readline.createInterface({ input: process.stdin });
22
+ let inputData = '';
23
+ for await (const line of rl) inputData += line + '\n';
24
+
25
+ let parsed;
26
+ try { parsed = JSON.parse(inputData); } catch { process.exit(0); }
27
+
28
+ if (parsed?.tool_name !== 'Read') process.exit(0);
29
+
30
+ const content = parsed?.tool_response?.content || '';
31
+ const matched = INJECTION_PATTERNS.some(p => p.test(content));
32
+ if (!matched) process.exit(0);
33
+
34
+ const file = parsed?.tool_input?.file_path || 'unknown';
35
+ const response = {
36
+ continue: true,
37
+ suppressOutput: false,
38
+ message: `gdd-injection-scanner: Suspicious prompt-injection pattern detected in content read from "${file}". Review before acting on instructions contained in that file.`,
39
+ };
40
+ process.stdout.write(JSON.stringify(response));
41
+ process.exit(0);
42
+ }
43
+
44
+ main().catch(() => process.exit(0));
@@ -0,0 +1,44 @@
1
+ {
2
+ "hooks": {
3
+ "SessionStart": [
4
+ {
5
+ "hooks": [
6
+ {
7
+ "type": "command",
8
+ "command": "bash \"${CLAUDE_PLUGIN_ROOT}/scripts/bootstrap.sh\""
9
+ }
10
+ ]
11
+ }
12
+ ],
13
+ "PreToolUse": [
14
+ {
15
+ "matcher": "Agent",
16
+ "hooks": [
17
+ {
18
+ "type": "command",
19
+ "command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/budget-enforcer.js\""
20
+ }
21
+ ]
22
+ }
23
+ ],
24
+ "PostToolUse": [
25
+ {
26
+ "matcher": "Read",
27
+ "hooks": [
28
+ {
29
+ "type": "command",
30
+ "command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/gdd-read-injection-scanner.js\""
31
+ }
32
+ ]
33
+ },
34
+ {
35
+ "hooks": [
36
+ {
37
+ "type": "command",
38
+ "command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/context-exhaustion.js\""
39
+ }
40
+ ]
41
+ }
42
+ ]
43
+ }
44
+ }
package/package.json ADDED
@@ -0,0 +1,60 @@
1
+ {
2
+ "name": "@hegemonart/get-design-done",
3
+ "version": "1.0.7",
4
+ "description": "A Claude Code plugin for systematic design improvement",
5
+ "author": "Hegemon",
6
+ "homepage": "https://github.com/hegemonart/get-design-done",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "https://github.com/hegemonart/get-design-done.git"
10
+ },
11
+ "license": "MIT",
12
+ "engines": {
13
+ "node": ">=22"
14
+ },
15
+ "files": [
16
+ ".claude-plugin/",
17
+ "agents/",
18
+ "skills/",
19
+ "hooks/",
20
+ "scripts/",
21
+ "connections/",
22
+ "reference/",
23
+ "SKILL.md",
24
+ "README.md",
25
+ "CHANGELOG.md",
26
+ "LICENSE"
27
+ ],
28
+ "publishConfig": {
29
+ "access": "public",
30
+ "provenance": true
31
+ },
32
+ "scripts": {
33
+ "test": "node --test \"tests/**/*.cjs\"",
34
+ "lint:md": "npx --yes markdownlint-cli2 \"**/*.md\" \"#node_modules\" \"#.planning\" \"#.claude\" \"#test-fixture/baselines\"",
35
+ "lint:links": "npx --yes lychee --no-progress --accept 200,206,403,429 \"**/*.md\" || true",
36
+ "validate:schemas": "node scripts/validate-schemas.cjs",
37
+ "validate:frontmatter": "node scripts/validate-frontmatter.cjs agents/",
38
+ "detect:stale-refs": "node scripts/detect-stale-refs.cjs",
39
+ "scan:injection": "node scripts/run-injection-scanner-ci.cjs",
40
+ "test:size-budget": "node --test tests/agent-size-budget.test.cjs",
41
+ "release:extract-changelog": "node scripts/extract-changelog-section.cjs",
42
+ "verify:version-sync": "node scripts/verify-version-sync.cjs"
43
+ },
44
+ "keywords": [
45
+ "claude",
46
+ "claude-code",
47
+ "claude-code-plugin",
48
+ "plugin",
49
+ "design",
50
+ "design-system",
51
+ "self-improvement",
52
+ "reflection",
53
+ "tested",
54
+ "ci"
55
+ ],
56
+ "skills": [
57
+ "SKILL.md"
58
+ ],
59
+ "hooks": "hooks/hooks.json"
60
+ }
@@ -0,0 +1,65 @@
1
+ # Branch Protection — Two-Phase Rollout
2
+
3
+ Per D-16 / D-17: branch protection on `main` is rolled out in two phases. The
4
+ repo admin applies each phase manually via `scripts/apply-branch-protection.sh`
5
+ (no CI automation — avoids leaking repo admin credentials).
6
+
7
+ ## Phase A — Advisory (initial state)
8
+
9
+ Status checks **run** on every push, but they are **not required to merge**.
10
+ This is the default posture while CI stabilizes and baselines are established.
11
+
12
+ Apply:
13
+
14
+ ```bash
15
+ bash scripts/apply-branch-protection.sh --advisory
16
+ ```
17
+
18
+ Effects:
19
+
20
+ - Required status checks: **none** (checks run but don't block)
21
+ - `main` accepts direct pushes (the solo maintainer's existing workflow)
22
+ - Force-push: allowed by admins
23
+
24
+ ## Phase B — Enforcing (after first clean release)
25
+
26
+ After the first full release cycle ships clean (plan 13-06 / 13-07 smoke test
27
+ passes on a real tag + GitHub Release), tighten to enforcing.
28
+
29
+ Apply:
30
+
31
+ ```bash
32
+ bash scripts/apply-branch-protection.sh --enforcing
33
+ ```
34
+
35
+ Effects:
36
+
37
+ - Required status checks (all must pass to merge):
38
+ - `Lint (markdown + frontmatter + stale-refs)`
39
+ - `Validate (schemas + plugin + shellcheck)`
40
+ - `Test (Node 22 / ubuntu-latest)`
41
+ - `Test (Node 22 / macos-latest)`
42
+ - `Test (Node 22 / windows-latest)`
43
+ - `Test (Node 24 / ubuntu-latest)`
44
+ - `Test (Node 24 / macos-latest)`
45
+ - `Test (Node 24 / windows-latest)`
46
+ - `Security (secrets + injection scan)`
47
+ - `Size budget (blocking)`
48
+ - Require linear history (no merge commits)
49
+ - Disallow force-push
50
+ - Admins still bypass for emergency fixes (logged)
51
+
52
+ ## When to promote Phase A → Phase B
53
+
54
+ - [ ] Wave A + B of Phase 13 merged to main
55
+ - [ ] `release.yml` (plan 13-06) merged and triggered at least once successfully
56
+ - [ ] Release smoke test (plan 13-07) passed on a real tag
57
+ - [ ] Baseline lock (plan 13-08) committed
58
+
59
+ When all four are true, run `apply-branch-protection.sh --enforcing`.
60
+
61
+ ## Rollback
62
+
63
+ To revert either phase: `apply-branch-protection.sh --disable` removes all
64
+ protection rules. Use only if a protection misconfiguration is blocking a
65
+ legitimate merge.