@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.
- package/LICENSE +21 -0
- package/README.md +1014 -0
- package/bin/compose.js +1515 -0
- package/dist/assets/_baseUniq-CQwX6VLz.js +1 -0
- package/dist/assets/arc-SxJ2J1sh.js +1 -0
- package/dist/assets/architectureDiagram-Q4EWVU46-BykunY1F.js +36 -0
- package/dist/assets/blockDiagram-DXYQGD6D-ohAKBOUw.js +132 -0
- package/dist/assets/c4Diagram-AHTNJAMY-DBDC3ENB.js +10 -0
- package/dist/assets/channel-DGElom1e.js +1 -0
- package/dist/assets/chunk-4BX2VUAB-Cv93Z7uM.js +1 -0
- package/dist/assets/chunk-4TB4RGXK-DE0WBDkj.js +206 -0
- package/dist/assets/chunk-55IACEB6-CE1EXenG.js +1 -0
- package/dist/assets/chunk-EDXVE4YY-DA7Ana6H.js +1 -0
- package/dist/assets/chunk-FMBD7UC4-CTDIPA3p.js +15 -0
- package/dist/assets/chunk-OYMX7WX6-uGBaPaTX.js +231 -0
- package/dist/assets/chunk-QZHKN3VN-CYlnXuUO.js +1 -0
- package/dist/assets/chunk-YZCP3GAM-ojGkzcZK.js +1 -0
- package/dist/assets/classDiagram-6PBFFD2Q-KqWP9wWZ.js +1 -0
- package/dist/assets/classDiagram-v2-HSJHXN6E-KqWP9wWZ.js +1 -0
- package/dist/assets/clone-DUJKJXd7.js +1 -0
- package/dist/assets/cose-bilkent-S5V4N54A-Bktn9hL-.js +1 -0
- package/dist/assets/dagre-KV5264BT-DFaSzuRF.js +4 -0
- package/dist/assets/defaultLocale-DX6XiGOO.js +1 -0
- package/dist/assets/diagram-5BDNPKRD-DnfmDzEm.js +10 -0
- package/dist/assets/diagram-G4DWMVQ6-Bm8W9YnG.js +24 -0
- package/dist/assets/diagram-MMDJMWI5-B5-TSKvp.js +43 -0
- package/dist/assets/diagram-TYMM5635-ls4rqlky.js +24 -0
- package/dist/assets/erDiagram-SMLLAGMA-giG6WO-r.js +85 -0
- package/dist/assets/flowDiagram-DWJPFMVM-XvlUuz-7.js +162 -0
- package/dist/assets/ganttDiagram-T4ZO3ILL-hLBV57oV.js +292 -0
- package/dist/assets/gitGraphDiagram-UUTBAWPF-BHu3s_Gn.js +106 -0
- package/dist/assets/graph-D0Cfv00Y.js +1 -0
- package/dist/assets/index-CUd6pFGF.css +1 -0
- package/dist/assets/index-DReRlzZI.js +1144 -0
- package/dist/assets/infoDiagram-42DDH7IO-DbqRsOo3.js +2 -0
- package/dist/assets/init-Gi6I4Gst.js +1 -0
- package/dist/assets/ishikawaDiagram-UXIWVN3A-DnCdx7zb.js +70 -0
- package/dist/assets/journeyDiagram-VCZTEJTY-CfD7eNcP.js +139 -0
- package/dist/assets/kanban-definition-6JOO6SKY-BYaO9-mK.js +89 -0
- package/dist/assets/katex-DkKDou_j.js +257 -0
- package/dist/assets/layout-Bj72wOEB.js +1 -0
- package/dist/assets/linear-BRFo114D.js +1 -0
- package/dist/assets/min-GCHnKlJS.js +1 -0
- package/dist/assets/mindmap-definition-QFDTVHPH-n0PMebY4.js +96 -0
- package/dist/assets/ordinal-Cboi1Yqb.js +1 -0
- package/dist/assets/pieDiagram-DEJITSTG-pN4CljHF.js +30 -0
- package/dist/assets/quadrantDiagram-34T5L4WZ-DNoAy8-D.js +7 -0
- package/dist/assets/requirementDiagram-MS252O5E-BhtY05PT.js +84 -0
- package/dist/assets/sankeyDiagram-XADWPNL6-B6AD-16A.js +10 -0
- package/dist/assets/sequenceDiagram-FGHM5R23-DShHM-uk.js +157 -0
- package/dist/assets/stateDiagram-FHFEXIEX-DMxn7HTo.js +1 -0
- package/dist/assets/stateDiagram-v2-QKLJ7IA2-o6PnCs4e.js +1 -0
- package/dist/assets/timeline-definition-GMOUNBTQ-Cdu6uq52.js +120 -0
- package/dist/assets/vennDiagram-DHZGUBPP-CpK29iRe.js +34 -0
- package/dist/assets/wardley-RL74JXVD-BQgSkdcO.js +162 -0
- package/dist/assets/wardleyDiagram-NUSXRM2D-DJHYev6O.js +20 -0
- package/dist/assets/xychartDiagram-5P7HB3ND-1d75pbaO.js +7 -0
- package/dist/index.html +30 -0
- package/lib/agent-chains.js +65 -0
- package/lib/agent-string.js +86 -0
- package/lib/budget-ledger.js +86 -0
- package/lib/build-all.js +162 -0
- package/lib/build-dag.js +120 -0
- package/lib/build-stream-writer.js +190 -0
- package/lib/build.js +2997 -0
- package/lib/capability-checker.js +53 -0
- package/lib/cert-inject.js +38 -0
- package/lib/cli-progress.js +483 -0
- package/lib/constants.js +69 -0
- package/lib/cross-layer-audit.js +84 -0
- package/lib/debug-discipline.js +173 -0
- package/lib/feature-json.js +106 -0
- package/lib/gate-prompt.js +291 -0
- package/lib/gate-tiers.js +194 -0
- package/lib/health-history.js +119 -0
- package/lib/health-score.js +227 -0
- package/lib/ideabox.js +570 -0
- package/lib/import.js +244 -0
- package/lib/migrate-roadmap.js +94 -0
- package/lib/model-pricing.js +67 -0
- package/lib/new.js +413 -0
- package/lib/pipeline-cli.js +489 -0
- package/lib/plan-parser.js +103 -0
- package/lib/qa-scoping.js +474 -0
- package/lib/questionnaire.js +200 -0
- package/lib/resolve-port.js +7 -0
- package/lib/result-normalizer.js +349 -0
- package/lib/review-lenses.js +166 -0
- package/lib/roadmap-gen.js +210 -0
- package/lib/roadmap-parser.js +176 -0
- package/lib/server-probe.js +23 -0
- package/lib/staleness.js +87 -0
- package/lib/step-prompt.js +260 -0
- package/lib/step-validator.js +49 -0
- package/lib/stratum-mcp-client.js +365 -0
- package/lib/team-flag.js +46 -0
- package/lib/test-bootstrap.js +401 -0
- package/lib/triage.js +274 -0
- package/lib/vision-writer.js +391 -0
- package/package.json +111 -0
- package/pipelines/bug-fix.stratum.yaml +230 -0
- package/pipelines/build.stratum.yaml +498 -0
- package/pipelines/content.stratum.yaml +112 -0
- package/pipelines/coverage-sweep.stratum.yaml +52 -0
- package/pipelines/refactor.stratum.yaml +169 -0
- package/pipelines/research.stratum.yaml +88 -0
- package/pipelines/review-fix.stratum.yaml +109 -0
- package/presets/team-feature.stratum.yaml +105 -0
- package/presets/team-research.stratum.yaml +108 -0
- package/presets/team-review.stratum.yaml +106 -0
- package/scripts/agent-activity-hook.sh +31 -0
- package/scripts/agent-error-hook.sh +28 -0
- package/scripts/analyze-orphans.mjs +50 -0
- package/scripts/find-orphans.mjs +26 -0
- package/scripts/fix-phases.mjs +49 -0
- package/scripts/generate-stratum-spec.mjs +137 -0
- package/scripts/import-roadmap.mjs +116 -0
- package/scripts/phase-audit.mjs +33 -0
- package/scripts/run-pipeline.mjs +314 -0
- package/scripts/session-end-hook.sh +18 -0
- package/scripts/session-start-hook.sh +38 -0
- package/scripts/vision-hook.sh +104 -0
- package/scripts/vision-track.mjs +554 -0
- package/scripts/wire-all-orphans.mjs +108 -0
- package/scripts/wire-orphans.mjs +164 -0
- package/server/activity-routes.js +123 -0
- package/server/agent-health.js +197 -0
- package/server/agent-hooks.js +102 -0
- package/server/agent-mcp.js +10 -0
- package/server/agent-registry.js +95 -0
- package/server/agent-server.js +290 -0
- package/server/agent-spawn.js +251 -0
- package/server/agent-templates.js +77 -0
- package/server/artifact-manager.js +247 -0
- package/server/artifact-templates/architecture.md +28 -0
- package/server/artifact-templates/blueprint.md +21 -0
- package/server/artifact-templates/design.md +36 -0
- package/server/artifact-templates/plan.md +25 -0
- package/server/artifact-templates/prd.md +43 -0
- package/server/artifact-templates/report.md +40 -0
- package/server/block-tracker.js +90 -0
- package/server/build-stream-bridge.js +502 -0
- package/server/coalescing-buffer.js +46 -0
- package/server/compose-mcp-tools.js +479 -0
- package/server/compose-mcp.js +324 -0
- package/server/connectors/agent-connector.js +78 -0
- package/server/connectors/claude-sdk-connector.js +198 -0
- package/server/connectors/codex-connector.js +240 -0
- package/server/connectors/connector-discovery.js +18 -0
- package/server/connectors/connector-runtime.js +13 -0
- package/server/connectors/opencode-connector.js +200 -0
- package/server/design-routes.js +540 -0
- package/server/design-session.js +161 -0
- package/server/feature-scan.js +593 -0
- package/server/file-watcher.js +284 -0
- package/server/find-root.js +29 -0
- package/server/graph-export.js +343 -0
- package/server/ideabox-cache.js +77 -0
- package/server/ideabox-routes.js +294 -0
- package/server/index.js +156 -0
- package/server/model-tiers.js +49 -0
- package/server/pipeline-routes.js +288 -0
- package/server/policy-evaluator.js +36 -0
- package/server/project-root.js +122 -0
- package/server/security.js +23 -0
- package/server/session-manager.js +403 -0
- package/server/session-routes.js +190 -0
- package/server/session-store.js +107 -0
- package/server/settings-routes.js +35 -0
- package/server/settings-store.js +234 -0
- package/server/stratum-api.js +102 -0
- package/server/stratum-client.js +192 -0
- package/server/stratum-sync.js +193 -0
- package/server/summarizer.js +139 -0
- package/server/supervisor.js +196 -0
- package/server/vision-routes.js +668 -0
- package/server/vision-server.js +393 -0
- package/server/vision-store.js +360 -0
- package/server/vision-utils.js +179 -0
- package/server/worktree-gc.js +137 -0
- 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
|