@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
@@ -20,6 +20,10 @@ import { FrameManager } from "../../core/context/index.js";
20
20
  import { LinearTaskManager } from "../../features/tasks/linear-task-manager.js";
21
21
  import { logger } from "../../core/monitoring/logger.js";
22
22
  import { EnhancedHandoffGenerator } from "../../core/session/handoff.js";
23
+ import {
24
+ getProjectHandoffPaths,
25
+ loadProjectHandoff
26
+ } from "../../core/session/project-handoff.js";
23
27
  const countTokens = (text) => Math.ceil(text.length / 3.5);
24
28
  const MAX_HANDOFF_VERSIONS = 10;
25
29
  function saveVersionedHandoff(projectRoot, branch, content) {
@@ -273,7 +277,7 @@ Generated by stackmemory capture at ${timestamp}
273
277
  if (!existsSync(stackmemoryDir)) {
274
278
  mkdirSync(stackmemoryDir, { recursive: true });
275
279
  }
276
- const handoffPath = join(stackmemoryDir, "last-handoff.md");
280
+ const { handoffPath, metadataPath } = getProjectHandoffPaths(projectRoot);
277
281
  writeFileSync(handoffPath, handoffPrompt);
278
282
  let branch = "unknown";
279
283
  try {
@@ -289,6 +293,18 @@ Generated by stackmemory capture at ${timestamp}
289
293
  branch,
290
294
  handoffPrompt
291
295
  );
296
+ writeFileSync(
297
+ metadataPath,
298
+ JSON.stringify(
299
+ {
300
+ branch,
301
+ capturedAt: (/* @__PURE__ */ new Date()).toISOString(),
302
+ projectRoot
303
+ },
304
+ null,
305
+ 2
306
+ )
307
+ );
292
308
  console.log(
293
309
  `Versioned: ${versionedPath.split("/").slice(-2).join("/")}`
294
310
  );
@@ -331,26 +347,41 @@ Generated by stackmemory capture at ${timestamp}
331
347
  }
332
348
  function createRestoreCommand() {
333
349
  const cmd = new Command("restore");
334
- cmd.description("Restore context from last handoff").option("--no-copy", "Do not copy prompt to clipboard").action(async (options) => {
350
+ cmd.description("Restore context from last handoff").option("--no-copy", "Do not copy prompt to clipboard").option("--force", "Restore even if the handoff branch does not match").action(async (options) => {
335
351
  try {
336
352
  const projectRoot = process.cwd();
337
- const handoffPath = join(
338
- projectRoot,
339
- ".stackmemory",
340
- "last-handoff.md"
341
- );
342
353
  const metaPath = join(
343
354
  process.env["HOME"] || "~",
344
355
  ".stackmemory",
345
356
  "handoffs",
346
357
  "last-handoff-meta.json"
347
358
  );
348
- if (!existsSync(handoffPath)) {
359
+ const currentBranch = (() => {
360
+ try {
361
+ return execSync("git rev-parse --abbrev-ref HEAD", {
362
+ encoding: "utf-8",
363
+ cwd: projectRoot,
364
+ stdio: ["pipe", "pipe", "pipe"]
365
+ }).trim();
366
+ } catch {
367
+ return void 0;
368
+ }
369
+ })();
370
+ const handoff = loadProjectHandoff(projectRoot, currentBranch);
371
+ if (!handoff) {
349
372
  console.log("\u274C No handoff found in this project");
350
373
  console.log('\u{1F4A1} Run "stackmemory capture" to create one');
351
374
  return;
352
375
  }
353
- const handoffPrompt = readFileSync(handoffPath, "utf-8");
376
+ if (!handoff.compatible && !options.force) {
377
+ console.log("\u26A0\uFE0F Skipping stale handoff");
378
+ console.log(` ${handoff.mismatchReason}`);
379
+ console.log(
380
+ ' Run "stackmemory restore --force" to inspect it anyway'
381
+ );
382
+ return;
383
+ }
384
+ const handoffPrompt = handoff.content;
354
385
  console.log("\n" + "=".repeat(60));
355
386
  console.log("\u{1F4CB} RESTORED HANDOFF");
356
387
  console.log("=".repeat(60));
@@ -383,6 +383,67 @@ DATABASE_URL=${config.databaseUrl}
383
383
  console.log(chalk.yellow(" \u26A0 Could not write hosted DB env file"));
384
384
  }
385
385
  }
386
+ {
387
+ const claudeCommandsDir = join(homedir(), ".claude", "commands");
388
+ const packsDir = join(__dirname, "..", "..", "..", "packs");
389
+ const coreManifest = join(packsDir, "core", "manifest.json");
390
+ if (existsSync(coreManifest)) {
391
+ const { installCommands } = await inquirer.prompt([
392
+ {
393
+ type: "confirm",
394
+ name: "installCommands",
395
+ message: "Install session commands for Claude Code? (/start, /stop, /restart, /next)",
396
+ default: true
397
+ }
398
+ ]);
399
+ if (installCommands) {
400
+ console.log(chalk.gray("Installing command pack: core..."));
401
+ try {
402
+ execFileSync(
403
+ process.execPath,
404
+ [process.argv[1], "setup-commands", "--force"],
405
+ { stdio: "pipe" }
406
+ );
407
+ console.log(
408
+ chalk.green(" \u2713 Installed /start, /stop, /restart, /next")
409
+ );
410
+ } catch {
411
+ try {
412
+ if (!existsSync(claudeCommandsDir)) {
413
+ mkdirSync(claudeCommandsDir, { recursive: true });
414
+ }
415
+ const manifest = JSON.parse(
416
+ (await import("fs")).readFileSync(coreManifest, "utf8")
417
+ );
418
+ const allCmds = [
419
+ ...manifest.commands?.public || [],
420
+ ...manifest.commands?.internal || []
421
+ ];
422
+ let count = 0;
423
+ for (const cmd of allCmds) {
424
+ const src = join(packsDir, "core", cmd.file);
425
+ const dst = join(claudeCommandsDir, `${cmd.name}.md`);
426
+ if (existsSync(src) && !existsSync(dst)) {
427
+ execFileSync("ln", ["-s", src, dst]);
428
+ count++;
429
+ }
430
+ }
431
+ if (count > 0) {
432
+ console.log(
433
+ chalk.green(` \u2713 Installed ${count} command(s)`)
434
+ );
435
+ }
436
+ } catch {
437
+ console.log(
438
+ chalk.yellow(
439
+ " \u26A0 Could not install commands. Run: stackmemory setup-commands"
440
+ )
441
+ );
442
+ }
443
+ }
444
+ }
445
+ }
446
+ }
386
447
  const binPath = "/usr/local/bin/claude-sm";
387
448
  const sourcePath = join(configPath, "bin", "stackmemory");
388
449
  try {
@@ -531,12 +592,18 @@ function showNextSteps(config) {
531
592
  )
532
593
  );
533
594
  }
534
- console.log("3. Use with Claude:");
595
+ console.log("3. Install session commands for Claude Code:");
596
+ console.log(
597
+ chalk.gray(
598
+ " stackmemory setup-commands # Installs /start, /stop, /restart, /next\n"
599
+ )
600
+ );
601
+ console.log("4. Use with Claude:");
535
602
  console.log(chalk.gray(" claude-sm # Or use stackmemory directly\n"));
536
- console.log("4. Use with Codex:");
603
+ console.log("5. Use with Codex:");
537
604
  console.log(chalk.gray(" codex-sm # Codex + StackMemory integration\n"));
538
605
  if (config.enableLinear) {
539
- console.log("5. Sync with Linear:");
606
+ console.log("6. Sync with Linear:");
540
607
  console.log(chalk.gray(" stackmemory linear sync\n"));
541
608
  }
542
609
  console.log("For more help:");
@@ -0,0 +1,127 @@
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 { Command } from "commander";
6
+ import { existsSync, writeFileSync, mkdirSync } from "fs";
7
+ import { join } from "path";
8
+ import { homedir } from "os";
9
+ import { OvernightRunner } from "../../features/operator/overnight-runner.js";
10
+ import { OperatorLogger } from "../../features/operator/operator-logger.js";
11
+ const OPERATOR_DIR = join(homedir(), ".stackmemory", "operator");
12
+ const STOP_SIGNAL_FILE = join(OPERATOR_DIR, "stop-signal");
13
+ const LOG_DIR = join(OPERATOR_DIR, "logs");
14
+ function createOperatorCommands() {
15
+ const operator = new Command("operator").description(
16
+ "Autonomous Claude Code operator \u2014 drives sessions overnight via tmux"
17
+ );
18
+ operator.command("start").description(
19
+ "Start the operator \u2014 drains task queue using Claude Code in tmux"
20
+ ).requiredOption("-f, --file <path>", "Path to master-tasks.md").option("--cwd <dir>", "Working directory for Claude", process.cwd()).option("--mode <mode>", "Adapter: tmux | desktop | browser | auto", "auto").option("--poll <ms>", "Poll interval in ms", "2000").option("--stuck-timeout <ms>", "Stuck detection threshold in ms", "300000").option("--session <name>", "tmux session name", "operator").option("--model <model>", "Claude model override (inner agent)").option(
21
+ "--api-key <key>",
22
+ "Anthropic API key for LLM outer loop (or ANTHROPIC_API_KEY env)"
23
+ ).option(
24
+ "--llm-model <model>",
25
+ "LLM model for outer loop decisions",
26
+ "claude-haiku-4-5-20251001"
27
+ ).option("--no-auto-commit", "Disable auto-commit after each task").option(
28
+ "--max-restarts <n>",
29
+ "Max consecutive restarts before stopping",
30
+ "10"
31
+ ).option("--log-dir <dir>", "Log directory", LOG_DIR).action(async (opts) => {
32
+ if (!existsSync(opts.file)) {
33
+ process.stderr.write(`Error: task file not found: ${opts.file}
34
+ `);
35
+ process.exit(1);
36
+ }
37
+ const apiKey = opts.apiKey ?? process.env.ANTHROPIC_API_KEY;
38
+ if (opts.mode === "desktop" && !apiKey) {
39
+ process.stderr.write(
40
+ "Error: desktop mode requires --api-key or ANTHROPIC_API_KEY for screenshot interpretation\n"
41
+ );
42
+ process.exit(1);
43
+ }
44
+ const config = {
45
+ taskFilePath: opts.file,
46
+ cwd: opts.cwd,
47
+ adapterMode: opts.mode,
48
+ pollIntervalMs: parseInt(opts.poll, 10),
49
+ stuckTimeoutMs: parseInt(opts.stuckTimeout, 10),
50
+ rateLimitBackoffMs: 6e4,
51
+ maxRateLimitBackoffMs: 9e5,
52
+ maxConsecutiveRestarts: parseInt(opts.maxRestarts, 10),
53
+ sessionName: opts.session,
54
+ model: opts.model,
55
+ anthropicApiKey: apiKey,
56
+ llmModel: opts.llmModel,
57
+ autoCommit: opts.autoCommit !== false,
58
+ logDir: opts.logDir
59
+ };
60
+ const runner = new OvernightRunner(config);
61
+ try {
62
+ await runner.run();
63
+ } catch (err) {
64
+ const msg = err instanceof Error ? err.message : String(err);
65
+ process.stderr.write(`Operator error: ${msg}
66
+ `);
67
+ process.exit(1);
68
+ }
69
+ });
70
+ operator.command("stop").description("Signal the running operator to stop gracefully").option("--session <name>", "tmux session name", "operator").action((_opts) => {
71
+ mkdirSync(OPERATOR_DIR, { recursive: true });
72
+ writeFileSync(STOP_SIGNAL_FILE, String(Date.now()), "utf-8");
73
+ process.stderr.write(
74
+ "Stop signal sent. Operator will shut down after current tick.\n"
75
+ );
76
+ });
77
+ operator.command("status").description("Show current operator status from checkpoint").option("--json", "Output raw JSON").action((opts) => {
78
+ const checkpoint = OperatorLogger.readCheckpoint(OPERATOR_DIR);
79
+ if (!checkpoint) {
80
+ process.stderr.write(
81
+ "No operator checkpoint found. Is the operator running?\n"
82
+ );
83
+ process.exit(1);
84
+ }
85
+ if (opts.json) {
86
+ process.stdout.write(JSON.stringify(checkpoint, null, 2) + "\n");
87
+ return;
88
+ }
89
+ const runtime = Date.now() - checkpoint.startedAt;
90
+ const hrs = Math.floor(runtime / 36e5);
91
+ const mins = Math.floor(runtime % 36e5 / 6e4);
92
+ const lines = [
93
+ `State: ${checkpoint.currentState}`,
94
+ `Active task: ${checkpoint.currentTaskId ?? "none"}`,
95
+ `Completed: ${checkpoint.tasksCompleted.length}`,
96
+ `Blocked: ${checkpoint.tasksBlocked.length}`,
97
+ `Runtime: ${hrs}h ${mins}m`,
98
+ `Restarts: ${checkpoint.totalRestarts}`,
99
+ `Approvals: ${checkpoint.totalPermissionApprovals}`,
100
+ `Rate limits: ${checkpoint.totalRateLimitHits}`
101
+ ];
102
+ process.stdout.write(lines.join("\n") + "\n");
103
+ });
104
+ operator.command("attach").description(
105
+ "Attach to the operator tmux session for live observation (Ctrl-B d to detach)"
106
+ ).option("--session <name>", "tmux session name", "operator").action((opts) => {
107
+ try {
108
+ const {
109
+ SessionManager
110
+ } = require("../../features/operator/session-manager.js");
111
+ const mgr = new SessionManager({
112
+ sessionName: opts.session,
113
+ cwd: process.cwd()
114
+ });
115
+ mgr.attach();
116
+ } catch (err) {
117
+ const msg = err instanceof Error ? err.message : String(err);
118
+ process.stderr.write(`Attach error: ${msg}
119
+ `);
120
+ process.exit(1);
121
+ }
122
+ });
123
+ return operator;
124
+ }
125
+ export {
126
+ createOperatorCommands
127
+ };
@@ -0,0 +1,117 @@
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 { Command } from "commander";
6
+ import Database from "better-sqlite3";
7
+ import { existsSync } from "fs";
8
+ import { join } from "path";
9
+ import chalk from "chalk";
10
+ import { TraceStore } from "../../core/trace/trace-store.js";
11
+ import { TraceOptimizer } from "../../core/optimization/trace-optimizer.js";
12
+ function createOptimizeCommand() {
13
+ const optimize = new Command("optimize").description(
14
+ "Offline optimizers for harnesses, traces, and prompts"
15
+ );
16
+ optimize.command("traces").description(
17
+ "Analyze stored traces and generate HALO-like offline optimizer recommendations"
18
+ ).option("-d, --days <n>", "Only analyze traces from the last N days", "30").option(
19
+ "-m, --min-occurrences <n>",
20
+ "Minimum repeated occurrences before surfacing a pattern",
21
+ "2"
22
+ ).option("--json", "Print machine-readable JSON").option(
23
+ "--no-write",
24
+ "Do not persist report files under .stackmemory/build"
25
+ ).action(async (options) => {
26
+ const projectRoot = process.cwd();
27
+ const dbPath = join(projectRoot, ".stackmemory", "context.db");
28
+ if (!existsSync(dbPath)) {
29
+ console.log(
30
+ chalk.red("StackMemory not initialized in this directory.")
31
+ );
32
+ console.log(chalk.gray('Run "stackmemory init" first.'));
33
+ return;
34
+ }
35
+ const db = new Database(dbPath);
36
+ try {
37
+ const traceStore = new TraceStore(db);
38
+ const optimizer = new TraceOptimizer(traceStore);
39
+ const report = optimizer.analyze({
40
+ lookbackDays: parseInt(options.days, 10) || 30,
41
+ minOccurrences: parseInt(options.minOccurrences, 10) || 2
42
+ });
43
+ const persisted = options.write ? optimizer.persistReport(projectRoot, report) : null;
44
+ if (options.json) {
45
+ console.log(
46
+ JSON.stringify(
47
+ {
48
+ ...report,
49
+ persisted
50
+ },
51
+ null,
52
+ 2
53
+ )
54
+ );
55
+ return;
56
+ }
57
+ console.log(chalk.blue("\nTrace Optimizer Report\n"));
58
+ console.log(`Lookback window: ${report.lookbackDays} day(s)`);
59
+ console.log(`Traces analyzed: ${report.totalTracesAnalyzed}`);
60
+ console.log(`Traces with errors: ${report.tracesWithErrors}`);
61
+ console.log(`Causal traces: ${report.causalTraces}`);
62
+ console.log(
63
+ `Average tools/trace: ${report.averageToolsPerTrace.toFixed(2)}`
64
+ );
65
+ console.log(
66
+ `Average trace score: ${report.averageTraceScore.toFixed(2)}`
67
+ );
68
+ if (report.recommendations.length === 0) {
69
+ console.log(
70
+ chalk.yellow(
71
+ "\nNo repeated patterns crossed the threshold. Lower --min-occurrences or collect more traces."
72
+ )
73
+ );
74
+ } else {
75
+ console.log(chalk.blue("\nRecommendations:\n"));
76
+ for (const recommendation of report.recommendations) {
77
+ const badge = recommendation.priority === "high" ? chalk.red("[high]") : chalk.yellow("[medium]");
78
+ console.log(
79
+ `${badge} ${chalk.white(recommendation.title)} (${recommendation.confidence.toFixed(2)} confidence)`
80
+ );
81
+ console.log(` ${recommendation.summary}`);
82
+ console.log(` Targets: ${recommendation.targetAreas.join(", ")}`);
83
+ console.log(` Actions: ${recommendation.actions.join(" | ")}`);
84
+ console.log(
85
+ ` Validate: ${recommendation.validations.join(" | ")}`
86
+ );
87
+ console.log("");
88
+ }
89
+ }
90
+ if (report.clusters.length > 0) {
91
+ console.log(chalk.blue("Detected clusters:\n"));
92
+ for (const cluster of report.clusters) {
93
+ console.log(
94
+ `- ${cluster.label} (${cluster.occurrences} traces, ${cluster.kind})`
95
+ );
96
+ if (cluster.toolPatterns.length > 0) {
97
+ console.log(` Tools: ${cluster.toolPatterns.join(", ")}`);
98
+ }
99
+ if (cluster.sampleSummaries.length > 0) {
100
+ console.log(` Examples: ${cluster.sampleSummaries.join(" | ")}`);
101
+ }
102
+ }
103
+ console.log("");
104
+ }
105
+ if (persisted) {
106
+ console.log(chalk.gray(`Saved JSON: ${persisted.jsonPath}`));
107
+ console.log(chalk.gray(`Saved Markdown: ${persisted.markdownPath}`));
108
+ }
109
+ } finally {
110
+ db.close();
111
+ }
112
+ });
113
+ return optimize;
114
+ }
115
+ export {
116
+ createOptimizeCommand
117
+ };