@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,247 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* artifact-manager.js — Artifact schema definitions, quality assessment,
|
|
3
|
+
* scaffolding, and template loading.
|
|
4
|
+
*
|
|
5
|
+
* Stateless — reads files and schemas, computes signals, returns data.
|
|
6
|
+
* No persistence beyond what the lifecycle manager already stores.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import fs from 'node:fs';
|
|
10
|
+
import path from 'node:path';
|
|
11
|
+
import { fileURLToPath } from 'node:url';
|
|
12
|
+
/** Phases that produce named artifacts (derived from contracts/lifecycle.json). */
|
|
13
|
+
const PHASE_ARTIFACTS = {
|
|
14
|
+
explore_design: 'design.md',
|
|
15
|
+
prd: 'prd.md',
|
|
16
|
+
architecture: 'architecture.md',
|
|
17
|
+
blueprint: 'blueprint.md',
|
|
18
|
+
plan: 'plan.md',
|
|
19
|
+
report: 'report.md',
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
23
|
+
const TEMPLATES_DIR = path.join(__dirname, 'artifact-templates');
|
|
24
|
+
|
|
25
|
+
// ---------------------------------------------------------------------------
|
|
26
|
+
// Artifact schemas
|
|
27
|
+
// ---------------------------------------------------------------------------
|
|
28
|
+
|
|
29
|
+
export const ARTIFACT_SCHEMAS = {
|
|
30
|
+
'design.md': {
|
|
31
|
+
requiredSections: ['Problem', 'Goal'],
|
|
32
|
+
optionalSections: ['Related Documents', 'Decision \\d+', 'Files', 'Open Questions', 'Resolved Questions'],
|
|
33
|
+
minWordCount: 200,
|
|
34
|
+
},
|
|
35
|
+
'prd.md': {
|
|
36
|
+
requiredSections: ['Problem Statement', 'Goals & Non-Goals', 'Requirements'],
|
|
37
|
+
optionalSections: ['Success Criteria', 'User Stories', 'Constraints', 'Open Questions'],
|
|
38
|
+
minWordCount: 300,
|
|
39
|
+
},
|
|
40
|
+
'architecture.md': {
|
|
41
|
+
requiredSections: ['Problem', 'Proposals'],
|
|
42
|
+
optionalSections: ['Trade-offs', 'Decision', 'Component Diagram'],
|
|
43
|
+
minWordCount: 200,
|
|
44
|
+
},
|
|
45
|
+
'blueprint.md': {
|
|
46
|
+
requiredSections: ['File Plan'],
|
|
47
|
+
optionalSections: ['Corrections Table'],
|
|
48
|
+
minWordCount: 300,
|
|
49
|
+
},
|
|
50
|
+
'plan.md': {
|
|
51
|
+
requiredSections: ['Task Order', 'Task 1'],
|
|
52
|
+
optionalSections: ['Files Summary'],
|
|
53
|
+
minWordCount: 150,
|
|
54
|
+
},
|
|
55
|
+
'report.md': {
|
|
56
|
+
requiredSections: ['Summary', 'Files Changed'],
|
|
57
|
+
optionalSections: ['Delivered vs Planned', 'Architecture Deviations', 'Key Decisions', 'Test Coverage', 'Known Issues', 'Lessons Learned'],
|
|
58
|
+
minWordCount: 200,
|
|
59
|
+
},
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
// ---------------------------------------------------------------------------
|
|
63
|
+
// Startup invariant: schema keys must match PHASE_ARTIFACTS values
|
|
64
|
+
// ---------------------------------------------------------------------------
|
|
65
|
+
|
|
66
|
+
const schemaKeys = new Set(Object.keys(ARTIFACT_SCHEMAS));
|
|
67
|
+
const artifactValues = new Set(Object.values(PHASE_ARTIFACTS));
|
|
68
|
+
if (schemaKeys.size !== artifactValues.size || [...schemaKeys].some(k => !artifactValues.has(k))) {
|
|
69
|
+
throw new Error('ARTIFACT_SCHEMAS keys and PHASE_ARTIFACTS values are out of sync');
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// ---------------------------------------------------------------------------
|
|
73
|
+
// Private helpers
|
|
74
|
+
// ---------------------------------------------------------------------------
|
|
75
|
+
|
|
76
|
+
function _validateFeatureCode(featureCode) {
|
|
77
|
+
if (!featureCode || !/^[A-Za-z0-9_-]+$/.test(featureCode)) {
|
|
78
|
+
throw new Error(`Invalid featureCode: ${featureCode}`);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function _featurePath(featureRoot, featureCode) {
|
|
83
|
+
_validateFeatureCode(featureCode);
|
|
84
|
+
const realRoot = fs.realpathSync(featureRoot);
|
|
85
|
+
const candidate = path.resolve(realRoot, featureCode);
|
|
86
|
+
if (!candidate.startsWith(realRoot + path.sep) && candidate !== realRoot) {
|
|
87
|
+
throw new Error(`Path escapes feature root: ${featureCode}`);
|
|
88
|
+
}
|
|
89
|
+
// Tier 2: if candidate exists, verify real path
|
|
90
|
+
if (fs.existsSync(candidate)) {
|
|
91
|
+
const realCandidate = fs.realpathSync(candidate);
|
|
92
|
+
if (!realCandidate.startsWith(realRoot + path.sep) && realCandidate !== realRoot) {
|
|
93
|
+
throw new Error(`Symlink escapes feature root: ${featureCode}`);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return candidate;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function _extractSections(markdown) {
|
|
100
|
+
const headings = [];
|
|
101
|
+
for (const line of markdown.split('\n')) {
|
|
102
|
+
const m = line.match(/^#{1,4}\s+(.+)$/);
|
|
103
|
+
if (m) {
|
|
104
|
+
let text = m[1].trim();
|
|
105
|
+
// Strip trailing punctuation (with optional leading/trailing whitespace)
|
|
106
|
+
text = text.replace(/\s*[:—–]+\s*$/, '');
|
|
107
|
+
headings.push(text);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return headings;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function _matchPattern(heading, pattern) {
|
|
114
|
+
if (pattern.includes('\\d+')) {
|
|
115
|
+
const re = new RegExp(`^${pattern}$`, 'i');
|
|
116
|
+
return re.test(heading);
|
|
117
|
+
}
|
|
118
|
+
return heading.toLowerCase() === pattern.toLowerCase();
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function _matchSections(foundHeadings, schema) {
|
|
122
|
+
const found = [];
|
|
123
|
+
const missing = [];
|
|
124
|
+
for (const pattern of schema.requiredSections) {
|
|
125
|
+
if (foundHeadings.some(h => _matchPattern(h, pattern))) {
|
|
126
|
+
found.push(pattern);
|
|
127
|
+
} else {
|
|
128
|
+
missing.push(pattern);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const optional = [];
|
|
133
|
+
for (const pattern of schema.optionalSections) {
|
|
134
|
+
if (foundHeadings.some(h => _matchPattern(h, pattern))) {
|
|
135
|
+
optional.push(pattern);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
return { found, missing, optional };
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// ---------------------------------------------------------------------------
|
|
143
|
+
// ArtifactManager
|
|
144
|
+
// ---------------------------------------------------------------------------
|
|
145
|
+
|
|
146
|
+
export class ArtifactManager {
|
|
147
|
+
#featureRoot;
|
|
148
|
+
|
|
149
|
+
constructor(featureRoot) {
|
|
150
|
+
this.#featureRoot = featureRoot;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
assess(featureCode) {
|
|
154
|
+
const artifacts = {};
|
|
155
|
+
for (const filename of Object.keys(ARTIFACT_SCHEMAS)) {
|
|
156
|
+
artifacts[filename] = this.assessOne(featureCode, filename);
|
|
157
|
+
}
|
|
158
|
+
return { artifacts };
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
assessOne(featureCode, filename) {
|
|
162
|
+
if (!ARTIFACT_SCHEMAS[filename]) {
|
|
163
|
+
throw new Error(`Unknown artifact: ${filename}`);
|
|
164
|
+
}
|
|
165
|
+
const dir = _featurePath(this.#featureRoot, featureCode);
|
|
166
|
+
const filePath = path.join(dir, filename);
|
|
167
|
+
const schema = ARTIFACT_SCHEMAS[filename];
|
|
168
|
+
|
|
169
|
+
// Check file-level symlink escape
|
|
170
|
+
if (fs.existsSync(filePath)) {
|
|
171
|
+
const realFile = fs.realpathSync(filePath);
|
|
172
|
+
const realRoot = fs.realpathSync(this.#featureRoot);
|
|
173
|
+
if (!realFile.startsWith(realRoot + path.sep)) {
|
|
174
|
+
throw new Error(`Symlink escapes feature root: ${filename}`);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
if (!fs.existsSync(filePath)) {
|
|
179
|
+
return {
|
|
180
|
+
exists: false,
|
|
181
|
+
wordCount: 0,
|
|
182
|
+
meetsMinWordCount: false,
|
|
183
|
+
sections: { found: [], missing: [...schema.requiredSections], optional: [] },
|
|
184
|
+
completeness: 0,
|
|
185
|
+
lastModified: null,
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
190
|
+
const wordCount = content.split(/\s+/).filter(Boolean).length;
|
|
191
|
+
const headings = _extractSections(content);
|
|
192
|
+
const sections = _matchSections(headings, schema);
|
|
193
|
+
const completeness = schema.requiredSections.length > 0
|
|
194
|
+
? sections.found.length / schema.requiredSections.length
|
|
195
|
+
: 1.0;
|
|
196
|
+
const lastModified = fs.statSync(filePath).mtime.toISOString();
|
|
197
|
+
|
|
198
|
+
return {
|
|
199
|
+
exists: true,
|
|
200
|
+
wordCount,
|
|
201
|
+
meetsMinWordCount: wordCount >= schema.minWordCount,
|
|
202
|
+
sections,
|
|
203
|
+
completeness,
|
|
204
|
+
lastModified,
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
scaffold(featureCode, options) {
|
|
209
|
+
const dir = _featurePath(this.#featureRoot, featureCode);
|
|
210
|
+
const created = [];
|
|
211
|
+
const skipped = [];
|
|
212
|
+
|
|
213
|
+
// Create directories
|
|
214
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
215
|
+
fs.mkdirSync(path.join(dir, 'sessions'), { recursive: true });
|
|
216
|
+
|
|
217
|
+
for (const filename of Object.keys(ARTIFACT_SCHEMAS)) {
|
|
218
|
+
if (options?.only && !options.only.includes(filename)) continue;
|
|
219
|
+
|
|
220
|
+
const filePath = path.join(dir, filename);
|
|
221
|
+
if (fs.existsSync(filePath)) {
|
|
222
|
+
skipped.push(filename);
|
|
223
|
+
} else {
|
|
224
|
+
const template = this.getTemplate(filename);
|
|
225
|
+
fs.writeFileSync(filePath, template, 'utf-8');
|
|
226
|
+
created.push(filename);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
return { created, skipped };
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
getTemplate(artifactName) {
|
|
234
|
+
if (!ARTIFACT_SCHEMAS[artifactName]) {
|
|
235
|
+
throw new Error(`Unknown artifact: ${artifactName}`);
|
|
236
|
+
}
|
|
237
|
+
const filePath = path.join(TEMPLATES_DIR, artifactName);
|
|
238
|
+
if (!fs.existsSync(filePath)) {
|
|
239
|
+
throw new Error(`Template not found: ${artifactName}`);
|
|
240
|
+
}
|
|
241
|
+
return fs.readFileSync(filePath, 'utf-8');
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
getSchema(artifactName) {
|
|
245
|
+
return ARTIFACT_SCHEMAS[artifactName] || null;
|
|
246
|
+
}
|
|
247
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# <Feature Name>: Architecture
|
|
2
|
+
|
|
3
|
+
**Status:** ARCHITECTURE
|
|
4
|
+
**Date:** <date>
|
|
5
|
+
|
|
6
|
+
## Problem
|
|
7
|
+
|
|
8
|
+
<!-- Technical problem statement -->
|
|
9
|
+
|
|
10
|
+
## Proposals
|
|
11
|
+
|
|
12
|
+
### Proposal A: <Name>
|
|
13
|
+
|
|
14
|
+
<!-- Description, trade-offs, diagram if helpful -->
|
|
15
|
+
|
|
16
|
+
### Proposal B: <Name>
|
|
17
|
+
|
|
18
|
+
<!-- Description, trade-offs, diagram if helpful -->
|
|
19
|
+
|
|
20
|
+
## Trade-offs
|
|
21
|
+
|
|
22
|
+
| Dimension | Proposal A | Proposal B |
|
|
23
|
+
|-----------|-----------|-----------|
|
|
24
|
+
| | | |
|
|
25
|
+
|
|
26
|
+
## Decision
|
|
27
|
+
|
|
28
|
+
<!-- Which proposal was chosen and why -->
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# <Feature Name>: Implementation Blueprint
|
|
2
|
+
|
|
3
|
+
**Status:** BLUEPRINT
|
|
4
|
+
**Date:** <date>
|
|
5
|
+
**Design:** [design.md](design.md)
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## File Plan
|
|
10
|
+
|
|
11
|
+
| File | Action | Purpose |
|
|
12
|
+
|------|--------|---------|
|
|
13
|
+
| | | |
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## Corrections Table
|
|
18
|
+
|
|
19
|
+
| Spec Assumption | Reality | Resolution |
|
|
20
|
+
|----------------|---------|------------|
|
|
21
|
+
| | | |
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# <Feature Name>: Design
|
|
2
|
+
|
|
3
|
+
**Status:** DESIGN
|
|
4
|
+
**Date:** <date>
|
|
5
|
+
|
|
6
|
+
## Related Documents
|
|
7
|
+
|
|
8
|
+
<!-- Link to roadmap, dependencies, and related features -->
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## Problem
|
|
13
|
+
|
|
14
|
+
<!-- Describe the problem this feature solves -->
|
|
15
|
+
|
|
16
|
+
## Goal
|
|
17
|
+
|
|
18
|
+
<!-- What does success look like? Scope and non-scope. -->
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## Decision 1: <Title>
|
|
23
|
+
|
|
24
|
+
<!-- Describe the decision, options considered, and rationale -->
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## Files
|
|
29
|
+
|
|
30
|
+
| File | Action | Purpose |
|
|
31
|
+
|------|--------|---------|
|
|
32
|
+
| | | |
|
|
33
|
+
|
|
34
|
+
## Open Questions
|
|
35
|
+
|
|
36
|
+
<!-- List unresolved questions -->
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# <Feature Name>: Implementation Plan
|
|
2
|
+
|
|
3
|
+
**Status:** PLAN
|
|
4
|
+
**Date:** <date>
|
|
5
|
+
**Blueprint:** [blueprint.md](blueprint.md)
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Task Order
|
|
10
|
+
|
|
11
|
+
<!-- List tasks in dependency order -->
|
|
12
|
+
|
|
13
|
+
## Task 1: <Title>
|
|
14
|
+
|
|
15
|
+
- **File:** `<path>` (new/existing)
|
|
16
|
+
- **What:** <!-- What to implement -->
|
|
17
|
+
- **Pattern:** <!-- Existing pattern to follow -->
|
|
18
|
+
- **Test:** <!-- What test to write -->
|
|
19
|
+
- **Depends on:** <!-- Task dependencies -->
|
|
20
|
+
|
|
21
|
+
## Files Summary
|
|
22
|
+
|
|
23
|
+
| File | Tasks |
|
|
24
|
+
|------|-------|
|
|
25
|
+
| | |
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# <Feature Name>: PRD
|
|
2
|
+
|
|
3
|
+
**Status:** PRD
|
|
4
|
+
**Date:** <date>
|
|
5
|
+
|
|
6
|
+
## Problem Statement
|
|
7
|
+
|
|
8
|
+
<!-- What problem are we solving and for whom? -->
|
|
9
|
+
|
|
10
|
+
## Goals & Non-Goals
|
|
11
|
+
|
|
12
|
+
### Goals
|
|
13
|
+
<!-- What this feature WILL do -->
|
|
14
|
+
|
|
15
|
+
### Non-Goals
|
|
16
|
+
<!-- What this feature will NOT do -->
|
|
17
|
+
|
|
18
|
+
## Requirements
|
|
19
|
+
|
|
20
|
+
### MUST
|
|
21
|
+
<!-- Non-negotiable requirements -->
|
|
22
|
+
|
|
23
|
+
### SHOULD
|
|
24
|
+
<!-- Important but not blocking -->
|
|
25
|
+
|
|
26
|
+
### MAY
|
|
27
|
+
<!-- Nice-to-have -->
|
|
28
|
+
|
|
29
|
+
## Success Criteria
|
|
30
|
+
|
|
31
|
+
<!-- How do we know this feature succeeded? -->
|
|
32
|
+
|
|
33
|
+
## User Stories
|
|
34
|
+
|
|
35
|
+
<!-- As a <role>, I want <goal> so that <benefit> -->
|
|
36
|
+
|
|
37
|
+
## Constraints
|
|
38
|
+
|
|
39
|
+
<!-- Technical, time, or resource constraints -->
|
|
40
|
+
|
|
41
|
+
## Open Questions
|
|
42
|
+
|
|
43
|
+
<!-- Unresolved questions -->
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# <Feature Name>: Implementation Report
|
|
2
|
+
|
|
3
|
+
**Status:** REPORT
|
|
4
|
+
**Date:** <date>
|
|
5
|
+
|
|
6
|
+
## Summary
|
|
7
|
+
|
|
8
|
+
<!-- 2-3 sentence overview of what was delivered -->
|
|
9
|
+
|
|
10
|
+
## Delivered vs Planned
|
|
11
|
+
|
|
12
|
+
| Planned | Delivered | Notes |
|
|
13
|
+
|---------|-----------|-------|
|
|
14
|
+
| | | |
|
|
15
|
+
|
|
16
|
+
## Architecture Deviations
|
|
17
|
+
|
|
18
|
+
<!-- Any deviations from the architecture/blueprint -->
|
|
19
|
+
|
|
20
|
+
## Key Decisions
|
|
21
|
+
|
|
22
|
+
<!-- Decisions made during implementation -->
|
|
23
|
+
|
|
24
|
+
## Test Coverage
|
|
25
|
+
|
|
26
|
+
<!-- What's tested, what's not, and why -->
|
|
27
|
+
|
|
28
|
+
## Files Changed
|
|
29
|
+
|
|
30
|
+
| File | Action | Purpose |
|
|
31
|
+
|------|--------|---------|
|
|
32
|
+
| | | |
|
|
33
|
+
|
|
34
|
+
## Known Issues
|
|
35
|
+
|
|
36
|
+
<!-- Known bugs, tech debt, or incomplete work -->
|
|
37
|
+
|
|
38
|
+
## Lessons Learned
|
|
39
|
+
|
|
40
|
+
<!-- What went well, what didn't, what to do differently -->
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* block-tracker.js — Work block detection and classification.
|
|
3
|
+
*
|
|
4
|
+
* These functions operate on a session object passed by reference,
|
|
5
|
+
* extracted from SessionManager to keep it under the line limit.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
// ---------------------------------------------------------------------------
|
|
9
|
+
// Block detection
|
|
10
|
+
// ---------------------------------------------------------------------------
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Detect block boundaries: a new block starts when the set of resolved items changes.
|
|
14
|
+
*
|
|
15
|
+
* @param {object} session — the current session object (mutated in place)
|
|
16
|
+
* @param {string[]} itemIds — current resolved item IDs
|
|
17
|
+
* @param {string} now — ISO timestamp
|
|
18
|
+
* @param {string} category — tool category string
|
|
19
|
+
*/
|
|
20
|
+
export function updateBlock(session, itemIds, now, category) {
|
|
21
|
+
const currentSet = session.currentBlock?.itemIds;
|
|
22
|
+
|
|
23
|
+
const sameBlock = currentSet
|
|
24
|
+
&& itemIds.length === currentSet.size
|
|
25
|
+
&& itemIds.every(id => currentSet.has(id));
|
|
26
|
+
|
|
27
|
+
if (sameBlock) {
|
|
28
|
+
session.currentBlock.toolCount++;
|
|
29
|
+
if (category) session.currentBlock.categories.add(category);
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Different set → close current block, start new one
|
|
34
|
+
closeCurrentBlock(session);
|
|
35
|
+
|
|
36
|
+
session.currentBlock = {
|
|
37
|
+
itemIds: new Set(itemIds),
|
|
38
|
+
startedAt: now,
|
|
39
|
+
toolCount: 1,
|
|
40
|
+
categories: new Set(category ? [category] : []),
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// ---------------------------------------------------------------------------
|
|
45
|
+
// Block closing
|
|
46
|
+
// ---------------------------------------------------------------------------
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Close the current block and push it to session.blocks.
|
|
50
|
+
*
|
|
51
|
+
* @param {object} session — the current session object (mutated in place)
|
|
52
|
+
*/
|
|
53
|
+
export function closeCurrentBlock(session) {
|
|
54
|
+
if (!session?.currentBlock) return;
|
|
55
|
+
|
|
56
|
+
const block = session.currentBlock;
|
|
57
|
+
session.blocks.push({
|
|
58
|
+
itemIds: Array.from(block.itemIds),
|
|
59
|
+
startedAt: block.startedAt,
|
|
60
|
+
endedAt: new Date().toISOString(),
|
|
61
|
+
toolCount: block.toolCount,
|
|
62
|
+
categories: Array.from(block.categories || []),
|
|
63
|
+
intent: classifyBlockIntent(block),
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
session.currentBlock = null;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// ---------------------------------------------------------------------------
|
|
70
|
+
// Block classification
|
|
71
|
+
// ---------------------------------------------------------------------------
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Classify a work block's intent from the categories of tools used.
|
|
75
|
+
*
|
|
76
|
+
* @param {object} block — block with a categories Set
|
|
77
|
+
* @returns {'building'|'debugging'|'testing'|'exploring'|'thinking'|'mixed'}
|
|
78
|
+
*/
|
|
79
|
+
export function classifyBlockIntent(block) {
|
|
80
|
+
const cats = block.categories || new Set();
|
|
81
|
+
const hasWriting = cats.has('writing');
|
|
82
|
+
const hasExecuting = cats.has('executing');
|
|
83
|
+
const hasReading = cats.has('reading') || cats.has('searching');
|
|
84
|
+
if (hasWriting && !hasExecuting) return 'building';
|
|
85
|
+
if (hasWriting && hasExecuting) return 'debugging';
|
|
86
|
+
if (hasExecuting && !hasWriting) return 'testing';
|
|
87
|
+
if (hasReading && !hasWriting && !hasExecuting) return 'exploring';
|
|
88
|
+
if (cats.size === 0) return 'thinking';
|
|
89
|
+
return 'mixed';
|
|
90
|
+
}
|