@stackmemoryai/stackmemory 1.10.4 → 1.12.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/README.md +104 -23
- package/dist/src/cli/claude-sm.js +266 -84
- package/dist/src/cli/codex-sm.js +185 -33
- package/dist/src/cli/commands/bench.js +209 -2
- package/dist/src/cli/commands/cache.js +126 -0
- package/dist/src/cli/commands/daemon.js +41 -0
- package/dist/src/cli/commands/handoff.js +40 -9
- package/dist/src/cli/commands/onboard.js +70 -3
- package/dist/src/cli/commands/optimize.js +117 -0
- package/dist/src/cli/commands/orchestrate.js +230 -5
- package/dist/src/cli/commands/orchestrator.js +312 -24
- package/dist/src/cli/commands/pack.js +322 -0
- package/dist/src/cli/commands/search.js +40 -1
- package/dist/src/cli/commands/setup.js +177 -7
- package/dist/src/cli/commands/skills.js +10 -1
- package/dist/src/cli/commands/state.js +265 -0
- package/dist/src/cli/commands/wiki.js +33 -0
- package/dist/src/cli/gemini-sm.js +19 -29
- package/dist/src/cli/index.js +90 -29
- package/dist/src/cli/opencode-sm.js +38 -21
- package/dist/src/cli/utils/determinism-watcher.js +66 -0
- package/dist/src/cli/utils/real-cli-bin.js +44 -0
- package/dist/src/core/cache/content-cache.js +238 -0
- package/dist/src/core/cache/index.js +11 -0
- package/dist/src/core/cache/token-estimator.js +16 -0
- package/dist/src/core/context/frame-database.js +38 -30
- package/dist/src/core/cross-search/cross-project-search.js +269 -0
- package/dist/src/core/{merge → cross-search}/index.js +6 -4
- package/dist/src/core/database/sqlite-adapter.js +0 -83
- package/dist/src/core/extensions/provider-adapter.js +5 -0
- package/dist/src/core/models/model-router.js +22 -2
- package/dist/src/core/monitoring/logger.js +2 -1
- package/dist/src/core/optimization/trace-optimizer.js +413 -0
- package/dist/src/core/provenance/confidence-scorer.js +128 -0
- package/dist/src/core/provenance/index.js +40 -0
- package/dist/src/core/provenance/provenance-store.js +194 -0
- package/dist/src/core/provenance/types.js +82 -0
- package/dist/src/core/session/project-handoff.js +64 -0
- package/dist/src/core/session/session-manager.js +28 -0
- package/dist/src/core/shared-state/canonical-store.js +564 -0
- package/dist/src/core/skill-packs/index.js +18 -0
- package/dist/src/core/skill-packs/parser.js +42 -0
- package/dist/src/core/skill-packs/registry.js +224 -0
- package/dist/src/core/skill-packs/types.js +66 -0
- package/dist/src/core/trace/trace-event-store.js +282 -0
- package/dist/src/core/trace/trace-event.js +4 -0
- package/dist/src/core/wiki/wiki-compiler.js +219 -0
- package/dist/src/daemon/daemon-config.js +7 -0
- package/dist/src/daemon/services/github-service.js +126 -0
- package/dist/src/daemon/unified-daemon.js +30 -0
- package/dist/src/features/sweep/pty-wrapper.js +13 -5
- package/dist/src/hooks/schemas.js +2 -0
- package/dist/src/integrations/claude-code/subagent-client.js +89 -0
- package/dist/src/integrations/github/pr-state.js +158 -0
- package/dist/src/integrations/linear/client.js +4 -1
- package/dist/src/integrations/mcp/handlers/cross-search-handlers.js +188 -0
- package/dist/src/integrations/mcp/handlers/index.js +40 -59
- package/dist/src/integrations/mcp/server.js +425 -311
- package/dist/src/integrations/mcp/tool-alias-registry.js +370 -0
- package/dist/src/integrations/mcp/tool-definitions.js +98 -229
- package/dist/src/integrations/ralph/context/stackmemory-context-loader.js +3 -40
- package/dist/src/integrations/ralph/learning/pattern-learner.js +1 -20
- package/dist/src/integrations/ralph/swarm/swarm-coordinator.js +0 -2
- package/dist/src/mcp/stackmemory-mcp-server.js +315 -0
- package/dist/src/orchestrators/multimodal/determinism.js +243 -0
- package/dist/src/orchestrators/multimodal/harness.js +147 -77
- package/dist/src/orchestrators/multimodal/providers.js +44 -3
- package/dist/src/utils/hook-installer.js +8 -8
- package/package.json +10 -1
- package/packs/coding/python-fastapi/instructions.md +60 -0
- package/packs/coding/python-fastapi/pack.yaml +28 -0
- package/packs/coding/typescript-react/instructions.md +47 -0
- package/packs/coding/typescript-react/pack.yaml +28 -0
- package/packs/core/commands/capture.md +32 -0
- package/packs/core/commands/learn.md +73 -0
- package/packs/core/commands/next.md +36 -0
- package/packs/core/commands/restart.md +58 -0
- package/packs/core/commands/restore.md +29 -0
- package/packs/core/commands/start.md +57 -0
- package/packs/core/commands/stop.md +65 -0
- package/packs/core/commands/summary.md +40 -0
- package/packs/core/manifest.json +24 -0
- package/packs/ops/decision-recovery/instructions.md +65 -0
- package/packs/ops/decision-recovery/pack.yaml +89 -0
- package/templates/claude-hooks/doc-ingest.js +76 -0
- package/dist/src/cli/commands/team.js +0 -168
- package/dist/src/core/context/shared-context-layer.js +0 -620
- package/dist/src/core/context/stack-merge-resolver.js +0 -748
- package/dist/src/core/merge/conflict-detector.js +0 -430
- package/dist/src/core/merge/resolution-engine.js +0 -557
- package/dist/src/core/merge/stack-diff.js +0 -531
- package/dist/src/core/merge/unified-merge-resolver.js +0 -302
- package/dist/src/integrations/mcp/handlers/cord-handlers.js +0 -397
- package/dist/src/integrations/mcp/handlers/team-handlers.js +0 -211
- /package/dist/src/core/{merge → cache}/types.js +0 -0
|
@@ -51,6 +51,45 @@ function heuristicPlan(input) {
|
|
|
51
51
|
]
|
|
52
52
|
};
|
|
53
53
|
}
|
|
54
|
+
function deterministicCritique(args) {
|
|
55
|
+
const issues = [];
|
|
56
|
+
const suggestions = [];
|
|
57
|
+
if (!args.ok) {
|
|
58
|
+
issues.push("Implementer command failed");
|
|
59
|
+
suggestions.push("Fix the command invocation before retrying");
|
|
60
|
+
}
|
|
61
|
+
if (args.diff.includes("<<<<<<<") || args.diff.includes(">>>>>>>")) {
|
|
62
|
+
issues.push("Merge conflict markers detected in diff");
|
|
63
|
+
suggestions.push("Resolve conflict markers before approval");
|
|
64
|
+
}
|
|
65
|
+
if (args.checks && !args.checks.lintOk) {
|
|
66
|
+
issues.push("Lint checks failed");
|
|
67
|
+
suggestions.push("Address lint failures before approval");
|
|
68
|
+
}
|
|
69
|
+
if (args.checks && !args.checks.testsOk) {
|
|
70
|
+
issues.push("Tests failed");
|
|
71
|
+
suggestions.push("Fix failing tests before approval");
|
|
72
|
+
}
|
|
73
|
+
const failedVerifications = args.checks?.verifications.filter((verification) => !verification.ok) || [];
|
|
74
|
+
if (failedVerifications.length > 0) {
|
|
75
|
+
issues.push(
|
|
76
|
+
`Verification command failed: ${failedVerifications[0]?.command}`
|
|
77
|
+
);
|
|
78
|
+
suggestions.push(
|
|
79
|
+
"Use the verification output as the primary repro signal and fix the root cause"
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
if (!args.diff || args.diff.startsWith("(no changes detected)")) {
|
|
83
|
+
suggestions.push(
|
|
84
|
+
"No code changes detected; verify the task can be satisfied without edits"
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
return {
|
|
88
|
+
approved: issues.length === 0,
|
|
89
|
+
issues,
|
|
90
|
+
suggestions
|
|
91
|
+
};
|
|
92
|
+
}
|
|
54
93
|
async function runSpike(input, options = {}) {
|
|
55
94
|
const plannerSystem = `You write concise, actionable implementation plans. Output raw JSON only (no markdown code fences). Schema: { "summary": "string", "steps": [{ "id": "step-1", "title": "string", "rationale": "string", "acceptanceCriteria": ["string"] }], "risks": ["string"] }`;
|
|
56
95
|
const contextSummary = getLocalContextSummary(input.repoPath);
|
|
@@ -61,23 +100,28 @@ ${contextSummary}
|
|
|
61
100
|
Constraints: Keep the plan minimal and implementable in a single PR.`;
|
|
62
101
|
const t0 = Date.now();
|
|
63
102
|
let plan;
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
system: plannerSystem
|
|
68
|
-
});
|
|
103
|
+
if (options.deterministicFixture) {
|
|
104
|
+
plan = heuristicPlan(input);
|
|
105
|
+
} else {
|
|
69
106
|
try {
|
|
70
|
-
const
|
|
71
|
-
|
|
107
|
+
const raw = await callClaude(plannerPrompt, {
|
|
108
|
+
model: options.plannerModel,
|
|
109
|
+
system: plannerSystem
|
|
110
|
+
});
|
|
111
|
+
try {
|
|
112
|
+
const cleaned = raw.replace(/^```(?:json)?\s*\n?/i, "").replace(/\n?```\s*$/i, "").trim();
|
|
113
|
+
plan = JSON.parse(cleaned);
|
|
114
|
+
} catch {
|
|
115
|
+
plan = heuristicPlan(input);
|
|
116
|
+
}
|
|
72
117
|
} catch {
|
|
73
118
|
plan = heuristicPlan(input);
|
|
74
119
|
}
|
|
75
|
-
} catch {
|
|
76
|
-
plan = heuristicPlan(input);
|
|
77
120
|
}
|
|
78
121
|
const planLatencyMs = Date.now() - t0;
|
|
79
122
|
const implementer = options.implementer || "codex";
|
|
80
123
|
const maxIters = Math.max(1, options.maxIters ?? 2);
|
|
124
|
+
const verificationCommands = options.verificationCommands || [];
|
|
81
125
|
const iterations = [];
|
|
82
126
|
let approved = false;
|
|
83
127
|
let lastCommand = "";
|
|
@@ -89,10 +133,14 @@ Constraints: Keep the plan minimal and implementable in a single PR.`;
|
|
|
89
133
|
};
|
|
90
134
|
for (let i = 0; i < maxIters; i++) {
|
|
91
135
|
const stepsList = plan.steps.map((s, idx) => `${idx + 1}. ${s.title}`).join("\n");
|
|
136
|
+
const verificationPrompt = verificationCommands.length > 0 ? `
|
|
137
|
+
|
|
138
|
+
Verification commands that must pass:
|
|
139
|
+
${verificationCommands.map((command) => `- ${command}`).join("\n")}` : "\n\nIf this task fixes uncertain behavior, first create or identify a deterministic repro/test/trace that fails for the current behavior, then use it to guide the fix.";
|
|
92
140
|
const basePrompt = `Implement the following plan:
|
|
93
141
|
${stepsList}
|
|
94
142
|
|
|
95
|
-
Keep changes minimal and focused. Avoid unrelated edits
|
|
143
|
+
Keep changes minimal and focused. Avoid unrelated edits.${verificationPrompt}`;
|
|
96
144
|
const refine = i === 0 ? "" : `
|
|
97
145
|
Incorporate reviewer suggestions: ${lastCritique.suggestions.join("; ")}`;
|
|
98
146
|
const implPrompt = basePrompt + refine;
|
|
@@ -116,14 +164,20 @@ Incorporate reviewer suggestions: ${lastCritique.suggestions.join("; ")}`;
|
|
|
116
164
|
_lastOutput = impl.output;
|
|
117
165
|
}
|
|
118
166
|
const diff = options.dryRun !== false ? "(dry run \u2014 no diff)" : captureGitDiff(input.repoPath);
|
|
119
|
-
const checks = options.dryRun !== false ? null : runPostImplChecks(input.repoPath);
|
|
167
|
+
const checks = options.dryRun !== false ? null : runPostImplChecks(input.repoPath, verificationCommands);
|
|
168
|
+
const verificationSection = checks && checks.verifications.length > 0 ? `
|
|
169
|
+
Custom verification:
|
|
170
|
+
${checks.verifications.map(
|
|
171
|
+
(verification) => ` - ${verification.ok ? "PASS" : "FAIL"} ${verification.command}
|
|
172
|
+
${verification.output}`
|
|
173
|
+
).join("\n")}` : "";
|
|
120
174
|
const checksSection = checks ? `
|
|
121
175
|
|
|
122
176
|
Post-implementation checks:
|
|
123
177
|
Lint: ${checks.lintOk ? "PASS" : "FAIL"}
|
|
124
178
|
${checks.lintOutput}
|
|
125
179
|
Tests: ${checks.testsOk ? "PASS" : "FAIL"}
|
|
126
|
-
${checks.testOutput}` : "";
|
|
180
|
+
${checks.testOutput}${verificationSection}` : "";
|
|
127
181
|
const criticSystem = `You are a strict code reviewer. Review the git diff against the plan. Check for: correctness, missing steps, unrelated changes, bugs, security issues. Also review lint and test results if provided. Return raw JSON only (no markdown fences): { "approved": boolean, "issues": ["string"], "suggestions": ["string"] }`;
|
|
128
182
|
const criticPrompt = `Plan: ${plan.summary}
|
|
129
183
|
Acceptance criteria:
|
|
@@ -134,25 +188,35 @@ Implementer exit: ${ok ? "success" : "failed"}
|
|
|
134
188
|
|
|
135
189
|
Git diff:
|
|
136
190
|
${diff}${checksSection}`;
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
191
|
+
if (options.deterministicFixture) {
|
|
192
|
+
lastCritique = deterministicCritique({
|
|
193
|
+
plan,
|
|
194
|
+
ok,
|
|
195
|
+
diff,
|
|
196
|
+
checks
|
|
141
197
|
});
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
198
|
+
} else {
|
|
199
|
+
try {
|
|
200
|
+
const raw = await callClaude(criticPrompt, {
|
|
201
|
+
model: options.reviewerModel,
|
|
202
|
+
system: criticSystem
|
|
203
|
+
});
|
|
204
|
+
const cleaned = raw.replace(/^```(?:json)?\s*\n?/i, "").replace(/\n?```\s*$/i, "").trim();
|
|
205
|
+
lastCritique = JSON.parse(cleaned);
|
|
206
|
+
} catch {
|
|
207
|
+
lastCritique = {
|
|
208
|
+
approved: ok,
|
|
209
|
+
issues: ok ? [] : ["Critique failed"],
|
|
210
|
+
suggestions: []
|
|
211
|
+
};
|
|
212
|
+
}
|
|
150
213
|
}
|
|
151
214
|
iterations.push({
|
|
152
215
|
command: lastCommand,
|
|
153
216
|
ok,
|
|
154
217
|
outputPreview: diff.slice(0, 2e3),
|
|
155
|
-
critique: lastCritique
|
|
218
|
+
critique: lastCritique,
|
|
219
|
+
checks
|
|
156
220
|
});
|
|
157
221
|
if (lastCritique.approved) {
|
|
158
222
|
approved = true;
|
|
@@ -177,62 +241,64 @@ ${diff}${checksSection}`;
|
|
|
177
241
|
editFuzzyFallbacks: editMetrics.editFuzzyFallbacks,
|
|
178
242
|
contextTokens: Math.ceil(finalDiff.length / 4)
|
|
179
243
|
};
|
|
180
|
-
|
|
181
|
-
const dir = options.auditDir || path.join(input.repoPath, ".stackmemory", "build");
|
|
182
|
-
fs.mkdirSync(dir, { recursive: true });
|
|
183
|
-
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
184
|
-
const file = path.join(dir, `spike-${stamp}.json`);
|
|
185
|
-
fs.writeFileSync(
|
|
186
|
-
file,
|
|
187
|
-
JSON.stringify(
|
|
188
|
-
{
|
|
189
|
-
input,
|
|
190
|
-
options: { ...options, auditDir: void 0 },
|
|
191
|
-
plan,
|
|
192
|
-
iterations,
|
|
193
|
-
metrics: runMetrics
|
|
194
|
-
},
|
|
195
|
-
null,
|
|
196
|
-
2
|
|
197
|
-
)
|
|
198
|
-
);
|
|
199
|
-
const metricsFile = path.join(dir, "harness-metrics.jsonl");
|
|
200
|
-
fs.appendFileSync(metricsFile, JSON.stringify(runMetrics) + "\n");
|
|
244
|
+
if (options.persistAudit !== false) {
|
|
201
245
|
try {
|
|
202
|
-
const
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
246
|
+
const dir = options.auditDir || path.join(input.repoPath, ".stackmemory", "build");
|
|
247
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
248
|
+
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
249
|
+
const file = path.join(dir, `spike-${stamp}.json`);
|
|
250
|
+
fs.writeFileSync(
|
|
251
|
+
file,
|
|
252
|
+
JSON.stringify(
|
|
253
|
+
{
|
|
254
|
+
input,
|
|
255
|
+
options: { ...options, auditDir: void 0 },
|
|
256
|
+
plan,
|
|
257
|
+
iterations,
|
|
258
|
+
metrics: runMetrics
|
|
259
|
+
},
|
|
260
|
+
null,
|
|
261
|
+
2
|
|
262
|
+
)
|
|
263
|
+
);
|
|
264
|
+
const metricsFile = path.join(dir, "harness-metrics.jsonl");
|
|
265
|
+
fs.appendFileSync(metricsFile, JSON.stringify(runMetrics) + "\n");
|
|
266
|
+
try {
|
|
267
|
+
const lines = fs.readFileSync(metricsFile, "utf-8").split("\n").filter((l) => l.trim());
|
|
268
|
+
const recent = lines.slice(-10).map((l) => JSON.parse(l));
|
|
269
|
+
if (recent.length >= 3) {
|
|
270
|
+
const summary = summarizeRuns(recent);
|
|
271
|
+
if (summary.approvalRate < HARNESS_TARGETS.firstPassApprovalRate) {
|
|
272
|
+
feedbackLoops.fire(
|
|
273
|
+
"harnessRegression",
|
|
274
|
+
"metrics_append",
|
|
275
|
+
{
|
|
276
|
+
metric: "approvalRate",
|
|
277
|
+
current: summary.approvalRate,
|
|
278
|
+
target: HARNESS_TARGETS.firstPassApprovalRate,
|
|
279
|
+
window: recent.length
|
|
280
|
+
},
|
|
281
|
+
"regression_alert"
|
|
282
|
+
);
|
|
283
|
+
}
|
|
284
|
+
if (summary.p95TotalLatencyMs > HARNESS_TARGETS.totalLatencyP95Ms) {
|
|
285
|
+
feedbackLoops.fire(
|
|
286
|
+
"harnessRegression",
|
|
287
|
+
"metrics_append",
|
|
288
|
+
{
|
|
289
|
+
metric: "totalLatencyP95",
|
|
290
|
+
current: summary.p95TotalLatencyMs,
|
|
291
|
+
target: HARNESS_TARGETS.totalLatencyP95Ms,
|
|
292
|
+
window: recent.length
|
|
293
|
+
},
|
|
294
|
+
"regression_alert"
|
|
295
|
+
);
|
|
296
|
+
}
|
|
231
297
|
}
|
|
298
|
+
} catch {
|
|
232
299
|
}
|
|
233
300
|
} catch {
|
|
234
301
|
}
|
|
235
|
-
} catch {
|
|
236
302
|
}
|
|
237
303
|
if (options.record) {
|
|
238
304
|
void recordContext(
|
|
@@ -265,7 +331,8 @@ ${diff}${checksSection}`;
|
|
|
265
331
|
commands: iterations.map((it) => it.command)
|
|
266
332
|
},
|
|
267
333
|
critique: lastCritique,
|
|
268
|
-
iterations
|
|
334
|
+
iterations,
|
|
335
|
+
verification: iterations.at(-1)?.checks || null
|
|
269
336
|
};
|
|
270
337
|
}
|
|
271
338
|
const runPlanAndCode = runSpike;
|
|
@@ -341,6 +408,9 @@ Repo: ${input.repoPath}
|
|
|
341
408
|
Notes: ${input.contextNotes || "(none)"}
|
|
342
409
|
${contextSummary}
|
|
343
410
|
Constraints: Keep the plan minimal and implementable in a single PR.`;
|
|
411
|
+
if (options.deterministicFixture) {
|
|
412
|
+
return heuristicPlan(input);
|
|
413
|
+
}
|
|
344
414
|
try {
|
|
345
415
|
const raw = await callClaude(plannerPrompt, {
|
|
346
416
|
model: options.plannerModel,
|
|
@@ -115,7 +115,7 @@ ${newFiles.join("\n")}`;
|
|
|
115
115
|
return "(git diff failed)";
|
|
116
116
|
}
|
|
117
117
|
}
|
|
118
|
-
function runPostImplChecks(cwd) {
|
|
118
|
+
function runPostImplChecks(cwd, verificationCommands = []) {
|
|
119
119
|
const maxOutput = 2e3;
|
|
120
120
|
function truncate(s) {
|
|
121
121
|
if (s.length <= maxOutput) return s;
|
|
@@ -152,7 +152,47 @@ function runPostImplChecks(cwd) {
|
|
|
152
152
|
} catch (e) {
|
|
153
153
|
testOutput = truncate(e instanceof Error ? e.message : String(e));
|
|
154
154
|
}
|
|
155
|
-
return {
|
|
155
|
+
return {
|
|
156
|
+
lintOk,
|
|
157
|
+
lintOutput,
|
|
158
|
+
testsOk,
|
|
159
|
+
testOutput,
|
|
160
|
+
verifications: runVerificationCommands(cwd, verificationCommands)
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
function runVerificationCommands(cwd, commands, options = {}) {
|
|
164
|
+
const timeout = options.timeoutMs ?? 12e4;
|
|
165
|
+
const maxOutput = options.maxOutput ?? 4e3;
|
|
166
|
+
const truncate = (value) => {
|
|
167
|
+
if (value.length <= maxOutput) return value;
|
|
168
|
+
return value.slice(0, maxOutput) + `
|
|
169
|
+
... (truncated, ${value.length} total chars)`;
|
|
170
|
+
};
|
|
171
|
+
return commands.map((command) => command.trim()).filter(Boolean).map((command) => {
|
|
172
|
+
try {
|
|
173
|
+
const result = spawnSync(command, {
|
|
174
|
+
cwd,
|
|
175
|
+
encoding: "utf8",
|
|
176
|
+
shell: true,
|
|
177
|
+
timeout,
|
|
178
|
+
maxBuffer: Math.max(maxOutput * 4, 1024 * 1024)
|
|
179
|
+
});
|
|
180
|
+
const output = truncate((result.stdout || "") + (result.stderr || ""));
|
|
181
|
+
return {
|
|
182
|
+
command,
|
|
183
|
+
ok: result.status === 0,
|
|
184
|
+
output: output || (result.status === 0 ? "(command completed with no output)" : `(command failed with exit ${result.status ?? "unknown"})`)
|
|
185
|
+
};
|
|
186
|
+
} catch (error) {
|
|
187
|
+
return {
|
|
188
|
+
command,
|
|
189
|
+
ok: false,
|
|
190
|
+
output: truncate(
|
|
191
|
+
error instanceof Error ? error.message : String(error)
|
|
192
|
+
)
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
});
|
|
156
196
|
}
|
|
157
197
|
function parseEditMetrics(diff) {
|
|
158
198
|
if (!diff || diff.startsWith("(")) {
|
|
@@ -204,5 +244,6 @@ export {
|
|
|
204
244
|
captureGitDiff,
|
|
205
245
|
implementWithClaude,
|
|
206
246
|
parseEditMetrics,
|
|
207
|
-
runPostImplChecks
|
|
247
|
+
runPostImplChecks,
|
|
248
|
+
runVerificationCommands
|
|
208
249
|
};
|
|
@@ -32,14 +32,6 @@ const CANONICAL_HOOKS = [
|
|
|
32
32
|
commandPrefix: "node",
|
|
33
33
|
required: true
|
|
34
34
|
},
|
|
35
|
-
{
|
|
36
|
-
scriptName: "cord-trace.js",
|
|
37
|
-
eventType: "PostToolUse",
|
|
38
|
-
matcher: "mcp__.*__cord_(spawn|fork|complete|ask|tree)",
|
|
39
|
-
timeout: 2,
|
|
40
|
-
commandPrefix: "node",
|
|
41
|
-
required: true
|
|
42
|
-
},
|
|
43
35
|
{
|
|
44
36
|
scriptName: "theory-capture.js",
|
|
45
37
|
eventType: "PostToolUse",
|
|
@@ -89,6 +81,14 @@ const CANONICAL_HOOKS = [
|
|
|
89
81
|
timeout: 10,
|
|
90
82
|
commandPrefix: "node",
|
|
91
83
|
required: false
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
scriptName: "doc-ingest.js",
|
|
87
|
+
eventType: "PostToolUse",
|
|
88
|
+
matcher: "WebFetch",
|
|
89
|
+
timeout: 15,
|
|
90
|
+
commandPrefix: "node",
|
|
91
|
+
required: false
|
|
92
92
|
}
|
|
93
93
|
];
|
|
94
94
|
const DEAD_HOOKS = ["sms-response-handler.js"];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stackmemoryai/stackmemory",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.12.0",
|
|
4
4
|
"description": "Lossless, project-scoped memory for AI coding tools. Durable context across sessions with 56 MCP tools, FTS5 search, conductor orchestrator, loop/watch monitoring, snapshot capture, pre-flight overlap checks, Claude/Codex/OpenCode wrappers, Linear sync, and automatic hooks.",
|
|
5
5
|
"engines": {
|
|
6
6
|
"node": ">=20.0.0",
|
|
@@ -45,6 +45,7 @@
|
|
|
45
45
|
"scripts/verify-dist.cjs",
|
|
46
46
|
"scripts/smoke-init-db.sh",
|
|
47
47
|
"templates",
|
|
48
|
+
"packs",
|
|
48
49
|
"README.md",
|
|
49
50
|
"LICENSE"
|
|
50
51
|
],
|
|
@@ -114,6 +115,11 @@
|
|
|
114
115
|
"test:run": "vitest run",
|
|
115
116
|
"test:pre-publish": "./scripts/test-pre-publish-quick.sh",
|
|
116
117
|
"test:pre-commit": "vitest related --run --reporter=dot --silent --bail=1",
|
|
118
|
+
"determinism:smoke": "node --import tsx src/cli/index.ts bench determinism --task \"Determinism probe\" --runs 5",
|
|
119
|
+
"determinism:watch": "node --import tsx src/cli/index.ts bench determinism --task \"Determinism probe\" --runs 3 --watch",
|
|
120
|
+
"determinism:latest": "node --import tsx src/cli/index.ts bench determinism --latest --json",
|
|
121
|
+
"determinism:test": "npx vitest run src/orchestrators/multimodal/__tests__/determinism.test.ts --reporter=dot",
|
|
122
|
+
"determinism:pre-commit": "bash scripts/determinism-pre-commit.sh",
|
|
117
123
|
"prepublishOnly": "npm run build && npm run verify:dist && npm run test:pre-publish",
|
|
118
124
|
"quality": "npm run lint && npm run test:run && npm run build",
|
|
119
125
|
"dev": "tsx watch src/integrations/mcp/server.ts",
|
|
@@ -136,6 +142,9 @@
|
|
|
136
142
|
"sync:start": "node scripts/background-sync-manager.js",
|
|
137
143
|
"sync:setup": "./scripts/setup-background-sync.sh",
|
|
138
144
|
"eval:cord": "npx tsx scripts/evals/cord-vs-flat-eval.ts",
|
|
145
|
+
"gepa:eval": "node scripts/gepa/eval-phases.js",
|
|
146
|
+
"gepa:eval:json": "node scripts/gepa/eval-phases.js --json",
|
|
147
|
+
"gepa:mine": "node scripts/gepa/gold/mine-traces.js",
|
|
139
148
|
"prepare": "echo 'Prepare step completed'",
|
|
140
149
|
"verify:dist": "node scripts/verify-dist.cjs",
|
|
141
150
|
"test:smoke-db": "bash scripts/smoke-init-db.sh",
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# coding/python-fastapi
|
|
2
|
+
|
|
3
|
+
## Python Conventions
|
|
4
|
+
|
|
5
|
+
- **Python 3.11+.** Use modern syntax: `match/case`, `type` aliases, `ExceptionGroup`.
|
|
6
|
+
- **Type hints everywhere.** All function signatures, return types, and class attributes. Use `from __future__ import annotations` for forward references.
|
|
7
|
+
- **Pydantic v2 for data validation.** `BaseModel` for schemas, `model_validator` for complex validation. Never use raw dicts for API I/O.
|
|
8
|
+
- **Async by default.** Use `async def` for route handlers and DB operations. Use `asyncio.gather` for concurrent I/O.
|
|
9
|
+
- **No global mutable state.** Use dependency injection via FastAPI's `Depends()`.
|
|
10
|
+
- **Naming.** snake_case for functions/variables. PascalCase for classes. UPPER_SNAKE for constants.
|
|
11
|
+
|
|
12
|
+
## FastAPI Patterns
|
|
13
|
+
|
|
14
|
+
- **Router per domain.** `app/routers/users.py`, `app/routers/items.py`. Mount with `app.include_router()`.
|
|
15
|
+
- **Pydantic schemas for I/O.** Separate `Create`, `Update`, `Response` schemas. Never expose ORM models directly.
|
|
16
|
+
- **Dependency injection.** DB sessions, auth, config — all via `Depends()`. Define in `app/dependencies.py`.
|
|
17
|
+
- **Status codes.** Use `status.HTTP_201_CREATED` for creates, `HTTP_204_NO_CONTENT` for deletes. Raise `HTTPException` with proper codes.
|
|
18
|
+
- **Background tasks.** Use `BackgroundTasks` for fire-and-forget. Use Celery/ARQ for durable jobs.
|
|
19
|
+
- **Middleware.** CORS, request logging, error handling. Define in `app/middleware.py`.
|
|
20
|
+
|
|
21
|
+
## Project Structure
|
|
22
|
+
|
|
23
|
+
```
|
|
24
|
+
app/
|
|
25
|
+
main.py # FastAPI app instance + startup
|
|
26
|
+
core/
|
|
27
|
+
config.py # Settings via pydantic-settings
|
|
28
|
+
security.py # Auth utilities
|
|
29
|
+
routers/ # APIRouter modules
|
|
30
|
+
models/ # SQLAlchemy/SQLModel ORM models
|
|
31
|
+
schemas/ # Pydantic request/response models
|
|
32
|
+
dependencies.py # Shared Depends() callables
|
|
33
|
+
middleware.py # Middleware stack
|
|
34
|
+
tests/
|
|
35
|
+
conftest.py # Fixtures (test client, DB)
|
|
36
|
+
test_*.py # Test modules
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Database
|
|
40
|
+
|
|
41
|
+
- **SQLAlchemy 2.0 style.** `select()` not `query()`. Mapped classes with `Mapped[]` annotations.
|
|
42
|
+
- **Alembic for migrations.** Auto-generate with `alembic revision --autogenerate`. Never edit the DB schema manually.
|
|
43
|
+
- **Session management.** Async sessions via `async_sessionmaker`. Yield in dependency.
|
|
44
|
+
- **Connection pooling.** Configure `pool_size`, `max_overflow` in production.
|
|
45
|
+
|
|
46
|
+
## Testing
|
|
47
|
+
|
|
48
|
+
- **pytest + httpx.** Use `AsyncClient` with `app` for integration tests.
|
|
49
|
+
- **Fixtures for DB.** Create test database, run migrations, yield session, rollback.
|
|
50
|
+
- **Factory pattern for test data.** Use `factory_boy` or simple fixture functions.
|
|
51
|
+
- **Test the API, not internals.** Call endpoints via client, assert response shape + status.
|
|
52
|
+
|
|
53
|
+
## Common Anti-Patterns to Catch
|
|
54
|
+
|
|
55
|
+
- Synchronous DB calls in async handlers → blocks the event loop
|
|
56
|
+
- Raw SQL without parameterization → SQL injection risk
|
|
57
|
+
- Returning ORM models from endpoints → use Pydantic response models
|
|
58
|
+
- Missing `async` on route handlers with I/O → blocks worker threads
|
|
59
|
+
- Hardcoded secrets → use environment variables via pydantic-settings
|
|
60
|
+
- Missing input validation → always validate via Pydantic schemas
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
name: coding/python-fastapi
|
|
2
|
+
version: 1.0.0
|
|
3
|
+
description: Python + FastAPI conventions, patterns, and guardrails for AI coding agents
|
|
4
|
+
author: stackmemory
|
|
5
|
+
license: MIT
|
|
6
|
+
runtime:
|
|
7
|
+
type: local
|
|
8
|
+
ingestion:
|
|
9
|
+
sources: []
|
|
10
|
+
ontology:
|
|
11
|
+
entities:
|
|
12
|
+
- endpoint
|
|
13
|
+
- model
|
|
14
|
+
- schema
|
|
15
|
+
- dependency
|
|
16
|
+
- middleware
|
|
17
|
+
relations:
|
|
18
|
+
- handles
|
|
19
|
+
- validates
|
|
20
|
+
- depends-on
|
|
21
|
+
mcp:
|
|
22
|
+
tools: []
|
|
23
|
+
examples:
|
|
24
|
+
- input: "Create a CRUD API endpoint"
|
|
25
|
+
output: "Use Pydantic models for request/response, dependency injection for DB session, proper HTTP status codes, async handlers"
|
|
26
|
+
- input: "How should I structure a FastAPI project?"
|
|
27
|
+
output: "app/ with routers/, models/, schemas/, dependencies/, core/ (config, security). Use APIRouter per domain."
|
|
28
|
+
instructions: instructions.md
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# coding/typescript-react
|
|
2
|
+
|
|
3
|
+
## TypeScript Conventions
|
|
4
|
+
|
|
5
|
+
- **Strict mode always.** `"strict": true` in tsconfig.json. No `any` unless genuinely unavoidable — use `unknown` and narrow.
|
|
6
|
+
- **ESM imports.** Always add `.js` extension to relative imports in ESM projects. Use `type` imports for type-only references.
|
|
7
|
+
- **Prefer interfaces** for object shapes. Use `type` for unions, intersections, and mapped types.
|
|
8
|
+
- **No enums.** Use `as const` objects or union types instead. Enums have runtime cost and poor tree-shaking.
|
|
9
|
+
- **Error handling.** Return `undefined` over throwing. If you must throw, use typed error classes. Never `catch (e: any)`.
|
|
10
|
+
- **Naming.** PascalCase for types/interfaces/components. camelCase for variables/functions. UPPER_SNAKE for constants.
|
|
11
|
+
|
|
12
|
+
## React Patterns
|
|
13
|
+
|
|
14
|
+
- **Functional components only.** No class components.
|
|
15
|
+
- **Custom hooks for data fetching.** Extract `useQuery`/`useMutation` patterns into `use*` hooks. Never fetch in component body.
|
|
16
|
+
- **State management.** useState for local, useReducer for complex local, Context for cross-tree, Zustand/Jotai for global.
|
|
17
|
+
- **Memoization.** Don't prematurely memo. Use `React.memo` only when profiler shows re-render cost. `useMemo`/`useCallback` for referential stability when passed to children.
|
|
18
|
+
- **Keys.** Never use array index as key. Use stable IDs from data.
|
|
19
|
+
- **Error boundaries.** Wrap route-level components. Use `react-error-boundary` library.
|
|
20
|
+
|
|
21
|
+
## File Structure
|
|
22
|
+
|
|
23
|
+
```
|
|
24
|
+
src/
|
|
25
|
+
components/ # Shared UI components
|
|
26
|
+
features/ # Feature-scoped modules (components + hooks + types)
|
|
27
|
+
hooks/ # Shared custom hooks
|
|
28
|
+
lib/ # Non-React utilities
|
|
29
|
+
types/ # Shared type definitions
|
|
30
|
+
routes/ # Route components (if not using file-based routing)
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Testing
|
|
34
|
+
|
|
35
|
+
- **Vitest or Jest** for unit tests. React Testing Library for component tests.
|
|
36
|
+
- **Test behavior, not implementation.** Query by role/text, not test-id.
|
|
37
|
+
- **No snapshot tests** unless testing serialized output.
|
|
38
|
+
- **Mock at boundaries.** Mock API calls (MSW), not internal modules.
|
|
39
|
+
|
|
40
|
+
## Common Anti-Patterns to Catch
|
|
41
|
+
|
|
42
|
+
- `useEffect` with missing dependencies → use ESLint exhaustive-deps rule
|
|
43
|
+
- Prop drilling > 2 levels → extract to Context or composition
|
|
44
|
+
- Giant components > 200 lines → split into smaller components
|
|
45
|
+
- Inline styles → use CSS modules, Tailwind, or styled-components
|
|
46
|
+
- `any` type assertions → narrow with type guards
|
|
47
|
+
- Non-null assertions (`!`) → handle the null case explicitly
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
name: coding/typescript-react
|
|
2
|
+
version: 1.0.0
|
|
3
|
+
description: TypeScript + React conventions, patterns, and guardrails for AI coding agents
|
|
4
|
+
author: stackmemory
|
|
5
|
+
license: MIT
|
|
6
|
+
runtime:
|
|
7
|
+
type: local
|
|
8
|
+
ingestion:
|
|
9
|
+
sources: []
|
|
10
|
+
ontology:
|
|
11
|
+
entities:
|
|
12
|
+
- component
|
|
13
|
+
- hook
|
|
14
|
+
- context
|
|
15
|
+
- route
|
|
16
|
+
- api-endpoint
|
|
17
|
+
relations:
|
|
18
|
+
- renders
|
|
19
|
+
- depends-on
|
|
20
|
+
- provides
|
|
21
|
+
mcp:
|
|
22
|
+
tools: []
|
|
23
|
+
examples:
|
|
24
|
+
- input: "Create a React component that fetches data"
|
|
25
|
+
output: "Use a custom hook with useEffect + useState, handle loading/error states, return typed JSX"
|
|
26
|
+
- input: "How should I structure my TypeScript types?"
|
|
27
|
+
output: "Prefer interfaces for object shapes, use type for unions/intersections, export from a types.ts barrel file"
|
|
28
|
+
instructions: instructions.md
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# /capture — Save session context via StackMemory
|
|
2
|
+
|
|
3
|
+
Run `stackmemory capture` to commit current work and generate a handoff prompt for the next session.
|
|
4
|
+
|
|
5
|
+
## Usage
|
|
6
|
+
|
|
7
|
+
`$ARGUMENTS` — optional flags passed directly to `stackmemory capture`.
|
|
8
|
+
|
|
9
|
+
## Behavior
|
|
10
|
+
|
|
11
|
+
1. Run `stackmemory capture $ARGUMENTS`
|
|
12
|
+
2. Display the generated handoff prompt
|
|
13
|
+
3. If `--copy` flag is present, confirm the prompt was copied to clipboard
|
|
14
|
+
|
|
15
|
+
## Common flags
|
|
16
|
+
|
|
17
|
+
| Flag | Effect |
|
|
18
|
+
|------|--------|
|
|
19
|
+
| `-m "message"` | Custom commit message |
|
|
20
|
+
| `--no-commit` | Skip git commit, just generate handoff |
|
|
21
|
+
| `--copy` | Copy handoff prompt to clipboard |
|
|
22
|
+
| `--format ultra` | Ultra-compact pipe-delimited format |
|
|
23
|
+
| `--format verbose` | Full markdown format |
|
|
24
|
+
|
|
25
|
+
## Examples
|
|
26
|
+
|
|
27
|
+
```
|
|
28
|
+
/capture # Default capture with auto-format
|
|
29
|
+
/capture -m "privacy policy PR" # With custom message
|
|
30
|
+
/capture --copy # Capture and copy to clipboard
|
|
31
|
+
/capture --no-commit --copy # Just generate handoff, no commit
|
|
32
|
+
```
|