@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
@@ -0,0 +1,109 @@
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
+ const SELECTORS = {
6
+ /** Main conversation/output area */
7
+ output: '[data-testid="conversation-panel"], .conversation-content, main',
8
+ /** Input textarea */
9
+ input: 'textarea[placeholder], [contenteditable="true"], .prompt-input',
10
+ /** Permission/approval buttons */
11
+ approveButton: 'button:has-text("Allow"), button:has-text("Approve"), button:has-text("Yes")',
12
+ /** Loading/working indicators */
13
+ spinner: '.loading, .spinner, [data-loading="true"]'
14
+ };
15
+ class BrowserAdapter {
16
+ constructor(page) {
17
+ this.page = page;
18
+ }
19
+ adapterType = "browser";
20
+ lastContent = "";
21
+ readScreen() {
22
+ return this.lastContent;
23
+ }
24
+ /** Async version — call before each tick in the runner */
25
+ async readScreenAsync() {
26
+ try {
27
+ const content = await this.page.locator(SELECTORS.output).textContent();
28
+ this.lastContent = content ?? "";
29
+ } catch {
30
+ this.lastContent = "";
31
+ }
32
+ return this.lastContent;
33
+ }
34
+ readScreenshot() {
35
+ return void 0;
36
+ }
37
+ async readScreenshotAsync() {
38
+ const buffer = await this.page.screenshot({ fullPage: false });
39
+ return {
40
+ base64: buffer.toString("base64"),
41
+ mediaType: "image/png"
42
+ };
43
+ }
44
+ sendInput(text, opts) {
45
+ this.sendInputAsync(text, opts).catch(() => {
46
+ });
47
+ }
48
+ async sendInputAsync(text, opts) {
49
+ try {
50
+ const inputLocator = this.page.locator(SELECTORS.input);
51
+ const count = await inputLocator.count();
52
+ if (count === 0) return;
53
+ await inputLocator.fill(text);
54
+ if (!opts?.raw) {
55
+ await this.page.keyboard.press("Enter");
56
+ }
57
+ } catch {
58
+ }
59
+ }
60
+ sendKey(key) {
61
+ this.sendKeyAsync(key).catch(() => {
62
+ });
63
+ }
64
+ async sendKeyAsync(key) {
65
+ const keyMap = {
66
+ Enter: "Enter",
67
+ "C-c": "Control+c",
68
+ Escape: "Escape",
69
+ Tab: "Tab",
70
+ y: "y",
71
+ n: "n"
72
+ };
73
+ try {
74
+ if (key === "y") {
75
+ const approveBtn = this.page.locator(SELECTORS.approveButton);
76
+ if (await approveBtn.count() > 0) {
77
+ await approveBtn.click();
78
+ return;
79
+ }
80
+ }
81
+ await this.page.keyboard.press(keyMap[key] ?? key);
82
+ } catch {
83
+ }
84
+ }
85
+ isAlive() {
86
+ try {
87
+ return !this.page.isClosed();
88
+ } catch {
89
+ return false;
90
+ }
91
+ }
92
+ clearHistory() {
93
+ }
94
+ /** Check if a spinner/loading indicator is visible */
95
+ async isWorking() {
96
+ try {
97
+ return await this.page.locator(SELECTORS.spinner).isVisible();
98
+ } catch {
99
+ return false;
100
+ }
101
+ }
102
+ /** Navigate to Claude Code web app */
103
+ async navigate(url = "https://claude.ai/code") {
104
+ await this.page.goto(url, { waitUntil: "networkidle" });
105
+ }
106
+ }
107
+ export {
108
+ BrowserAdapter
109
+ };
@@ -0,0 +1,125 @@
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 { execSync } from "child_process";
6
+ import { readFileSync, existsSync } from "fs";
7
+ import { join } from "path";
8
+ import { tmpdir } from "os";
9
+ const SCREENSHOT_PATH = join(tmpdir(), "operator-screenshot.png");
10
+ class DesktopAdapter {
11
+ constructor(appName = "Claude", llmConfig) {
12
+ this.appName = appName;
13
+ this.llmConfig = llmConfig;
14
+ }
15
+ adapterType = "desktop";
16
+ lastClassifiedText = "";
17
+ readScreen() {
18
+ this.captureWindow();
19
+ const screenshot = this.readScreenshotFile();
20
+ if (!screenshot) return this.lastClassifiedText;
21
+ try {
22
+ const result = execSync(
23
+ `node -e "
24
+ const { classifyScreenState } = require('${__dirname}/llm-decision.js');
25
+ classifyScreenState(
26
+ { base64: '${screenshot.base64.slice(0, 100)}...', mediaType: '${screenshot.mediaType}' },
27
+ { apiKey: '${this.llmConfig.apiKey}' }
28
+ ).then(r => process.stdout.write(JSON.stringify(r)));
29
+ "`,
30
+ { encoding: "utf-8", timeout: 15e3 }
31
+ );
32
+ this.lastClassifiedText = result;
33
+ } catch {
34
+ }
35
+ return this.lastClassifiedText;
36
+ }
37
+ readScreenshot() {
38
+ this.captureWindow();
39
+ return this.readScreenshotFile();
40
+ }
41
+ sendInput(text, opts) {
42
+ this.activateApp();
43
+ const escaped = text.replace(/"/g, '\\"').replace(/\\/g, "\\\\");
44
+ try {
45
+ execSync(
46
+ `osascript -e 'tell application "System Events" to keystroke "${escaped}"'`,
47
+ { stdio: "ignore", timeout: 5e3 }
48
+ );
49
+ if (!opts?.raw) {
50
+ execSync(
51
+ `osascript -e 'tell application "System Events" to key code 36'`,
52
+ { stdio: "ignore", timeout: 5e3 }
53
+ );
54
+ }
55
+ } catch {
56
+ }
57
+ }
58
+ sendKey(key) {
59
+ this.activateApp();
60
+ const keyMap = {
61
+ Enter: "key code 36",
62
+ "C-c": 'keystroke "c" using control down',
63
+ Escape: "key code 53",
64
+ Tab: "key code 48",
65
+ y: 'keystroke "y"',
66
+ n: 'keystroke "n"'
67
+ };
68
+ const action = keyMap[key] ?? `keystroke "${key}"`;
69
+ try {
70
+ execSync(`osascript -e 'tell application "System Events" to ${action}'`, {
71
+ stdio: "ignore",
72
+ timeout: 5e3
73
+ });
74
+ } catch {
75
+ }
76
+ }
77
+ isAlive() {
78
+ try {
79
+ const result = execSync(
80
+ `osascript -e 'tell application "System Events" to (name of processes) contains "${this.appName}"'`,
81
+ { encoding: "utf-8", timeout: 5e3 }
82
+ ).trim();
83
+ return result === "true";
84
+ } catch {
85
+ return false;
86
+ }
87
+ }
88
+ clearHistory() {
89
+ }
90
+ // ── Private ───────────────────────────────────────────
91
+ captureWindow() {
92
+ try {
93
+ execSync(`screencapture -x -o ${SCREENSHOT_PATH}`, {
94
+ stdio: "ignore",
95
+ timeout: 5e3
96
+ });
97
+ } catch {
98
+ }
99
+ }
100
+ readScreenshotFile() {
101
+ if (!existsSync(SCREENSHOT_PATH)) return void 0;
102
+ try {
103
+ const data = readFileSync(SCREENSHOT_PATH);
104
+ return {
105
+ base64: data.toString("base64"),
106
+ mediaType: "image/png"
107
+ };
108
+ } catch {
109
+ return void 0;
110
+ }
111
+ }
112
+ activateApp() {
113
+ try {
114
+ execSync(
115
+ `osascript -e 'tell application "${this.appName}" to activate'`,
116
+ { stdio: "ignore", timeout: 5e3 }
117
+ );
118
+ execSync("sleep 0.3", { stdio: "ignore" });
119
+ } catch {
120
+ }
121
+ }
122
+ }
123
+ export {
124
+ DesktopAdapter
125
+ };
@@ -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
+ import { TmuxAdapter, ScreenshotAdapter } from "./screen-adapter.js";
6
+ import { DesktopAdapter } from "./desktop-adapter.js";
7
+ import { BrowserAdapter } from "./browser-adapter.js";
8
+ import { createAdapter } from "./adapter-factory.js";
9
+ import {
10
+ detectState,
11
+ decideAction,
12
+ detectCompletion
13
+ } from "./state-machine.js";
14
+ import {
15
+ classifyScreenState,
16
+ generateNudge,
17
+ generateRecoveryPrompt
18
+ } from "./llm-decision.js";
19
+ import { TaskQueue } from "./task-queue.js";
20
+ import { SessionManager } from "./session-manager.js";
21
+ import { OvernightRunner } from "./overnight-runner.js";
22
+ import { OperatorLogger } from "./operator-logger.js";
23
+ export {
24
+ BrowserAdapter,
25
+ DesktopAdapter,
26
+ OperatorLogger,
27
+ OvernightRunner,
28
+ ScreenshotAdapter,
29
+ SessionManager,
30
+ TaskQueue,
31
+ TmuxAdapter,
32
+ classifyScreenState,
33
+ createAdapter,
34
+ decideAction,
35
+ detectCompletion,
36
+ detectState,
37
+ generateNudge,
38
+ generateRecoveryPrompt
39
+ };
@@ -0,0 +1,137 @@
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
+ const ANTHROPIC_API_URL = "https://api.anthropic.com/v1/messages";
6
+ const CLASSIFY_SYSTEM = `You are a screen state classifier for Claude Code CLI sessions.
7
+ Given screen content (text or screenshot), classify the state as exactly one of:
8
+ IDLE, WORKING, PERMISSION_PROMPT, ERROR, COMPLETE, STUCK, RATE_LIMITED, SESSION_ENDED, UNKNOWN
9
+
10
+ Respond with ONLY a JSON object:
11
+ {"state": "<STATE>", "confidence": "high"|"medium"|"low", "detail": "<brief reason>"}
12
+
13
+ Rules:
14
+ - PERMISSION_PROMPT: Claude is asking for user approval (Y/n, Allow, Proceed?)
15
+ - RATE_LIMITED: usage limit, rate limit, 429, "try again"
16
+ - SESSION_ENDED: shell prompt visible, process exited, no Claude session
17
+ - ERROR: error message not inside a code block or file read
18
+ - IDLE: Claude's ">" prompt visible, waiting for input
19
+ - WORKING: Claude is reading files, searching, running commands
20
+ - COMPLETE: task finished, "TASK COMPLETE" visible
21
+ - STUCK: same content repeated, no progress indicators
22
+ - UNKNOWN: cannot determine`;
23
+ async function classifyScreenState(content, config) {
24
+ const userContent = [];
25
+ if (typeof content === "string") {
26
+ userContent.push({
27
+ type: "text",
28
+ text: `Screen content:
29
+ ${content.slice(-3e3)}`
30
+ });
31
+ } else {
32
+ userContent.push({
33
+ type: "image",
34
+ source: {
35
+ type: "base64",
36
+ media_type: content.mediaType,
37
+ data: content.base64
38
+ }
39
+ });
40
+ userContent.push({
41
+ type: "text",
42
+ text: "Classify the state of this Claude Code session screenshot."
43
+ });
44
+ }
45
+ try {
46
+ const response = await callHaiku(config, CLASSIFY_SYSTEM, userContent);
47
+ const parsed = JSON.parse(response);
48
+ return {
49
+ state: parsed.state,
50
+ confidence: parsed.confidence ?? "medium",
51
+ detail: parsed.detail
52
+ };
53
+ } catch {
54
+ return {
55
+ state: "UNKNOWN",
56
+ confidence: "low",
57
+ detail: "LLM classification failed"
58
+ };
59
+ }
60
+ }
61
+ const NUDGE_SYSTEM = `You are an operator for an autonomous Claude Code session.
62
+ The inner Claude session appears stuck or going in circles.
63
+
64
+ Given the recent screen content and task context, generate a SHORT nudge message
65
+ (1-3 sentences) to get Claude back on track. Be direct and specific.
66
+
67
+ Examples of good nudges:
68
+ - "The tests are failing because of a missing import. Try: import { Foo } from './foo.js'"
69
+ - "You've been editing the same file for 5 minutes. Run the tests to check progress."
70
+ - "Stop investigating \u2014 just fix the type error on line 42 and move on."
71
+
72
+ Respond with ONLY the nudge text, no JSON wrapper.`;
73
+ async function generateNudge(screenContent, taskDescription, config) {
74
+ const prompt = `Task: ${taskDescription}
75
+
76
+ Recent screen:
77
+ ${screenContent.slice(-2e3)}`;
78
+ try {
79
+ return await callHaiku(config, NUDGE_SYSTEM, [
80
+ { type: "text", text: prompt }
81
+ ]);
82
+ } catch {
83
+ return void 0;
84
+ }
85
+ }
86
+ const RECOVERY_SYSTEM = `You are an operator for an autonomous Claude Code session.
87
+ The session has encountered an error or is blocked.
88
+
89
+ Given the error context, generate a recovery prompt that Claude can use to fix the issue.
90
+ Be specific: name files, error messages, and concrete next steps.
91
+
92
+ Respond with ONLY the recovery prompt text.`;
93
+ async function generateRecoveryPrompt(screenContent, errorDetail, checkpoint, config) {
94
+ const context = [
95
+ `Error: ${errorDetail}`,
96
+ `Current task: ${checkpoint.currentTaskId ?? "none"}`,
97
+ `Restarts so far: ${checkpoint.totalRestarts}`,
98
+ `
99
+ Recent screen:
100
+ ${screenContent.slice(-2e3)}`
101
+ ].join("\n");
102
+ try {
103
+ return await callHaiku(config, RECOVERY_SYSTEM, [
104
+ { type: "text", text: context }
105
+ ]);
106
+ } catch {
107
+ return void 0;
108
+ }
109
+ }
110
+ async function callHaiku(config, system, content) {
111
+ const model = config.model ?? "claude-haiku-4-5-20251001";
112
+ const maxTokens = config.maxTokens ?? 256;
113
+ const response = await fetch(ANTHROPIC_API_URL, {
114
+ method: "POST",
115
+ headers: {
116
+ "Content-Type": "application/json",
117
+ "x-api-key": config.apiKey,
118
+ "anthropic-version": "2023-06-01"
119
+ },
120
+ body: JSON.stringify({
121
+ model,
122
+ max_tokens: maxTokens,
123
+ system,
124
+ messages: [{ role: "user", content }]
125
+ })
126
+ });
127
+ if (!response.ok) {
128
+ throw new Error(`Haiku API ${response.status}: ${await response.text()}`);
129
+ }
130
+ const data = await response.json();
131
+ return data.content[0]?.text ?? "";
132
+ }
133
+ export {
134
+ classifyScreenState,
135
+ generateNudge,
136
+ generateRecoveryPrompt
137
+ };
@@ -0,0 +1,92 @@
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
+ mkdirSync,
7
+ appendFileSync,
8
+ writeFileSync,
9
+ existsSync,
10
+ readFileSync
11
+ } from "fs";
12
+ import { join } from "path";
13
+ class OperatorLogger {
14
+ constructor(logDir, checkpointDir) {
15
+ this.checkpointDir = checkpointDir;
16
+ mkdirSync(logDir, { recursive: true });
17
+ mkdirSync(checkpointDir, { recursive: true });
18
+ const ts = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
19
+ this.logFile = join(logDir, `operator-${ts}.jsonl`);
20
+ this.checkpointFile = join(checkpointDir, "checkpoint.json");
21
+ }
22
+ logFile;
23
+ checkpointFile;
24
+ /** Log a tick (called each poll cycle) */
25
+ logTick(entry) {
26
+ const tick = { timestamp: Date.now(), ...entry };
27
+ this.appendLine(tick);
28
+ }
29
+ /** Log a named event */
30
+ logEvent(event, data) {
31
+ const entry = { timestamp: Date.now(), event, data };
32
+ this.appendLine(entry);
33
+ const msg = data ? `[operator] ${event}: ${JSON.stringify(data)}` : `[operator] ${event}`;
34
+ process.stderr.write(msg + "\n");
35
+ }
36
+ /** Write checkpoint file for status reads */
37
+ writeCheckpoint(checkpoint) {
38
+ writeFileSync(
39
+ this.checkpointFile,
40
+ JSON.stringify(checkpoint, null, 2),
41
+ "utf-8"
42
+ );
43
+ }
44
+ /** Read checkpoint from disk (for status command) */
45
+ static readCheckpoint(checkpointDir) {
46
+ const file = join(checkpointDir, "checkpoint.json");
47
+ if (!existsSync(file)) return void 0;
48
+ try {
49
+ return JSON.parse(readFileSync(file, "utf-8"));
50
+ } catch {
51
+ return void 0;
52
+ }
53
+ }
54
+ /** Print summary to stderr */
55
+ printSummary(summary) {
56
+ const runtime = formatDuration(summary.totalRuntimeMs);
57
+ const lines = [
58
+ "",
59
+ "=== Operator Summary ===",
60
+ `Runtime: ${runtime}`,
61
+ `Completed: ${summary.tasksCompleted}`,
62
+ `Blocked: ${summary.tasksBlocked}`,
63
+ `Remaining: ${summary.tasksRemaining}`,
64
+ `Restarts: ${summary.totalRestarts}`,
65
+ `Approvals: ${summary.totalPermissionApprovals}`,
66
+ `Rate limits: ${summary.totalRateLimitHits}`,
67
+ `Log file: ${this.logFile}`,
68
+ `Checkpoint: ${this.checkpointFile}`,
69
+ "========================",
70
+ ""
71
+ ];
72
+ process.stderr.write(lines.join("\n"));
73
+ }
74
+ getLogFile() {
75
+ return this.logFile;
76
+ }
77
+ // ── Private ───────────────────────────────────────────
78
+ appendLine(entry) {
79
+ appendFileSync(this.logFile, JSON.stringify(entry) + "\n", "utf-8");
80
+ }
81
+ }
82
+ function formatDuration(ms) {
83
+ const secs = Math.floor(ms / 1e3);
84
+ const mins = Math.floor(secs / 60);
85
+ const hrs = Math.floor(mins / 60);
86
+ if (hrs > 0) return `${hrs}h ${mins % 60}m`;
87
+ if (mins > 0) return `${mins}m ${secs % 60}s`;
88
+ return `${secs}s`;
89
+ }
90
+ export {
91
+ OperatorLogger
92
+ };