@stackmemoryai/stackmemory 1.2.2 → 1.2.6
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 +10 -6
- package/dist/src/cli/claude-sm.js +33 -4
- package/dist/src/cli/codex-sm-danger.js +4 -1
- package/dist/src/cli/codex-sm.js +6 -8
- package/dist/src/cli/commands/config.js +0 -81
- package/dist/src/cli/commands/context-rehydrate.js +133 -47
- package/dist/src/cli/commands/db.js +35 -8
- package/dist/src/cli/commands/handoff.js +1 -1
- package/dist/src/cli/commands/linear.js +9 -0
- package/dist/src/cli/commands/ralph.js +2 -2
- package/dist/src/cli/commands/setup.js +2 -2
- package/dist/src/cli/commands/signup.js +3 -1
- package/dist/src/cli/commands/skills.js +108 -1
- package/dist/src/cli/commands/storage-tier.js +26 -8
- package/dist/src/cli/index.js +1 -57
- package/dist/src/core/config/feature-flags.js +0 -4
- package/dist/src/core/context/dual-stack-manager.js +10 -3
- package/dist/src/core/context/frame-database.js +32 -0
- package/dist/src/core/context/frame-handoff-manager.js +2 -2
- package/dist/src/core/context/{refactored-frame-manager.js → frame-manager.js} +3 -3
- package/dist/src/core/context/index.js +2 -2
- package/dist/src/core/database/sqlite-adapter.js +161 -1
- package/dist/src/core/digest/frame-digest-integration.js +1 -1
- package/dist/src/core/digest/index.js +1 -1
- package/dist/src/core/execution/parallel-executor.js +5 -1
- package/dist/src/core/projects/project-isolation.js +18 -4
- package/dist/src/core/security/index.js +2 -0
- package/dist/src/core/security/input-sanitizer.js +23 -0
- package/dist/src/core/utils/update-checker.js +10 -6
- package/dist/src/daemon/daemon-config.js +2 -1
- package/dist/src/daemon/services/auto-save-service.js +121 -0
- package/dist/src/daemon/services/maintenance-service.js +76 -1
- package/dist/src/features/sweep/prompt-builder.js +2 -2
- package/dist/src/hooks/daemon.js +0 -8
- package/dist/src/integrations/linear/config.js +3 -1
- package/dist/src/integrations/linear/sync.js +18 -5
- package/dist/src/integrations/linear/webhook.js +0 -16
- package/dist/src/integrations/mcp/handlers/code-execution-handlers.js +33 -7
- package/dist/src/integrations/mcp/handlers/cord-handlers.js +397 -0
- package/dist/src/integrations/mcp/handlers/index.js +55 -9
- package/dist/src/integrations/mcp/handlers/task-handlers.js +55 -12
- package/dist/src/integrations/mcp/handlers/team-handlers.js +211 -0
- package/dist/src/integrations/mcp/handlers/trace-handlers.js +28 -9
- package/dist/src/integrations/mcp/index.js +2 -2
- package/dist/src/integrations/mcp/refactored-server.js +31 -10
- package/dist/src/integrations/mcp/server.js +0 -130
- package/dist/src/integrations/mcp/tool-definitions.js +196 -67
- package/dist/src/integrations/ralph/context/context-budget-manager.js +10 -2
- package/dist/src/integrations/ralph/context/stackmemory-context-loader.js +54 -22
- package/dist/src/integrations/ralph/learning/pattern-learner.js +59 -24
- package/dist/src/integrations/ralph/orchestration/multi-loop-orchestrator.js +81 -35
- package/dist/src/integrations/ralph/patterns/compounding-engineering-pattern.js +12 -4
- package/dist/src/integrations/ralph/patterns/extended-coherence-sessions.js +32 -9
- package/dist/src/integrations/ralph/swarm/git-workflow-manager.js +25 -8
- package/dist/src/integrations/ralph/swarm/swarm-coordinator.js +17 -5
- package/dist/src/integrations/ralph/visualization/ralph-debugger.js +73 -22
- package/dist/src/skills/claude-skills.js +37 -103
- package/dist/src/skills/theory-skill.js +191 -0
- package/dist/src/utils/hook-installer.js +16 -0
- package/package.json +6 -6
- package/scripts/install-claude-hooks-auto.js +8 -0
- package/templates/claude-hooks/cord-trace.js +225 -0
- package/templates/claude-hooks/theory-capture.js +100 -0
- package/dist/src/core/config/storage-config.js +0 -114
- package/dist/src/core/storage/chromadb-adapter.js +0 -379
- package/dist/src/hooks/graphiti-hooks.js +0 -253
- package/dist/src/integrations/claude-code/enhanced-pre-clear-hooks.js +0 -458
- package/dist/src/integrations/graphiti/client.js +0 -115
- package/dist/src/integrations/graphiti/config.js +0 -17
- package/dist/src/integrations/graphiti/linear-graphiti-bridge.js +0 -115
- package/dist/src/integrations/graphiti/types.js +0 -4
- package/dist/src/integrations/ralph/coordination/enhanced-coordination.js +0 -409
- package/dist/src/skills/repo-ingestion-skill.js +0 -631
- package/templates/claude-hooks/chromadb-wrapper +0 -21
- /package/dist/src/core/context/{enhanced-rehydration.js → rehydration.js} +0 -0
- /package/dist/src/core/digest/{enhanced-hybrid-digest.js → hybrid-digest.js} +0 -0
- /package/dist/src/core/session/{enhanced-handoff.js → handoff.js} +0 -0
|
@@ -27,7 +27,10 @@ class RalphDebugger {
|
|
|
27
27
|
await sessionManager.initialize();
|
|
28
28
|
const session = await sessionManager.getOrCreateSession({});
|
|
29
29
|
if (session.database) {
|
|
30
|
-
this.frameManager = new FrameManager(
|
|
30
|
+
this.frameManager = new FrameManager(
|
|
31
|
+
session.database,
|
|
32
|
+
session.projectId
|
|
33
|
+
);
|
|
31
34
|
}
|
|
32
35
|
logger.info("Debugger initialized successfully");
|
|
33
36
|
} catch (error) {
|
|
@@ -111,7 +114,10 @@ class RalphDebugger {
|
|
|
111
114
|
contextSize: iter.contextSize,
|
|
112
115
|
phase: iter.phase
|
|
113
116
|
})),
|
|
114
|
-
totalDuration: session.performance.iterationTimes.reduce(
|
|
117
|
+
totalDuration: session.performance.iterationTimes.reduce(
|
|
118
|
+
(sum, time) => sum + time,
|
|
119
|
+
0
|
|
120
|
+
)
|
|
115
121
|
};
|
|
116
122
|
const html = await this.generateTimelineHTML(timeline);
|
|
117
123
|
const timelinePath = path.join(".ralph-debug", `timeline-${loopId}.html`);
|
|
@@ -165,7 +171,10 @@ class RalphDebugger {
|
|
|
165
171
|
diagram.metrics = {
|
|
166
172
|
totalNodes: diagram.nodes.length,
|
|
167
173
|
totalEdges: diagram.edges.length,
|
|
168
|
-
avgContextSize: session.performance.contextSizes.length > 0 ? session.performance.contextSizes.reduce(
|
|
174
|
+
avgContextSize: session.performance.contextSizes.length > 0 ? session.performance.contextSizes.reduce(
|
|
175
|
+
(sum, size) => sum + size,
|
|
176
|
+
0
|
|
177
|
+
) / session.performance.contextSizes.length : 0,
|
|
169
178
|
maxContextSize: Math.max(...session.performance.contextSizes)
|
|
170
179
|
};
|
|
171
180
|
return diagram;
|
|
@@ -230,15 +239,24 @@ class RalphDebugger {
|
|
|
230
239
|
async updatePerformanceMetrics(session) {
|
|
231
240
|
const currentMemory = process.memoryUsage().heapUsed;
|
|
232
241
|
session.performance.memoryUsage.push(currentMemory);
|
|
233
|
-
session.performance.peakMemory = Math.max(
|
|
242
|
+
session.performance.peakMemory = Math.max(
|
|
243
|
+
session.performance.peakMemory,
|
|
244
|
+
currentMemory
|
|
245
|
+
);
|
|
234
246
|
const contextSize = await this.calculateContextSize(session.ralphDir);
|
|
235
247
|
session.performance.contextSizes.push(contextSize);
|
|
236
248
|
if (session.performance.iterationTimes.length > 0) {
|
|
237
|
-
session.performance.averageIterationTime = session.performance.iterationTimes.reduce(
|
|
249
|
+
session.performance.averageIterationTime = session.performance.iterationTimes.reduce(
|
|
250
|
+
(sum, time) => sum + time,
|
|
251
|
+
0
|
|
252
|
+
) / session.performance.iterationTimes.length;
|
|
238
253
|
}
|
|
239
254
|
if (session.performance.contextSizes.length > 0) {
|
|
240
255
|
const avgContextSize = session.performance.contextSizes.reduce((sum, size) => sum + size, 0) / session.performance.contextSizes.length;
|
|
241
|
-
session.performance.contextEfficiency = Math.max(
|
|
256
|
+
session.performance.contextEfficiency = Math.max(
|
|
257
|
+
0,
|
|
258
|
+
1 - avgContextSize / 1e4
|
|
259
|
+
);
|
|
242
260
|
}
|
|
243
261
|
}
|
|
244
262
|
/**
|
|
@@ -246,8 +264,13 @@ class RalphDebugger {
|
|
|
246
264
|
*/
|
|
247
265
|
async generateSummary(session) {
|
|
248
266
|
const totalIterations = session.iterations.length;
|
|
249
|
-
const successfulIterations = session.iterations.filter(
|
|
250
|
-
|
|
267
|
+
const successfulIterations = session.iterations.filter(
|
|
268
|
+
(i) => i.success
|
|
269
|
+
).length;
|
|
270
|
+
const totalDuration = session.performance.iterationTimes.reduce(
|
|
271
|
+
(sum, time) => sum + time,
|
|
272
|
+
0
|
|
273
|
+
);
|
|
251
274
|
return {
|
|
252
275
|
loopId: session.loopId,
|
|
253
276
|
totalIterations,
|
|
@@ -271,18 +294,26 @@ class RalphDebugger {
|
|
|
271
294
|
const avgDuration = durations.reduce((sum, d) => sum + d, 0) / durations.length;
|
|
272
295
|
if (durations.some((d) => d > avgDuration * 2)) {
|
|
273
296
|
patterns.push("Variable iteration times detected");
|
|
274
|
-
insights.push(
|
|
297
|
+
insights.push(
|
|
298
|
+
"Some iterations took significantly longer than average - investigate bottlenecks"
|
|
299
|
+
);
|
|
275
300
|
}
|
|
276
|
-
const consecutiveFailures = this.findConsecutiveFailures(
|
|
301
|
+
const consecutiveFailures = this.findConsecutiveFailures(
|
|
302
|
+
session.iterations
|
|
303
|
+
);
|
|
277
304
|
if (consecutiveFailures.length > 2) {
|
|
278
305
|
patterns.push("Multiple consecutive failures detected");
|
|
279
|
-
insights.push(
|
|
306
|
+
insights.push(
|
|
307
|
+
"Consider adjusting approach or criteria after consecutive failures"
|
|
308
|
+
);
|
|
280
309
|
}
|
|
281
310
|
if (session.performance.contextSizes.length > 1) {
|
|
282
311
|
const contextGrowth = session.performance.contextSizes[session.performance.contextSizes.length - 1] - session.performance.contextSizes[0];
|
|
283
312
|
if (contextGrowth > 1e3) {
|
|
284
313
|
patterns.push("Significant context growth");
|
|
285
|
-
insights.push(
|
|
314
|
+
insights.push(
|
|
315
|
+
"Context is growing rapidly - consider context pruning strategies"
|
|
316
|
+
);
|
|
286
317
|
}
|
|
287
318
|
}
|
|
288
319
|
return { patterns, insights };
|
|
@@ -292,9 +323,14 @@ class RalphDebugger {
|
|
|
292
323
|
*/
|
|
293
324
|
async analyzeContextFlow(session) {
|
|
294
325
|
return {
|
|
295
|
-
avgContextSize: session.performance.contextSizes.length > 0 ? session.performance.contextSizes.reduce(
|
|
326
|
+
avgContextSize: session.performance.contextSizes.length > 0 ? session.performance.contextSizes.reduce(
|
|
327
|
+
(sum, size) => sum + size,
|
|
328
|
+
0
|
|
329
|
+
) / session.performance.contextSizes.length : 0,
|
|
296
330
|
maxContextSize: Math.max(...session.performance.contextSizes),
|
|
297
|
-
contextGrowthRate: this.calculateGrowthRate(
|
|
331
|
+
contextGrowthRate: this.calculateGrowthRate(
|
|
332
|
+
session.performance.contextSizes
|
|
333
|
+
),
|
|
298
334
|
efficiency: session.performance.contextEfficiency
|
|
299
335
|
};
|
|
300
336
|
}
|
|
@@ -303,7 +339,9 @@ class RalphDebugger {
|
|
|
303
339
|
*/
|
|
304
340
|
async analyzePerformance(session) {
|
|
305
341
|
return {
|
|
306
|
-
memoryEfficiency: this.calculateMemoryEfficiency(
|
|
342
|
+
memoryEfficiency: this.calculateMemoryEfficiency(
|
|
343
|
+
session.performance.memoryUsage
|
|
344
|
+
),
|
|
307
345
|
iterationEfficiency: session.performance.averageIterationTime,
|
|
308
346
|
resourceUtilization: {
|
|
309
347
|
cpu: "N/A",
|
|
@@ -318,7 +356,10 @@ class RalphDebugger {
|
|
|
318
356
|
*/
|
|
319
357
|
async generateVisualization(session) {
|
|
320
358
|
const htmlContent = await this.generateVisualizationHTML(session);
|
|
321
|
-
const vizPath = path.join(
|
|
359
|
+
const vizPath = path.join(
|
|
360
|
+
".ralph-debug",
|
|
361
|
+
`visualization-${session.loopId}.html`
|
|
362
|
+
);
|
|
322
363
|
await fs.mkdir(path.dirname(vizPath), { recursive: true });
|
|
323
364
|
await fs.writeFile(vizPath, htmlContent);
|
|
324
365
|
return {
|
|
@@ -343,17 +384,25 @@ class RalphDebugger {
|
|
|
343
384
|
async generateRecommendations(session) {
|
|
344
385
|
const recommendations = [];
|
|
345
386
|
if (session.performance.averageIterationTime > 3e4) {
|
|
346
|
-
recommendations.push(
|
|
387
|
+
recommendations.push(
|
|
388
|
+
"Consider breaking down complex tasks into smaller iterations"
|
|
389
|
+
);
|
|
347
390
|
}
|
|
348
391
|
if (session.performance.contextEfficiency < 0.7) {
|
|
349
|
-
recommendations.push(
|
|
392
|
+
recommendations.push(
|
|
393
|
+
"Optimize context management - consider using context budgeting"
|
|
394
|
+
);
|
|
350
395
|
}
|
|
351
396
|
const successRate = session.iterations.filter((i) => i.success).length / Math.max(1, session.iterations.length);
|
|
352
397
|
if (successRate < 0.5) {
|
|
353
|
-
recommendations.push(
|
|
398
|
+
recommendations.push(
|
|
399
|
+
"Low success rate detected - review task criteria and approach"
|
|
400
|
+
);
|
|
354
401
|
}
|
|
355
402
|
if (session.performance.peakMemory > 500 * 1024 * 1024) {
|
|
356
|
-
recommendations.push(
|
|
403
|
+
recommendations.push(
|
|
404
|
+
"High memory usage detected - investigate memory leaks"
|
|
405
|
+
);
|
|
357
406
|
}
|
|
358
407
|
return recommendations;
|
|
359
408
|
}
|
|
@@ -450,14 +499,16 @@ class RalphDebugger {
|
|
|
450
499
|
<body>
|
|
451
500
|
<h1>${timeline.title}</h1>
|
|
452
501
|
<div class="timeline">
|
|
453
|
-
${timeline.iterations.map(
|
|
502
|
+
${timeline.iterations.map(
|
|
503
|
+
(iter) => `
|
|
454
504
|
<div class="iteration ${iter.success ? "success" : "failure"}">
|
|
455
505
|
<h3>Iteration ${iter.iteration}</h3>
|
|
456
506
|
<p>Duration: ${iter.duration}ms</p>
|
|
457
507
|
<p>Changes: ${iter.changes} | Errors: ${iter.errors}</p>
|
|
458
508
|
<p>Context Size: ${iter.contextSize}</p>
|
|
459
509
|
</div>
|
|
460
|
-
`
|
|
510
|
+
`
|
|
511
|
+
).join("")}
|
|
461
512
|
</div>
|
|
462
513
|
</body>
|
|
463
514
|
</html>
|
|
@@ -3,9 +3,6 @@ import { dirname as __pathDirname } from 'path';
|
|
|
3
3
|
const __filename = __fileURLToPath(import.meta.url);
|
|
4
4
|
const __dirname = __pathDirname(__filename);
|
|
5
5
|
import { logger } from "../core/monitoring/logger.js";
|
|
6
|
-
import {
|
|
7
|
-
RepoIngestionSkill
|
|
8
|
-
} from "./repo-ingestion-skill.js";
|
|
9
6
|
import {
|
|
10
7
|
RecursiveAgentOrchestrator
|
|
11
8
|
} from "./recursive-agent-orchestrator.js";
|
|
@@ -617,22 +614,6 @@ class ClaudeSkillsManager {
|
|
|
617
614
|
this.dashboardLauncher = new module.DashboardLauncherSkill();
|
|
618
615
|
logger.info("Dashboard launcher initialized (manual launch required)");
|
|
619
616
|
});
|
|
620
|
-
const chromaConfig = {
|
|
621
|
-
apiKey: process.env["CHROMADB_API_KEY"] || "",
|
|
622
|
-
tenant: process.env["CHROMADB_TENANT"] || "",
|
|
623
|
-
database: process.env["CHROMADB_DATABASE"] || "stackmemory",
|
|
624
|
-
collectionName: process.env["CHROMADB_COLLECTION"] || "stackmemory_repos"
|
|
625
|
-
};
|
|
626
|
-
if (chromaConfig.apiKey && chromaConfig.tenant) {
|
|
627
|
-
this.repoIngestionSkill = new RepoIngestionSkill(
|
|
628
|
-
chromaConfig,
|
|
629
|
-
context.userId,
|
|
630
|
-
process.env["CHROMADB_TEAM_ID"]
|
|
631
|
-
);
|
|
632
|
-
this.repoIngestionSkill.initialize().catch((error) => {
|
|
633
|
-
logger.warn("Repo ingestion skill initialization failed:", error);
|
|
634
|
-
});
|
|
635
|
-
}
|
|
636
617
|
import("../features/tasks/linear-task-manager.js").then((module) => {
|
|
637
618
|
const taskStore = new module.LinearTaskManager();
|
|
638
619
|
const frameManager = context.frameManager;
|
|
@@ -662,7 +643,6 @@ class ClaudeSkillsManager {
|
|
|
662
643
|
checkpointSkill;
|
|
663
644
|
archaeologistSkill;
|
|
664
645
|
dashboardLauncher;
|
|
665
|
-
repoIngestionSkill = null;
|
|
666
646
|
rlmOrchestrator = null;
|
|
667
647
|
apiSkill;
|
|
668
648
|
specGeneratorSkill;
|
|
@@ -738,64 +718,6 @@ class ClaudeSkillsManager {
|
|
|
738
718
|
};
|
|
739
719
|
}
|
|
740
720
|
return this.rlmOrchestrator.execute(args[0], options);
|
|
741
|
-
case "repo":
|
|
742
|
-
case "ingest":
|
|
743
|
-
if (!this.repoIngestionSkill) {
|
|
744
|
-
return {
|
|
745
|
-
success: false,
|
|
746
|
-
message: "Repo ingestion skill not initialized. Please configure ChromaDB."
|
|
747
|
-
};
|
|
748
|
-
}
|
|
749
|
-
const repoCommand = args[0];
|
|
750
|
-
switch (repoCommand) {
|
|
751
|
-
case "ingest":
|
|
752
|
-
const repoPath = args[1] || process.cwd();
|
|
753
|
-
const repoName = args[2] || path.basename(repoPath);
|
|
754
|
-
return await this.repoIngestionSkill.ingestRepository(
|
|
755
|
-
repoPath,
|
|
756
|
-
repoName,
|
|
757
|
-
options
|
|
758
|
-
);
|
|
759
|
-
case "update":
|
|
760
|
-
const updatePath = args[1] || process.cwd();
|
|
761
|
-
const updateName = args[2] || path.basename(updatePath);
|
|
762
|
-
return await this.repoIngestionSkill.updateRepository(
|
|
763
|
-
updatePath,
|
|
764
|
-
updateName,
|
|
765
|
-
options
|
|
766
|
-
);
|
|
767
|
-
case "search":
|
|
768
|
-
const query = args[1];
|
|
769
|
-
if (!query) {
|
|
770
|
-
return {
|
|
771
|
-
success: false,
|
|
772
|
-
message: "Search query required"
|
|
773
|
-
};
|
|
774
|
-
}
|
|
775
|
-
const results = await this.repoIngestionSkill.searchCode(query, {
|
|
776
|
-
repoName: options?.repoName,
|
|
777
|
-
language: options?.language,
|
|
778
|
-
limit: options?.limit,
|
|
779
|
-
includeContext: options?.includeContext
|
|
780
|
-
});
|
|
781
|
-
return {
|
|
782
|
-
success: true,
|
|
783
|
-
message: `Found ${results.length} results`,
|
|
784
|
-
data: results
|
|
785
|
-
};
|
|
786
|
-
case "stats":
|
|
787
|
-
const stats = await this.repoIngestionSkill.getRepoStats(args[1]);
|
|
788
|
-
return {
|
|
789
|
-
success: true,
|
|
790
|
-
message: "Repository statistics",
|
|
791
|
-
data: stats
|
|
792
|
-
};
|
|
793
|
-
default:
|
|
794
|
-
return {
|
|
795
|
-
success: false,
|
|
796
|
-
message: `Unknown repo command: ${repoCommand}. Use: ingest, update, search, or stats`
|
|
797
|
-
};
|
|
798
|
-
}
|
|
799
721
|
case "dashboard":
|
|
800
722
|
const dashboardCmd = args[0];
|
|
801
723
|
if (!this.dashboardLauncher) {
|
|
@@ -967,6 +889,25 @@ class ClaudeSkillsManager {
|
|
|
967
889
|
};
|
|
968
890
|
}
|
|
969
891
|
}
|
|
892
|
+
case "theory": {
|
|
893
|
+
const { TheorySkill } = await import("./theory-skill.js");
|
|
894
|
+
const theorySkill = new TheorySkill(this.context);
|
|
895
|
+
switch (args[0]) {
|
|
896
|
+
case "show":
|
|
897
|
+
return theorySkill.show();
|
|
898
|
+
case "update":
|
|
899
|
+
return theorySkill.update(args.slice(1).join(" "));
|
|
900
|
+
case "init":
|
|
901
|
+
return theorySkill.init(args.slice(1).join(" "));
|
|
902
|
+
case "status":
|
|
903
|
+
return theorySkill.status();
|
|
904
|
+
default:
|
|
905
|
+
return {
|
|
906
|
+
success: false,
|
|
907
|
+
message: "Usage: theory show|init|update|status"
|
|
908
|
+
};
|
|
909
|
+
}
|
|
910
|
+
}
|
|
970
911
|
case "linear-run": {
|
|
971
912
|
if (!this.linearTaskRunner) {
|
|
972
913
|
return {
|
|
@@ -1009,11 +950,9 @@ class ClaudeSkillsManager {
|
|
|
1009
950
|
"dashboard",
|
|
1010
951
|
"api",
|
|
1011
952
|
"spec",
|
|
1012
|
-
"agent"
|
|
953
|
+
"agent",
|
|
954
|
+
"theory"
|
|
1013
955
|
];
|
|
1014
|
-
if (this.repoIngestionSkill) {
|
|
1015
|
-
skills.push("repo");
|
|
1016
|
-
}
|
|
1017
956
|
if (this.rlmOrchestrator) {
|
|
1018
957
|
skills.push("rlm", "lint");
|
|
1019
958
|
}
|
|
@@ -1097,27 +1036,6 @@ Launch the StackMemory web dashboard for real-time monitoring
|
|
|
1097
1036
|
- launch: Start the web dashboard and open in browser (default)
|
|
1098
1037
|
- stop: Stop the dashboard server
|
|
1099
1038
|
Auto-launches on new sessions when configured
|
|
1100
|
-
`;
|
|
1101
|
-
case "repo":
|
|
1102
|
-
return `
|
|
1103
|
-
/repo ingest [path] [name] [--incremental] [--include-tests] [--include-docs]
|
|
1104
|
-
/repo update [path] [name] [--force-update]
|
|
1105
|
-
/repo search "query" [--repo-name name] [--language lang] [--limit n]
|
|
1106
|
-
/repo stats [repo-name]
|
|
1107
|
-
|
|
1108
|
-
Ingest and search code repositories in ChromaDB:
|
|
1109
|
-
- ingest: Index a new repository (defaults to current directory)
|
|
1110
|
-
- update: Update an existing repository with changes
|
|
1111
|
-
- search: Semantic search across ingested code
|
|
1112
|
-
- stats: View statistics about ingested repositories
|
|
1113
|
-
|
|
1114
|
-
Options:
|
|
1115
|
-
- --incremental: Only process changed files
|
|
1116
|
-
- --include-tests: Include test files in indexing
|
|
1117
|
-
- --include-docs: Include documentation files
|
|
1118
|
-
- --force-update: Force re-indexing of all files
|
|
1119
|
-
- --language: Filter search by programming language
|
|
1120
|
-
- --limit: Maximum search results (default: 20)
|
|
1121
1039
|
`;
|
|
1122
1040
|
case "recursive":
|
|
1123
1041
|
return `
|
|
@@ -1190,6 +1108,22 @@ Options:
|
|
|
1190
1108
|
Each agent runs in a disposable /tmp workspace (git clone --depth=1).
|
|
1191
1109
|
Patches: git apply .stackmemory/patches/<file>.patch
|
|
1192
1110
|
Spec branches: cd /tmp/sm-spec-* && git log --oneline
|
|
1111
|
+
`;
|
|
1112
|
+
case "theory":
|
|
1113
|
+
return `
|
|
1114
|
+
/theory show
|
|
1115
|
+
/theory init "<problem statement>"
|
|
1116
|
+
/theory update "<full content>"
|
|
1117
|
+
/theory status
|
|
1118
|
+
|
|
1119
|
+
Maintain a living THEORY.MD at repo root \u2014 a narrative operating theory:
|
|
1120
|
+
show \u2014 Display current THEORY.MD content
|
|
1121
|
+
init \u2014 Create THEORY.MD with scaffold sections
|
|
1122
|
+
update \u2014 Overwrite THEORY.MD (validates length, warns on anti-patterns)
|
|
1123
|
+
status \u2014 Show metadata: exists, line count, sections, last modified
|
|
1124
|
+
|
|
1125
|
+
Sections: Problem, Operating Theory, Strategy, Key Discoveries, Open Questions
|
|
1126
|
+
Based on Theorist by @blader (MIT).
|
|
1193
1127
|
`;
|
|
1194
1128
|
case "linear-run":
|
|
1195
1129
|
return `
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
import { fileURLToPath as __fileURLToPath } from 'url';
|
|
2
|
+
import { dirname as __pathDirname } from 'path';
|
|
3
|
+
const __filename = __fileURLToPath(import.meta.url);
|
|
4
|
+
const __dirname = __pathDirname(__filename);
|
|
5
|
+
import * as fs from "fs";
|
|
6
|
+
import * as path from "path";
|
|
7
|
+
import { execSync } from "child_process";
|
|
8
|
+
import { logger } from "../core/monitoring/logger.js";
|
|
9
|
+
const THEORY_FILE = "THEORY.MD";
|
|
10
|
+
const MIN_CONTENT_LENGTH = 100;
|
|
11
|
+
const MAX_LINE_WARNING = 200;
|
|
12
|
+
const TEMPLATE_SECTIONS = [
|
|
13
|
+
"## Problem",
|
|
14
|
+
"## Operating Theory",
|
|
15
|
+
"## Strategy",
|
|
16
|
+
"## Key Discoveries",
|
|
17
|
+
"## Open Questions"
|
|
18
|
+
];
|
|
19
|
+
function getGitRoot() {
|
|
20
|
+
try {
|
|
21
|
+
return execSync("git rev-parse --show-toplevel", {
|
|
22
|
+
encoding: "utf-8",
|
|
23
|
+
timeout: 5e3
|
|
24
|
+
}).trim();
|
|
25
|
+
} catch {
|
|
26
|
+
return void 0;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
function generateTemplate(problemStatement) {
|
|
30
|
+
return `# THEORY.MD
|
|
31
|
+
|
|
32
|
+
## Problem
|
|
33
|
+
|
|
34
|
+
${problemStatement}
|
|
35
|
+
|
|
36
|
+
## Operating Theory
|
|
37
|
+
|
|
38
|
+
_What is your current mental model of how this system works or should work?_
|
|
39
|
+
|
|
40
|
+
## Strategy
|
|
41
|
+
|
|
42
|
+
_What approach are you taking and why?_
|
|
43
|
+
|
|
44
|
+
## Key Discoveries
|
|
45
|
+
|
|
46
|
+
_What have you learned that changed your thinking?_
|
|
47
|
+
|
|
48
|
+
## Open Questions
|
|
49
|
+
|
|
50
|
+
_What don't you know yet? What assumptions need validation?_
|
|
51
|
+
`;
|
|
52
|
+
}
|
|
53
|
+
class TheorySkill {
|
|
54
|
+
constructor(context) {
|
|
55
|
+
this.context = context;
|
|
56
|
+
this.rootDir = getGitRoot() || process.cwd();
|
|
57
|
+
}
|
|
58
|
+
rootDir;
|
|
59
|
+
get theoryPath() {
|
|
60
|
+
return path.join(this.rootDir, THEORY_FILE);
|
|
61
|
+
}
|
|
62
|
+
show() {
|
|
63
|
+
if (!fs.existsSync(this.theoryPath)) {
|
|
64
|
+
return {
|
|
65
|
+
success: false,
|
|
66
|
+
message: `No ${THEORY_FILE} found. Run \`theory init "<problem>"\` to create one.`
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
const content = fs.readFileSync(this.theoryPath, "utf-8");
|
|
70
|
+
return {
|
|
71
|
+
success: true,
|
|
72
|
+
message: content,
|
|
73
|
+
data: { path: this.theoryPath, length: content.length }
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
init(problemStatement) {
|
|
77
|
+
if (!problemStatement || problemStatement.trim().length === 0) {
|
|
78
|
+
return {
|
|
79
|
+
success: false,
|
|
80
|
+
message: "A problem statement is required to initialize THEORY.MD."
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
if (fs.existsSync(this.theoryPath)) {
|
|
84
|
+
return {
|
|
85
|
+
success: false,
|
|
86
|
+
message: `${THEORY_FILE} already exists at ${this.theoryPath}. Use \`theory update\` to modify it.`
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
const content = generateTemplate(problemStatement.trim());
|
|
90
|
+
fs.writeFileSync(this.theoryPath, content, "utf-8");
|
|
91
|
+
logger.info("Created THEORY.MD", { path: this.theoryPath });
|
|
92
|
+
return {
|
|
93
|
+
success: true,
|
|
94
|
+
message: `Created ${THEORY_FILE} at ${this.theoryPath}`,
|
|
95
|
+
data: { path: this.theoryPath, sections: TEMPLATE_SECTIONS }
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
update(content) {
|
|
99
|
+
if (!content || content.trim().length === 0) {
|
|
100
|
+
return { success: false, message: "Content is required for update." };
|
|
101
|
+
}
|
|
102
|
+
if (content.trim().length < MIN_CONTENT_LENGTH) {
|
|
103
|
+
return {
|
|
104
|
+
success: false,
|
|
105
|
+
message: `Content too short (${content.trim().length} chars). THEORY.MD should be at least ${MIN_CONTENT_LENGTH} characters to be meaningful.`
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
const warnings = [];
|
|
109
|
+
if (/\[[ x]\]/i.test(content)) {
|
|
110
|
+
warnings.push(
|
|
111
|
+
"Contains checkboxes \u2014 THEORY.MD is narrative, not a checklist."
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
if (/\d{4}-\d{2}-\d{2}/.test(content)) {
|
|
115
|
+
warnings.push(
|
|
116
|
+
"Contains dates \u2014 THEORY.MD captures current thinking, not a changelog."
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
const lineCount = content.split("\n").length;
|
|
120
|
+
if (lineCount > MAX_LINE_WARNING) {
|
|
121
|
+
warnings.push(
|
|
122
|
+
`${lineCount} lines is long. Consider distilling to keep THEORY.MD focused.`
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
fs.writeFileSync(this.theoryPath, content, "utf-8");
|
|
126
|
+
if (this.context.frameManager) {
|
|
127
|
+
try {
|
|
128
|
+
const frameId = this.context.frameManager.createFrame(
|
|
129
|
+
"write",
|
|
130
|
+
"theory-update",
|
|
131
|
+
{ source: THEORY_FILE, length: content.length }
|
|
132
|
+
);
|
|
133
|
+
this.context.frameManager.addEvent(
|
|
134
|
+
"artifact",
|
|
135
|
+
{
|
|
136
|
+
type: "theory-update",
|
|
137
|
+
path: this.theoryPath,
|
|
138
|
+
length: content.length,
|
|
139
|
+
lineCount
|
|
140
|
+
},
|
|
141
|
+
frameId
|
|
142
|
+
);
|
|
143
|
+
this.context.frameManager.closeFrame(frameId, {
|
|
144
|
+
theory_updated: true
|
|
145
|
+
});
|
|
146
|
+
} catch (err) {
|
|
147
|
+
logger.warn("Failed to record theory update frame", { error: err });
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
logger.info("Updated THEORY.MD", {
|
|
151
|
+
path: this.theoryPath,
|
|
152
|
+
length: content.length
|
|
153
|
+
});
|
|
154
|
+
const message = warnings.length > 0 ? `Updated ${THEORY_FILE}. Warnings:
|
|
155
|
+
${warnings.map((w) => ` - ${w}`).join("\n")}` : `Updated ${THEORY_FILE} (${lineCount} lines)`;
|
|
156
|
+
return {
|
|
157
|
+
success: true,
|
|
158
|
+
message,
|
|
159
|
+
data: { path: this.theoryPath, lineCount, warnings }
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
status() {
|
|
163
|
+
if (!fs.existsSync(this.theoryPath)) {
|
|
164
|
+
return {
|
|
165
|
+
success: true,
|
|
166
|
+
message: `No ${THEORY_FILE} found.`,
|
|
167
|
+
data: { exists: false }
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
const content = fs.readFileSync(this.theoryPath, "utf-8");
|
|
171
|
+
const lines = content.split("\n");
|
|
172
|
+
const stat = fs.statSync(this.theoryPath);
|
|
173
|
+
const sections = TEMPLATE_SECTIONS.filter((s) => content.includes(s));
|
|
174
|
+
return {
|
|
175
|
+
success: true,
|
|
176
|
+
message: `${THEORY_FILE}: ${lines.length} lines, ${sections.length}/${TEMPLATE_SECTIONS.length} sections`,
|
|
177
|
+
data: {
|
|
178
|
+
exists: true,
|
|
179
|
+
path: this.theoryPath,
|
|
180
|
+
lineCount: lines.length,
|
|
181
|
+
charCount: content.length,
|
|
182
|
+
sections,
|
|
183
|
+
totalSections: TEMPLATE_SECTIONS.length,
|
|
184
|
+
lastModified: stat.mtime.toISOString()
|
|
185
|
+
}
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
export {
|
|
190
|
+
TheorySkill
|
|
191
|
+
};
|
|
@@ -31,6 +31,22 @@ const CANONICAL_HOOKS = [
|
|
|
31
31
|
timeout: 2,
|
|
32
32
|
commandPrefix: "node",
|
|
33
33
|
required: true
|
|
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
|
+
{
|
|
44
|
+
scriptName: "theory-capture.js",
|
|
45
|
+
eventType: "PostToolUse",
|
|
46
|
+
matcher: "Edit|Write|MultiEdit",
|
|
47
|
+
timeout: 2,
|
|
48
|
+
commandPrefix: "node",
|
|
49
|
+
required: false
|
|
34
50
|
}
|
|
35
51
|
];
|
|
36
52
|
const DEAD_HOOKS = ["sms-response-handler.js"];
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stackmemoryai/stackmemory",
|
|
3
|
-
"version": "1.2.
|
|
4
|
-
"description": "Project-scoped memory for AI coding tools. Durable context across sessions with MCP
|
|
3
|
+
"version": "1.2.6",
|
|
4
|
+
"description": "Project-scoped memory for AI coding tools. Durable context across sessions with 32 MCP tools, FTS5 search, Claude/Codex/OpenCode wrappers, Linear sync, automatic hooks, and log analysis.",
|
|
5
5
|
"engines": {
|
|
6
6
|
"node": ">=20.0.0",
|
|
7
7
|
"npm": ">=10.0.0"
|
|
@@ -87,8 +87,8 @@
|
|
|
87
87
|
"postinstall": "node scripts/install-claude-hooks-auto.js || true",
|
|
88
88
|
"init": "node dist/scripts/initialize.js",
|
|
89
89
|
"build": "rm -rf dist && node esbuild.config.js",
|
|
90
|
-
"lint": "eslint src/**/*.ts scripts/**/*.ts",
|
|
91
|
-
"lint:fix": "eslint src/**/*.ts scripts/**/*.ts --fix --max-warnings=-1",
|
|
90
|
+
"lint": "eslint 'src/**/*.ts' 'scripts/**/*.ts'",
|
|
91
|
+
"lint:fix": "eslint 'src/**/*.ts' 'scripts/**/*.ts' --fix --max-warnings=-1",
|
|
92
92
|
"lint:fast": "oxlint src scripts",
|
|
93
93
|
"format": "prettier --write src/**/*.ts scripts/**/*.ts",
|
|
94
94
|
"test": "vitest",
|
|
@@ -121,6 +121,7 @@
|
|
|
121
121
|
"daemon:status": "node dist/cli/index.js daemon status",
|
|
122
122
|
"sync:start": "node scripts/background-sync-manager.js",
|
|
123
123
|
"sync:setup": "./scripts/setup-background-sync.sh",
|
|
124
|
+
"eval:cord": "npx tsx scripts/evals/cord-vs-flat-eval.ts",
|
|
124
125
|
"prepare": "echo 'Prepare step completed'",
|
|
125
126
|
"verify:dist": "node scripts/verify-dist.cjs",
|
|
126
127
|
"test:smoke-db": "bash scripts/smoke-init-db.sh",
|
|
@@ -197,7 +198,6 @@
|
|
|
197
198
|
"@xenova/transformers": "^2.17.2",
|
|
198
199
|
"blessed": "^0.1.81",
|
|
199
200
|
"blessed-contrib": "^4.11.0",
|
|
200
|
-
"chokidar": "^5.0.0"
|
|
201
|
-
"chromadb": "^3.2.2"
|
|
201
|
+
"chokidar": "^5.0.0"
|
|
202
202
|
}
|
|
203
203
|
}
|
|
@@ -81,6 +81,14 @@ try {
|
|
|
81
81
|
commandPrefix: 'node',
|
|
82
82
|
required: true,
|
|
83
83
|
},
|
|
84
|
+
{
|
|
85
|
+
scriptName: 'cord-trace.js',
|
|
86
|
+
eventType: 'PostToolUse',
|
|
87
|
+
matcher: 'mcp__.*__cord_(spawn|fork|complete|ask|tree)',
|
|
88
|
+
timeout: 2,
|
|
89
|
+
commandPrefix: 'node',
|
|
90
|
+
required: true,
|
|
91
|
+
},
|
|
84
92
|
];
|
|
85
93
|
|
|
86
94
|
const DEAD_HOOKS = ['sms-response-handler.js'];
|