@stackmemoryai/stackmemory 1.10.5 → 1.14.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 (225) hide show
  1. package/LICENSE +131 -64
  2. package/README.md +107 -24
  3. package/bin/claude-sm +16 -1
  4. package/bin/claude-smd +16 -1
  5. package/bin/codex-smd +16 -1
  6. package/bin/gemini-sm +16 -1
  7. package/bin/hermes-sm +21 -0
  8. package/bin/hermes-smd +21 -0
  9. package/bin/opencode-sm +16 -1
  10. package/dist/src/cli/claude-sm.js +266 -84
  11. package/dist/src/cli/codex-sm.js +225 -33
  12. package/dist/src/cli/commands/bench.js +209 -2
  13. package/dist/src/cli/commands/brain.js +206 -0
  14. package/dist/src/cli/commands/cache.js +126 -0
  15. package/dist/src/cli/commands/company-os.js +184 -0
  16. package/dist/src/cli/commands/context.js +5 -0
  17. package/dist/src/cli/commands/daemon.js +41 -0
  18. package/dist/src/cli/commands/handoff.js +40 -9
  19. package/dist/src/cli/commands/onboard.js +70 -3
  20. package/dist/src/cli/commands/operator.js +127 -0
  21. package/dist/src/cli/commands/optimize.js +117 -0
  22. package/dist/src/cli/commands/orchestrate.js +232 -5
  23. package/dist/src/cli/commands/orchestrator.js +315 -26
  24. package/dist/src/cli/commands/pack.js +322 -0
  25. package/dist/src/cli/commands/patterns.js +254 -0
  26. package/dist/src/cli/commands/portal.js +161 -0
  27. package/dist/src/cli/commands/scaffold.js +92 -0
  28. package/dist/src/cli/commands/search.js +40 -1
  29. package/dist/src/cli/commands/setup.js +178 -11
  30. package/dist/src/cli/commands/skills.js +10 -1
  31. package/dist/src/cli/commands/state.js +265 -0
  32. package/dist/src/cli/commands/sync.js +253 -0
  33. package/dist/src/cli/commands/tasks.js +130 -1
  34. package/dist/src/cli/commands/vision.js +221 -0
  35. package/dist/src/cli/gemini-sm.js +19 -29
  36. package/dist/src/cli/hermes-sm.js +224 -0
  37. package/dist/src/cli/index.js +105 -39
  38. package/dist/src/cli/opencode-sm.js +38 -21
  39. package/dist/src/cli/utils/determinism-watcher.js +66 -0
  40. package/dist/src/cli/utils/real-cli-bin.js +116 -0
  41. package/dist/src/core/brain/brain-store.js +187 -0
  42. package/dist/src/core/brain/brain-sync.js +193 -0
  43. package/dist/src/core/brain/index.js +78 -0
  44. package/dist/src/core/brain/types.js +10 -0
  45. package/dist/src/core/cache/content-cache.js +238 -0
  46. package/dist/src/{integrations/diffmem → core/cache}/index.js +5 -5
  47. package/dist/src/core/cache/token-estimator.js +39 -0
  48. package/dist/src/core/config/feature-flags.js +2 -6
  49. package/dist/src/core/context/frame-database.js +79 -27
  50. package/dist/src/core/context/recursive-context-manager.js +1 -1
  51. package/dist/src/core/context/rehydration.js +2 -1
  52. package/dist/src/core/cross-search/cross-project-search.js +269 -0
  53. package/dist/src/core/cross-search/index.js +10 -0
  54. package/dist/src/core/database/sqlite-adapter.js +14 -84
  55. package/dist/src/core/extensions/provider-adapter.js +5 -0
  56. package/dist/src/core/models/model-router.js +54 -2
  57. package/dist/src/core/models/provider-pricing.js +58 -4
  58. package/dist/src/core/monitoring/logger.js +2 -1
  59. package/dist/src/core/optimization/trace-optimizer.js +413 -0
  60. package/dist/src/core/patterns/index.js +22 -0
  61. package/dist/src/core/patterns/pattern-applier.js +39 -0
  62. package/dist/src/core/patterns/pattern-observer.js +157 -0
  63. package/dist/src/core/patterns/pattern-store.js +259 -0
  64. package/dist/src/core/patterns/types.js +19 -0
  65. package/dist/src/core/provenance/confidence-scorer.js +128 -0
  66. package/dist/src/core/provenance/index.js +40 -0
  67. package/dist/src/core/provenance/provenance-store.js +194 -0
  68. package/dist/src/core/provenance/types.js +82 -0
  69. package/dist/src/core/retrieval/llm-context-retrieval.js +5 -4
  70. package/dist/src/core/retrieval/unified-context-assembler.js +11 -66
  71. package/dist/src/core/session/project-handoff.js +64 -0
  72. package/dist/src/core/session/session-manager.js +28 -0
  73. package/dist/src/core/shared-state/canonical-store.js +564 -0
  74. package/dist/src/core/skill-packs/index.js +18 -0
  75. package/dist/src/core/skill-packs/parser.js +42 -0
  76. package/dist/src/core/skill-packs/registry.js +224 -0
  77. package/dist/src/core/skill-packs/types.js +79 -0
  78. package/dist/src/core/storage/cloud-sync-manager.js +116 -0
  79. package/dist/src/core/storage/cloud-sync.js +574 -0
  80. package/dist/src/core/storage/two-tier-storage.js +5 -1
  81. package/dist/src/core/tasks/master-tasks-template.js +43 -0
  82. package/dist/src/core/tasks/md-task-parser.js +138 -0
  83. package/dist/src/core/trace/trace-event-store.js +282 -0
  84. package/dist/src/core/vision/index.js +27 -0
  85. package/dist/src/core/vision/signals.js +79 -0
  86. package/dist/src/core/vision/types.js +22 -0
  87. package/dist/src/core/vision/vision-file.js +146 -0
  88. package/dist/src/core/vision/vision-loop.js +220 -0
  89. package/dist/src/core/wiki/wiki-compiler.js +103 -1
  90. package/dist/src/daemon/daemon-config.js +52 -0
  91. package/dist/src/daemon/services/desire-path-service.js +566 -0
  92. package/dist/src/daemon/services/github-service.js +126 -0
  93. package/dist/src/daemon/services/research-stream-service.js +320 -0
  94. package/dist/src/daemon/services/telemetry-service.js +192 -0
  95. package/dist/src/daemon/unified-daemon.js +58 -1
  96. package/dist/src/features/browser/cli-browser-agent.js +417 -0
  97. package/dist/src/features/browser/stagehand-workflows.js +578 -0
  98. package/dist/src/features/operator/adapter-factory.js +62 -0
  99. package/dist/src/features/operator/browser-adapter.js +109 -0
  100. package/dist/src/features/operator/desktop-adapter.js +125 -0
  101. package/dist/src/features/operator/index.js +39 -0
  102. package/dist/src/features/operator/llm-decision.js +137 -0
  103. package/dist/src/features/operator/operator-logger.js +92 -0
  104. package/dist/src/features/operator/overnight-runner.js +327 -0
  105. package/dist/src/features/operator/screen-adapter.js +91 -0
  106. package/dist/src/features/operator/session-manager.js +127 -0
  107. package/dist/src/features/operator/state-machine.js +227 -0
  108. package/dist/src/features/operator/task-queue.js +81 -0
  109. package/dist/src/features/portal/index.js +26 -0
  110. package/dist/src/features/portal/server.js +240 -0
  111. package/dist/src/features/portal/types.js +14 -0
  112. package/dist/src/features/portal/ui.js +195 -0
  113. package/dist/src/features/sweep/pty-wrapper.js +13 -5
  114. package/dist/src/features/tasks/task-aware-context.js +2 -1
  115. package/dist/src/features/tui/simple-monitor.js +0 -23
  116. package/dist/src/features/tui/swarm-monitor.js +8 -66
  117. package/dist/src/features/web/client/hooks/use-socket.js +12 -0
  118. package/dist/src/{core/merge/index.js → features/web/client/lib/utils.js} +8 -4
  119. package/dist/src/features/web/client/next-env.d.js +4 -0
  120. package/dist/src/features/web/client/stores/session-store.js +12 -0
  121. package/dist/src/features/web/server/gcp-billing.js +76 -0
  122. package/dist/src/features/web/server/index.js +10 -0
  123. package/dist/src/features/web/server/spend-calculator.js +228 -0
  124. package/dist/src/hooks/schemas.js +6 -1
  125. package/dist/src/integrations/anthropic/client.js +3 -2
  126. package/dist/src/integrations/claude-code/agent-bridge.js +0 -3
  127. package/dist/src/integrations/claude-code/subagent-client.js +307 -11
  128. package/dist/src/integrations/claude-code/task-coordinator.js +2 -1
  129. package/dist/src/integrations/github/pr-state.js +158 -0
  130. package/dist/src/integrations/linear/client.js +4 -1
  131. package/dist/src/integrations/linear/webhook-retry.js +196 -0
  132. package/dist/src/integrations/linear/webhook-server.js +18 -22
  133. package/dist/src/integrations/mcp/handlers/cloud-sync-handlers.js +101 -0
  134. package/dist/src/integrations/mcp/handlers/index.js +40 -84
  135. package/dist/src/integrations/mcp/server.js +542 -641
  136. package/dist/src/integrations/mcp/tool-alias-registry.js +297 -0
  137. package/dist/src/integrations/mcp/tool-definitions.js +152 -682
  138. package/dist/src/mcp/stackmemory-mcp-server.js +571 -231
  139. package/dist/src/orchestrators/multimodal/determinism.js +244 -0
  140. package/dist/src/orchestrators/multimodal/harness.js +149 -78
  141. package/dist/src/orchestrators/multimodal/providers.js +44 -3
  142. package/dist/src/skills/recursive-agent-orchestrator.js +2 -4
  143. package/dist/src/utils/hook-installer.js +0 -8
  144. package/dist/src/utils/process-cleanup.js +1 -7
  145. package/docs/README.md +42 -0
  146. package/docs/guides/README_INSTALL.md +208 -0
  147. package/package.json +27 -9
  148. package/packs/coding/python-fastapi/instructions.md +60 -0
  149. package/packs/coding/python-fastapi/pack.yaml +28 -0
  150. package/packs/coding/typescript-react/instructions.md +47 -0
  151. package/packs/coding/typescript-react/pack.yaml +28 -0
  152. package/packs/core/commands/capture.md +32 -0
  153. package/packs/core/commands/learn.md +73 -0
  154. package/packs/core/commands/next.md +36 -0
  155. package/packs/core/commands/restart.md +58 -0
  156. package/packs/core/commands/restore.md +29 -0
  157. package/packs/core/commands/start.md +57 -0
  158. package/packs/core/commands/stop.md +65 -0
  159. package/packs/core/commands/summary.md +40 -0
  160. package/packs/core/manifest.json +24 -0
  161. package/packs/ops/decision-recovery/instructions.md +65 -0
  162. package/packs/ops/decision-recovery/pack.yaml +89 -0
  163. package/scripts/claude-code-wrapper.sh +11 -0
  164. package/scripts/claude-sm-setup.sh +12 -1
  165. package/scripts/codex-wrapper.sh +11 -0
  166. package/scripts/git-hooks/branch-context-manager.sh +11 -0
  167. package/scripts/git-hooks/post-checkout-stackmemory.sh +11 -0
  168. package/scripts/git-hooks/post-commit-stackmemory.sh +11 -0
  169. package/scripts/git-hooks/pre-commit-stackmemory.sh +11 -0
  170. package/scripts/hooks/cleanup-shell.sh +12 -1
  171. package/scripts/hooks/task-complete.sh +12 -1
  172. package/scripts/install-code-execution-hooks.sh +12 -1
  173. package/scripts/install-sweep-hook.sh +12 -0
  174. package/scripts/install.sh +11 -0
  175. package/scripts/opencode-wrapper.sh +11 -0
  176. package/scripts/portal/cloud-init.yaml +69 -0
  177. package/scripts/portal/setup.sh +69 -0
  178. package/scripts/portal/stackmemory-portal.service +34 -0
  179. package/scripts/setup-claude-integration.sh +12 -1
  180. package/scripts/smoke-init-db.sh +23 -0
  181. package/scripts/stackmemory-daemon.sh +11 -0
  182. package/scripts/verify-dist.cjs +11 -4
  183. package/dist/src/cli/commands/ralph.js +0 -1053
  184. package/dist/src/cli/commands/team.js +0 -168
  185. package/dist/src/core/context/shared-context-layer.js +0 -620
  186. package/dist/src/core/context/stack-merge-resolver.js +0 -748
  187. package/dist/src/core/merge/conflict-detector.js +0 -430
  188. package/dist/src/core/merge/resolution-engine.js +0 -557
  189. package/dist/src/core/merge/stack-diff.js +0 -531
  190. package/dist/src/core/merge/unified-merge-resolver.js +0 -302
  191. package/dist/src/hooks/diffmem-hooks.js +0 -376
  192. package/dist/src/integrations/diffmem/client.js +0 -208
  193. package/dist/src/integrations/diffmem/config.js +0 -14
  194. package/dist/src/integrations/greptile/client.js +0 -101
  195. package/dist/src/integrations/greptile/config.js +0 -14
  196. package/dist/src/integrations/greptile/index.js +0 -11
  197. package/dist/src/integrations/mcp/handlers/cord-handlers.js +0 -397
  198. package/dist/src/integrations/mcp/handlers/diffmem-handlers.js +0 -455
  199. package/dist/src/integrations/mcp/handlers/greptile-handlers.js +0 -456
  200. package/dist/src/integrations/mcp/handlers/provider-handlers.js +0 -227
  201. package/dist/src/integrations/mcp/handlers/team-handlers.js +0 -211
  202. package/dist/src/integrations/ralph/bridge/ralph-stackmemory-bridge.js +0 -863
  203. package/dist/src/integrations/ralph/context/context-budget-manager.js +0 -308
  204. package/dist/src/integrations/ralph/context/stackmemory-context-loader.js +0 -391
  205. package/dist/src/integrations/ralph/index.js +0 -17
  206. package/dist/src/integrations/ralph/learning/pattern-learner.js +0 -435
  207. package/dist/src/integrations/ralph/lifecycle/iteration-lifecycle.js +0 -448
  208. package/dist/src/integrations/ralph/loopmax.js +0 -488
  209. package/dist/src/integrations/ralph/monitoring/swarm-dashboard.js +0 -293
  210. package/dist/src/integrations/ralph/monitoring/swarm-registry.js +0 -107
  211. package/dist/src/integrations/ralph/orchestration/multi-loop-orchestrator.js +0 -508
  212. package/dist/src/integrations/ralph/patterns/compounding-engineering-pattern.js +0 -407
  213. package/dist/src/integrations/ralph/patterns/extended-coherence-sessions.js +0 -495
  214. package/dist/src/integrations/ralph/patterns/oracle-worker-pattern.js +0 -387
  215. package/dist/src/integrations/ralph/performance/performance-optimizer.js +0 -357
  216. package/dist/src/integrations/ralph/recovery/crash-recovery.js +0 -461
  217. package/dist/src/integrations/ralph/state/state-reconciler.js +0 -420
  218. package/dist/src/integrations/ralph/swarm/git-workflow-manager.js +0 -444
  219. package/dist/src/integrations/ralph/swarm/swarm-coordinator.js +0 -1007
  220. package/dist/src/integrations/ralph/visualization/ralph-debugger.js +0 -635
  221. package/scripts/ralph-loop-implementation.js +0 -404
  222. /package/dist/src/core/{merge → cache}/types.js +0 -0
  223. /package/dist/src/{integrations/diffmem/types.js → core/storage/cloud-sync-types.js} +0 -0
  224. /package/dist/src/{integrations/greptile/types.js → core/trace/trace-event.js} +0 -0
  225. /package/dist/src/{integrations/ralph → features/operator}/types.js +0 -0
@@ -35,7 +35,18 @@ const MODEL_TOKEN_LIMITS = {
35
35
  // Cerebras
36
36
  "llama-4-scout-17b-16e-instruct": 131072,
37
37
  // DeepInfra
38
- "THUDM/glm-4-9b-chat": 128e3
38
+ "THUDM/glm-4-9b-chat": 128e3,
39
+ // Moonshot (Kimi)
40
+ "kimi-k2.6": 256e3,
41
+ "kimi-k2.5": 256e3,
42
+ // xAI (Grok)
43
+ "grok-4.1-fast": 131072,
44
+ "grok-4.3": 131072,
45
+ "grok-4": 131072,
46
+ // DeepSeek
47
+ "deepseek-v4-flash": 131072,
48
+ "deepseek-v4": 131072,
49
+ "deepseek-v4-pro": 131072
39
50
  };
40
51
  const DEFAULT_MODEL_TOKEN_LIMIT = 2e5;
41
52
  function getModelTokenLimit(model) {
@@ -91,6 +102,24 @@ const DEFAULT_CONFIG = {
91
102
  baseUrl: "https://openrouter.ai/api",
92
103
  apiKeyEnv: "OPENROUTER_API_KEY"
93
104
  },
105
+ moonshot: {
106
+ provider: "moonshot",
107
+ model: "kimi-k2.6",
108
+ baseUrl: "https://api.moonshot.ai/v1",
109
+ apiKeyEnv: "MOONSHOT_API_KEY"
110
+ },
111
+ xai: {
112
+ provider: "xai",
113
+ model: "grok-4.1-fast",
114
+ baseUrl: "https://api.x.ai/v1",
115
+ apiKeyEnv: "XAI_API_KEY"
116
+ },
117
+ deepseek: {
118
+ provider: "deepseek",
119
+ model: "deepseek-v4-flash",
120
+ baseUrl: "https://api.deepseek.com/v1",
121
+ apiKeyEnv: "DEEPSEEK_API_KEY"
122
+ },
94
123
  "anthropic-batch": {
95
124
  provider: "anthropic-batch",
96
125
  model: "claude-sonnet-4-5-20250929",
@@ -235,8 +264,31 @@ const OPTIMAL_ROUTING = {
235
264
  apiKeyEnv: "ANTHROPIC_API_KEY"
236
265
  }
237
266
  };
238
- const FALLBACK_CHAIN = ["deepinfra", "cerebras", "anthropic"];
267
+ const FALLBACK_CHAIN = [
268
+ "moonshot",
269
+ "deepinfra",
270
+ "cerebras",
271
+ "anthropic"
272
+ ];
239
273
  const CHEAP_PROVIDERS = [
274
+ {
275
+ provider: "deepseek",
276
+ model: "deepseek-v4-flash",
277
+ apiKeyEnv: "DEEPSEEK_API_KEY",
278
+ baseUrl: "https://api.deepseek.com/v1"
279
+ },
280
+ {
281
+ provider: "xai",
282
+ model: "grok-4.1-fast",
283
+ apiKeyEnv: "XAI_API_KEY",
284
+ baseUrl: "https://api.x.ai/v1"
285
+ },
286
+ {
287
+ provider: "moonshot",
288
+ model: "kimi-k2.6",
289
+ apiKeyEnv: "MOONSHOT_API_KEY",
290
+ baseUrl: "https://api.moonshot.ai/v1"
291
+ },
240
292
  {
241
293
  provider: "openrouter",
242
294
  model: "meta-llama/llama-4-scout",
@@ -3,7 +3,28 @@ import { dirname as __pathDirname } from 'path';
3
3
  const __filename = __fileURLToPath(import.meta.url);
4
4
  const __dirname = __pathDirname(__filename);
5
5
  const MODEL_PRICING = {
6
- // Anthropic (direct API)
6
+ // Anthropic (direct API) — Opus 4.x share one price; 1M context, no
7
+ // long-context premium. Sourced platform.claude.com 2026-05-26.
8
+ "anthropic/claude-opus-4-8": {
9
+ inputPer1M: 5,
10
+ outputPer1M: 25,
11
+ source: "platform.claude.com"
12
+ },
13
+ "anthropic/claude-opus-4-7": {
14
+ inputPer1M: 5,
15
+ outputPer1M: 25,
16
+ source: "platform.claude.com"
17
+ },
18
+ "anthropic/claude-opus-4-6": {
19
+ inputPer1M: 5,
20
+ outputPer1M: 25,
21
+ source: "platform.claude.com"
22
+ },
23
+ "anthropic/claude-sonnet-4-6": {
24
+ inputPer1M: 3,
25
+ outputPer1M: 15,
26
+ source: "platform.claude.com"
27
+ },
7
28
  "anthropic/claude-sonnet-4-5-20250929": {
8
29
  inputPer1M: 3,
9
30
  outputPer1M: 15,
@@ -15,9 +36,9 @@ const MODEL_PRICING = {
15
36
  source: "anthropic.com"
16
37
  },
17
38
  "anthropic/claude-haiku-4-5-20251001": {
18
- inputPer1M: 0.8,
19
- outputPer1M: 4,
20
- source: "anthropic.com"
39
+ inputPer1M: 1,
40
+ outputPer1M: 5,
41
+ source: "platform.claude.com"
21
42
  },
22
43
  // OpenAI (direct API)
23
44
  "openai/gpt-4o": {
@@ -56,8 +77,41 @@ function formatCost(usd) {
56
77
  if (usd < 0.01) return `$${usd.toFixed(6)}`;
57
78
  return `$${usd.toFixed(4)}`;
58
79
  }
80
+ const MAX_PLAN_DISCOUNT_RAMP = {
81
+ start: process.env.STACKMEMORY_COST_RAMP_START ?? "2026-06-06",
82
+ end: process.env.STACKMEMORY_COST_RAMP_END ?? "2026-09-06",
83
+ startMultiplier: Number(
84
+ process.env.STACKMEMORY_COST_RAMP_START_MULTIPLIER ?? "0.2"
85
+ ),
86
+ endMultiplier: 1
87
+ };
88
+ function effectiveSpendMultiplier(date = /* @__PURE__ */ new Date(), ramp = MAX_PLAN_DISCOUNT_RAMP) {
89
+ const start = Date.parse(ramp.start);
90
+ const end = Date.parse(ramp.end);
91
+ const now = date.getTime();
92
+ if (!Number.isFinite(start) || !Number.isFinite(end) || end <= start) {
93
+ return ramp.endMultiplier;
94
+ }
95
+ if (now <= start) return ramp.startMultiplier;
96
+ if (now >= end) return ramp.endMultiplier;
97
+ const progress = (now - start) / (end - start);
98
+ return ramp.startMultiplier + progress * (ramp.endMultiplier - ramp.startMultiplier);
99
+ }
100
+ function effectiveCost(provider, model, inputTokens, outputTokens, date = /* @__PURE__ */ new Date()) {
101
+ const list = calculateCost(provider, model, inputTokens, outputTokens);
102
+ if (!list) return null;
103
+ const multiplier = effectiveSpendMultiplier(date);
104
+ return {
105
+ listCost: list.totalCost,
106
+ effectiveCost: list.totalCost * multiplier,
107
+ multiplier
108
+ };
109
+ }
59
110
  export {
111
+ MAX_PLAN_DISCOUNT_RAMP,
60
112
  MODEL_PRICING,
61
113
  calculateCost,
114
+ effectiveCost,
115
+ effectiveSpendMultiplier,
62
116
  formatCost
63
117
  };
@@ -76,6 +76,7 @@ class Logger {
76
76
  fileLoggingDisabledNotified = false;
77
77
  constructor() {
78
78
  const envLevel = process.env["STACKMEMORY_LOG_LEVEL"]?.toUpperCase();
79
+ const jsonCliMode = !envLevel && process.argv.includes("--json");
79
80
  switch (envLevel) {
80
81
  case "ERROR":
81
82
  this.logLevel = 0 /* ERROR */;
@@ -87,7 +88,7 @@ class Logger {
87
88
  this.logLevel = 3 /* DEBUG */;
88
89
  break;
89
90
  default:
90
- this.logLevel = 2 /* INFO */;
91
+ this.logLevel = jsonCliMode ? 0 /* ERROR */ : 2 /* INFO */;
91
92
  }
92
93
  if (this.logLevel === 3 /* DEBUG */ || process.env["STACKMEMORY_LOG_FILE"]) {
93
94
  this.logFile = process.env["STACKMEMORY_LOG_FILE"] || path.join(
@@ -0,0 +1,413 @@
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, mkdirSync, writeFileSync } from "fs";
6
+ import { join } from "path";
7
+ const DEFAULT_OPTIONS = {
8
+ lookbackDays: 30,
9
+ minOccurrences: 2,
10
+ maxExamples: 3
11
+ };
12
+ const MUTATING_TOOLS = /* @__PURE__ */ new Set(["edit", "write", "multi_edit"]);
13
+ const SEARCH_TOOLS = /* @__PURE__ */ new Set(["search", "grep", "read", "glob", "find"]);
14
+ const VERIFICATION_TOOLS = /* @__PURE__ */ new Set([
15
+ "test",
16
+ "bash",
17
+ "lint",
18
+ "build",
19
+ "npm",
20
+ "pytest",
21
+ "vitest",
22
+ "jest"
23
+ ]);
24
+ function classifyErrorText(text) {
25
+ const lower = text.toLowerCase();
26
+ if (lower.includes("lint") || lower.includes("eslint") || lower.includes("prettier")) {
27
+ return "lint_failure";
28
+ }
29
+ if (lower.includes("test") && (lower.includes("fail") || lower.includes("error"))) {
30
+ return "test_failure";
31
+ }
32
+ if (lower.includes("timeout") || lower.includes("timed out")) {
33
+ return "timeout";
34
+ }
35
+ if (lower.includes("rate limit") || lower.includes("429")) {
36
+ return "rate_limit";
37
+ }
38
+ if (lower.includes("permission") || lower.includes("eacces")) {
39
+ return "permission_failure";
40
+ }
41
+ if (lower.includes("build") && lower.includes("error")) {
42
+ return "build_failure";
43
+ }
44
+ return "unknown_failure";
45
+ }
46
+ function uniq(items) {
47
+ return [...new Set(items)];
48
+ }
49
+ function truncate(value, max = 120) {
50
+ return value.length > max ? `${value.slice(0, max - 1)}\u2026` : value;
51
+ }
52
+ function toolPattern(trace) {
53
+ return trace.tools.map((tool) => tool.tool).join("\u2192");
54
+ }
55
+ function hasVerification(trace) {
56
+ return trace.tools.some((tool) => VERIFICATION_TOOLS.has(tool.tool));
57
+ }
58
+ function hasMutation(trace) {
59
+ return trace.tools.some((tool) => MUTATING_TOOLS.has(tool.tool));
60
+ }
61
+ function countSearchTools(trace) {
62
+ return trace.tools.filter((tool) => SEARCH_TOOLS.has(tool.tool)).length;
63
+ }
64
+ function countRepeatedFailingTools(trace) {
65
+ const counts = /* @__PURE__ */ new Map();
66
+ for (const tool of trace.tools) {
67
+ if (!tool.error) continue;
68
+ counts.set(tool.tool, (counts.get(tool.tool) || 0) + 1);
69
+ }
70
+ let max = 0;
71
+ for (const value of counts.values()) {
72
+ if (value > max) max = value;
73
+ }
74
+ return max;
75
+ }
76
+ function createAccumulator(kind, label, targetAreas, actions, validations) {
77
+ return {
78
+ kind,
79
+ label,
80
+ traceIds: /* @__PURE__ */ new Set(),
81
+ summaries: [],
82
+ affectedFiles: /* @__PURE__ */ new Set(),
83
+ toolPatterns: /* @__PURE__ */ new Set(),
84
+ targetAreas: new Set(targetAreas),
85
+ actions,
86
+ validations
87
+ };
88
+ }
89
+ function pushTraceEvidence(bucket, trace, maxExamples) {
90
+ bucket.traceIds.add(trace.id);
91
+ if (bucket.summaries.length < maxExamples) {
92
+ bucket.summaries.push(truncate(trace.summary));
93
+ }
94
+ for (const file of trace.metadata.filesModified) {
95
+ bucket.affectedFiles.add(file);
96
+ }
97
+ bucket.toolPatterns.add(toolPattern(trace));
98
+ }
99
+ function buildCluster(id, bucket) {
100
+ return {
101
+ id,
102
+ kind: bucket.kind,
103
+ label: bucket.label,
104
+ occurrences: bucket.traceIds.size,
105
+ traceIds: [...bucket.traceIds],
106
+ sampleSummaries: bucket.summaries,
107
+ affectedFiles: [...bucket.affectedFiles].sort(),
108
+ toolPatterns: [...bucket.toolPatterns].sort(),
109
+ targetAreas: [...bucket.targetAreas],
110
+ actions: bucket.actions,
111
+ validations: bucket.validations
112
+ };
113
+ }
114
+ function recommendationForCluster(cluster) {
115
+ const highPriority = cluster.kind === "error_pattern" || cluster.occurrences >= 3;
116
+ return {
117
+ id: `rec-${cluster.id}`,
118
+ title: cluster.label,
119
+ priority: highPriority ? "high" : "medium",
120
+ confidence: Math.min(0.45 + cluster.occurrences * 0.12, 0.95),
121
+ summary: `${cluster.label} appeared in ${cluster.occurrences} trace${cluster.occurrences === 1 ? "" : "s"}. Focus on ${cluster.targetAreas.join(", ")} first.`,
122
+ targetAreas: cluster.targetAreas,
123
+ actions: cluster.actions,
124
+ validations: cluster.validations,
125
+ supportingClusters: [cluster.id]
126
+ };
127
+ }
128
+ class TraceOptimizer {
129
+ constructor(traceStore) {
130
+ this.traceStore = traceStore;
131
+ }
132
+ analyze(options = {}) {
133
+ const config = { ...DEFAULT_OPTIONS, ...options };
134
+ const cutoff = Date.now() - config.lookbackDays * 24 * 60 * 60 * 1e3;
135
+ const traces = this.traceStore.getAllTraces().filter((trace) => trace.metadata.startTime >= cutoff);
136
+ const clusters = this.buildClusters(traces, config);
137
+ const recommendations = clusters.map(recommendationForCluster);
138
+ const tracesByType = {};
139
+ for (const trace of traces) {
140
+ tracesByType[trace.type] = (tracesByType[trace.type] || 0) + 1;
141
+ }
142
+ const averageToolsPerTrace = traces.length > 0 ? traces.reduce((sum, trace) => sum + trace.tools.length, 0) / traces.length : 0;
143
+ const averageTraceScore = traces.length > 0 ? traces.reduce((sum, trace) => sum + trace.score, 0) / traces.length : 0;
144
+ return {
145
+ generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
146
+ lookbackDays: config.lookbackDays,
147
+ totalTracesAnalyzed: traces.length,
148
+ tracesWithErrors: traces.filter(hasErrors).length,
149
+ causalTraces: traces.filter((trace) => trace.metadata.causalChain).length,
150
+ averageToolsPerTrace,
151
+ averageTraceScore,
152
+ tracesByType,
153
+ clusters,
154
+ recommendations
155
+ };
156
+ }
157
+ persistReport(projectRoot, report) {
158
+ const outputDir = join(projectRoot, ".stackmemory", "build");
159
+ if (!existsSync(outputDir)) {
160
+ mkdirSync(outputDir, { recursive: true });
161
+ }
162
+ const jsonPath = join(outputDir, "trace-optimizer-latest.json");
163
+ const markdownPath = join(outputDir, "trace-optimizer-latest.md");
164
+ writeFileSync(jsonPath, `${JSON.stringify(report, null, 2)}
165
+ `, "utf8");
166
+ writeFileSync(markdownPath, renderMarkdownReport(report), "utf8");
167
+ return { jsonPath, markdownPath };
168
+ }
169
+ buildClusters(traces, options) {
170
+ const buckets = /* @__PURE__ */ new Map();
171
+ for (const trace of traces) {
172
+ const errors = extractErrors(trace);
173
+ for (const error of errors) {
174
+ const code = classifyErrorText(error);
175
+ const key = `error:${code}`;
176
+ if (!buckets.has(key)) {
177
+ buckets.set(
178
+ key,
179
+ createAccumulator(
180
+ "error_pattern",
181
+ labelForError(code),
182
+ targetAreasForError(code),
183
+ actionsForError(code),
184
+ validationsForError(code)
185
+ )
186
+ );
187
+ }
188
+ pushTraceEvidence(buckets.get(key), trace, options.maxExamples);
189
+ }
190
+ if (hasMutation(trace) && !hasVerification(trace)) {
191
+ const key = "verification_gap";
192
+ if (!buckets.has(key)) {
193
+ buckets.set(
194
+ key,
195
+ createAccumulator(
196
+ "verification_gap",
197
+ "Mutating traces often finish without an explicit verification step",
198
+ ["hooks", "wrappers", "orchestrator"],
199
+ [
200
+ "Add a post-edit verification policy that requires targeted test, lint, or build execution before a task is considered complete.",
201
+ "Teach wrappers and agent prompts to prefer the smallest validating command after edits instead of stopping at file changes."
202
+ ],
203
+ [
204
+ "npm run test:run",
205
+ "npm run lint",
206
+ "stackmemory bench determinism --latest --json"
207
+ ]
208
+ )
209
+ );
210
+ }
211
+ pushTraceEvidence(buckets.get(key), trace, options.maxExamples);
212
+ }
213
+ if (countRepeatedFailingTools(trace) >= 2) {
214
+ const key = "retry_loop";
215
+ if (!buckets.has(key)) {
216
+ buckets.set(
217
+ key,
218
+ createAccumulator(
219
+ "retry_loop",
220
+ "Failing tools are being retried in loops instead of changing strategy",
221
+ ["orchestrator", "hooks", "prompts"],
222
+ [
223
+ "Add retry guards that trigger diagnosis or fallback prompts after the second failing attempt of the same tool.",
224
+ "Capture the first failure reason and inject it into the next planning step so the harness pivots instead of repeating."
225
+ ],
226
+ ["npm run determinism:test", "stackmemory conductor trace-stats"]
227
+ )
228
+ );
229
+ }
230
+ pushTraceEvidence(buckets.get(key), trace, options.maxExamples);
231
+ }
232
+ if (countSearchTools(trace) >= 4 && !hasMutation(trace) && trace.tools.length >= 5) {
233
+ const key = "context_thrash";
234
+ if (!buckets.has(key)) {
235
+ buckets.set(
236
+ key,
237
+ createAccumulator(
238
+ "context_thrash",
239
+ "Search-heavy traces suggest context assembly or retrieval is too weak",
240
+ ["retrieval", "hooks", "context bundling"],
241
+ [
242
+ "Promote recurring search\u2192read loops into explicit retrieval bundles or preloaded context packets.",
243
+ "Use trace summaries to precompute likely files, anchors, or commands for similar future tasks."
244
+ ],
245
+ ["python scripts/dspy/eval.py", "stackmemory retrieval stats"]
246
+ )
247
+ );
248
+ }
249
+ pushTraceEvidence(buckets.get(key), trace, options.maxExamples);
250
+ }
251
+ }
252
+ return [...buckets.entries()].map(([id, bucket]) => buildCluster(id, bucket)).filter((cluster) => cluster.occurrences >= options.minOccurrences).sort((a, b) => {
253
+ if (b.occurrences !== a.occurrences) {
254
+ return b.occurrences - a.occurrences;
255
+ }
256
+ return a.label.localeCompare(b.label);
257
+ });
258
+ }
259
+ }
260
+ function hasErrors(trace) {
261
+ return extractErrors(trace).length > 0;
262
+ }
263
+ function extractErrors(trace) {
264
+ const toolErrors = trace.tools.map((tool) => tool.error).filter((value) => Boolean(value));
265
+ return uniq([...trace.metadata.errorsEncountered, ...toolErrors]);
266
+ }
267
+ function labelForError(code) {
268
+ switch (code) {
269
+ case "lint_failure":
270
+ return "Lint failures recur across traces and should be gated earlier";
271
+ case "test_failure":
272
+ return "Test failures recur and need tighter edit-time validation";
273
+ case "timeout":
274
+ return "Timeouts recur and need fallback or budget-aware orchestration";
275
+ case "rate_limit":
276
+ return "Rate-limit failures recur and need backoff-aware retry policies";
277
+ case "permission_failure":
278
+ return "Permission or missing-file failures recur and need environment preflight checks";
279
+ case "build_failure":
280
+ return "Build failures recur and should be surfaced before finalization";
281
+ default:
282
+ return "Unclassified failures recur and need structured diagnosis";
283
+ }
284
+ }
285
+ function targetAreasForError(code) {
286
+ switch (code) {
287
+ case "lint_failure":
288
+ case "test_failure":
289
+ case "build_failure":
290
+ return ["hooks", "wrappers", "verification"];
291
+ case "timeout":
292
+ case "rate_limit":
293
+ return ["orchestrator", "fallbacks", "retry policy"];
294
+ case "permission_failure":
295
+ return ["setup", "hooks", "environment checks"];
296
+ default:
297
+ return ["orchestrator", "prompts", "diagnostics"];
298
+ }
299
+ }
300
+ function actionsForError(code) {
301
+ switch (code) {
302
+ case "lint_failure":
303
+ return [
304
+ "Insert a fast lint or formatting guard after edits when touched files match configured source globs.",
305
+ "Surface the exact lint failure in the next planning turn so the harness repairs before moving on."
306
+ ];
307
+ case "test_failure":
308
+ return [
309
+ "Require targeted test execution after code edits in code paths that already have tests.",
310
+ "Teach the orchestrator to stop on the second failing test loop and switch to diagnosis mode."
311
+ ];
312
+ case "timeout":
313
+ return [
314
+ "Add time-budget-aware fallbacks and cut off repeated long-running commands sooner.",
315
+ "Persist timeout causes so later attempts can shorten prompts, narrow file scopes, or switch models."
316
+ ];
317
+ case "rate_limit":
318
+ return [
319
+ "Back off and downgrade to a cheaper model or cached context path after a rate-limit event.",
320
+ "Record rate-limit state in session context so retry attempts do not immediately hit the same ceiling."
321
+ ];
322
+ case "permission_failure":
323
+ return [
324
+ "Run environment and path preflight checks before invoking tools that assume local binaries or files exist.",
325
+ "Convert common permission failures into actionable setup hints instead of opaque retries."
326
+ ];
327
+ case "build_failure":
328
+ return [
329
+ "Add a build gate for changes that touch runtime entrypoints, package manifests, or bundler config.",
330
+ "Capture compiler diagnostics into the next repair prompt instead of relying on the model to infer them."
331
+ ];
332
+ default:
333
+ return [
334
+ "Capture richer error metadata and turn repeated failures into a structured diagnosis step.",
335
+ "Route repeated unknown failures through a narrower repair prompt instead of repeating the same harness path."
336
+ ];
337
+ }
338
+ }
339
+ function validationsForError(code) {
340
+ switch (code) {
341
+ case "lint_failure":
342
+ return ["npm run lint", "npm run test:run"];
343
+ case "test_failure":
344
+ return ["npm run test:run", "npm run determinism:test"];
345
+ case "build_failure":
346
+ return ["npm run build", "npm run test:run"];
347
+ case "timeout":
348
+ return [
349
+ "stackmemory conductor trace-stats",
350
+ "npm run determinism:latest"
351
+ ];
352
+ case "rate_limit":
353
+ return ["stackmemory conductor trace-stats"];
354
+ case "permission_failure":
355
+ return ["stackmemory doctor", "npm run test:smoke-db"];
356
+ default:
357
+ return ["npm run test:run"];
358
+ }
359
+ }
360
+ function renderMarkdownReport(report) {
361
+ const lines = [];
362
+ lines.push("# Trace Optimizer Report");
363
+ lines.push("");
364
+ lines.push(`Generated: ${report.generatedAt}`);
365
+ lines.push(`Lookback: ${report.lookbackDays} day(s)`);
366
+ lines.push("");
367
+ lines.push("## Summary");
368
+ lines.push(`- Traces analyzed: ${report.totalTracesAnalyzed}`);
369
+ lines.push(`- Traces with errors: ${report.tracesWithErrors}`);
370
+ lines.push(`- Causal traces: ${report.causalTraces}`);
371
+ lines.push(`- Avg tools/trace: ${report.averageToolsPerTrace.toFixed(2)}`);
372
+ lines.push(`- Avg trace score: ${report.averageTraceScore.toFixed(2)}`);
373
+ lines.push("");
374
+ lines.push("## Recommendations");
375
+ if (report.recommendations.length === 0) {
376
+ lines.push("- No repeated failure patterns crossed the current threshold.");
377
+ } else {
378
+ for (const recommendation of report.recommendations) {
379
+ lines.push(
380
+ `- ${recommendation.title} (${recommendation.priority}, confidence ${recommendation.confidence.toFixed(2)})`
381
+ );
382
+ lines.push(` Summary: ${recommendation.summary}`);
383
+ lines.push(` Targets: ${recommendation.targetAreas.join(", ")}`);
384
+ lines.push(` Actions: ${recommendation.actions.join(" | ")}`);
385
+ lines.push(` Validate: ${recommendation.validations.join(" | ")}`);
386
+ }
387
+ }
388
+ lines.push("");
389
+ lines.push("## Clusters");
390
+ if (report.clusters.length === 0) {
391
+ lines.push("- No clusters found.");
392
+ } else {
393
+ for (const cluster of report.clusters) {
394
+ lines.push(`### ${cluster.label}`);
395
+ lines.push(`- Occurrences: ${cluster.occurrences}`);
396
+ lines.push(`- Kind: ${cluster.kind}`);
397
+ lines.push(
398
+ `- Tool patterns: ${cluster.toolPatterns.join(", ") || "n/a"}`
399
+ );
400
+ lines.push(`- Files: ${cluster.affectedFiles.join(", ") || "n/a"}`);
401
+ lines.push(
402
+ `- Sample traces: ${cluster.sampleSummaries.join(" | ") || "n/a"}`
403
+ );
404
+ lines.push("");
405
+ }
406
+ }
407
+ return `${lines.join("\n")}
408
+ `;
409
+ }
410
+ export {
411
+ TraceOptimizer,
412
+ renderMarkdownReport
413
+ };
@@ -0,0 +1,22 @@
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 {
6
+ computeConfidence,
7
+ CONFIDENCE_DECAY_PER_WEEK,
8
+ CONFIDENCE_BOOST_PER_OBSERVATION,
9
+ CONFIDENCE_PENALTY_PER_CONTRADICTION
10
+ } from "./types.js";
11
+ import { PatternStore } from "./pattern-store.js";
12
+ import { PatternObserver } from "./pattern-observer.js";
13
+ import { PatternApplier } from "./pattern-applier.js";
14
+ export {
15
+ CONFIDENCE_BOOST_PER_OBSERVATION,
16
+ CONFIDENCE_DECAY_PER_WEEK,
17
+ CONFIDENCE_PENALTY_PER_CONTRADICTION,
18
+ PatternApplier,
19
+ PatternObserver,
20
+ PatternStore,
21
+ computeConfidence
22
+ };
@@ -0,0 +1,39 @@
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
+ class PatternApplier {
6
+ constructor(store) {
7
+ this.store = store;
8
+ }
9
+ /**
10
+ * Find and format patterns relevant to a task.
11
+ * Returns a markdown block to inject into context.
12
+ */
13
+ apply(taskDescription, projectId) {
14
+ const patterns = this.store.search(taskDescription, projectId);
15
+ if (patterns.length === 0) return "";
16
+ for (const p of patterns) {
17
+ this.store.recordMatch(p.id);
18
+ }
19
+ const lines = ["## Learned Patterns", ""];
20
+ for (const p of patterns) {
21
+ const bar = this.confidenceBar(p.confidence);
22
+ lines.push(
23
+ `- ${bar} **${p.trigger}** \u2192 ${p.action} _(${p.domain}, ${p.observationCount} obs)_`
24
+ );
25
+ }
26
+ return lines.join("\n");
27
+ }
28
+ /** Get patterns as structured data (for MCP tool) */
29
+ query(taskDescription, projectId) {
30
+ return this.store.search(taskDescription, projectId);
31
+ }
32
+ confidenceBar(confidence) {
33
+ const filled = Math.round(confidence * 5);
34
+ return "\u2588".repeat(filled) + "\u2591".repeat(5 - filled);
35
+ }
36
+ }
37
+ export {
38
+ PatternApplier
39
+ };