@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,417 @@
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 { spawn } from "child_process";
6
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
7
+ import { join } from "path";
8
+ import { homedir } from "os";
9
+ import { createHash } from "crypto";
10
+ import { logger } from "../../core/monitoring/logger.js";
11
+ const DEFAULT_CACHE_DIR = join(
12
+ homedir(),
13
+ ".stackmemory",
14
+ "workflows",
15
+ "cli-cache"
16
+ );
17
+ const DEFAULT_CLI_TIMEOUT = 6e4;
18
+ const DEFAULT_MAX_SNAPSHOT = 15e3;
19
+ class CliBrowserAgent {
20
+ config;
21
+ cache = /* @__PURE__ */ new Map();
22
+ page = null;
23
+ // Playwright Page
24
+ browser = null;
25
+ // Playwright Browser
26
+ constructor(config) {
27
+ this.config = {
28
+ provider: config.provider,
29
+ headless: config.headless ?? true,
30
+ cacheDir: config.cacheDir ?? DEFAULT_CACHE_DIR,
31
+ cliTimeout: config.cliTimeout ?? DEFAULT_CLI_TIMEOUT,
32
+ maxSnapshotSize: config.maxSnapshotSize ?? DEFAULT_MAX_SNAPSHOT
33
+ };
34
+ this.loadCache();
35
+ }
36
+ /** Initialize — launch browser */
37
+ async init() {
38
+ const pw = await import("playwright");
39
+ this.browser = await pw.chromium.launch({ headless: this.config.headless });
40
+ this.page = await this.browser.newPage();
41
+ logger.info("CliBrowserAgent initialized", {
42
+ provider: this.config.provider
43
+ });
44
+ }
45
+ /** Navigate to URL */
46
+ async goto(url) {
47
+ await this.page.goto(url, { waitUntil: "domcontentloaded" });
48
+ }
49
+ /** Current page URL */
50
+ url() {
51
+ return this.page?.url() || "";
52
+ }
53
+ /**
54
+ * Extract structured data from the current page.
55
+ * Checks cache first → on miss, sends DOM snapshot to CLI for interpretation.
56
+ */
57
+ async extract(instruction) {
58
+ const start = Date.now();
59
+ const cacheKey = this.buildCacheKey("extract", instruction);
60
+ const cached = this.cache.get(cacheKey);
61
+ if (cached) {
62
+ cached.hits++;
63
+ this.saveCache();
64
+ return {
65
+ data: cached.result,
66
+ fromCache: true,
67
+ cliTokens: 0,
68
+ duration: Date.now() - start
69
+ };
70
+ }
71
+ const snapshot = await this.getA11ySnapshot();
72
+ const prompt = buildExtractPrompt(instruction, snapshot, this.url());
73
+ const cliResult = await this.callCli(prompt);
74
+ let data;
75
+ try {
76
+ data = JSON.parse(cliResult.text);
77
+ } catch {
78
+ const jsonMatch = cliResult.text.match(/```(?:json)?\s*([\s\S]*?)```/);
79
+ if (jsonMatch) {
80
+ data = JSON.parse(jsonMatch[1].trim());
81
+ } else {
82
+ data = { raw: cliResult.text };
83
+ }
84
+ }
85
+ this.cache.set(cacheKey, {
86
+ instruction,
87
+ urlPattern: extractUrlPattern(this.url()),
88
+ result: data,
89
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
90
+ hits: 0
91
+ });
92
+ this.saveCache();
93
+ return {
94
+ data,
95
+ fromCache: false,
96
+ cliTokens: cliResult.tokens,
97
+ duration: Date.now() - start
98
+ };
99
+ }
100
+ /**
101
+ * Perform a natural language action on the page.
102
+ * Checks cache for matching selectors → on miss, asks CLI to identify the right element.
103
+ */
104
+ async act(instruction) {
105
+ const start = Date.now();
106
+ const cacheKey = this.buildCacheKey("act", instruction);
107
+ const cached = this.cache.get(cacheKey);
108
+ if (cached?.actions?.length) {
109
+ try {
110
+ await this.executeActions(cached.actions);
111
+ cached.hits++;
112
+ this.saveCache();
113
+ return {
114
+ success: true,
115
+ fromCache: true,
116
+ cliTokens: 0,
117
+ duration: Date.now() - start,
118
+ actions: cached.actions
119
+ };
120
+ } catch {
121
+ logger.info("Cached action failed, self-healing via CLI", {
122
+ instruction
123
+ });
124
+ }
125
+ }
126
+ const snapshot = await this.getA11ySnapshot();
127
+ const prompt = buildActPrompt(instruction, snapshot, this.url());
128
+ const cliResult = await this.callCli(prompt);
129
+ let actions;
130
+ try {
131
+ const parsed = JSON.parse(cliResult.text);
132
+ actions = Array.isArray(parsed) ? parsed : parsed.actions || [parsed];
133
+ } catch {
134
+ const jsonMatch = cliResult.text.match(/```(?:json)?\s*([\s\S]*?)```/);
135
+ if (jsonMatch) {
136
+ const parsed = JSON.parse(jsonMatch[1].trim());
137
+ actions = Array.isArray(parsed) ? parsed : parsed.actions || [parsed];
138
+ } else {
139
+ return {
140
+ success: false,
141
+ fromCache: false,
142
+ cliTokens: cliResult.tokens,
143
+ duration: Date.now() - start,
144
+ actions: [],
145
+ error: `Failed to parse CLI response: ${cliResult.text.slice(0, 200)}`
146
+ };
147
+ }
148
+ }
149
+ try {
150
+ await this.executeActions(actions);
151
+ } catch (e) {
152
+ return {
153
+ success: false,
154
+ fromCache: false,
155
+ cliTokens: cliResult.tokens,
156
+ duration: Date.now() - start,
157
+ actions,
158
+ error: e.message
159
+ };
160
+ }
161
+ this.cache.set(cacheKey, {
162
+ instruction,
163
+ urlPattern: extractUrlPattern(this.url()),
164
+ result: { success: true },
165
+ actions,
166
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
167
+ hits: 0
168
+ });
169
+ this.saveCache();
170
+ return {
171
+ success: true,
172
+ fromCache: false,
173
+ cliTokens: cliResult.tokens,
174
+ duration: Date.now() - start,
175
+ actions
176
+ };
177
+ }
178
+ /** Get the Playwright page for direct Playwright code */
179
+ getPage() {
180
+ return this.page;
181
+ }
182
+ /** Close browser */
183
+ async close() {
184
+ if (this.browser) {
185
+ await this.browser.close();
186
+ this.browser = null;
187
+ this.page = null;
188
+ }
189
+ }
190
+ // ─── Internal ─────────────────────────────────────────────
191
+ /** Get page content snapshot — prefer visible text (fast + small) */
192
+ async getA11ySnapshot() {
193
+ const text = await this.page.evaluate(
194
+ (max) => document.body.innerText.slice(0, max),
195
+ this.config.maxSnapshotSize
196
+ );
197
+ return text;
198
+ }
199
+ /**
200
+ * Call LLM for interpretation.
201
+ * Uses CLI wrapper (claude/codex) or falls back to direct API.
202
+ * CLI wrappers use subscription (no per-token cost).
203
+ */
204
+ async callCli(prompt) {
205
+ try {
206
+ return await this.callCliSubprocess(prompt);
207
+ } catch (cliError) {
208
+ logger.warn("CLI wrapper failed, trying direct API fallback", {
209
+ error: cliError.message
210
+ });
211
+ }
212
+ if (process.env.ANTHROPIC_API_KEY) {
213
+ return this.callAnthropicDirect(prompt);
214
+ }
215
+ throw new Error(
216
+ "No LLM backend available. Need claude CLI or ANTHROPIC_API_KEY."
217
+ );
218
+ }
219
+ /** Call via CLI subprocess */
220
+ callCliSubprocess(prompt) {
221
+ return new Promise((resolve, reject) => {
222
+ const { cmd, args } = this.config.provider === "claude" ? { cmd: "claude", args: ["--print", "--model", "sonnet"] } : { cmd: "codex", args: ["-q"] };
223
+ const proc = spawn(cmd, args, {
224
+ cwd: process.cwd(),
225
+ stdio: ["pipe", "pipe", "pipe"],
226
+ timeout: this.config.cliTimeout,
227
+ env: {
228
+ ...process.env,
229
+ DISABLE_HOOKS: "1",
230
+ STACKMEMORY_DESIRE_PATHS: "0",
231
+ // Skip all Claude Code hooks
232
+ CLAUDE_CODE_SKIP_HOOKS: "1"
233
+ }
234
+ });
235
+ proc.stdin.write(prompt);
236
+ proc.stdin.end();
237
+ let stdout = "";
238
+ let stderr = "";
239
+ proc.stdout.on("data", (d) => {
240
+ stdout += d.toString();
241
+ });
242
+ proc.stderr.on("data", (d) => {
243
+ stderr += d.toString();
244
+ });
245
+ proc.on("close", (code) => {
246
+ const text = stdout.trim();
247
+ if (!text) {
248
+ reject(
249
+ new Error(
250
+ `CLI exited ${code} with no output: ${stderr.slice(0, 300)}`
251
+ )
252
+ );
253
+ return;
254
+ }
255
+ let finalText = text;
256
+ if (this.config.provider === "codex") {
257
+ try {
258
+ const parsed = JSON.parse(text);
259
+ finalText = parsed.message || parsed.output || text;
260
+ } catch {
261
+ }
262
+ }
263
+ const tokens = Math.ceil((prompt.length + finalText.length) / 4);
264
+ resolve({ text: finalText, tokens });
265
+ });
266
+ proc.on("error", (err) => {
267
+ reject(new Error(`Failed to spawn ${cmd}: ${err.message}`));
268
+ });
269
+ });
270
+ }
271
+ /** Direct Anthropic API call (fallback when CLI hooks interfere) */
272
+ async callAnthropicDirect(prompt) {
273
+ const resp = await fetch("https://api.anthropic.com/v1/messages", {
274
+ method: "POST",
275
+ headers: {
276
+ "Content-Type": "application/json",
277
+ "x-api-key": process.env.ANTHROPIC_API_KEY,
278
+ "anthropic-version": "2023-06-01"
279
+ },
280
+ body: JSON.stringify({
281
+ model: "claude-sonnet-4-20250514",
282
+ max_tokens: 2048,
283
+ messages: [{ role: "user", content: prompt }]
284
+ })
285
+ });
286
+ if (!resp.ok) {
287
+ throw new Error(`Anthropic API ${resp.status}: ${await resp.text()}`);
288
+ }
289
+ const data = await resp.json();
290
+ const text = data.content?.[0]?.text || "";
291
+ const tokens = (data.usage?.input_tokens || 0) + (data.usage?.output_tokens || 0);
292
+ return { text, tokens };
293
+ }
294
+ /** Execute Playwright actions from CLI response */
295
+ async executeActions(actions) {
296
+ for (const action of actions) {
297
+ const el = this.page.locator(action.selector).first();
298
+ await el.waitFor({ timeout: 5e3 });
299
+ switch (action.action) {
300
+ case "click":
301
+ await el.click();
302
+ break;
303
+ case "fill":
304
+ await el.fill(action.value || "");
305
+ break;
306
+ case "select":
307
+ await el.selectOption(action.value || "");
308
+ break;
309
+ case "check":
310
+ await el.check();
311
+ break;
312
+ case "press":
313
+ await el.press(action.value || "Enter");
314
+ break;
315
+ }
316
+ await this.page.waitForTimeout(200);
317
+ }
318
+ }
319
+ /** Build cache key from instruction + URL pattern */
320
+ buildCacheKey(type, instruction) {
321
+ const urlPattern = extractUrlPattern(this.url());
322
+ const input = `${type}:${urlPattern}:${instruction}`;
323
+ return createHash("sha256").update(input).digest("hex").slice(0, 16);
324
+ }
325
+ /** Load cache from disk */
326
+ loadCache() {
327
+ const cacheFile = join(this.config.cacheDir, "cache.json");
328
+ if (!existsSync(cacheFile)) return;
329
+ try {
330
+ const data = JSON.parse(readFileSync(cacheFile, "utf-8"));
331
+ for (const [key, entry] of Object.entries(data)) {
332
+ this.cache.set(key, entry);
333
+ }
334
+ } catch {
335
+ }
336
+ }
337
+ /** Save cache to disk */
338
+ saveCache() {
339
+ ensureDir(this.config.cacheDir);
340
+ const cacheFile = join(this.config.cacheDir, "cache.json");
341
+ const data = {};
342
+ for (const [key, entry] of this.cache) {
343
+ data[key] = entry;
344
+ }
345
+ writeFileSync(cacheFile, JSON.stringify(data, null, 2));
346
+ }
347
+ }
348
+ function buildExtractPrompt(instruction, snapshot, url) {
349
+ return `You are a browser data extraction assistant. Given the accessibility tree of a web page, extract the requested data as JSON.
350
+
351
+ Page URL: ${url}
352
+
353
+ Accessibility tree:
354
+ ${snapshot}
355
+
356
+ Instruction: ${instruction}
357
+
358
+ Respond with ONLY a JSON object containing the extracted data. No explanation, no markdown, just JSON.`;
359
+ }
360
+ function buildActPrompt(instruction, snapshot, url) {
361
+ return `You are a browser automation assistant. Given the accessibility tree of a web page, determine which element(s) to interact with.
362
+
363
+ Page URL: ${url}
364
+
365
+ Accessibility tree:
366
+ ${snapshot}
367
+
368
+ Instruction: ${instruction}
369
+
370
+ Respond with ONLY a JSON array of actions. Each action has:
371
+ - "selector": CSS selector or text selector (prefer [role], [aria-label], text= selectors)
372
+ - "action": "click" | "fill" | "select" | "check" | "press"
373
+ - "value": (optional) text to type or option to select
374
+ - "description": brief description of what this does
375
+
376
+ Example: [{"selector": "button:has-text('Submit')", "action": "click", "description": "Click submit button"}]
377
+
378
+ No explanation, no markdown, just JSON array.`;
379
+ }
380
+ function ensureDir(dir) {
381
+ if (!existsSync(dir)) {
382
+ mkdirSync(dir, { recursive: true });
383
+ }
384
+ }
385
+ function extractUrlPattern(url) {
386
+ try {
387
+ const u = new URL(url);
388
+ return `${u.host}${u.pathname}`;
389
+ } catch {
390
+ return url;
391
+ }
392
+ }
393
+ function formatA11yTree(node, indent, maxSize) {
394
+ if (!node) return "(empty page)";
395
+ let result = "";
396
+ const role = node.role || "";
397
+ const name = node.name || "";
398
+ const value = node.value || "";
399
+ if (role && role !== "none" && role !== "generic") {
400
+ result += `${indent}[${role}] ${name}`;
401
+ if (value) result += ` = "${value}"`;
402
+ result += "\n";
403
+ }
404
+ if (result.length > maxSize) {
405
+ return result.slice(0, maxSize) + "\n... (truncated)";
406
+ }
407
+ if (node.children) {
408
+ for (const child of node.children) {
409
+ result += formatA11yTree(child, indent + " ", maxSize - result.length);
410
+ if (result.length > maxSize) break;
411
+ }
412
+ }
413
+ return result;
414
+ }
415
+ export {
416
+ CliBrowserAgent
417
+ };