@stackmemoryai/stackmemory 1.10.5 → 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.
Files changed (92) hide show
  1. package/README.md +104 -23
  2. package/dist/src/cli/claude-sm.js +266 -84
  3. package/dist/src/cli/codex-sm.js +185 -33
  4. package/dist/src/cli/commands/bench.js +209 -2
  5. package/dist/src/cli/commands/cache.js +126 -0
  6. package/dist/src/cli/commands/daemon.js +41 -0
  7. package/dist/src/cli/commands/handoff.js +40 -9
  8. package/dist/src/cli/commands/onboard.js +70 -3
  9. package/dist/src/cli/commands/optimize.js +117 -0
  10. package/dist/src/cli/commands/orchestrate.js +230 -5
  11. package/dist/src/cli/commands/orchestrator.js +312 -24
  12. package/dist/src/cli/commands/pack.js +322 -0
  13. package/dist/src/cli/commands/search.js +40 -1
  14. package/dist/src/cli/commands/setup.js +177 -7
  15. package/dist/src/cli/commands/skills.js +10 -1
  16. package/dist/src/cli/commands/state.js +265 -0
  17. package/dist/src/cli/gemini-sm.js +19 -29
  18. package/dist/src/cli/index.js +90 -29
  19. package/dist/src/cli/opencode-sm.js +38 -21
  20. package/dist/src/cli/utils/determinism-watcher.js +66 -0
  21. package/dist/src/cli/utils/real-cli-bin.js +44 -0
  22. package/dist/src/core/cache/content-cache.js +238 -0
  23. package/dist/src/core/cache/index.js +11 -0
  24. package/dist/src/core/cache/token-estimator.js +16 -0
  25. package/dist/src/core/context/frame-database.js +38 -30
  26. package/dist/src/core/cross-search/cross-project-search.js +269 -0
  27. package/dist/src/core/{merge → cross-search}/index.js +6 -4
  28. package/dist/src/core/database/sqlite-adapter.js +0 -83
  29. package/dist/src/core/extensions/provider-adapter.js +5 -0
  30. package/dist/src/core/models/model-router.js +22 -2
  31. package/dist/src/core/monitoring/logger.js +2 -1
  32. package/dist/src/core/optimization/trace-optimizer.js +413 -0
  33. package/dist/src/core/provenance/confidence-scorer.js +128 -0
  34. package/dist/src/core/provenance/index.js +40 -0
  35. package/dist/src/core/provenance/provenance-store.js +194 -0
  36. package/dist/src/core/provenance/types.js +82 -0
  37. package/dist/src/core/session/project-handoff.js +64 -0
  38. package/dist/src/core/session/session-manager.js +28 -0
  39. package/dist/src/core/shared-state/canonical-store.js +564 -0
  40. package/dist/src/core/skill-packs/index.js +18 -0
  41. package/dist/src/core/skill-packs/parser.js +42 -0
  42. package/dist/src/core/skill-packs/registry.js +224 -0
  43. package/dist/src/core/skill-packs/types.js +66 -0
  44. package/dist/src/core/trace/trace-event-store.js +282 -0
  45. package/dist/src/core/trace/trace-event.js +4 -0
  46. package/dist/src/daemon/daemon-config.js +7 -0
  47. package/dist/src/daemon/services/github-service.js +126 -0
  48. package/dist/src/daemon/unified-daemon.js +30 -0
  49. package/dist/src/features/sweep/pty-wrapper.js +13 -5
  50. package/dist/src/hooks/schemas.js +2 -0
  51. package/dist/src/integrations/claude-code/subagent-client.js +89 -0
  52. package/dist/src/integrations/github/pr-state.js +158 -0
  53. package/dist/src/integrations/linear/client.js +4 -1
  54. package/dist/src/integrations/mcp/handlers/cross-search-handlers.js +188 -0
  55. package/dist/src/integrations/mcp/handlers/index.js +40 -59
  56. package/dist/src/integrations/mcp/server.js +425 -311
  57. package/dist/src/integrations/mcp/tool-alias-registry.js +370 -0
  58. package/dist/src/integrations/mcp/tool-definitions.js +98 -229
  59. package/dist/src/integrations/ralph/context/stackmemory-context-loader.js +3 -40
  60. package/dist/src/integrations/ralph/learning/pattern-learner.js +1 -20
  61. package/dist/src/integrations/ralph/swarm/swarm-coordinator.js +0 -2
  62. package/dist/src/mcp/stackmemory-mcp-server.js +315 -0
  63. package/dist/src/orchestrators/multimodal/determinism.js +243 -0
  64. package/dist/src/orchestrators/multimodal/harness.js +147 -77
  65. package/dist/src/orchestrators/multimodal/providers.js +44 -3
  66. package/dist/src/utils/hook-installer.js +0 -8
  67. package/package.json +10 -1
  68. package/packs/coding/python-fastapi/instructions.md +60 -0
  69. package/packs/coding/python-fastapi/pack.yaml +28 -0
  70. package/packs/coding/typescript-react/instructions.md +47 -0
  71. package/packs/coding/typescript-react/pack.yaml +28 -0
  72. package/packs/core/commands/capture.md +32 -0
  73. package/packs/core/commands/learn.md +73 -0
  74. package/packs/core/commands/next.md +36 -0
  75. package/packs/core/commands/restart.md +58 -0
  76. package/packs/core/commands/restore.md +29 -0
  77. package/packs/core/commands/start.md +57 -0
  78. package/packs/core/commands/stop.md +65 -0
  79. package/packs/core/commands/summary.md +40 -0
  80. package/packs/core/manifest.json +24 -0
  81. package/packs/ops/decision-recovery/instructions.md +65 -0
  82. package/packs/ops/decision-recovery/pack.yaml +89 -0
  83. package/dist/src/cli/commands/team.js +0 -168
  84. package/dist/src/core/context/shared-context-layer.js +0 -620
  85. package/dist/src/core/context/stack-merge-resolver.js +0 -748
  86. package/dist/src/core/merge/conflict-detector.js +0 -430
  87. package/dist/src/core/merge/resolution-engine.js +0 -557
  88. package/dist/src/core/merge/stack-diff.js +0 -531
  89. package/dist/src/core/merge/unified-merge-resolver.js +0 -302
  90. package/dist/src/integrations/mcp/handlers/cord-handlers.js +0 -397
  91. package/dist/src/integrations/mcp/handlers/team-handlers.js +0 -211
  92. /package/dist/src/core/{merge → cache}/types.js +0 -0
@@ -0,0 +1,194 @@
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 { logger } from "../monitoring/logger.js";
6
+ import { TraceEventSchema } from "./types.js";
7
+ class ProvenanceStore {
8
+ db;
9
+ constructor(db) {
10
+ this.db = db;
11
+ this.initializeSchema();
12
+ }
13
+ /**
14
+ * Initialize database schema for trace events
15
+ */
16
+ initializeSchema() {
17
+ this.db.exec(`
18
+ CREATE TABLE IF NOT EXISTS trace_events (
19
+ trace_id TEXT PRIMARY KEY,
20
+ session_id TEXT NOT NULL,
21
+ parent_trace_id TEXT,
22
+ tenant_id TEXT NOT NULL,
23
+ timestamp TEXT NOT NULL,
24
+ actor TEXT NOT NULL,
25
+ operation TEXT NOT NULL,
26
+ inputs TEXT NOT NULL,
27
+ outputs TEXT NOT NULL,
28
+ tokens_in INTEGER NOT NULL DEFAULT 0,
29
+ tokens_out INTEGER NOT NULL DEFAULT 0,
30
+ cost_usd REAL NOT NULL DEFAULT 0,
31
+ score REAL,
32
+ feedback TEXT,
33
+ provenance TEXT NOT NULL,
34
+ created_at INTEGER DEFAULT (unixepoch())
35
+ )
36
+ `);
37
+ this.db.exec(`
38
+ CREATE INDEX IF NOT EXISTS idx_trace_events_session_id ON trace_events(session_id);
39
+ CREATE INDEX IF NOT EXISTS idx_trace_events_tenant_id ON trace_events(tenant_id);
40
+ CREATE INDEX IF NOT EXISTS idx_trace_events_operation ON trace_events(operation);
41
+ CREATE INDEX IF NOT EXISTS idx_trace_events_timestamp ON trace_events(timestamp);
42
+ CREATE INDEX IF NOT EXISTS idx_trace_events_parent_trace_id ON trace_events(parent_trace_id);
43
+ `);
44
+ }
45
+ /**
46
+ * Record a trace event
47
+ */
48
+ record(event) {
49
+ const stmt = this.db.prepare(`
50
+ INSERT OR REPLACE INTO trace_events (
51
+ trace_id, session_id, parent_trace_id, tenant_id, timestamp,
52
+ actor, operation, inputs, outputs,
53
+ tokens_in, tokens_out, cost_usd, score, feedback, provenance
54
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
55
+ `);
56
+ try {
57
+ stmt.run(
58
+ event.traceId,
59
+ event.sessionId,
60
+ event.parentTraceId ?? null,
61
+ event.tenantId,
62
+ event.timestamp,
63
+ JSON.stringify(event.actor),
64
+ event.operation,
65
+ JSON.stringify(event.inputs),
66
+ JSON.stringify(event.outputs),
67
+ event.tokensIn,
68
+ event.tokensOut,
69
+ event.costUsd,
70
+ event.score ?? null,
71
+ event.feedback ?? null,
72
+ JSON.stringify(event.provenance)
73
+ );
74
+ logger.debug(`Recorded trace event ${event.traceId}`);
75
+ } catch (error) {
76
+ logger.error(
77
+ `Failed to record trace event ${event.traceId}:`,
78
+ error
79
+ );
80
+ throw error;
81
+ }
82
+ }
83
+ /**
84
+ * Get a trace event by traceId
85
+ */
86
+ get(traceId) {
87
+ const row = this.db.prepare("SELECT * FROM trace_events WHERE trace_id = ?").get(traceId);
88
+ if (!row) return void 0;
89
+ return this.rowToEvent(row);
90
+ }
91
+ /**
92
+ * Query trace events with filters
93
+ */
94
+ query(opts = {}) {
95
+ const conditions = [];
96
+ const params = [];
97
+ if (opts.sessionId) {
98
+ conditions.push("session_id = ?");
99
+ params.push(opts.sessionId);
100
+ }
101
+ if (opts.tenantId) {
102
+ conditions.push("tenant_id = ?");
103
+ params.push(opts.tenantId);
104
+ }
105
+ if (opts.operation) {
106
+ conditions.push("operation = ?");
107
+ params.push(opts.operation);
108
+ }
109
+ if (opts.since) {
110
+ conditions.push("timestamp >= ?");
111
+ params.push(opts.since);
112
+ }
113
+ const where = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
114
+ const limit = opts.limit ?? 100;
115
+ const rows = this.db.prepare(
116
+ `SELECT * FROM trace_events ${where} ORDER BY timestamp DESC LIMIT ?`
117
+ ).all(...params, limit);
118
+ return rows.map((row) => this.rowToEvent(row));
119
+ }
120
+ /**
121
+ * Mark a trace event as superseded by another
122
+ */
123
+ supersede(traceId, supersededBy) {
124
+ const row = this.db.prepare("SELECT provenance FROM trace_events WHERE trace_id = ?").get(traceId);
125
+ if (!row) return;
126
+ const provenance = JSON.parse(row.provenance);
127
+ provenance.supersededBy = supersededBy;
128
+ this.db.prepare("UPDATE trace_events SET provenance = ? WHERE trace_id = ?").run(JSON.stringify(provenance), traceId);
129
+ }
130
+ /**
131
+ * Follow parentTraceId chain to build lineage
132
+ */
133
+ getLineage(traceId) {
134
+ const lineage = [];
135
+ let currentId = traceId;
136
+ while (currentId) {
137
+ const event = this.get(currentId);
138
+ if (!event) break;
139
+ lineage.push(event);
140
+ currentId = event.parentTraceId;
141
+ }
142
+ return lineage;
143
+ }
144
+ /**
145
+ * Aggregate stats across trace events
146
+ */
147
+ getStats(tenantId) {
148
+ const where = tenantId ? "WHERE tenant_id = ?" : "";
149
+ const params = tenantId ? [tenantId] : [];
150
+ const row = this.db.prepare(
151
+ `
152
+ SELECT
153
+ COUNT(*) as total_events,
154
+ COALESCE(SUM(tokens_in), 0) as total_tokens_in,
155
+ COALESCE(SUM(tokens_out), 0) as total_tokens_out,
156
+ COALESCE(SUM(cost_usd), 0) as total_cost_usd,
157
+ COALESCE(AVG(json_extract(provenance, '$.confidence')), 0) as avg_confidence
158
+ FROM trace_events ${where}
159
+ `
160
+ ).get(...params);
161
+ return {
162
+ totalEvents: row.total_events,
163
+ totalTokensIn: row.total_tokens_in,
164
+ totalTokensOut: row.total_tokens_out,
165
+ totalCostUsd: row.total_cost_usd,
166
+ avgConfidence: row.avg_confidence
167
+ };
168
+ }
169
+ /**
170
+ * Convert a database row to a TraceEvent
171
+ */
172
+ rowToEvent(row) {
173
+ return TraceEventSchema.parse({
174
+ timestamp: row.timestamp,
175
+ sessionId: row.session_id,
176
+ traceId: row.trace_id,
177
+ parentTraceId: row.parent_trace_id ?? void 0,
178
+ tenantId: row.tenant_id,
179
+ actor: JSON.parse(row.actor),
180
+ operation: row.operation,
181
+ inputs: JSON.parse(row.inputs),
182
+ outputs: JSON.parse(row.outputs),
183
+ tokensIn: row.tokens_in,
184
+ tokensOut: row.tokens_out,
185
+ costUsd: row.cost_usd,
186
+ score: row.score ?? void 0,
187
+ feedback: row.feedback ?? void 0,
188
+ provenance: JSON.parse(row.provenance)
189
+ });
190
+ }
191
+ }
192
+ export {
193
+ ProvenanceStore
194
+ };
@@ -0,0 +1,82 @@
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 { z } from "zod";
6
+ const SourceRefSchema = z.object({
7
+ system: z.string(),
8
+ // e.g., "linear", "github", "slack", "manual"
9
+ externalId: z.string(),
10
+ // ID in source system
11
+ url: z.string().optional(),
12
+ // link to source
13
+ fetchedAt: z.string().datetime(),
14
+ // ISO8601
15
+ hash: z.string().optional()
16
+ // content hash for change detection
17
+ });
18
+ const ProvenanceRecordSchema = z.object({
19
+ sources: z.array(SourceRefSchema),
20
+ derivation: z.array(z.string()),
21
+ // chain of transformations
22
+ confidence: z.number().min(0).max(1),
23
+ supersededBy: z.string().optional(),
24
+ // ID of superseding record
25
+ programVersion: z.string().optional()
26
+ // version that produced this
27
+ });
28
+ const ActorSchema = z.object({
29
+ host: z.string(),
30
+ // e.g., "claude-code", "cursor", "codex"
31
+ agent: z.string(),
32
+ // agent identifier
33
+ user: z.string()
34
+ // user identifier
35
+ });
36
+ const TraceEventSchema = z.object({
37
+ timestamp: z.string().datetime(),
38
+ // ISO8601
39
+ sessionId: z.string(),
40
+ traceId: z.string(),
41
+ parentTraceId: z.string().optional(),
42
+ tenantId: z.string(),
43
+ actor: ActorSchema,
44
+ operation: z.string(),
45
+ // what happened
46
+ inputs: z.unknown(),
47
+ outputs: z.unknown(),
48
+ tokensIn: z.number().int().min(0),
49
+ tokensOut: z.number().int().min(0),
50
+ costUsd: z.number().min(0),
51
+ score: z.number().optional(),
52
+ // numeric eval (ASI-shaped)
53
+ feedback: z.string().optional(),
54
+ // textual feedback for GEPA
55
+ provenance: ProvenanceRecordSchema
56
+ });
57
+ const ConfidenceClassificationSchema = z.enum([
58
+ "accept",
59
+ "review",
60
+ "discard"
61
+ ]);
62
+ const ConfidenceScoreSchema = z.object({
63
+ confidence: z.number().min(0).max(1),
64
+ signals: z.record(z.string(), z.unknown()),
65
+ classification: ConfidenceClassificationSchema
66
+ });
67
+ const ConfidenceConfigSchema = z.object({
68
+ thresholds: z.object({
69
+ accept: z.number().min(0).max(1),
70
+ review: z.number().min(0).max(1)
71
+ }),
72
+ weights: z.record(z.string(), z.number())
73
+ });
74
+ export {
75
+ ActorSchema,
76
+ ConfidenceClassificationSchema,
77
+ ConfidenceConfigSchema,
78
+ ConfidenceScoreSchema,
79
+ ProvenanceRecordSchema,
80
+ SourceRefSchema,
81
+ TraceEventSchema
82
+ };
@@ -0,0 +1,64 @@
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 { existsSync, readFileSync } from "fs";
6
+ import { join } from "path";
7
+ function getProjectHandoffPaths(projectRoot) {
8
+ return {
9
+ handoffPath: join(projectRoot, ".stackmemory", "last-handoff.md"),
10
+ metadataPath: join(projectRoot, ".stackmemory", "last-handoff-meta.json")
11
+ };
12
+ }
13
+ function parseBranchFromHandoffContent(content) {
14
+ const compactMatch = content.match(/^# Handoff:\s+.+?@([^\n]+)$/m);
15
+ if (compactMatch?.[1]) {
16
+ return compactMatch[1].trim();
17
+ }
18
+ const verboseMatch = content.match(/^\*\*Branch\*\*:\s+([^\n]+)$/m);
19
+ if (verboseMatch?.[1]) {
20
+ return verboseMatch[1].trim();
21
+ }
22
+ const ultraMatch = content.match(/^\[H\].+?@([^|\n]+)\|/m);
23
+ if (ultraMatch?.[1]) {
24
+ return ultraMatch[1].trim();
25
+ }
26
+ return null;
27
+ }
28
+ function loadProjectHandoff(projectRoot, currentBranch) {
29
+ const { handoffPath, metadataPath } = getProjectHandoffPaths(projectRoot);
30
+ if (!existsSync(handoffPath)) {
31
+ return null;
32
+ }
33
+ const content = readFileSync(handoffPath, "utf8").trim();
34
+ if (!content) {
35
+ return null;
36
+ }
37
+ let metadata = null;
38
+ if (existsSync(metadataPath)) {
39
+ try {
40
+ metadata = JSON.parse(readFileSync(metadataPath, "utf8"));
41
+ } catch {
42
+ metadata = null;
43
+ }
44
+ }
45
+ const branch = metadata?.branch || parseBranchFromHandoffContent(content);
46
+ if (currentBranch && branch && branch !== currentBranch) {
47
+ return {
48
+ content,
49
+ branch,
50
+ compatible: false,
51
+ mismatchReason: `handoff is for branch ${branch}, current branch is ${currentBranch}`
52
+ };
53
+ }
54
+ return {
55
+ content,
56
+ branch: branch || null,
57
+ compatible: true
58
+ };
59
+ }
60
+ export {
61
+ getProjectHandoffPaths,
62
+ loadProjectHandoff,
63
+ parseBranchFromHandoffContent
64
+ };
@@ -7,6 +7,7 @@ import * as fs from "fs/promises";
7
7
  import * as path from "path";
8
8
  import { logger } from "../monitoring/logger.js";
9
9
  import { SystemError, ErrorCode } from "../errors/index.js";
10
+ import { canonicalStateStore } from "../shared-state/canonical-store.js";
10
11
  function _getEnv(key, defaultValue) {
11
12
  const value = process.env[key];
12
13
  if (value === void 0) {
@@ -119,6 +120,16 @@ class SessionManager {
119
120
  };
120
121
  await this.saveSession(session);
121
122
  await this.setProjectActiveSession(params.projectId, session.sessionId);
123
+ await canonicalStateStore.appendEvent({
124
+ type: "session_created",
125
+ tool: "stackmemory",
126
+ sessionId: session.sessionId,
127
+ projectId: session.projectId,
128
+ branch: session.branch,
129
+ payload: {
130
+ state: session.state
131
+ }
132
+ });
122
133
  this.currentSession = session;
123
134
  logger.info("Created new session", {
124
135
  sessionId: session.sessionId,
@@ -152,6 +163,14 @@ class SessionManager {
152
163
  `${session.sessionId}.json`
153
164
  );
154
165
  await fs.writeFile(sessionPath, JSON.stringify(session, null, 2));
166
+ await canonicalStateStore.upsertSession({
167
+ sessionId: session.sessionId,
168
+ tool: "stackmemory",
169
+ projectId: session.projectId,
170
+ branch: session.branch,
171
+ status: session.state,
172
+ metadata: session.metadata
173
+ });
155
174
  }
156
175
  async suspendSession(sessionId) {
157
176
  const id = sessionId || this.currentSession?.sessionId;
@@ -193,6 +212,15 @@ class SessionManager {
193
212
  `${session.sessionId}.json`
194
213
  );
195
214
  await fs.rename(sessionPath, historyPath);
215
+ await canonicalStateStore.endSession(session.sessionId, "closed");
216
+ await canonicalStateStore.appendEvent({
217
+ type: "session_closed",
218
+ tool: "stackmemory",
219
+ sessionId: session.sessionId,
220
+ projectId: session.projectId,
221
+ branch: session.branch,
222
+ payload: {}
223
+ });
196
224
  }
197
225
  }
198
226
  async listSessions(filter) {