@stackmemoryai/stackmemory 1.12.0 → 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 (162) hide show
  1. package/LICENSE +131 -64
  2. package/README.md +3 -1
  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/codex-sm.js +51 -11
  11. package/dist/src/cli/commands/brain.js +206 -0
  12. package/dist/src/cli/commands/company-os.js +184 -0
  13. package/dist/src/cli/commands/context.js +5 -0
  14. package/dist/src/cli/commands/operator.js +127 -0
  15. package/dist/src/cli/commands/orchestrate.js +2 -0
  16. package/dist/src/cli/commands/orchestrator.js +3 -2
  17. package/dist/src/cli/commands/patterns.js +254 -0
  18. package/dist/src/cli/commands/portal.js +161 -0
  19. package/dist/src/cli/commands/scaffold.js +92 -0
  20. package/dist/src/cli/commands/setup.js +1 -4
  21. package/dist/src/cli/commands/sync.js +253 -0
  22. package/dist/src/cli/commands/tasks.js +130 -1
  23. package/dist/src/cli/commands/vision.js +221 -0
  24. package/dist/src/cli/hermes-sm.js +224 -0
  25. package/dist/src/cli/index.js +15 -10
  26. package/dist/src/cli/utils/real-cli-bin.js +72 -0
  27. package/dist/src/core/brain/brain-store.js +187 -0
  28. package/dist/src/core/brain/brain-sync.js +193 -0
  29. package/dist/src/core/brain/index.js +78 -0
  30. package/dist/src/core/brain/types.js +10 -0
  31. package/dist/src/core/cache/token-estimator.js +24 -1
  32. package/dist/src/core/config/feature-flags.js +2 -6
  33. package/dist/src/core/context/frame-database.js +44 -0
  34. package/dist/src/core/context/recursive-context-manager.js +1 -1
  35. package/dist/src/core/context/rehydration.js +2 -1
  36. package/dist/src/core/database/sqlite-adapter.js +14 -1
  37. package/dist/src/core/models/model-router.js +33 -1
  38. package/dist/src/core/models/provider-pricing.js +58 -4
  39. package/dist/src/core/patterns/index.js +22 -0
  40. package/dist/src/core/patterns/pattern-applier.js +39 -0
  41. package/dist/src/core/patterns/pattern-observer.js +157 -0
  42. package/dist/src/core/patterns/pattern-store.js +259 -0
  43. package/dist/src/core/patterns/types.js +19 -0
  44. package/dist/src/core/retrieval/llm-context-retrieval.js +5 -4
  45. package/dist/src/core/retrieval/unified-context-assembler.js +11 -66
  46. package/dist/src/core/skill-packs/types.js +14 -1
  47. package/dist/src/core/storage/cloud-sync-manager.js +116 -0
  48. package/dist/src/core/storage/cloud-sync.js +574 -0
  49. package/dist/src/core/storage/two-tier-storage.js +5 -1
  50. package/dist/src/core/tasks/master-tasks-template.js +43 -0
  51. package/dist/src/core/tasks/md-task-parser.js +138 -0
  52. package/dist/src/core/vision/index.js +27 -0
  53. package/dist/src/core/vision/signals.js +79 -0
  54. package/dist/src/core/vision/types.js +22 -0
  55. package/dist/src/core/vision/vision-file.js +146 -0
  56. package/dist/src/core/vision/vision-loop.js +220 -0
  57. package/dist/src/core/wiki/wiki-compiler.js +103 -1
  58. package/dist/src/daemon/daemon-config.js +45 -0
  59. package/dist/src/daemon/services/desire-path-service.js +566 -0
  60. package/dist/src/daemon/services/research-stream-service.js +320 -0
  61. package/dist/src/daemon/services/telemetry-service.js +192 -0
  62. package/dist/src/daemon/unified-daemon.js +28 -1
  63. package/dist/src/features/browser/cli-browser-agent.js +417 -0
  64. package/dist/src/features/browser/stagehand-workflows.js +578 -0
  65. package/dist/src/features/operator/adapter-factory.js +62 -0
  66. package/dist/src/features/operator/browser-adapter.js +109 -0
  67. package/dist/src/features/operator/desktop-adapter.js +125 -0
  68. package/dist/src/features/operator/index.js +39 -0
  69. package/dist/src/features/operator/llm-decision.js +137 -0
  70. package/dist/src/features/operator/operator-logger.js +92 -0
  71. package/dist/src/features/operator/overnight-runner.js +327 -0
  72. package/dist/src/features/operator/screen-adapter.js +91 -0
  73. package/dist/src/features/operator/session-manager.js +127 -0
  74. package/dist/src/features/operator/state-machine.js +227 -0
  75. package/dist/src/features/operator/task-queue.js +81 -0
  76. package/dist/src/features/portal/index.js +26 -0
  77. package/dist/src/features/portal/server.js +240 -0
  78. package/dist/src/features/portal/types.js +14 -0
  79. package/dist/src/features/portal/ui.js +195 -0
  80. package/dist/src/features/tasks/task-aware-context.js +2 -1
  81. package/dist/src/features/tui/simple-monitor.js +0 -23
  82. package/dist/src/features/tui/swarm-monitor.js +8 -66
  83. package/dist/src/{integrations/diffmem/index.js → features/web/client/hooks/use-socket.js} +6 -5
  84. package/dist/src/features/web/client/lib/utils.js +12 -0
  85. package/dist/src/features/web/client/stores/session-store.js +12 -0
  86. package/dist/src/features/web/server/gcp-billing.js +76 -0
  87. package/dist/src/features/web/server/index.js +10 -0
  88. package/dist/src/features/web/server/spend-calculator.js +228 -0
  89. package/dist/src/hooks/schemas.js +4 -1
  90. package/dist/src/integrations/anthropic/client.js +3 -2
  91. package/dist/src/integrations/claude-code/agent-bridge.js +0 -3
  92. package/dist/src/integrations/claude-code/subagent-client.js +218 -11
  93. package/dist/src/integrations/claude-code/task-coordinator.js +2 -1
  94. package/dist/src/integrations/linear/webhook-retry.js +196 -0
  95. package/dist/src/integrations/linear/webhook-server.js +18 -22
  96. package/dist/src/integrations/mcp/handlers/cloud-sync-handlers.js +101 -0
  97. package/dist/src/integrations/mcp/handlers/index.js +27 -52
  98. package/dist/src/integrations/mcp/server.js +122 -335
  99. package/dist/src/integrations/mcp/tool-alias-registry.js +0 -73
  100. package/dist/src/integrations/mcp/tool-definitions.js +111 -510
  101. package/dist/src/mcp/stackmemory-mcp-server.js +404 -379
  102. package/dist/src/orchestrators/multimodal/determinism.js +2 -1
  103. package/dist/src/orchestrators/multimodal/harness.js +2 -1
  104. package/dist/src/skills/recursive-agent-orchestrator.js +2 -4
  105. package/dist/src/utils/process-cleanup.js +1 -7
  106. package/docs/README.md +42 -0
  107. package/docs/guides/README_INSTALL.md +208 -0
  108. package/package.json +18 -9
  109. package/scripts/claude-code-wrapper.sh +11 -0
  110. package/scripts/claude-sm-setup.sh +12 -1
  111. package/scripts/codex-wrapper.sh +11 -0
  112. package/scripts/git-hooks/branch-context-manager.sh +11 -0
  113. package/scripts/git-hooks/post-checkout-stackmemory.sh +11 -0
  114. package/scripts/git-hooks/post-commit-stackmemory.sh +11 -0
  115. package/scripts/git-hooks/pre-commit-stackmemory.sh +11 -0
  116. package/scripts/hooks/cleanup-shell.sh +12 -1
  117. package/scripts/hooks/task-complete.sh +12 -1
  118. package/scripts/install-code-execution-hooks.sh +12 -1
  119. package/scripts/install-sweep-hook.sh +12 -0
  120. package/scripts/install.sh +11 -0
  121. package/scripts/opencode-wrapper.sh +11 -0
  122. package/scripts/portal/cloud-init.yaml +69 -0
  123. package/scripts/portal/setup.sh +69 -0
  124. package/scripts/portal/stackmemory-portal.service +34 -0
  125. package/scripts/setup-claude-integration.sh +12 -1
  126. package/scripts/smoke-init-db.sh +23 -0
  127. package/scripts/stackmemory-daemon.sh +11 -0
  128. package/scripts/verify-dist.cjs +11 -4
  129. package/dist/src/cli/commands/ralph.js +0 -1053
  130. package/dist/src/hooks/diffmem-hooks.js +0 -376
  131. package/dist/src/integrations/diffmem/client.js +0 -208
  132. package/dist/src/integrations/diffmem/config.js +0 -14
  133. package/dist/src/integrations/greptile/client.js +0 -101
  134. package/dist/src/integrations/greptile/config.js +0 -14
  135. package/dist/src/integrations/greptile/index.js +0 -11
  136. package/dist/src/integrations/mcp/handlers/cross-search-handlers.js +0 -188
  137. package/dist/src/integrations/mcp/handlers/diffmem-handlers.js +0 -455
  138. package/dist/src/integrations/mcp/handlers/greptile-handlers.js +0 -456
  139. package/dist/src/integrations/mcp/handlers/provider-handlers.js +0 -227
  140. package/dist/src/integrations/ralph/bridge/ralph-stackmemory-bridge.js +0 -863
  141. package/dist/src/integrations/ralph/context/context-budget-manager.js +0 -308
  142. package/dist/src/integrations/ralph/context/stackmemory-context-loader.js +0 -354
  143. package/dist/src/integrations/ralph/index.js +0 -17
  144. package/dist/src/integrations/ralph/learning/pattern-learner.js +0 -416
  145. package/dist/src/integrations/ralph/lifecycle/iteration-lifecycle.js +0 -448
  146. package/dist/src/integrations/ralph/loopmax.js +0 -488
  147. package/dist/src/integrations/ralph/monitoring/swarm-dashboard.js +0 -293
  148. package/dist/src/integrations/ralph/monitoring/swarm-registry.js +0 -107
  149. package/dist/src/integrations/ralph/orchestration/multi-loop-orchestrator.js +0 -508
  150. package/dist/src/integrations/ralph/patterns/compounding-engineering-pattern.js +0 -407
  151. package/dist/src/integrations/ralph/patterns/extended-coherence-sessions.js +0 -495
  152. package/dist/src/integrations/ralph/patterns/oracle-worker-pattern.js +0 -387
  153. package/dist/src/integrations/ralph/performance/performance-optimizer.js +0 -357
  154. package/dist/src/integrations/ralph/recovery/crash-recovery.js +0 -461
  155. package/dist/src/integrations/ralph/state/state-reconciler.js +0 -420
  156. package/dist/src/integrations/ralph/swarm/git-workflow-manager.js +0 -444
  157. package/dist/src/integrations/ralph/swarm/swarm-coordinator.js +0 -1005
  158. package/dist/src/integrations/ralph/visualization/ralph-debugger.js +0 -635
  159. package/scripts/ralph-loop-implementation.js +0 -404
  160. /package/dist/src/{integrations/diffmem/types.js → core/storage/cloud-sync-types.js} +0 -0
  161. /package/dist/src/{integrations/greptile → features/operator}/types.js +0 -0
  162. /package/dist/src/{integrations/ralph/types.js → features/web/client/next-env.d.js} +0 -0
@@ -0,0 +1,195 @@
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 XTERM_VERSION = "5.3.0";
6
+ const FIT_VERSION = "0.10.0";
7
+ const SOCKET_IO_VERSION = "4.7.5";
8
+ function renderPortalPage(opts) {
9
+ return `<!doctype html>
10
+ <html lang="en">
11
+ <head>
12
+ <meta charset="utf-8" />
13
+ <meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
14
+ <meta name="theme-color" content="#0b0f17" />
15
+ <title>StackMemory Portal \u2014 ${escapeHtml(opts.session)}</title>
16
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@xterm/xterm@${XTERM_VERSION}/css/xterm.min.css" />
17
+ <style>
18
+ :root {
19
+ --bg: #0b0f17;
20
+ --panel: #11161f;
21
+ --border: #1e2733;
22
+ --accent: #7c5cff;
23
+ --accent-dim: #4a3a99;
24
+ --ok: #2ecc71;
25
+ --warn: #f1c40f;
26
+ --err: #e74c3c;
27
+ --text: #c7d0dc;
28
+ --muted: #6b7686;
29
+ }
30
+ * { box-sizing: border-box; }
31
+ html, body {
32
+ margin: 0; height: 100%;
33
+ background: var(--bg); color: var(--text);
34
+ font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
35
+ overflow: hidden;
36
+ }
37
+ #app { display: flex; flex-direction: column; height: 100dvh; }
38
+ header {
39
+ display: flex; align-items: center; gap: 12px;
40
+ padding: 10px 14px; background: var(--panel);
41
+ border-bottom: 1px solid var(--border);
42
+ -webkit-app-region: drag; user-select: none;
43
+ }
44
+ .brand { display: flex; align-items: center; gap: 9px; font-weight: 700; letter-spacing: .3px; }
45
+ .logo {
46
+ width: 22px; height: 22px; border-radius: 6px;
47
+ background: linear-gradient(135deg, var(--accent), #34d3ff);
48
+ box-shadow: 0 0 18px rgba(124, 92, 255, .55);
49
+ }
50
+ .session { color: var(--muted); font-size: 12px; }
51
+ .session b { color: var(--text); font-weight: 600; }
52
+ .spacer { flex: 1; }
53
+ .dot { width: 9px; height: 9px; border-radius: 50%; background: var(--muted); box-shadow: 0 0 0 0 rgba(0,0,0,0); transition: background .2s; }
54
+ .dot.ok { background: var(--ok); box-shadow: 0 0 10px var(--ok); }
55
+ .dot.err { background: var(--err); box-shadow: 0 0 10px var(--err); }
56
+ .status { font-size: 12px; color: var(--muted); min-width: 92px; text-align: right; }
57
+ button.tool {
58
+ background: transparent; color: var(--muted); border: 1px solid var(--border);
59
+ border-radius: 7px; padding: 5px 10px; font: inherit; font-size: 12px; cursor: pointer;
60
+ transition: all .15s;
61
+ }
62
+ button.tool:hover { color: var(--text); border-color: var(--accent-dim); }
63
+ #term-wrap { flex: 1; padding: 8px 10px 10px; min-height: 0; }
64
+ #terminal { height: 100%; }
65
+ #overlay {
66
+ position: fixed; inset: 0; display: none; place-items: center;
67
+ background: rgba(5, 8, 13, .85); backdrop-filter: blur(3px); z-index: 10;
68
+ }
69
+ #overlay.show { display: grid; }
70
+ .card {
71
+ background: var(--panel); border: 1px solid var(--border); border-radius: 14px;
72
+ padding: 26px 30px; max-width: 380px; text-align: center;
73
+ box-shadow: 0 24px 60px rgba(0,0,0,.5);
74
+ }
75
+ .card h2 { margin: 0 0 8px; font-size: 17px; }
76
+ .card p { margin: 0; color: var(--muted); font-size: 13px; line-height: 1.5; }
77
+ .reconnect { margin-top: 16px; }
78
+ .reconnect button {
79
+ background: var(--accent); color: #fff; border: 0; border-radius: 8px;
80
+ padding: 8px 18px; font: inherit; font-weight: 600; cursor: pointer;
81
+ }
82
+ </style>
83
+ </head>
84
+ <body>
85
+ <div id="app">
86
+ <header>
87
+ <span class="brand"><span class="logo"></span>StackMemory Portal</span>
88
+ <span class="session">session <b>${escapeHtml(opts.session)}</b></span>
89
+ <span class="spacer"></span>
90
+ <button class="tool" id="btn-clear" title="Clear the local view">clear</button>
91
+ <button class="tool" id="btn-fit" title="Re-fit the terminal">fit</button>
92
+ <span class="dot" id="dot"></span>
93
+ <span class="status" id="status">connecting\u2026</span>
94
+ </header>
95
+ <div id="term-wrap"><div id="terminal"></div></div>
96
+ </div>
97
+
98
+ <div id="overlay">
99
+ <div class="card">
100
+ <h2 id="ov-title">Disconnected</h2>
101
+ <p id="ov-msg">The connection to your agent was lost.</p>
102
+ <div class="reconnect"><button id="btn-reconnect">Reconnect</button></div>
103
+ </div>
104
+ </div>
105
+
106
+ <script src="https://cdn.jsdelivr.net/npm/@xterm/xterm@${XTERM_VERSION}/lib/xterm.min.js"></script>
107
+ <script src="https://cdn.jsdelivr.net/npm/@xterm/addon-fit@${FIT_VERSION}/lib/addon-fit.min.js"></script>
108
+ <script src="https://cdn.jsdelivr.net/npm/socket.io-client@${SOCKET_IO_VERSION}/dist/socket.io.min.js"></script>
109
+ <script>
110
+ (function () {
111
+ var params = new URLSearchParams(location.search);
112
+ var token = params.get('token') || '';
113
+
114
+ var term = new Terminal({
115
+ cursorBlink: true,
116
+ fontSize: 14,
117
+ fontFamily: 'ui-monospace, SFMono-Regular, Menlo, Consolas, monospace',
118
+ scrollback: 10000,
119
+ theme: {
120
+ background: '#0b0f17', foreground: '#c7d0dc', cursor: '#7c5cff',
121
+ selectionBackground: '#2a3550', black: '#0b0f17', brightBlack: '#3a4658'
122
+ }
123
+ });
124
+ var fit = new FitAddon.FitAddon();
125
+ term.loadAddon(fit);
126
+ term.open(document.getElementById('terminal'));
127
+ fit.fit();
128
+
129
+ var dot = document.getElementById('dot');
130
+ var status = document.getElementById('status');
131
+ var overlay = document.getElementById('overlay');
132
+ var ovTitle = document.getElementById('ov-title');
133
+ var ovMsg = document.getElementById('ov-msg');
134
+
135
+ function setStatus(text, state) {
136
+ status.textContent = text;
137
+ dot.className = 'dot' + (state ? ' ' + state : '');
138
+ }
139
+ function showOverlay(title, msg) {
140
+ ovTitle.textContent = title; ovMsg.textContent = msg; overlay.classList.add('show');
141
+ }
142
+ function hideOverlay() { overlay.classList.remove('show'); }
143
+
144
+ var socket = io({ auth: { token: token }, query: { token: token }, reconnectionAttempts: 8 });
145
+
146
+ socket.on('connect', function () {
147
+ setStatus('connected', 'ok'); hideOverlay(); term.focus(); sendResize();
148
+ });
149
+ socket.on('output', function (data) { term.write(data); });
150
+ socket.on('portal:error', function (msg) {
151
+ setStatus('error', 'err'); showOverlay('Cannot start session', String(msg));
152
+ });
153
+ socket.on('disconnect', function () {
154
+ setStatus('disconnected', 'err');
155
+ showOverlay('Disconnected', 'The portal connection dropped. Your tmux session keeps running in the background.');
156
+ });
157
+ socket.on('connect_error', function (err) {
158
+ setStatus('auth failed', 'err');
159
+ showOverlay('Connection refused', (err && err.message) || 'Check your access token and try again.');
160
+ });
161
+
162
+ term.onData(function (data) { socket.emit('input', data); });
163
+
164
+ function sendResize() {
165
+ socket.emit('resize', { cols: term.cols, rows: term.rows });
166
+ }
167
+ function doFit() { try { fit.fit(); sendResize(); } catch (e) {} }
168
+
169
+ var rt;
170
+ window.addEventListener('resize', function () { clearTimeout(rt); rt = setTimeout(doFit, 120); });
171
+ document.getElementById('btn-fit').addEventListener('click', doFit);
172
+ document.getElementById('btn-clear').addEventListener('click', function () { term.clear(); term.focus(); });
173
+ document.getElementById('btn-reconnect').addEventListener('click', function () { hideOverlay(); socket.connect(); });
174
+
175
+ setStatus('connecting\u2026');
176
+ })();
177
+ </script>
178
+ </body>
179
+ </html>`;
180
+ }
181
+ function escapeHtml(s) {
182
+ return s.replace(
183
+ /[&<>"']/g,
184
+ (c) => ({
185
+ "&": "&amp;",
186
+ "<": "&lt;",
187
+ ">": "&gt;",
188
+ '"': "&quot;",
189
+ "'": "&#39;"
190
+ })[c]
191
+ );
192
+ }
193
+ export {
194
+ renderPortalPage
195
+ };
@@ -3,6 +3,7 @@ import { dirname as __pathDirname } from 'path';
3
3
  const __filename = __fileURLToPath(import.meta.url);
4
4
  const __dirname = __pathDirname(__filename);
5
5
  import { logger } from "../../core/monitoring/logger.js";
6
+ import { estimateTokens } from "../../core/cache/token-estimator.js";
6
7
  class TaskAwareContextManager {
7
8
  db;
8
9
  frameManager;
@@ -293,7 +294,7 @@ class TaskAwareContextManager {
293
294
  const line = `- [${task.status.toUpperCase()}] ${task.name} (${task.priority})
294
295
  `;
295
296
  context += line;
296
- totalTokens += line.length / 4;
297
+ totalTokens += estimateTokens(line);
297
298
  relevanceScores[task.task_id] = 1;
298
299
  });
299
300
  context += "\n";
@@ -2,7 +2,6 @@ import { fileURLToPath as __fileURLToPath } from 'url';
2
2
  import { dirname as __pathDirname } from 'path';
3
3
  const __filename = __fileURLToPath(import.meta.url);
4
4
  const __dirname = __pathDirname(__filename);
5
- import { SwarmRegistry } from "../../integrations/ralph/monitoring/swarm-registry.js";
6
5
  import { execSync } from "child_process";
7
6
  class SimpleSwarmMonitor {
8
7
  refreshInterval = null;
@@ -10,7 +9,6 @@ class SimpleSwarmMonitor {
10
9
  * Start simple text-based monitoring
11
10
  */
12
11
  start() {
13
- console.log("\u{1F9BE} Ralph Swarm Monitor (Text Mode)");
14
12
  console.log("=====================================");
15
13
  console.log("");
16
14
  console.log("Press Ctrl+C to quit");
@@ -42,7 +40,6 @@ class SimpleSwarmMonitor {
42
40
  \u23F0 ${timestamp} - Swarm Status Update`);
43
41
  console.log("\u2500".repeat(50));
44
42
  try {
45
- const registry = SwarmRegistry.getInstance();
46
43
  const activeSwarms = registry.listActiveSwarms();
47
44
  const stats = registry.getStatistics();
48
45
  console.log(
@@ -59,26 +56,6 @@ class SimpleSwarmMonitor {
59
56
  } else {
60
57
  console.log("\u274C No active swarms in registry");
61
58
  }
62
- try {
63
- const ralphProcesses = execSync(
64
- 'ps aux | grep "ralph" | grep -v grep',
65
- { encoding: "utf8" }
66
- );
67
- if (ralphProcesses.trim()) {
68
- console.log("\n\u{1F50D} External Ralph Processes:");
69
- const processLines = ralphProcesses.split("\n").filter((line) => line.trim());
70
- processLines.slice(0, 3).forEach((line) => {
71
- const parts = line.split(/\s+/);
72
- console.log(
73
- ` PID ${parts[1]}: ${parts.slice(10).join(" ").slice(0, 50)}...`
74
- );
75
- });
76
- if (processLines.length > 3) {
77
- console.log(` ... and ${processLines.length - 3} more processes`);
78
- }
79
- }
80
- } catch {
81
- }
82
59
  try {
83
60
  const recentCommits = execSync(
84
61
  'git log --oneline --since="1 hour ago" --pretty=format:"%h %an %s" | head -3',
@@ -4,8 +4,6 @@ const __filename = __fileURLToPath(import.meta.url);
4
4
  const __dirname = __pathDirname(__filename);
5
5
  import blessed from "blessed";
6
6
  import { logger } from "../../core/monitoring/logger.js";
7
- import { SwarmDashboard } from "../../integrations/ralph/monitoring/swarm-dashboard.js";
8
- import { SwarmRegistry } from "../../integrations/ralph/monitoring/swarm-registry.js";
9
7
  import { execSync } from "child_process";
10
8
  class SwarmTUI {
11
9
  screen;
@@ -14,8 +12,6 @@ class SwarmTUI {
14
12
  agentsTable;
15
13
  metricsBox;
16
14
  logBox;
17
- swarmCoordinator = null;
18
- swarmDashboard = null;
19
15
  refreshInterval = null;
20
16
  commitMetrics = /* @__PURE__ */ new Map();
21
17
  constructor() {
@@ -24,7 +20,6 @@ class SwarmTUI {
24
20
  this.screen = blessed.screen({
25
21
  smartCSR: !isGhostty,
26
22
  // Disable smart CSR for ghostty
27
- title: "Ralph Swarm Monitor",
28
23
  terminal: isGhostty ? "xterm-256color" : void 0,
29
24
  fullUnicode: !isBasicTerm,
30
25
  dockBorders: false,
@@ -39,9 +34,7 @@ class SwarmTUI {
39
34
  console.log(
40
35
  "\u26A0\uFE0F TUI display error detected. Try setting TERM=xterm-256color"
41
36
  );
42
- console.log(
43
- " Alternative: Use stackmemory ralph status for text-based monitoring"
44
- );
37
+ console.log();
45
38
  });
46
39
  this.createUI();
47
40
  this.setupKeyHandlers();
@@ -66,7 +59,6 @@ class SwarmTUI {
66
59
  left: 0,
67
60
  width: "100%",
68
61
  height: 3,
69
- content: "\u{1F9BE} Ralph Swarm Monitor - Real-time Swarm Operations",
70
62
  style: safeColors ? {
71
63
  fg: "white",
72
64
  bold: false
@@ -229,10 +221,9 @@ class SwarmTUI {
229
221
  /**
230
222
  * Initialize swarm monitoring
231
223
  */
232
- async initialize(swarmCoordinator, swarmId) {
224
+ async initialize(swarmId) {
233
225
  try {
234
226
  if (swarmId) {
235
- const registry = SwarmRegistry.getInstance();
236
227
  const swarm = registry.getSwarm(swarmId);
237
228
  if (swarm) {
238
229
  this.swarmCoordinator = swarm.coordinator;
@@ -243,7 +234,6 @@ class SwarmTUI {
243
234
  } else if (swarmCoordinator) {
244
235
  this.swarmCoordinator = swarmCoordinator;
245
236
  } else {
246
- const registry = SwarmRegistry.getInstance();
247
237
  const activeSwarms = registry.listActiveSwarms();
248
238
  if (activeSwarms.length > 0) {
249
239
  this.swarmCoordinator = activeSwarms[0].coordinator;
@@ -251,7 +241,6 @@ class SwarmTUI {
251
241
  }
252
242
  }
253
243
  if (this.swarmCoordinator) {
254
- this.swarmDashboard = new SwarmDashboard(this.swarmCoordinator);
255
244
  this.swarmDashboard.startMonitoring(2e3);
256
245
  this.swarmDashboard.on("metricsUpdated", (metrics) => {
257
246
  this.updateUI(metrics);
@@ -271,7 +260,6 @@ class SwarmTUI {
271
260
  */
272
261
  start() {
273
262
  this.screen.render();
274
- this.logBox.log("Ralph Swarm Monitor started");
275
263
  this.logBox.log("Monitoring for active swarms...");
276
264
  }
277
265
  /**
@@ -450,7 +438,6 @@ Success Rate: ${status.performance.efficiency > 0 ? (status.performance.efficien
450
438
  */
451
439
  async detectActiveSwarms() {
452
440
  try {
453
- const registry = SwarmRegistry.getInstance();
454
441
  const activeSwarms = registry.listActiveSwarms();
455
442
  const stats = registry.getStatistics();
456
443
  if (activeSwarms.length > 0) {
@@ -469,22 +456,9 @@ Success Rate: ${status.performance.efficiency > 0 ? (status.performance.efficien
469
456
  `Found ${activeSwarms.length} active swarms in registry`
470
457
  );
471
458
  } else {
472
- const ralphProcesses = execSync(
473
- 'ps aux | grep "ralph" | grep -v grep',
474
- { encoding: "utf8" }
475
- );
476
- if (ralphProcesses.trim()) {
477
- this.logBox.log("Detected Ralph processes running");
478
- this.statusBox.setContent(
479
- "External Ralph processes detected\n(Use swarm coordinator for full monitoring)"
480
- );
481
- } else {
482
- this.statusBox.setContent(`No active swarms detected
459
+ this.statusBox.setContent(`No active swarms detected
483
460
  Total swarms: ${stats.totalSwarms}
484
- Completed: ${stats.completedSwarms}
485
-
486
- Run: stackmemory ralph swarm <task>`);
487
- }
461
+ Completed: ${stats.completedSwarms}`);
488
462
  }
489
463
  } catch {
490
464
  }
@@ -518,9 +492,7 @@ Run: stackmemory ralph swarm <task>`);
518
492
  */
519
493
  startSwarmInteractive() {
520
494
  this.logBox.log("\u{1F680} Start Swarm Interactive Mode:");
521
- this.logBox.log(
522
- 'Example: stackmemory ralph swarm "Implement feature" --agents developer,tester'
523
- );
495
+ this.logBox.log();
524
496
  this.logBox.log(
525
497
  'Tip: Run the command in another terminal, then press "d" to detect it'
526
498
  );
@@ -536,14 +508,12 @@ Run: stackmemory ralph swarm <task>`);
536
508
  );
537
509
  } else {
538
510
  this.logBox.log("\u274C No active swarm coordinator to stop");
539
- this.logBox.log("External Ralph processes must be stopped manually");
540
511
  }
541
512
  }
542
513
  /**
543
514
  * Show help dialog
544
515
  */
545
516
  showHelp() {
546
- this.logBox.log("\u{1F9BE} Ralph Swarm Monitor - Help");
547
517
  this.logBox.log("");
548
518
  this.logBox.log("Keyboard Shortcuts:");
549
519
  this.logBox.log(" q, Esc, Ctrl+C - Quit TUI");
@@ -555,17 +525,11 @@ Run: stackmemory ralph swarm <task>`);
555
525
  this.logBox.log(" d - Detect and list available swarms");
556
526
  this.logBox.log("");
557
527
  this.logBox.log("Usage:");
558
- this.logBox.log(
559
- " stackmemory ralph tui # Auto-detect swarms"
560
- );
561
- this.logBox.log(
562
- " stackmemory ralph tui --swarm-id <id> # Monitor specific swarm"
563
- );
528
+ this.logBox.log();
529
+ this.logBox.log();
564
530
  this.logBox.log("");
565
531
  this.logBox.log("Starting Swarms:");
566
- this.logBox.log(
567
- ' stackmemory ralph swarm "Task description" --agents developer,tester'
568
- );
532
+ this.logBox.log();
569
533
  this.logBox.log("");
570
534
  }
571
535
  /**
@@ -581,7 +545,6 @@ Run: stackmemory ralph swarm <task>`);
581
545
  async showDetectedSwarms() {
582
546
  this.logBox.log("\u{1F50D} Detecting active swarms...");
583
547
  try {
584
- const registry = SwarmRegistry.getInstance();
585
548
  const activeSwarms = registry.listActiveSwarms();
586
549
  const stats = registry.getStatistics();
587
550
  this.logBox.log("");
@@ -601,27 +564,6 @@ Run: stackmemory ralph swarm <task>`);
601
564
  } else {
602
565
  this.logBox.log("");
603
566
  this.logBox.log("\u274C No active swarms in registry");
604
- try {
605
- const ralphProcesses = execSync(
606
- 'ps aux | grep "ralph" | grep -v grep',
607
- { encoding: "utf8" }
608
- );
609
- if (ralphProcesses.trim()) {
610
- this.logBox.log("\u{1F50D} External Ralph processes detected:");
611
- ralphProcesses.split("\n").filter((line) => line.trim()).forEach((line) => {
612
- const parts = line.split(/\s+/);
613
- this.logBox.log(
614
- ` PID ${parts[1]}: ${parts.slice(10).join(" ").slice(0, 60)}`
615
- );
616
- });
617
- }
618
- } catch {
619
- this.logBox.log("\u{1F50D} No external Ralph processes found");
620
- }
621
- this.logBox.log("");
622
- this.logBox.log(
623
- '\u{1F4A1} Start a swarm: stackmemory ralph swarm "Task" --agents developer'
624
- );
625
567
  }
626
568
  } catch (error) {
627
569
  this.logBox.log(`\u274C Detection failed: ${error.message}`);
@@ -2,10 +2,11 @@ import { fileURLToPath as __fileURLToPath } from 'url';
2
2
  import { dirname as __pathDirname } from 'path';
3
3
  const __filename = __fileURLToPath(import.meta.url);
4
4
  const __dirname = __pathDirname(__filename);
5
- import { DEFAULT_DIFFMEM_CONFIG } from "./config.js";
6
- import { DiffMemClient, DiffMemClientError } from "./client.js";
5
+ "use client";
6
+ import { useSocketContext } from "@/components/socket-provider";
7
+ function useSocket() {
8
+ return useSocketContext();
9
+ }
7
10
  export {
8
- DEFAULT_DIFFMEM_CONFIG,
9
- DiffMemClient,
10
- DiffMemClientError
11
+ useSocket
11
12
  };
@@ -0,0 +1,12 @@
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 { clsx } from "clsx";
6
+ import { twMerge } from "tailwind-merge";
7
+ function cn(...inputs) {
8
+ return twMerge(clsx(inputs));
9
+ }
10
+ export {
11
+ cn
12
+ };
@@ -0,0 +1,12 @@
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 { create } from "zustand";
6
+ const useSessionStore = create((set) => ({
7
+ sessions: [],
8
+ setSessions: (sessions) => set({ sessions })
9
+ }));
10
+ export {
11
+ useSessionStore
12
+ };
@@ -0,0 +1,76 @@
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
+ function getTableReference() {
6
+ const fullTable = process.env["GCP_BILLING_BIGQUERY_TABLE"];
7
+ if (fullTable) return fullTable;
8
+ const project = process.env["GCP_BILLING_PROJECT_ID"];
9
+ const dataset = process.env["GCP_BILLING_DATASET"];
10
+ const table = process.env["GCP_BILLING_TABLE"];
11
+ if (project && dataset && table) {
12
+ return `${project}.${dataset}.${table}`;
13
+ }
14
+ return void 0;
15
+ }
16
+ async function getBigQuery() {
17
+ const { BigQuery } = await import("@google-cloud/bigquery");
18
+ return new BigQuery();
19
+ }
20
+ async function getGcpSpend(days = 30) {
21
+ const table = getTableReference();
22
+ if (!table) return void 0;
23
+ try {
24
+ const bq = await getBigQuery();
25
+ const [totalRows] = await bq.query({
26
+ query: `
27
+ SELECT
28
+ SUM(cost) + SUM(IFNULL((SELECT SUM(c.amount) FROM UNNEST(credits) c), 0)) AS total_cost,
29
+ currency
30
+ FROM \`${table}\`
31
+ WHERE usage_start_time >= TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL @days DAY)
32
+ LIMIT 1
33
+ `,
34
+ params: { days },
35
+ location: "US"
36
+ });
37
+ const [dailyRows] = await bq.query({
38
+ query: `
39
+ SELECT
40
+ DATE(usage_start_time) AS date,
41
+ SUM(cost) + SUM(IFNULL((SELECT SUM(c.amount) FROM UNNEST(credits) c), 0)) AS cost_usd
42
+ FROM \`${table}\`
43
+ WHERE usage_start_time >= TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL @days DAY)
44
+ GROUP BY date
45
+ ORDER BY date ASC
46
+ `,
47
+ params: { days },
48
+ location: "US"
49
+ });
50
+ const total = Number(totalRows?.[0]?.total_cost ?? 0);
51
+ const currency = String(totalRows?.[0]?.currency ?? "USD");
52
+ const daily = (dailyRows || []).map((r) => ({
53
+ date: String(r.date ?? ""),
54
+ costUsd: Number(r.cost_usd ?? 0)
55
+ })).filter((d) => d.date);
56
+ return {
57
+ totalCostUsd: total,
58
+ daily,
59
+ currency,
60
+ table
61
+ };
62
+ } catch (error) {
63
+ console.warn("Failed to fetch GCP spend from BigQuery:", error);
64
+ return void 0;
65
+ }
66
+ }
67
+ function getGcpSpendEnvFallback() {
68
+ const raw = process.env["GCP_AI_SPEND_USD"];
69
+ if (!raw) return void 0;
70
+ const value = Number.parseFloat(raw);
71
+ return Number.isFinite(value) && value >= 0 ? value : void 0;
72
+ }
73
+ export {
74
+ getGcpSpend,
75
+ getGcpSpendEnvFallback
76
+ };
@@ -12,6 +12,7 @@ import { FrameManager } from "../../../core/context/index.js";
12
12
  import Database from "better-sqlite3";
13
13
  import { existsSync } from "fs";
14
14
  import { join } from "path";
15
+ import { getSpendSummary } from "./spend-calculator.js";
15
16
  function _getEnv(key, defaultValue) {
16
17
  const value = process.env[key];
17
18
  if (value === void 0) {
@@ -149,6 +150,15 @@ app.get("/api/analytics", (req, res) => {
149
150
  res.status(500).json({ error: "Failed to fetch analytics" });
150
151
  }
151
152
  });
153
+ app.get("/api/evals", async (req, res) => {
154
+ try {
155
+ const spend = await getSpendSummary();
156
+ res.json(spend);
157
+ } catch (error) {
158
+ console.error("Error fetching evals spend:", error);
159
+ res.status(500).json({ error: "Failed to fetch AI spend estimate" });
160
+ }
161
+ });
152
162
  io.on("connection", (socket) => {
153
163
  console.log("\u{1F464} Client connected:", socket.id);
154
164
  socket.emit("initial-data", {