@smartmemory/compose 0.1.0

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 (181) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +1014 -0
  3. package/bin/compose.js +1515 -0
  4. package/dist/assets/_baseUniq-CQwX6VLz.js +1 -0
  5. package/dist/assets/arc-SxJ2J1sh.js +1 -0
  6. package/dist/assets/architectureDiagram-Q4EWVU46-BykunY1F.js +36 -0
  7. package/dist/assets/blockDiagram-DXYQGD6D-ohAKBOUw.js +132 -0
  8. package/dist/assets/c4Diagram-AHTNJAMY-DBDC3ENB.js +10 -0
  9. package/dist/assets/channel-DGElom1e.js +1 -0
  10. package/dist/assets/chunk-4BX2VUAB-Cv93Z7uM.js +1 -0
  11. package/dist/assets/chunk-4TB4RGXK-DE0WBDkj.js +206 -0
  12. package/dist/assets/chunk-55IACEB6-CE1EXenG.js +1 -0
  13. package/dist/assets/chunk-EDXVE4YY-DA7Ana6H.js +1 -0
  14. package/dist/assets/chunk-FMBD7UC4-CTDIPA3p.js +15 -0
  15. package/dist/assets/chunk-OYMX7WX6-uGBaPaTX.js +231 -0
  16. package/dist/assets/chunk-QZHKN3VN-CYlnXuUO.js +1 -0
  17. package/dist/assets/chunk-YZCP3GAM-ojGkzcZK.js +1 -0
  18. package/dist/assets/classDiagram-6PBFFD2Q-KqWP9wWZ.js +1 -0
  19. package/dist/assets/classDiagram-v2-HSJHXN6E-KqWP9wWZ.js +1 -0
  20. package/dist/assets/clone-DUJKJXd7.js +1 -0
  21. package/dist/assets/cose-bilkent-S5V4N54A-Bktn9hL-.js +1 -0
  22. package/dist/assets/dagre-KV5264BT-DFaSzuRF.js +4 -0
  23. package/dist/assets/defaultLocale-DX6XiGOO.js +1 -0
  24. package/dist/assets/diagram-5BDNPKRD-DnfmDzEm.js +10 -0
  25. package/dist/assets/diagram-G4DWMVQ6-Bm8W9YnG.js +24 -0
  26. package/dist/assets/diagram-MMDJMWI5-B5-TSKvp.js +43 -0
  27. package/dist/assets/diagram-TYMM5635-ls4rqlky.js +24 -0
  28. package/dist/assets/erDiagram-SMLLAGMA-giG6WO-r.js +85 -0
  29. package/dist/assets/flowDiagram-DWJPFMVM-XvlUuz-7.js +162 -0
  30. package/dist/assets/ganttDiagram-T4ZO3ILL-hLBV57oV.js +292 -0
  31. package/dist/assets/gitGraphDiagram-UUTBAWPF-BHu3s_Gn.js +106 -0
  32. package/dist/assets/graph-D0Cfv00Y.js +1 -0
  33. package/dist/assets/index-CUd6pFGF.css +1 -0
  34. package/dist/assets/index-DReRlzZI.js +1144 -0
  35. package/dist/assets/infoDiagram-42DDH7IO-DbqRsOo3.js +2 -0
  36. package/dist/assets/init-Gi6I4Gst.js +1 -0
  37. package/dist/assets/ishikawaDiagram-UXIWVN3A-DnCdx7zb.js +70 -0
  38. package/dist/assets/journeyDiagram-VCZTEJTY-CfD7eNcP.js +139 -0
  39. package/dist/assets/kanban-definition-6JOO6SKY-BYaO9-mK.js +89 -0
  40. package/dist/assets/katex-DkKDou_j.js +257 -0
  41. package/dist/assets/layout-Bj72wOEB.js +1 -0
  42. package/dist/assets/linear-BRFo114D.js +1 -0
  43. package/dist/assets/min-GCHnKlJS.js +1 -0
  44. package/dist/assets/mindmap-definition-QFDTVHPH-n0PMebY4.js +96 -0
  45. package/dist/assets/ordinal-Cboi1Yqb.js +1 -0
  46. package/dist/assets/pieDiagram-DEJITSTG-pN4CljHF.js +30 -0
  47. package/dist/assets/quadrantDiagram-34T5L4WZ-DNoAy8-D.js +7 -0
  48. package/dist/assets/requirementDiagram-MS252O5E-BhtY05PT.js +84 -0
  49. package/dist/assets/sankeyDiagram-XADWPNL6-B6AD-16A.js +10 -0
  50. package/dist/assets/sequenceDiagram-FGHM5R23-DShHM-uk.js +157 -0
  51. package/dist/assets/stateDiagram-FHFEXIEX-DMxn7HTo.js +1 -0
  52. package/dist/assets/stateDiagram-v2-QKLJ7IA2-o6PnCs4e.js +1 -0
  53. package/dist/assets/timeline-definition-GMOUNBTQ-Cdu6uq52.js +120 -0
  54. package/dist/assets/vennDiagram-DHZGUBPP-CpK29iRe.js +34 -0
  55. package/dist/assets/wardley-RL74JXVD-BQgSkdcO.js +162 -0
  56. package/dist/assets/wardleyDiagram-NUSXRM2D-DJHYev6O.js +20 -0
  57. package/dist/assets/xychartDiagram-5P7HB3ND-1d75pbaO.js +7 -0
  58. package/dist/index.html +30 -0
  59. package/lib/agent-chains.js +65 -0
  60. package/lib/agent-string.js +86 -0
  61. package/lib/budget-ledger.js +86 -0
  62. package/lib/build-all.js +162 -0
  63. package/lib/build-dag.js +120 -0
  64. package/lib/build-stream-writer.js +190 -0
  65. package/lib/build.js +2997 -0
  66. package/lib/capability-checker.js +53 -0
  67. package/lib/cert-inject.js +38 -0
  68. package/lib/cli-progress.js +483 -0
  69. package/lib/constants.js +69 -0
  70. package/lib/cross-layer-audit.js +84 -0
  71. package/lib/debug-discipline.js +173 -0
  72. package/lib/feature-json.js +106 -0
  73. package/lib/gate-prompt.js +291 -0
  74. package/lib/gate-tiers.js +194 -0
  75. package/lib/health-history.js +119 -0
  76. package/lib/health-score.js +227 -0
  77. package/lib/ideabox.js +570 -0
  78. package/lib/import.js +244 -0
  79. package/lib/migrate-roadmap.js +94 -0
  80. package/lib/model-pricing.js +67 -0
  81. package/lib/new.js +413 -0
  82. package/lib/pipeline-cli.js +489 -0
  83. package/lib/plan-parser.js +103 -0
  84. package/lib/qa-scoping.js +474 -0
  85. package/lib/questionnaire.js +200 -0
  86. package/lib/resolve-port.js +7 -0
  87. package/lib/result-normalizer.js +349 -0
  88. package/lib/review-lenses.js +166 -0
  89. package/lib/roadmap-gen.js +210 -0
  90. package/lib/roadmap-parser.js +176 -0
  91. package/lib/server-probe.js +23 -0
  92. package/lib/staleness.js +87 -0
  93. package/lib/step-prompt.js +260 -0
  94. package/lib/step-validator.js +49 -0
  95. package/lib/stratum-mcp-client.js +365 -0
  96. package/lib/team-flag.js +46 -0
  97. package/lib/test-bootstrap.js +401 -0
  98. package/lib/triage.js +274 -0
  99. package/lib/vision-writer.js +391 -0
  100. package/package.json +111 -0
  101. package/pipelines/bug-fix.stratum.yaml +230 -0
  102. package/pipelines/build.stratum.yaml +498 -0
  103. package/pipelines/content.stratum.yaml +112 -0
  104. package/pipelines/coverage-sweep.stratum.yaml +52 -0
  105. package/pipelines/refactor.stratum.yaml +169 -0
  106. package/pipelines/research.stratum.yaml +88 -0
  107. package/pipelines/review-fix.stratum.yaml +109 -0
  108. package/presets/team-feature.stratum.yaml +105 -0
  109. package/presets/team-research.stratum.yaml +108 -0
  110. package/presets/team-review.stratum.yaml +106 -0
  111. package/scripts/agent-activity-hook.sh +31 -0
  112. package/scripts/agent-error-hook.sh +28 -0
  113. package/scripts/analyze-orphans.mjs +50 -0
  114. package/scripts/find-orphans.mjs +26 -0
  115. package/scripts/fix-phases.mjs +49 -0
  116. package/scripts/generate-stratum-spec.mjs +137 -0
  117. package/scripts/import-roadmap.mjs +116 -0
  118. package/scripts/phase-audit.mjs +33 -0
  119. package/scripts/run-pipeline.mjs +314 -0
  120. package/scripts/session-end-hook.sh +18 -0
  121. package/scripts/session-start-hook.sh +38 -0
  122. package/scripts/vision-hook.sh +104 -0
  123. package/scripts/vision-track.mjs +554 -0
  124. package/scripts/wire-all-orphans.mjs +108 -0
  125. package/scripts/wire-orphans.mjs +164 -0
  126. package/server/activity-routes.js +123 -0
  127. package/server/agent-health.js +197 -0
  128. package/server/agent-hooks.js +102 -0
  129. package/server/agent-mcp.js +10 -0
  130. package/server/agent-registry.js +95 -0
  131. package/server/agent-server.js +290 -0
  132. package/server/agent-spawn.js +251 -0
  133. package/server/agent-templates.js +77 -0
  134. package/server/artifact-manager.js +247 -0
  135. package/server/artifact-templates/architecture.md +28 -0
  136. package/server/artifact-templates/blueprint.md +21 -0
  137. package/server/artifact-templates/design.md +36 -0
  138. package/server/artifact-templates/plan.md +25 -0
  139. package/server/artifact-templates/prd.md +43 -0
  140. package/server/artifact-templates/report.md +40 -0
  141. package/server/block-tracker.js +90 -0
  142. package/server/build-stream-bridge.js +502 -0
  143. package/server/coalescing-buffer.js +46 -0
  144. package/server/compose-mcp-tools.js +479 -0
  145. package/server/compose-mcp.js +324 -0
  146. package/server/connectors/agent-connector.js +78 -0
  147. package/server/connectors/claude-sdk-connector.js +198 -0
  148. package/server/connectors/codex-connector.js +240 -0
  149. package/server/connectors/connector-discovery.js +18 -0
  150. package/server/connectors/connector-runtime.js +13 -0
  151. package/server/connectors/opencode-connector.js +200 -0
  152. package/server/design-routes.js +540 -0
  153. package/server/design-session.js +161 -0
  154. package/server/feature-scan.js +593 -0
  155. package/server/file-watcher.js +284 -0
  156. package/server/find-root.js +29 -0
  157. package/server/graph-export.js +343 -0
  158. package/server/ideabox-cache.js +77 -0
  159. package/server/ideabox-routes.js +294 -0
  160. package/server/index.js +156 -0
  161. package/server/model-tiers.js +49 -0
  162. package/server/pipeline-routes.js +288 -0
  163. package/server/policy-evaluator.js +36 -0
  164. package/server/project-root.js +122 -0
  165. package/server/security.js +23 -0
  166. package/server/session-manager.js +403 -0
  167. package/server/session-routes.js +190 -0
  168. package/server/session-store.js +107 -0
  169. package/server/settings-routes.js +35 -0
  170. package/server/settings-store.js +234 -0
  171. package/server/stratum-api.js +102 -0
  172. package/server/stratum-client.js +192 -0
  173. package/server/stratum-sync.js +193 -0
  174. package/server/summarizer.js +139 -0
  175. package/server/supervisor.js +196 -0
  176. package/server/vision-routes.js +668 -0
  177. package/server/vision-server.js +393 -0
  178. package/server/vision-store.js +360 -0
  179. package/server/vision-utils.js +179 -0
  180. package/server/worktree-gc.js +137 -0
  181. package/templates/ROADMAP.md +46 -0
@@ -0,0 +1,314 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * run-pipeline.mjs — drive the review-fix pipeline end-to-end.
4
+ *
5
+ * Usage:
6
+ * node scripts/run-pipeline.mjs [--task "..."] [--blueprint "..."] [--dry-run] [--max-retries N]
7
+ *
8
+ * Defaults to a trivial clamp() utility task if no --task is provided.
9
+ * Writes an audit trace to stdout and to .compose/pipeline-audit.json.
10
+ *
11
+ * This script exercises the 18h acceptance gate:
12
+ * 1. agent_run(type=claude) — execute the task
13
+ * 2. agent_run(type=codex, schema) — review against blueprint
14
+ * 3. If not clean, claude fixes + codex re-reviews (up to --max-retries)
15
+ * 4. Audit trace logged
16
+ */
17
+
18
+ import { resolve, dirname } from 'node:path';
19
+ import { fileURLToPath } from 'node:url';
20
+ import { writeFileSync, mkdirSync } from 'node:fs';
21
+ import { parseArgs } from 'node:util';
22
+
23
+ const __dirname = dirname(fileURLToPath(import.meta.url));
24
+ const REPO_ROOT = resolve(__dirname, '..');
25
+
26
+ // ---------------------------------------------------------------------------
27
+ // CLI args
28
+ // ---------------------------------------------------------------------------
29
+
30
+ const { values: flags } = parseArgs({
31
+ options: {
32
+ task: { type: 'string', default: '' },
33
+ blueprint: { type: 'string', default: '' },
34
+ 'dry-run': { type: 'boolean', default: false },
35
+ 'max-retries':{ type: 'string', default: '10' },
36
+ cwd: { type: 'string', default: REPO_ROOT },
37
+ help: { type: 'boolean', default: false },
38
+ },
39
+ strict: false,
40
+ });
41
+
42
+ if (flags.help) {
43
+ console.log(`
44
+ Usage: node scripts/run-pipeline.mjs [options]
45
+
46
+ Options:
47
+ --task "..." Task description (default: add clamp utility)
48
+ --blueprint "..." Blueprint to review against (default: generated)
49
+ --dry-run Print what would run without calling agents
50
+ --max-retries N Max review-fix iterations (default: 10)
51
+ --cwd PATH Working directory for agents (default: repo root)
52
+ --help Show this help
53
+ `);
54
+ process.exit(0);
55
+ }
56
+
57
+ const MAX_RETRIES = parseInt(flags['max-retries'], 10) || 10;
58
+ const DRY_RUN = flags['dry-run'];
59
+ const CWD = flags.cwd;
60
+
61
+ const DEFAULT_TASK = `
62
+ Add a clamp(value, min, max) utility function to src/lib/math-utils.js (create the file if it doesn't exist).
63
+
64
+ Requirements:
65
+ - Export a named function: export function clamp(value, min, max)
66
+ - If value < min, return min
67
+ - If value > max, return max
68
+ - Otherwise return value
69
+ - Handle edge case: if min > max, swap them
70
+ - No dependencies
71
+ `.trim();
72
+
73
+ const DEFAULT_BLUEPRINT = `
74
+ ## clamp() utility — acceptance criteria
75
+
76
+ - [ ] File exists at src/lib/math-utils.js
77
+ - [ ] Named export: clamp(value, min, max)
78
+ - [ ] Returns min when value < min
79
+ - [ ] Returns max when value > max
80
+ - [ ] Returns value when min <= value <= max
81
+ - [ ] Swaps min/max if min > max
82
+ - [ ] No external dependencies
83
+ - [ ] No side effects
84
+ `.trim();
85
+
86
+ const task = flags.task || DEFAULT_TASK;
87
+ const blueprint = flags.blueprint || DEFAULT_BLUEPRINT;
88
+
89
+ // ---------------------------------------------------------------------------
90
+ // Connector imports
91
+ // ---------------------------------------------------------------------------
92
+
93
+ const { ClaudeSDKConnector } = await import(
94
+ resolve(REPO_ROOT, 'server/connectors/claude-sdk-connector.js')
95
+ );
96
+ const { CodexConnector } = await import(
97
+ resolve(REPO_ROOT, 'server/connectors/codex-connector.js')
98
+ );
99
+ const { injectSchema } = await import(
100
+ resolve(REPO_ROOT, 'server/connectors/agent-connector.js')
101
+ );
102
+
103
+ // ---------------------------------------------------------------------------
104
+ // Agent helpers
105
+ // ---------------------------------------------------------------------------
106
+
107
+ const REVIEW_SCHEMA = {
108
+ type: 'object',
109
+ required: ['clean', 'summary', 'findings'],
110
+ properties: {
111
+ clean: { type: 'boolean' },
112
+ summary: { type: 'string' },
113
+ findings: { type: 'array', items: { type: 'string' } },
114
+ },
115
+ };
116
+
117
+ async function runAgent(type, prompt, { schema } = {}) {
118
+ const connector = type === 'codex'
119
+ ? new CodexConnector({ cwd: CWD })
120
+ : new ClaudeSDKConnector({ cwd: CWD });
121
+
122
+ const parts = [];
123
+ for await (const event of connector.run(prompt, { schema, cwd: CWD })) {
124
+ if (event.type === 'assistant' && event.content) {
125
+ parts.push(event.content);
126
+ } else if (event.type === 'error') {
127
+ throw new Error(`${type}: ${event.message}`);
128
+ }
129
+ }
130
+
131
+ const text = parts.join('');
132
+
133
+ if (schema) {
134
+ // Try full text parse, then code block extraction (matches agent-mcp.js)
135
+ try {
136
+ return { text, result: JSON.parse(text) };
137
+ } catch {
138
+ const match = text.match(/```json\s*\n([\s\S]*?)\n\s*```/g);
139
+ if (match) {
140
+ const lastBlock = match[match.length - 1]
141
+ .replace(/^```json\s*\n/, '')
142
+ .replace(/\n\s*```$/, '');
143
+ try {
144
+ return { text, result: JSON.parse(lastBlock) };
145
+ } catch { /* fall through */ }
146
+ }
147
+ return { text, result: null };
148
+ }
149
+ }
150
+
151
+ return { text };
152
+ }
153
+
154
+ // ---------------------------------------------------------------------------
155
+ // Pipeline execution
156
+ // ---------------------------------------------------------------------------
157
+
158
+ const audit = {
159
+ pipeline: 'review-fix',
160
+ startedAt: new Date().toISOString(),
161
+ task: task.slice(0, 200),
162
+ steps: [],
163
+ result: null,
164
+ completedAt: null,
165
+ };
166
+
167
+ function log(msg) {
168
+ const ts = new Date().toISOString().slice(11, 19);
169
+ console.log(`[${ts}] ${msg}`);
170
+ }
171
+
172
+ async function run() {
173
+ log('Pipeline: review-fix');
174
+ log(`Task: ${task.slice(0, 80)}...`);
175
+ log(`Max retries: ${MAX_RETRIES}`);
176
+ log('');
177
+
178
+ if (DRY_RUN) {
179
+ log('[dry-run] Step 1: agent_run(type=claude) — execute task');
180
+ log('[dry-run] Step 2: agent_run(type=codex, schema=ReviewResult) — review');
181
+ log('[dry-run] Step 2 loops up to ' + MAX_RETRIES + ' times if not clean');
182
+ log('[dry-run] Done.');
183
+ return;
184
+ }
185
+
186
+ // ── Step 1: Execute ──────────────────────────────────────────────────────
187
+ log('Step 1/2: execute_task (claude)');
188
+ const executeStart = Date.now();
189
+
190
+ const executeResult = await runAgent('claude', task);
191
+ const executeSummary = executeResult.text.slice(0, 500);
192
+
193
+ const executeStep = {
194
+ id: 'execute',
195
+ function: 'execute_task',
196
+ agent: 'claude',
197
+ startedAt: new Date(executeStart).toISOString(),
198
+ durationMs: Date.now() - executeStart,
199
+ outputLength: executeResult.text.length,
200
+ summary: executeSummary.slice(0, 200),
201
+ };
202
+ audit.steps.push(executeStep);
203
+ log(` Done (${executeStep.durationMs}ms, ${executeResult.text.length} chars)`);
204
+
205
+ // ── Step 2: Review-fix loop ──────────────────────────────────────────────
206
+ let reviewResult = null;
207
+ let iteration = 0;
208
+ let previousFindings = [];
209
+
210
+ while (iteration < MAX_RETRIES) {
211
+ iteration++;
212
+ log(`Step 2/2: fix_and_review — iteration ${iteration}/${MAX_RETRIES}`);
213
+ const iterStart = Date.now();
214
+
215
+ // Fix pass (skip on first iteration if no findings)
216
+ if (previousFindings.length > 0) {
217
+ log(' Fixing findings...');
218
+ const fixPrompt = [
219
+ 'Fix the following issues found during code review.',
220
+ '',
221
+ 'Original task:',
222
+ task,
223
+ '',
224
+ 'Findings to fix:',
225
+ ...previousFindings.map((f, i) => `${i + 1}. ${f}`),
226
+ '',
227
+ 'Fix every finding. Do not introduce new issues.',
228
+ ].join('\n');
229
+
230
+ await runAgent('claude', fixPrompt);
231
+ log(' Fix complete.');
232
+ }
233
+
234
+ // Review pass
235
+ log(' Reviewing (codex)...');
236
+ const reviewPrompt = [
237
+ 'Review the following implementation against the blueprint.',
238
+ '',
239
+ '## Task',
240
+ task,
241
+ '',
242
+ '## What was implemented',
243
+ executeSummary,
244
+ '',
245
+ '## Blueprint (acceptance criteria)',
246
+ blueprint,
247
+ '',
248
+ 'List actionable findings with confidence >= 80.',
249
+ 'Set clean=true only if no actionable findings remain.',
250
+ ].join('\n');
251
+
252
+ const { result } = await runAgent('codex', reviewPrompt, { schema: REVIEW_SCHEMA });
253
+
254
+ const iterStep = {
255
+ id: `review_iteration_${iteration}`,
256
+ function: 'fix_and_review',
257
+ agents: previousFindings.length > 0 ? ['claude', 'codex'] : ['codex'],
258
+ iteration,
259
+ durationMs: Date.now() - iterStart,
260
+ result: result || { clean: false, summary: 'parse error', findings: [] },
261
+ };
262
+ audit.steps.push(iterStep);
263
+
264
+ if (!result) {
265
+ log(` Review parse error — treating as not clean`);
266
+ previousFindings = ['Review response was not parseable JSON'];
267
+ continue;
268
+ }
269
+
270
+ log(` clean=${result.clean}, findings=${result.findings?.length || 0}`);
271
+ if (result.summary) log(` summary: ${result.summary.slice(0, 120)}`);
272
+
273
+ if (result.clean) {
274
+ reviewResult = result;
275
+ break;
276
+ }
277
+
278
+ previousFindings = result.findings || [];
279
+ if (previousFindings.length === 0) {
280
+ log(' Not clean but no findings — treating as clean');
281
+ reviewResult = result;
282
+ break;
283
+ }
284
+ }
285
+
286
+ // ── Audit ────────────────────────────────────────────────────────────────
287
+ audit.result = reviewResult || { clean: false, summary: 'max retries exceeded' };
288
+ audit.completedAt = new Date().toISOString();
289
+ audit.totalIterations = iteration;
290
+ audit.success = !!reviewResult?.clean;
291
+
292
+ const auditPath = resolve(REPO_ROOT, '.compose/pipeline-audit.json');
293
+ mkdirSync(dirname(auditPath), { recursive: true });
294
+ writeFileSync(auditPath, JSON.stringify(audit, null, 2) + '\n');
295
+
296
+ log('');
297
+ log(`Result: ${audit.success ? 'CLEAN' : 'NOT CLEAN'} after ${iteration} iteration(s)`);
298
+ log(`Audit trace: .compose/pipeline-audit.json`);
299
+ log(`Total steps: ${audit.steps.length}`);
300
+
301
+ if (!audit.success) {
302
+ process.exitCode = 1;
303
+ }
304
+ }
305
+
306
+ run().catch((err) => {
307
+ log(`FATAL: ${err.message}`);
308
+ audit.result = { clean: false, summary: err.message };
309
+ audit.completedAt = new Date().toISOString();
310
+ const auditPath = resolve(REPO_ROOT, '.compose/pipeline-audit.json');
311
+ mkdirSync(dirname(auditPath), { recursive: true });
312
+ writeFileSync(auditPath, JSON.stringify(audit, null, 2) + '\n');
313
+ process.exitCode = 1;
314
+ });
@@ -0,0 +1,18 @@
1
+ #!/bin/bash
2
+ # session-end-hook.sh — SessionEnd hook. Closes session, triggers journal if threshold met.
3
+ # Receives JSON on stdin: { reason, transcript_path }
4
+ # Cannot block termination — fire and forget.
5
+
6
+ INPUT=$(cat)
7
+ REASON=$(echo "$INPUT" | jq -r '.reason // "exit"')
8
+ TRANSCRIPT=$(echo "$INPUT" | jq -r '.transcript_path // empty')
9
+
10
+ BODY=$(jq -n --arg reason "$REASON" --arg transcript "$TRANSCRIPT" \
11
+ 'if $transcript == "" then {reason: $reason} else {reason: $reason, transcriptPath: $transcript} end')
12
+
13
+ # Fire and forget — session end cannot block
14
+ curl -s -m 10 -X POST http://localhost:3001/api/session/end \
15
+ -H 'Content-Type: application/json' \
16
+ -d "$BODY" > /dev/null 2>&1 & disown
17
+
18
+ exit 0
@@ -0,0 +1,38 @@
1
+ #!/bin/bash
2
+ # session-start-hook.sh — SessionStart hook. Creates a session on the Compose server.
3
+ # Receives JSON on stdin: { source, model, agent_type }
4
+ # Outputs context to stdout (becomes Claude's session context).
5
+
6
+ INPUT=$(cat)
7
+ SOURCE=$(echo "$INPUT" | jq -r '.source // "startup"')
8
+
9
+ BODY=$(jq -n --arg source "$SOURCE" '{source: $source}')
10
+
11
+ RESPONSE=$(curl -s -m 2 -X POST http://localhost:3001/api/session/start \
12
+ -H 'Content-Type: application/json' \
13
+ -d "$BODY" 2>/dev/null)
14
+
15
+ [ -z "$RESPONSE" ] && exit 0
16
+
17
+ # Extract context — .context is the raw last session object
18
+ LAST_SESSION=$(echo "$RESPONSE" | jq -r '.context // empty')
19
+ if [ -n "$LAST_SESSION" ] && [ "$LAST_SESSION" != "null" ]; then
20
+ LAST_ITEMS=$(echo "$LAST_SESSION" | jq -r '.items | to_entries[] | "- \(.value.title): \(.value.writes) writes, \(.value.reads) reads"' 2>/dev/null)
21
+ LAST_TOOLS=$(echo "$LAST_SESSION" | jq -r '.toolCount // 0')
22
+ START=$(echo "$LAST_SESSION" | jq -r '.startedAt // empty')
23
+ END=$(echo "$LAST_SESSION" | jq -r '.endedAt // empty')
24
+ if [ -n "$START" ] && [ -n "$END" ]; then
25
+ START_S=$(date -j -f '%Y-%m-%dT%H:%M:%S' "${START%%.*}" '+%s' 2>/dev/null || echo 0)
26
+ END_S=$(date -j -f '%Y-%m-%dT%H:%M:%S' "${END%%.*}" '+%s' 2>/dev/null || echo 0)
27
+ DURATION=$(( END_S - START_S ))
28
+ else
29
+ DURATION=0
30
+ fi
31
+ echo "Last session: ${DURATION}s, ${LAST_TOOLS} tool uses."
32
+ if [ -n "$LAST_ITEMS" ]; then
33
+ echo "Items worked on:"
34
+ echo "$LAST_ITEMS"
35
+ fi
36
+ fi
37
+
38
+ exit 0
@@ -0,0 +1,104 @@
1
+ #!/bin/bash
2
+ # vision-hook.sh — PostToolUse hook that auto-tracks docs on the Vision Surface.
3
+ #
4
+ # Triggered after Write|Edit tool uses. Reads JSON from stdin, extracts the
5
+ # file path, and calls vision-track.mjs to create or log items.
6
+ #
7
+ # Only acts on files under docs/. Ignores everything else silently.
8
+ # Runs async (backgrounded) so it doesn't block the agent.
9
+
10
+ set -euo pipefail
11
+
12
+ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
13
+ TRACK="$SCRIPT_DIR/vision-track.mjs"
14
+ LOG="/tmp/compose-vision-hook.log"
15
+
16
+ # Read hook JSON from stdin
17
+ INPUT=$(cat)
18
+
19
+ # Extract file path from tool_input (works for both Write and Edit)
20
+ FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')
21
+ TOOL_NAME=$(echo "$INPUT" | jq -r '.tool_name // empty')
22
+
23
+ # Skip if no file path
24
+ [ -z "$FILE_PATH" ] && exit 0
25
+
26
+ # Only track docs/ files
27
+ case "$FILE_PATH" in
28
+ */docs/*) ;;
29
+ *) exit 0 ;;
30
+ esac
31
+
32
+ # Extract just the relative path from docs/ onward
33
+ REL_PATH="${FILE_PATH##*/docs/}"
34
+ FILENAME=$(basename "$FILE_PATH" .md)
35
+
36
+ # Determine type and phase from path conventions
37
+ TYPE=""
38
+ PHASE=""
39
+ case "$REL_PATH" in
40
+ plans/*)
41
+ TYPE="spec"
42
+ PHASE="planning"
43
+ ;;
44
+ specs/*)
45
+ TYPE="spec"
46
+ PHASE="requirements"
47
+ ;;
48
+ design/*)
49
+ TYPE="decision"
50
+ PHASE="design"
51
+ ;;
52
+ discovery/*)
53
+ TYPE="idea"
54
+ PHASE="vision"
55
+ ;;
56
+ requirements/*)
57
+ TYPE="spec"
58
+ PHASE="requirements"
59
+ ;;
60
+ decisions/*)
61
+ TYPE="decision"
62
+ PHASE="design"
63
+ ;;
64
+ evaluations/*)
65
+ TYPE="evaluation"
66
+ PHASE="verification"
67
+ ;;
68
+ journal/*)
69
+ TYPE="artifact"
70
+ PHASE="implementation"
71
+ ;;
72
+ *)
73
+ # Unknown docs subdirectory — log but don't track
74
+ echo "$(date -Iseconds) | SKIP unknown docs path: $REL_PATH" >> "$LOG"
75
+ exit 0
76
+ ;;
77
+ esac
78
+
79
+ # Check if an item for this file already exists (search by filename)
80
+ SEARCH_RESULT=$(node "$TRACK" search "$FILENAME" 2>/dev/null || true)
81
+
82
+ if [ -n "$SEARCH_RESULT" ]; then
83
+ # Item likely exists — just log the update, don't create duplicates
84
+ echo "$(date -Iseconds) | UPDATE $TOOL_NAME $REL_PATH (existing item found)" >> "$LOG"
85
+ else
86
+ # Create a new item
87
+ TITLE=$(echo "$FILENAME" | sed 's/-/ /g' | sed 's/^[0-9 ]*//' | sed 's/^ *//')
88
+ # Capitalize first letter
89
+ TITLE="$(echo "${TITLE:0:1}" | tr '[:lower:]' '[:upper:]')${TITLE:1}"
90
+
91
+ ID=$(node "$TRACK" create "$TITLE" \
92
+ --type "$TYPE" \
93
+ --phase "$PHASE" \
94
+ --description "Auto-tracked from $TOOL_NAME to docs/$REL_PATH" \
95
+ 2>/dev/null || true)
96
+
97
+ if [ -n "$ID" ]; then
98
+ echo "$(date -Iseconds) | CREATE $ID type=$TYPE phase=$PHASE path=$REL_PATH" >> "$LOG"
99
+ else
100
+ echo "$(date -Iseconds) | FAIL create for $REL_PATH" >> "$LOG"
101
+ fi
102
+ fi
103
+
104
+ exit 0