tarsk 0.5.40 → 0.5.42

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 (83) hide show
  1. package/dist/index.js +1898 -485
  2. package/dist/public/assets/{account-view-BT90zNSx.js → account-view-BAm4Az95.js} +1 -1
  3. package/dist/public/assets/api-C44iPrsZ.js +1 -0
  4. package/dist/public/assets/browser-tab-DV6mFCbu.js +1 -0
  5. package/dist/public/assets/commit-dialog-DYdmiA4p.js +1 -0
  6. package/dist/public/assets/context-menu-BVP2PUoS.js +1 -0
  7. package/dist/public/assets/create-repo-dialog-DrGJUrGO.js +1 -0
  8. package/dist/public/assets/{dialogs-config-DZKtVbNK.js → dialogs-config-BPzX42HH.js} +14 -14
  9. package/dist/public/assets/diff-view-BnNtDcmG.js +3 -0
  10. package/dist/public/assets/explorer-tab-view-OGWG1dH9.js +2 -0
  11. package/dist/public/assets/explorer-tree--Ho2K8-i.js +1 -0
  12. package/dist/public/assets/explorer-view-Dw0Kyxb8.js +1 -0
  13. package/dist/public/assets/git-history-dialog-BpOpNbze.js +1 -0
  14. package/dist/public/assets/git-ops-button-DKOfNm4s.js +2 -0
  15. package/dist/public/assets/history-view-DwSqsTGI.js +9 -0
  16. package/dist/public/assets/index-BdLH7zyV.css +1 -0
  17. package/dist/public/assets/index-BqaiWs7x.js +88 -0
  18. package/dist/public/assets/mcp-server-card-BbAUiI-X.js +1 -0
  19. package/dist/public/assets/merged-pr-dialog-Cvo93bRH.js +1 -0
  20. package/dist/public/assets/onboarding-BmBbv0At.js +1 -0
  21. package/dist/public/assets/project-settings-view-D-8Ua1Tu.js +1 -0
  22. package/dist/public/assets/providers-list-view-Gm13Fagd.js +1 -0
  23. package/dist/public/assets/pull-request-dialog-BkzdP5F2.js +1 -0
  24. package/dist/public/assets/pull-with-changes-dialog-DQU7jHYL.js +1 -0
  25. package/dist/public/assets/push-before-pr-dialog-DII3gUVw.js +1 -0
  26. package/dist/public/assets/radio-group-DkDRaQdF.js +1 -0
  27. package/dist/public/assets/react-vendor-BmFalJ0W.js +16 -0
  28. package/dist/public/assets/settings-general-view-Br89Vrbv.js +1 -0
  29. package/dist/public/assets/{settings-instructions-view-CV7lRPuw.js → settings-instructions-view-ByxlAb9U.js} +1 -1
  30. package/dist/public/assets/settings-list-DSrSoTv7.js +1 -0
  31. package/dist/public/assets/settings-mcp-servers-view-U_la7C8z.js +5 -0
  32. package/dist/public/assets/{settings-models-skeleton-l966LEN8.js → settings-models-skeleton-C5XrzM39.js} +1 -1
  33. package/dist/public/assets/settings-models-view-obQa4doG.js +1 -0
  34. package/dist/public/assets/settings-rules-view-Cd9WHEAE.js +8 -0
  35. package/dist/public/assets/settings-skills-view-B81njz98.js +2 -0
  36. package/dist/public/assets/settings-slash-commands-view-Ct8WC1Z0.js +1 -0
  37. package/dist/public/assets/settings-subagents-view-BwNTnGkz.js +2 -0
  38. package/dist/public/assets/{settings-system-prompt-view-DQmSdSnp.js → settings-system-prompt-view-C4uLn68y.js} +1 -1
  39. package/dist/public/assets/settings-view-DQW_9pnm.js +2 -0
  40. package/dist/public/assets/skeleton-HsHVnOKO.js +1 -0
  41. package/dist/public/assets/{terminal-panel-BzCO142Q.js → terminal-panel-wOltBu70.js} +2 -2
  42. package/dist/public/assets/{ui-components-f8jJJzEo.js → ui-components-CaeQlGTd.js} +1 -1
  43. package/dist/public/assets/{utils-BlXPFbY9.js → utils-5mrXWXCs.js} +1 -1
  44. package/dist/public/assets/web-Gjw6klhQ.js +1 -0
  45. package/dist/public/assets/web-NsuCXAqU.js +1 -0
  46. package/dist/public/browser-preview-rpc.js +484 -0
  47. package/dist/public/index.html +7 -7
  48. package/dist/recommended-models.txt +1 -1
  49. package/package.json +1 -1
  50. package/dist/public/assets/api-DWU0D34A.js +0 -1
  51. package/dist/public/assets/browser-tab-Bp486a9f.js +0 -1
  52. package/dist/public/assets/commit-dialog-90PGbqs8.js +0 -1
  53. package/dist/public/assets/context-menu-C7FTMa2c.js +0 -1
  54. package/dist/public/assets/create-repo-dialog-BkUdww3q.js +0 -1
  55. package/dist/public/assets/diff-view-B4G8vqZa.js +0 -3
  56. package/dist/public/assets/explorer-tab-view-DEQFid8r.js +0 -2
  57. package/dist/public/assets/explorer-tree-CPHGgahA.js +0 -1
  58. package/dist/public/assets/explorer-view-BTvhSMDQ.js +0 -1
  59. package/dist/public/assets/git-history-dialog-Bqv0bD41.js +0 -1
  60. package/dist/public/assets/git-ops-button-BnVMlOO3.js +0 -2
  61. package/dist/public/assets/history-view-jX3PPID6.js +0 -9
  62. package/dist/public/assets/index-DjD2SPNZ.js +0 -65
  63. package/dist/public/assets/index-jIBJk8xl.css +0 -1
  64. package/dist/public/assets/mcp-server-card-CTCrhFIS.js +0 -1
  65. package/dist/public/assets/merged-pr-dialog-B2qqrQ1g.js +0 -1
  66. package/dist/public/assets/onboarding-CxtzQldG.js +0 -1
  67. package/dist/public/assets/project-settings-view-ZJjQbFbQ.js +0 -1
  68. package/dist/public/assets/providers-list-view-BH13AB_H.js +0 -1
  69. package/dist/public/assets/pull-request-dialog-ZzVXh6M9.js +0 -1
  70. package/dist/public/assets/pull-with-changes-dialog-LUgd2a4S.js +0 -1
  71. package/dist/public/assets/push-before-pr-dialog-CFmgu9H_.js +0 -1
  72. package/dist/public/assets/radio-group-Bd8YbtFD.js +0 -1
  73. package/dist/public/assets/react-vendor-Ba8DSNiO.js +0 -16
  74. package/dist/public/assets/settings-general-view-C8muiP_K.js +0 -1
  75. package/dist/public/assets/settings-list-CKPEGohm.js +0 -1
  76. package/dist/public/assets/settings-mcp-servers-view-3gN9ly3z.js +0 -5
  77. package/dist/public/assets/settings-models-view-CAMouMhE.js +0 -1
  78. package/dist/public/assets/settings-rules-view-BnazfbJb.js +0 -8
  79. package/dist/public/assets/settings-skills-view-DpzKFcpf.js +0 -2
  80. package/dist/public/assets/settings-slash-commands-view-Drje2Dl4.js +0 -1
  81. package/dist/public/assets/settings-subagents-view-D05kHO1W.js +0 -2
  82. package/dist/public/assets/settings-view-B6e0GfSv.js +0 -2
  83. package/dist/public/assets/skeleton-qzmFgKhS.js +0 -1
package/dist/index.js CHANGED
@@ -50,7 +50,7 @@ import { homedir } from "node:os";
50
50
  import { join } from "node:path";
51
51
  import { spawn, spawnSync, execSync } from "child_process";
52
52
  function delay(ms) {
53
- return new Promise((resolve6) => setTimeout(resolve6, ms));
53
+ return new Promise((resolve7) => setTimeout(resolve7, ms));
54
54
  }
55
55
  function mergePath(parentPath, shellPath) {
56
56
  const parent = (parentPath ?? "").split(":").filter(Boolean);
@@ -1224,6 +1224,15 @@ function buildDefaultSystemPrompt(toolNames) {
1224
1224
  const toolList = toolNames.length > 0 ? toolNames.join(", ") : "read, bash, edit, and write";
1225
1225
  return `You are a helpful coding assistant. You have access to ${toolList} tools. Use them to explore and modify the codebase as needed. Skills are created and stored in .agents/skills/ . Use Mermaid for flowcharts, sequence diagrams, state diagrams, or graphs when appropriate. Do not use emojis.`;
1226
1226
  }
1227
+ function formatCurrentDateLine(referenceDate = /* @__PURE__ */ new Date()) {
1228
+ const date = referenceDate.toLocaleDateString("en-US", {
1229
+ weekday: "long",
1230
+ year: "numeric",
1231
+ month: "long",
1232
+ day: "numeric"
1233
+ });
1234
+ return `The current date is ${date}.`;
1235
+ }
1227
1236
 
1228
1237
  // ../shared/dist/image-payload.js
1229
1238
  function isDataImageUrl(value) {
@@ -1287,6 +1296,7 @@ function parseGeneratedImagePayload(value) {
1287
1296
  function stripGeneratedImagePayload(payload) {
1288
1297
  const next = { ...payload };
1289
1298
  delete next.imageData;
1299
+ delete next.image_data;
1290
1300
  if (typeof next.imageUrl === "string" && isDataImageUrl(next.imageUrl)) {
1291
1301
  delete next.imageUrl;
1292
1302
  }
@@ -1308,12 +1318,13 @@ function stripGeneratedImageText(text) {
1308
1318
  return text;
1309
1319
  }
1310
1320
  }
1321
+ var IMAGE_RESULT_TOOL_NAMES = /* @__PURE__ */ new Set(["generate_image", "find_images", "browser"]);
1311
1322
  function sanitizeToolResultBlock(block, collected) {
1312
1323
  if (block.type !== "tool-result") {
1313
1324
  return block;
1314
1325
  }
1315
1326
  const toolName = typeof block.toolName === "string" ? block.toolName : "";
1316
- if (toolName !== "generate_image") {
1327
+ if (!IMAGE_RESULT_TOOL_NAMES.has(toolName)) {
1317
1328
  return block;
1318
1329
  }
1319
1330
  const blockRefs = parseGeneratedImagePayload(block);
@@ -1458,7 +1469,7 @@ function extractGeneratedImageFromEvents(events) {
1458
1469
  const parsed = JSON.parse(event.content.trim());
1459
1470
  const blocks = Array.isArray(parsed) ? parsed : [parsed];
1460
1471
  for (const block of blocks) {
1461
- if (block && typeof block === "object" && "type" in block && block.type === "tool-result" && block.toolName === "generate_image") {
1472
+ if (block && typeof block === "object" && "type" in block && block.type === "tool-result" && IMAGE_RESULT_TOOL_NAMES.has(block.toolName)) {
1462
1473
  const refs = parseGeneratedImagePayload(block);
1463
1474
  if (refs?.imageUrl) {
1464
1475
  collected.imageUrl = refs.imageUrl;
@@ -1481,11 +1492,29 @@ function extractGeneratedImageFromEvents(events) {
1481
1492
  return collected;
1482
1493
  }
1483
1494
 
1495
+ // ../shared/dist/image-url.js
1496
+ function isAbsoluteHttpOrDataUrl(url) {
1497
+ return !url || url.startsWith("data:") || /^https?:\/\//i.test(url);
1498
+ }
1499
+ function buildThreadExplorerMediaUrl(threadId, projectPath, origin = "http://localhost:641") {
1500
+ const base = origin.replace(/\/$/, "");
1501
+ return `${base}/api/threads/${encodeURIComponent(threadId)}/explorer/media?path=${encodeURIComponent(projectPath)}`;
1502
+ }
1503
+ function resolveAbsoluteImageUrl(imageUrl, baseUrl) {
1504
+ if (isAbsoluteHttpOrDataUrl(imageUrl)) {
1505
+ return imageUrl;
1506
+ }
1507
+ const origin = baseUrl.replace(/\/$/, "");
1508
+ const path7 = imageUrl.startsWith("/") ? imageUrl : `/${imageUrl}`;
1509
+ return `${origin}${path7}`;
1510
+ }
1511
+
1484
1512
  // ../shared/dist/tool-display.js
1485
1513
  var toolDisplayRegistry = [
1486
1514
  { name: "agent", displayName: "Subagent", activeDisplayName: "Subagent" },
1487
1515
  { name: "ask_user", displayName: "Ask", activeDisplayName: "Asking" },
1488
1516
  { name: "bash", displayName: "Ran", activeDisplayName: "Running" },
1517
+ { name: "browser", displayName: "Browser", activeDisplayName: "Browsing" },
1489
1518
  { name: "code_search", displayName: "Code Search", activeDisplayName: "Searching" },
1490
1519
  { name: "edit", displayName: "Edit", activeDisplayName: "Editing" },
1491
1520
  { name: "execute_skill_script", displayName: "Run Script", activeDisplayName: "Running Script" },
@@ -1506,6 +1535,11 @@ var toolDisplayRegistry = [
1506
1535
  { name: "simple_grep", displayName: "Search", activeDisplayName: "Searching" },
1507
1536
  { name: "todo", displayName: "Todo", activeDisplayName: "Updating" },
1508
1537
  { name: "tool_search", displayName: "Find Tools", activeDisplayName: "Finding Tools" },
1538
+ {
1539
+ name: "web_search",
1540
+ displayName: "Web Search",
1541
+ activeDisplayName: "Searching Web"
1542
+ },
1509
1543
  { name: "write", displayName: "Write", activeDisplayName: "Writing" }
1510
1544
  ];
1511
1545
  var displayByName = new Map(toolDisplayRegistry.map((entry) => [entry.name, entry]));
@@ -1628,9 +1662,27 @@ function getExplorerFileMediaType(path7, name) {
1628
1662
  return null;
1629
1663
  }
1630
1664
 
1665
+ // ../shared/dist/browser-tool.js
1666
+ var BROWSER_HOST_ACTIONS = [
1667
+ "reload",
1668
+ "go_back",
1669
+ "go_forward",
1670
+ "get_state",
1671
+ "set_viewport"
1672
+ ];
1673
+ function isBrowserHostAction(action) {
1674
+ return BROWSER_HOST_ACTIONS.includes(action);
1675
+ }
1676
+
1677
+ // ../shared/dist/tmp-images.js
1678
+ var TARSK_TMP_IMAGES_DIR = ".tarsk/tmp-images";
1679
+ function buildTmpImagePath(filename) {
1680
+ return `${TARSK_TMP_IMAGES_DIR}/${filename}`;
1681
+ }
1682
+
1631
1683
  // src/server.ts
1632
1684
  import fs3 from "fs";
1633
- import { Hono as Hono26 } from "hono";
1685
+ import { Hono as Hono27 } from "hono";
1634
1686
  import { cors } from "hono/cors";
1635
1687
  import open3 from "open";
1636
1688
  import path5 from "path";
@@ -2439,7 +2491,7 @@ var bashSchema = Type.Object({
2439
2491
  });
2440
2492
  var defaultBashOperations = {
2441
2493
  exec: (command, cwd, { onData, signal, timeout, env }) => {
2442
- return new Promise((resolve6, reject) => {
2494
+ return new Promise((resolve7, reject) => {
2443
2495
  const { shell, args: args2 } = getShellConfig();
2444
2496
  if (!existsSync3(cwd)) {
2445
2497
  reject(new Error(`Working directory does not exist: ${cwd}`));
@@ -2488,7 +2540,7 @@ var defaultBashOperations = {
2488
2540
  reject(new Error(`timeout:${timeout}`));
2489
2541
  return;
2490
2542
  }
2491
- resolve6({ exitCode: code });
2543
+ resolve7({ exitCode: code });
2492
2544
  });
2493
2545
  });
2494
2546
  }
@@ -2511,7 +2563,7 @@ ${command}` : command;
2511
2563
  if (shellPermissionGate) {
2512
2564
  await shellPermissionGate.ensureAllowed(toolCallId, resolvedCommand, "bash", signal);
2513
2565
  }
2514
- return new Promise((resolve6, reject) => {
2566
+ return new Promise((resolve7, reject) => {
2515
2567
  let tempFilePath;
2516
2568
  let tempFileStream;
2517
2569
  let totalBytes = 0;
@@ -2580,7 +2632,7 @@ ${command}` : command;
2580
2632
  Command exited with code ${exitCode}`;
2581
2633
  reject(new Error(outputText));
2582
2634
  } else {
2583
- resolve6({ content: [{ type: "text", text: outputText }], details });
2635
+ resolve7({ content: [{ type: "text", text: outputText }], details });
2584
2636
  }
2585
2637
  }).catch((err) => {
2586
2638
  tarskDebugLog(`[ai] bash-end: ${resolvedCommand}`);
@@ -2736,7 +2788,7 @@ function generateDiffString(oldContent, newContent, contextLines = 4) {
2736
2788
 
2737
2789
  // src/tools/tool-helpers.ts
2738
2790
  async function withAbortSignal(signal, operation) {
2739
- return new Promise((resolve6, reject) => {
2791
+ return new Promise((resolve7, reject) => {
2740
2792
  if (signal?.aborted) {
2741
2793
  reject(new Error("Operation aborted"));
2742
2794
  return;
@@ -2755,7 +2807,7 @@ async function withAbortSignal(signal, operation) {
2755
2807
  const abortCheck = () => aborted;
2756
2808
  operation(abortCheck).then((result) => {
2757
2809
  cleanup();
2758
- if (!aborted) resolve6(result);
2810
+ if (!aborted) resolve7(result);
2759
2811
  }).catch((error) => {
2760
2812
  cleanup();
2761
2813
  if (!aborted) reject(error);
@@ -2878,7 +2930,7 @@ function createFindTool(cwd, options) {
2878
2930
  description: `Search for files by glob pattern. Returns matching file paths relative to the search directory. Output truncated to ${DEFAULT_LIMIT} results or ${DEFAULT_MAX_BYTES / 1024}KB.`,
2879
2931
  parameters: findSchema,
2880
2932
  execute: async (_toolCallId, { pattern, path: searchDir, limit }, signal) => {
2881
- return new Promise((resolve6, reject) => {
2933
+ return new Promise((resolve7, reject) => {
2882
2934
  if (signal?.aborted) {
2883
2935
  reject(new Error("Operation aborted"));
2884
2936
  return;
@@ -2911,7 +2963,7 @@ function createFindTool(cwd, options) {
2911
2963
  });
2912
2964
  signal?.removeEventListener("abort", onAbort);
2913
2965
  if (results.length === 0) {
2914
- resolve6({
2966
+ resolve7({
2915
2967
  content: [{ type: "text", text: "No files found matching pattern" }],
2916
2968
  details: void 0
2917
2969
  });
@@ -2942,7 +2994,7 @@ function createFindTool(cwd, options) {
2942
2994
  if (notices.length > 0) resultOutput += `
2943
2995
 
2944
2996
  [${notices.join(". ")}]`;
2945
- resolve6({
2997
+ resolve7({
2946
2998
  content: [{ type: "text", text: resultOutput }],
2947
2999
  details: Object.keys(details).length > 0 ? details : void 0
2948
3000
  });
@@ -2967,7 +3019,7 @@ import path2 from "path";
2967
3019
  // src/tools/resolve-bin.ts
2968
3020
  init_utils();
2969
3021
  function resolveBin(name) {
2970
- return new Promise((resolve6) => {
3022
+ return new Promise((resolve7) => {
2971
3023
  const cmd = process.platform === "win32" ? "where" : "which";
2972
3024
  const args2 = process.platform === "win32" ? [name + ".exe", name] : [name];
2973
3025
  try {
@@ -2975,13 +3027,13 @@ function resolveBin(name) {
2975
3027
  if (result.status === 0 && result.stdout) {
2976
3028
  const first = (typeof result.stdout === "string" ? result.stdout : result.stdout?.toString() || "").trim().split(/\r?\n/)[0]?.trim();
2977
3029
  if (first) {
2978
- resolve6(first);
3030
+ resolve7(first);
2979
3031
  return;
2980
3032
  }
2981
3033
  }
2982
3034
  } catch {
2983
3035
  }
2984
- resolve6(null);
3036
+ resolve7(null);
2985
3037
  });
2986
3038
  }
2987
3039
 
@@ -3020,7 +3072,7 @@ function createSimpleGrepTool(cwd, options) {
3020
3072
  description: `Search file contents for a pattern. Returns matching lines with file paths and line numbers. Respects .gitignore. Output truncated to ${DEFAULT_LIMIT2} matches or ${DEFAULT_MAX_BYTES / 1024}KB. Long lines truncated to ${GREP_MAX_LINE_LENGTH} chars.`,
3021
3073
  parameters: grepSchema,
3022
3074
  execute: async (_toolCallId, { pattern, path: searchDir, glob: glob3, ignoreCase, literal, context, limit }, signal) => {
3023
- return new Promise((resolve6, reject) => {
3075
+ return new Promise((resolve7, reject) => {
3024
3076
  if (signal?.aborted) {
3025
3077
  reject(new Error("Operation aborted"));
3026
3078
  return;
@@ -3169,7 +3221,7 @@ function createSimpleGrepTool(cwd, options) {
3169
3221
  }
3170
3222
  if (matchCount === 0) {
3171
3223
  settle(
3172
- () => resolve6({
3224
+ () => resolve7({
3173
3225
  content: [{ type: "text", text: "No matches found" }],
3174
3226
  details: void 0
3175
3227
  })
@@ -3205,7 +3257,7 @@ function createSimpleGrepTool(cwd, options) {
3205
3257
 
3206
3258
  [${notices.join(". ")}]`;
3207
3259
  settle(
3208
- () => resolve6({
3260
+ () => resolve7({
3209
3261
  content: [{ type: "text", text: output }],
3210
3262
  details: Object.keys(details).length > 0 ? details : void 0
3211
3263
  })
@@ -4232,7 +4284,7 @@ function createLsTool(cwd, options) {
4232
4284
  description: `List directory contents. Returns entries sorted alphabetically, with '/' suffix for directories. Output truncated to ${DEFAULT_LIMIT4} entries or ${DEFAULT_MAX_BYTES / 1024}KB.`,
4233
4285
  parameters: lsSchema,
4234
4286
  execute: async (_toolCallId, { path: dirPathArg, limit }, signal) => {
4235
- return new Promise((resolve6, reject) => {
4287
+ return new Promise((resolve7, reject) => {
4236
4288
  if (signal?.aborted) {
4237
4289
  reject(new Error("Operation aborted"));
4238
4290
  return;
@@ -4280,7 +4332,7 @@ function createLsTool(cwd, options) {
4280
4332
  }
4281
4333
  signal?.removeEventListener("abort", onAbort);
4282
4334
  if (results.length === 0) {
4283
- resolve6({
4335
+ resolve7({
4284
4336
  content: [{ type: "text", text: "(empty directory)" }],
4285
4337
  details: void 0
4286
4338
  });
@@ -4304,7 +4356,7 @@ function createLsTool(cwd, options) {
4304
4356
  if (notices.length > 0) output += `
4305
4357
 
4306
4358
  [${notices.join(". ")}]`;
4307
- resolve6({
4359
+ resolve7({
4308
4360
  content: [{ type: "text", text: output }],
4309
4361
  details: Object.keys(details).length > 0 ? details : void 0
4310
4362
  });
@@ -4430,7 +4482,7 @@ function createWriteTool(cwd, options) {
4430
4482
  validatePathWithinCwd(absolutePath, cwd);
4431
4483
  const dir = dirname(absolutePath);
4432
4484
  return new Promise(
4433
- (resolve6, reject) => {
4485
+ (resolve7, reject) => {
4434
4486
  if (signal?.aborted) {
4435
4487
  reject(new Error("Operation aborted"));
4436
4488
  return;
@@ -4448,7 +4500,7 @@ function createWriteTool(cwd, options) {
4448
4500
  await ops.writeFile(absolutePath, content);
4449
4501
  if (aborted) return;
4450
4502
  if (signal) signal.removeEventListener("abort", onAbort);
4451
- resolve6({
4503
+ resolve7({
4452
4504
  content: [
4453
4505
  { type: "text", text: `Successfully wrote ${content.length} bytes to ${path7}` }
4454
4506
  ],
@@ -4500,7 +4552,7 @@ async function executeScript(scriptPath, args2, cwd, timeout = 30) {
4500
4552
  if (!interpreter) {
4501
4553
  throw new Error(`Unsupported script type: ${scriptPath}`);
4502
4554
  }
4503
- return new Promise((resolve6, reject) => {
4555
+ return new Promise((resolve7, reject) => {
4504
4556
  const child = spawnProcess(interpreter.command, [...interpreter.args, ...args2], {
4505
4557
  cwd,
4506
4558
  timeout: timeout * 1e3
@@ -4532,7 +4584,7 @@ async function executeScript(scriptPath, args2, cwd, timeout = 30) {
4532
4584
  reject(new Error(`Script execution timed out after ${timeout}s`));
4533
4585
  return;
4534
4586
  }
4535
- resolve6({ stdout, stderr, exitCode: code });
4587
+ resolve7({ stdout, stderr, exitCode: code });
4536
4588
  });
4537
4589
  });
4538
4590
  }
@@ -4811,9 +4863,9 @@ function createAskUserTool() {
4811
4863
  if (signal?.aborted) {
4812
4864
  throw new Error("Operation aborted");
4813
4865
  }
4814
- const answer = await new Promise((resolve6, reject) => {
4866
+ const answer = await new Promise((resolve7, reject) => {
4815
4867
  pendingQuestions.set(toolCallId, {
4816
- resolve: resolve6,
4868
+ resolve: resolve7,
4817
4869
  reject,
4818
4870
  question,
4819
4871
  options,
@@ -6541,8 +6593,46 @@ async function executeGenerateImage(options) {
6541
6593
 
6542
6594
  // src/tools/find-images.ts
6543
6595
  import { Type as Type15 } from "@sinclair/typebox";
6596
+ import { join as join9 } from "path";
6597
+
6598
+ // src/core/server-origin.ts
6599
+ function getServerOrigin() {
6600
+ const port = process.env.PORT ?? "641";
6601
+ return `http://localhost:${port}`;
6602
+ }
6603
+
6604
+ // src/tools/tool-image-storage.ts
6544
6605
  import { mkdir as fsMkdir3, writeFile as fsWriteFile4 } from "fs/promises";
6545
6606
  import { dirname as dirname3 } from "path";
6607
+ function decodeBase64ImageData(imageData) {
6608
+ const binary = atob(imageData);
6609
+ const bytes = new Uint8Array(binary.length);
6610
+ for (let i = 0; i < binary.length; i++) {
6611
+ bytes[i] = binary.charCodeAt(i);
6612
+ }
6613
+ return bytes;
6614
+ }
6615
+ async function saveToolImage(options) {
6616
+ const bytes = decodeBase64ImageData(options.imageData);
6617
+ const absolutePath = resolveToCwd(options.relativePath, options.cwd);
6618
+ validatePathWithinCwd(absolutePath, options.cwd);
6619
+ await fsMkdir3(dirname3(absolutePath), { recursive: true });
6620
+ await fsWriteFile4(absolutePath, bytes);
6621
+ const saved = {
6622
+ savedPath: options.relativePath,
6623
+ sizeInBytes: bytes.length
6624
+ };
6625
+ if (options.threadId && options.serverOrigin) {
6626
+ saved.imageUrl = buildThreadExplorerMediaUrl(
6627
+ options.threadId,
6628
+ options.relativePath,
6629
+ options.serverOrigin
6630
+ );
6631
+ }
6632
+ return saved;
6633
+ }
6634
+
6635
+ // src/tools/find-images.ts
6546
6636
  var findImagesSchema = Type15.Object({
6547
6637
  query: Type15.String({ description: "Search term for the image (e.g. 'sunset over mountains')" }),
6548
6638
  size: Type15.Optional(
@@ -6564,15 +6654,36 @@ var findImagesSchema = Type15.Object({
6564
6654
  ),
6565
6655
  save_to_file: Type15.Optional(
6566
6656
  Type15.String({
6567
- description: "File path relative to the project root to save the image to (e.g. 'assets/hero.jpg'). Parent directories are created automatically. If omitted the image is returned as base64 data only."
6657
+ description: "File path relative to the project root to save the image to (e.g. 'assets/hero.jpg'). Parent directories are created automatically. If omitted the image is saved under .tarsk/tmp-images/."
6568
6658
  })
6569
6659
  )
6570
6660
  });
6571
- function createFindImagesTool(cwd) {
6661
+ function extensionForContentType(contentType) {
6662
+ if (contentType.includes("png")) {
6663
+ return ".png";
6664
+ }
6665
+ if (contentType.includes("webp")) {
6666
+ return ".webp";
6667
+ }
6668
+ if (contentType.includes("gif")) {
6669
+ return ".gif";
6670
+ }
6671
+ if (contentType.includes("jpeg") || contentType.includes("jpg")) {
6672
+ return ".jpg";
6673
+ }
6674
+ return ".jpg";
6675
+ }
6676
+ function defaultSavePath(query, contentType) {
6677
+ const slug = query.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 40) || "image";
6678
+ const id = Math.random().toString(36).slice(2, 8);
6679
+ return buildTmpImagePath(`${slug}-${id}${extensionForContentType(contentType)}`);
6680
+ }
6681
+ function createFindImagesTool(options = {}) {
6682
+ const { cwd, threadId, serverOrigin = getServerOrigin() } = options;
6572
6683
  return {
6573
6684
  name: "find_images",
6574
6685
  label: "find_images",
6575
- description: "Find a stock image matching a search query. Returns the image as base64 data and optionally saves it to a file. Use this when the user needs an image and the user has not requested it be generated.",
6686
+ description: "Find a stock image matching a search query. Returns a URL served by the Tarsk API and optionally saves the image to a file. Use this when the user needs an image and the user has not requested it be generated.",
6576
6687
  parameters: findImagesSchema,
6577
6688
  execute: async (_toolCallId, { query, size, save_to_file }, signal) => {
6578
6689
  return withAbortSignal(signal, async (isAborted) => {
@@ -6593,52 +6704,889 @@ function createFindImagesTool(cwd) {
6593
6704
  };
6594
6705
  }
6595
6706
  if (isAborted()) return { content: [], details: void 0 };
6596
- let savedPath;
6597
- if (cwd && save_to_file) {
6598
- try {
6599
- const absolutePath = resolveToCwd(save_to_file, cwd);
6707
+ if (!cwd) {
6708
+ return {
6709
+ content: [
6710
+ {
6711
+ type: "text",
6712
+ text: "Image search succeeded but no project directory is available to save the image."
6713
+ }
6714
+ ],
6715
+ details: void 0
6716
+ };
6717
+ }
6718
+ const relativePath = save_to_file ?? defaultSavePath(query, contentType);
6719
+ let saved;
6720
+ try {
6721
+ if (save_to_file) {
6722
+ const absolutePath = resolveToCwd(relativePath, cwd);
6600
6723
  validatePathWithinCwd(absolutePath, cwd);
6601
- const binary = atob(imageData);
6602
- const bytes = new Uint8Array(binary.length);
6603
- for (let i = 0; i < binary.length; i++) {
6604
- bytes[i] = binary.charCodeAt(i);
6605
- }
6606
- await fsMkdir3(dirname3(absolutePath), { recursive: true });
6607
- await fsWriteFile4(absolutePath, bytes);
6608
- savedPath = save_to_file;
6609
- console.log(`[find_images] Saved image to ${absolutePath}`);
6610
- } catch (saveError) {
6611
- const errMsg = saveError instanceof Error ? saveError.message : String(saveError);
6612
- return {
6613
- content: [
6614
- {
6615
- type: "text",
6616
- text: `Image was found but failed to save to "${save_to_file}": ${errMsg}`
6617
- }
6618
- ],
6619
- details: { imageData, contentType }
6620
- };
6621
6724
  }
6725
+ saved = await saveToolImage({
6726
+ imageData,
6727
+ cwd,
6728
+ relativePath,
6729
+ threadId,
6730
+ serverOrigin
6731
+ });
6732
+ console.log(`[find_images] Saved image to ${join9(cwd, saved.savedPath)}`);
6733
+ } catch (saveError) {
6734
+ const errMsg = saveError instanceof Error ? saveError.message : String(saveError);
6735
+ return {
6736
+ content: [
6737
+ {
6738
+ type: "text",
6739
+ text: `Image was found but failed to save to "${relativePath}": ${errMsg}`
6740
+ }
6741
+ ],
6742
+ details: void 0
6743
+ };
6744
+ }
6745
+ const resultPayload = {
6746
+ contentType,
6747
+ savedPath: saved.savedPath,
6748
+ sizeInBytes: saved.sizeInBytes
6749
+ };
6750
+ if (saved.imageUrl) {
6751
+ resultPayload.imageUrl = saved.imageUrl;
6622
6752
  }
6623
- const resultPayload = { imageData, contentType };
6624
- if (savedPath) resultPayload.savedPath = savedPath;
6625
6753
  return {
6626
- content: [{ type: "text", text: JSON.stringify(resultPayload) }],
6627
- details: { imageData, contentType }
6754
+ content: [{ type: "text", text: buildGenerateImageToolText(resultPayload) }],
6755
+ details: saved.imageUrl ? { imageUrl: saved.imageUrl } : void 0
6628
6756
  };
6629
6757
  });
6630
6758
  }
6631
6759
  };
6632
6760
  }
6633
6761
 
6762
+ // src/tools/browser.ts
6763
+ import { Type as Type16 } from "@sinclair/typebox";
6764
+
6765
+ // src/core/browser-preview-bridge.ts
6766
+ async function invokePreviewApi(webview, method, params) {
6767
+ if (!webview.rpc?.request?.evaluateJavascriptWithResponse) {
6768
+ throw new Error("Browser preview webview RPC is not available");
6769
+ }
6770
+ const paramsLiteral = JSON.stringify(params ?? null);
6771
+ const script = `return (async () => {
6772
+ const api = window.__tarskBrowserPreview;
6773
+ if (!api) {
6774
+ throw new Error("Browser preview API is not available");
6775
+ }
6776
+ const fn = api[${JSON.stringify(method)}];
6777
+ if (typeof fn !== "function") {
6778
+ throw new Error("Unknown browser preview API method: ${method}");
6779
+ }
6780
+ return await fn.call(api, ${paramsLiteral});
6781
+ })()`;
6782
+ return webview.rpc.request.evaluateJavascriptWithResponse({ script });
6783
+ }
6784
+
6785
+ // src/core/browser-native-screenshot.ts
6786
+ import { execFile } from "node:child_process";
6787
+ import { randomUUID as randomUUID3 } from "node:crypto";
6788
+ import { readFile as readFile5, unlink } from "node:fs/promises";
6789
+ import { tmpdir as tmpdir2 } from "node:os";
6790
+ import { join as join10 } from "node:path";
6791
+ import { setTimeout as sleep } from "node:timers/promises";
6792
+ import { promisify } from "node:util";
6793
+
6794
+ // src/core/browser-view-access.ts
6795
+ var electrobunModule = null;
6796
+ var electrobunLoadAttempted = false;
6797
+ async function loadElectrobunBrowserView() {
6798
+ if (electrobunLoadAttempted) {
6799
+ return electrobunModule;
6800
+ }
6801
+ electrobunLoadAttempted = true;
6802
+ try {
6803
+ electrobunModule = await import("electrobun/bun");
6804
+ } catch {
6805
+ electrobunModule = null;
6806
+ }
6807
+ return electrobunModule;
6808
+ }
6809
+ function getBrowserViewById(webviewId) {
6810
+ if (!electrobunModule) {
6811
+ return null;
6812
+ }
6813
+ return electrobunModule.BrowserView.getById(webviewId) ?? null;
6814
+ }
6815
+ function getBrowserWindowById(windowId) {
6816
+ if (!electrobunModule) {
6817
+ return null;
6818
+ }
6819
+ return electrobunModule.BrowserWindow.getById(windowId) ?? null;
6820
+ }
6821
+ function waitForBrowserEvent(webview, eventName, timeoutMs) {
6822
+ return new Promise((resolve7, reject) => {
6823
+ const timeout = setTimeout(() => {
6824
+ reject(new Error(`Timed out waiting for ${eventName} after ${timeoutMs}ms`));
6825
+ }, timeoutMs);
6826
+ webview.on(eventName, () => {
6827
+ clearTimeout(timeout);
6828
+ resolve7();
6829
+ });
6830
+ });
6831
+ }
6832
+
6833
+ // src/core/browser-native-screenshot.ts
6834
+ var execFileAsync = promisify(execFile);
6835
+ var NATIVE_FOCUS_DELAY_MS = 75;
6836
+ var PNG_SIGNATURE = Buffer.from([137, 80, 78, 71, 13, 10, 26, 10]);
6837
+ var SCREEN_RECORDING_HINT = "Grant Screen Recording permission to Tarsk in System Settings \u2192 Privacy & Security \u2192 Screen Recording, then restart Tarsk.";
6838
+ function parsePngDimensions(bytes) {
6839
+ if (bytes.length < 24 || !bytes.subarray(0, 8).equals(PNG_SIGNATURE)) {
6840
+ return null;
6841
+ }
6842
+ return {
6843
+ width: bytes.readUInt32BE(16),
6844
+ height: bytes.readUInt32BE(20)
6845
+ };
6846
+ }
6847
+ function pngHasImageData(bytes) {
6848
+ return bytes.includes(Buffer.from("IDAT")) && bytes.includes(Buffer.from("IEND"));
6849
+ }
6850
+ function parseJpegDimensions(bytes) {
6851
+ if (bytes.length < 4 || bytes[0] !== 255 || bytes[1] !== 216) {
6852
+ return null;
6853
+ }
6854
+ let offset = 2;
6855
+ while (offset + 9 < bytes.length) {
6856
+ if (bytes[offset] !== 255) {
6857
+ return null;
6858
+ }
6859
+ const marker = bytes[offset + 1];
6860
+ if (marker === 217) {
6861
+ break;
6862
+ }
6863
+ const segmentLength = bytes.readUInt16BE(offset + 2);
6864
+ if (segmentLength < 2 || offset + 2 + segmentLength > bytes.length) {
6865
+ return null;
6866
+ }
6867
+ const isStartOfFrame = marker === 192 || marker === 193 || marker === 194 || marker === 195;
6868
+ if (isStartOfFrame) {
6869
+ return {
6870
+ height: bytes.readUInt16BE(offset + 5),
6871
+ width: bytes.readUInt16BE(offset + 7)
6872
+ };
6873
+ }
6874
+ offset += 2 + segmentLength;
6875
+ }
6876
+ return null;
6877
+ }
6878
+ function isTruncatedCapture(bytes, width, height) {
6879
+ if (width >= 64 || height >= 64) {
6880
+ return bytes.length < 1024;
6881
+ }
6882
+ return bytes.length < 68;
6883
+ }
6884
+ function validateCapturedImage(bytes, format) {
6885
+ if (bytes.length === 0) {
6886
+ return { error: "macOS screencapture produced an empty image file." };
6887
+ }
6888
+ if (format === "png") {
6889
+ const dimensions2 = parsePngDimensions(bytes);
6890
+ if (!dimensions2) {
6891
+ return { error: "macOS screencapture produced an invalid PNG file." };
6892
+ }
6893
+ if (!pngHasImageData(bytes) || isTruncatedCapture(bytes, dimensions2.width, dimensions2.height)) {
6894
+ return {
6895
+ error: `macOS screencapture wrote a truncated PNG (${bytes.length} bytes for ${dimensions2.width}x${dimensions2.height}). ` + SCREEN_RECORDING_HINT
6896
+ };
6897
+ }
6898
+ return { ...dimensions2, format: "png" };
6899
+ }
6900
+ const dimensions = parseJpegDimensions(bytes);
6901
+ if (!dimensions) {
6902
+ return { error: "macOS screencapture produced an invalid JPEG file." };
6903
+ }
6904
+ if (isTruncatedCapture(bytes, dimensions.width, dimensions.height)) {
6905
+ return {
6906
+ error: `macOS screencapture wrote a truncated JPEG (${bytes.length} bytes for ${dimensions.width}x${dimensions.height}). ` + SCREEN_RECORDING_HINT
6907
+ };
6908
+ }
6909
+ return { ...dimensions, format: "jpeg" };
6910
+ }
6911
+ function isValidRect(rect) {
6912
+ return Number.isFinite(rect.x) && Number.isFinite(rect.y) && rect.width > 0 && rect.height > 0;
6913
+ }
6914
+ async function resolveViewFrame(view) {
6915
+ if (view.getFrame) {
6916
+ return await view.getFrame();
6917
+ }
6918
+ if (view.frame) {
6919
+ return view.frame;
6920
+ }
6921
+ return { x: 0, y: 0, width: 0, height: 0 };
6922
+ }
6923
+ async function captureMacOSRect(options) {
6924
+ const formatFlag = options.format === "jpeg" ? "jpg" : "png";
6925
+ const region = `${Math.round(options.rect.x)},${Math.round(options.rect.y)},${Math.round(options.rect.width)},${Math.round(options.rect.height)}`;
6926
+ await execFileAsync("screencapture", ["-x", "-t", formatFlag, "-R", region, options.path]);
6927
+ }
6928
+ async function resolveCaptureRect(webviewId) {
6929
+ const view = getBrowserViewById(webviewId);
6930
+ if (!view) {
6931
+ throw new Error(`Browser preview webview ${webviewId} is not available`);
6932
+ }
6933
+ const window = getBrowserWindowById(view.windowId);
6934
+ if (!window) {
6935
+ throw new Error(`Electrobun window ${view.windowId} is not available for screenshot capture`);
6936
+ }
6937
+ const windowFrame = window.getFrame();
6938
+ const viewFrame = await resolveViewFrame(view);
6939
+ if (!isValidRect(windowFrame)) {
6940
+ throw new Error(`Window ${window.id} does not expose a usable frame for screenshots`);
6941
+ }
6942
+ if (!isValidRect(viewFrame)) {
6943
+ throw new Error(`View ${view.id} does not expose a usable frame for screenshots`);
6944
+ }
6945
+ return {
6946
+ rect: {
6947
+ x: windowFrame.x + viewFrame.x,
6948
+ y: windowFrame.y + viewFrame.y,
6949
+ width: viewFrame.width,
6950
+ height: viewFrame.height
6951
+ },
6952
+ viewId: view.id,
6953
+ windowId: window.id
6954
+ };
6955
+ }
6956
+ async function captureNativeBrowserScreenshot(webviewId, format) {
6957
+ if (process.platform !== "darwin") {
6958
+ return {
6959
+ ok: false,
6960
+ reason: "Native Electrobun screenshots are only supported on macOS."
6961
+ };
6962
+ }
6963
+ const outputPath = join10(
6964
+ tmpdir2(),
6965
+ `tarsk-browser-screenshot-${randomUUID3()}.${format === "jpeg" ? "jpg" : "png"}`
6966
+ );
6967
+ try {
6968
+ const target = await resolveCaptureRect(webviewId);
6969
+ const window = getBrowserWindowById(target.windowId);
6970
+ window?.focus();
6971
+ await sleep(NATIVE_FOCUS_DELAY_MS);
6972
+ await captureMacOSRect({
6973
+ format,
6974
+ path: outputPath,
6975
+ rect: target.rect
6976
+ });
6977
+ const imageBytes = await readFile5(outputPath);
6978
+ const validated = validateCapturedImage(imageBytes, format);
6979
+ if ("error" in validated) {
6980
+ return {
6981
+ ok: false,
6982
+ reason: validated.error
6983
+ };
6984
+ }
6985
+ return {
6986
+ ok: true,
6987
+ result: {
6988
+ format: validated.format,
6989
+ width: validated.width,
6990
+ height: validated.height,
6991
+ image_data: imageBytes.toString("base64"),
6992
+ capture_method: "native-screencapture",
6993
+ window_id: target.windowId,
6994
+ view_id: target.viewId
6995
+ }
6996
+ };
6997
+ } catch (error) {
6998
+ const message = error instanceof Error ? error.message : String(error);
6999
+ return {
7000
+ ok: false,
7001
+ reason: `${message} Native screenshots require a visible on-screen preview. ${SCREEN_RECORDING_HINT}`
7002
+ };
7003
+ } finally {
7004
+ await unlink(outputPath).catch(() => void 0);
7005
+ }
7006
+ }
7007
+
7008
+ // src/tools/browser.ts
7009
+ var browserSchema = Type16.Object({
7010
+ action: Type16.Union(
7011
+ [
7012
+ Type16.Literal("open_url"),
7013
+ Type16.Literal("evaluate_javascript"),
7014
+ Type16.Literal("find_in_page"),
7015
+ Type16.Literal("stop_find_in_page"),
7016
+ Type16.Literal("reload"),
7017
+ Type16.Literal("go_back"),
7018
+ Type16.Literal("go_forward"),
7019
+ Type16.Literal("get_state"),
7020
+ Type16.Literal("screenshot"),
7021
+ Type16.Literal("set_viewport"),
7022
+ Type16.Literal("snapshot"),
7023
+ Type16.Literal("click"),
7024
+ Type16.Literal("fill"),
7025
+ Type16.Literal("press"),
7026
+ Type16.Literal("hover"),
7027
+ Type16.Literal("wait_for"),
7028
+ Type16.Literal("get_console_logs")
7029
+ ],
7030
+ {
7031
+ description: "Browser action. Navigation: open_url, reload, go_back, go_forward, get_state. Inspection: snapshot, screenshot, get_console_logs, find_in_page, stop_find_in_page. Interaction: click, fill, press, hover, wait_for, evaluate_javascript. Layout: set_viewport."
7032
+ }
7033
+ ),
7034
+ url: Type16.Optional(
7035
+ Type16.String({
7036
+ description: "URL to open in the Browser tab preview (required for open_url)."
7037
+ })
7038
+ ),
7039
+ script: Type16.Optional(
7040
+ Type16.String({
7041
+ description: "JavaScript to execute in the preview page context (required for evaluate_javascript)."
7042
+ })
7043
+ ),
7044
+ text: Type16.Optional(
7045
+ Type16.String({
7046
+ description: "Text to find in the page (required for find_in_page)."
7047
+ })
7048
+ ),
7049
+ forward: Type16.Optional(
7050
+ Type16.Boolean({
7051
+ description: "Search forward through matches for find_in_page (default true)."
7052
+ })
7053
+ ),
7054
+ match_case: Type16.Optional(
7055
+ Type16.Boolean({
7056
+ description: "Case-sensitive search for find_in_page (default false)."
7057
+ })
7058
+ ),
7059
+ wait_for_load_ms: Type16.Optional(
7060
+ Type16.Number({
7061
+ description: "Milliseconds to wait for the dom-ready event after navigation (default 5000, max 30000)."
7062
+ })
7063
+ ),
7064
+ preset: Type16.Optional(
7065
+ Type16.Union([Type16.Literal("desktop"), Type16.Literal("tablet"), Type16.Literal("mobile")], {
7066
+ description: "Viewport preset for set_viewport."
7067
+ })
7068
+ ),
7069
+ format: Type16.Optional(
7070
+ Type16.Union([Type16.Literal("png"), Type16.Literal("jpeg")], {
7071
+ description: "Image format for screenshot (default png)."
7072
+ })
7073
+ ),
7074
+ ref: Type16.Optional(
7075
+ Type16.String({
7076
+ description: "Element ref from snapshot for click, fill, or hover (e.g. @e1)."
7077
+ })
7078
+ ),
7079
+ value: Type16.Optional(
7080
+ Type16.String({
7081
+ description: "Value to type for fill."
7082
+ })
7083
+ ),
7084
+ key: Type16.Optional(
7085
+ Type16.String({
7086
+ description: "Key to press for press (e.g. Enter, Tab, Escape)."
7087
+ })
7088
+ ),
7089
+ selector: Type16.Optional(
7090
+ Type16.String({
7091
+ description: "CSS selector to wait for in wait_for."
7092
+ })
7093
+ ),
7094
+ timeout_ms: Type16.Optional(
7095
+ Type16.Number({
7096
+ description: "Timeout in milliseconds for wait_for (default 10000, max 60000)."
7097
+ })
7098
+ ),
7099
+ interactive_only: Type16.Optional(
7100
+ Type16.Boolean({
7101
+ description: "Only include interactive elements in snapshot (default false)."
7102
+ })
7103
+ ),
7104
+ max_depth: Type16.Optional(
7105
+ Type16.Number({
7106
+ description: "Maximum DOM depth for snapshot (default 15)."
7107
+ })
7108
+ ),
7109
+ since_ms: Type16.Optional(
7110
+ Type16.Number({
7111
+ description: "Only return console logs from the last N milliseconds for get_console_logs."
7112
+ })
7113
+ ),
7114
+ levels: Type16.Optional(
7115
+ Type16.Array(Type16.String(), {
7116
+ description: 'Console levels to include for get_console_logs (e.g. ["error", "warn"]).'
7117
+ })
7118
+ ),
7119
+ clear: Type16.Optional(
7120
+ Type16.Boolean({
7121
+ description: "Clear buffered console logs after reading for get_console_logs (default false)."
7122
+ })
7123
+ )
7124
+ });
7125
+ var pendingBrowserTasks = /* @__PURE__ */ new Map();
7126
+ var bufferedWebviewIds = /* @__PURE__ */ new Map();
7127
+ var pendingBrowserActionResults = /* @__PURE__ */ new Map();
7128
+ var bufferedBrowserActionResults = /* @__PURE__ */ new Map();
7129
+ var DEFAULT_WAIT_FOR_LOAD_MS = 5e3;
7130
+ var MAX_WAIT_FOR_LOAD_MS = 3e4;
7131
+ var DEFAULT_WEBVIEW_READY_TIMEOUT_MS = 2e4;
7132
+ var DEFAULT_WAIT_FOR_TIMEOUT_MS = 1e4;
7133
+ var MAX_WAIT_FOR_TIMEOUT_MS = 6e4;
7134
+ var DEFAULT_BROWSER_ACTION_TIMEOUT_MS = 2e4;
7135
+ function submitBrowserWebviewReady(toolCallId, webviewId) {
7136
+ const pending = pendingBrowserTasks.get(toolCallId);
7137
+ if (pending) {
7138
+ pending.resolve(webviewId);
7139
+ pendingBrowserTasks.delete(toolCallId);
7140
+ return true;
7141
+ }
7142
+ bufferedWebviewIds.set(toolCallId, webviewId);
7143
+ return true;
7144
+ }
7145
+ function submitBrowserActionResult(toolCallId, result) {
7146
+ const pending = pendingBrowserActionResults.get(toolCallId);
7147
+ if (pending) {
7148
+ pending.resolve(result);
7149
+ pendingBrowserActionResults.delete(toolCallId);
7150
+ return true;
7151
+ }
7152
+ bufferedBrowserActionResults.set(toolCallId, result);
7153
+ return true;
7154
+ }
7155
+ function cancelPendingBrowserTask(toolCallId) {
7156
+ const pending = pendingBrowserTasks.get(toolCallId);
7157
+ if (!pending) return false;
7158
+ pending.reject(new Error("Browser tool execution cancelled"));
7159
+ pendingBrowserTasks.delete(toolCallId);
7160
+ return true;
7161
+ }
7162
+ function cancelPendingBrowserActionResult(toolCallId) {
7163
+ const pending = pendingBrowserActionResults.get(toolCallId);
7164
+ if (!pending) return false;
7165
+ pending.reject(new Error("Browser tool execution cancelled"));
7166
+ pendingBrowserActionResults.delete(toolCallId);
7167
+ return true;
7168
+ }
7169
+ function normalizeUrl(url) {
7170
+ const trimmed = url.trim();
7171
+ if (!trimmed) {
7172
+ throw new Error("url must not be empty");
7173
+ }
7174
+ if (trimmed.startsWith("http://") || trimmed.startsWith("https://")) {
7175
+ return trimmed;
7176
+ }
7177
+ return `http://${trimmed}`;
7178
+ }
7179
+ function waitForPreviewWebview(toolCallId, signal, timeoutMs = DEFAULT_WEBVIEW_READY_TIMEOUT_MS) {
7180
+ const buffered = bufferedWebviewIds.get(toolCallId);
7181
+ if (buffered !== void 0) {
7182
+ bufferedWebviewIds.delete(toolCallId);
7183
+ return Promise.resolve(buffered);
7184
+ }
7185
+ return new Promise((resolve7, reject) => {
7186
+ if (signal?.aborted) {
7187
+ reject(new Error("Operation aborted"));
7188
+ return;
7189
+ }
7190
+ let timeoutHandle = null;
7191
+ const pending = pendingBrowserTasks.get(toolCallId);
7192
+ const onAbort = () => {
7193
+ cancelPendingBrowserTask(toolCallId);
7194
+ };
7195
+ function cleanup() {
7196
+ signal?.removeEventListener("abort", onAbort);
7197
+ if (timeoutHandle) {
7198
+ clearTimeout(timeoutHandle);
7199
+ }
7200
+ }
7201
+ const wrappedResolve = (webviewId) => {
7202
+ cleanup();
7203
+ resolve7(webviewId);
7204
+ };
7205
+ const wrappedReject = (error) => {
7206
+ cleanup();
7207
+ reject(error);
7208
+ };
7209
+ if (pending) {
7210
+ pending.resolve = wrappedResolve;
7211
+ pending.reject = wrappedReject;
7212
+ } else {
7213
+ pendingBrowserTasks.set(toolCallId, {
7214
+ resolve: wrappedResolve,
7215
+ reject: wrappedReject
7216
+ });
7217
+ }
7218
+ signal?.addEventListener("abort", onAbort, { once: true });
7219
+ timeoutHandle = setTimeout(() => {
7220
+ if (!pendingBrowserTasks.has(toolCallId)) return;
7221
+ pendingBrowserTasks.delete(toolCallId);
7222
+ wrappedReject(
7223
+ new Error(
7224
+ `browser tool timed out after ${timeoutMs}ms waiting for the Browser tab preview webview`
7225
+ )
7226
+ );
7227
+ }, timeoutMs);
7228
+ });
7229
+ }
7230
+ function waitForBrowserActionResult(toolCallId, signal, timeoutMs = DEFAULT_BROWSER_ACTION_TIMEOUT_MS) {
7231
+ const buffered = bufferedBrowserActionResults.get(toolCallId);
7232
+ if (buffered) {
7233
+ bufferedBrowserActionResults.delete(toolCallId);
7234
+ return Promise.resolve(buffered);
7235
+ }
7236
+ return new Promise((resolve7, reject) => {
7237
+ if (signal?.aborted) {
7238
+ reject(new Error("Operation aborted"));
7239
+ return;
7240
+ }
7241
+ let timeoutHandle = null;
7242
+ const onAbort = () => {
7243
+ cancelPendingBrowserActionResult(toolCallId);
7244
+ };
7245
+ function cleanup() {
7246
+ signal?.removeEventListener("abort", onAbort);
7247
+ if (timeoutHandle) {
7248
+ clearTimeout(timeoutHandle);
7249
+ }
7250
+ }
7251
+ const wrappedResolve = (result) => {
7252
+ cleanup();
7253
+ resolve7(result);
7254
+ };
7255
+ const wrappedReject = (error) => {
7256
+ cleanup();
7257
+ reject(error);
7258
+ };
7259
+ pendingBrowserActionResults.set(toolCallId, {
7260
+ resolve: wrappedResolve,
7261
+ reject: wrappedReject
7262
+ });
7263
+ signal?.addEventListener("abort", onAbort, { once: true });
7264
+ timeoutHandle = setTimeout(() => {
7265
+ if (!pendingBrowserActionResults.has(toolCallId)) return;
7266
+ pendingBrowserActionResults.delete(toolCallId);
7267
+ wrappedReject(
7268
+ new Error(
7269
+ `browser tool timed out after ${timeoutMs}ms waiting for the Browser tab host action`
7270
+ )
7271
+ );
7272
+ }, timeoutMs);
7273
+ });
7274
+ }
7275
+ function logBrowserPreviewFailure(action, webviewId, webview, error, context) {
7276
+ console.error(`[browser] ${action} failed`, {
7277
+ webview_id: webviewId,
7278
+ current_url: webview.url,
7279
+ ...context,
7280
+ error: error instanceof Error ? error.message : String(error),
7281
+ stack: error instanceof Error ? error.stack : void 0
7282
+ });
7283
+ }
7284
+ async function executeBrowserAction(webviewId, input) {
7285
+ const webview = getBrowserViewById(webviewId);
7286
+ if (!webview) {
7287
+ throw new Error(`Browser preview webview ${webviewId} is not available`);
7288
+ }
7289
+ switch (input.action) {
7290
+ case "open_url": {
7291
+ if (!input.url?.trim()) {
7292
+ throw new Error("url is required for open_url");
7293
+ }
7294
+ const url = normalizeUrl(input.url);
7295
+ const waitMs = Math.min(
7296
+ Math.max(input.wait_for_load_ms ?? DEFAULT_WAIT_FOR_LOAD_MS, 0),
7297
+ MAX_WAIT_FOR_LOAD_MS
7298
+ );
7299
+ const domReadyPromise = waitForBrowserEvent(webview, "dom-ready", waitMs);
7300
+ webview.loadURL(url);
7301
+ let domReady = false;
7302
+ try {
7303
+ await domReadyPromise;
7304
+ domReady = true;
7305
+ } catch {
7306
+ }
7307
+ return {
7308
+ action: "open_url",
7309
+ url,
7310
+ webview_id: webviewId,
7311
+ current_url: webview.url,
7312
+ dom_ready: domReady
7313
+ };
7314
+ }
7315
+ case "evaluate_javascript": {
7316
+ if (!input.script?.trim()) {
7317
+ throw new Error("script is required for evaluate_javascript");
7318
+ }
7319
+ if (!webview.rpc?.request?.evaluateJavascriptWithResponse) {
7320
+ throw new Error("Browser preview webview RPC is not available for JavaScript execution");
7321
+ }
7322
+ const result = await webview.rpc.request.evaluateJavascriptWithResponse({
7323
+ script: input.script
7324
+ });
7325
+ return {
7326
+ action: "evaluate_javascript",
7327
+ webview_id: webviewId,
7328
+ result
7329
+ };
7330
+ }
7331
+ case "find_in_page": {
7332
+ if (!input.text?.trim()) {
7333
+ throw new Error("text is required for find_in_page");
7334
+ }
7335
+ webview.findInPage(input.text, {
7336
+ forward: input.forward ?? true,
7337
+ matchCase: input.match_case ?? false
7338
+ });
7339
+ return {
7340
+ action: "find_in_page",
7341
+ webview_id: webviewId,
7342
+ text: input.text,
7343
+ forward: input.forward ?? true,
7344
+ match_case: input.match_case ?? false
7345
+ };
7346
+ }
7347
+ case "stop_find_in_page": {
7348
+ webview.stopFindInPage();
7349
+ return {
7350
+ action: "stop_find_in_page",
7351
+ webview_id: webviewId
7352
+ };
7353
+ }
7354
+ case "snapshot": {
7355
+ const result = await invokePreviewApi(webview, "snapshot", {
7356
+ interactive_only: input.interactive_only ?? false,
7357
+ max_depth: input.max_depth ?? 15
7358
+ });
7359
+ return {
7360
+ action: "snapshot",
7361
+ webview_id: webviewId,
7362
+ ...typeof result === "object" && result !== null ? result : { result }
7363
+ };
7364
+ }
7365
+ case "click": {
7366
+ if (!input.ref?.trim()) {
7367
+ throw new Error("ref is required for click");
7368
+ }
7369
+ const result = await invokePreviewApi(webview, "click", { ref: input.ref });
7370
+ return {
7371
+ action: "click",
7372
+ webview_id: webviewId,
7373
+ ref: input.ref,
7374
+ result
7375
+ };
7376
+ }
7377
+ case "fill": {
7378
+ if (!input.ref?.trim()) {
7379
+ throw new Error("ref is required for fill");
7380
+ }
7381
+ if (input.value === void 0) {
7382
+ throw new Error("value is required for fill");
7383
+ }
7384
+ const result = await invokePreviewApi(webview, "fill", {
7385
+ ref: input.ref,
7386
+ value: input.value
7387
+ });
7388
+ return {
7389
+ action: "fill",
7390
+ webview_id: webviewId,
7391
+ ref: input.ref,
7392
+ value: input.value,
7393
+ result
7394
+ };
7395
+ }
7396
+ case "press": {
7397
+ if (!input.key?.trim()) {
7398
+ throw new Error("key is required for press");
7399
+ }
7400
+ const result = await invokePreviewApi(webview, "press", { key: input.key });
7401
+ return {
7402
+ action: "press",
7403
+ webview_id: webviewId,
7404
+ key: input.key,
7405
+ result
7406
+ };
7407
+ }
7408
+ case "hover": {
7409
+ if (!input.ref?.trim()) {
7410
+ throw new Error("ref is required for hover");
7411
+ }
7412
+ const result = await invokePreviewApi(webview, "hover", { ref: input.ref });
7413
+ return {
7414
+ action: "hover",
7415
+ webview_id: webviewId,
7416
+ ref: input.ref,
7417
+ result
7418
+ };
7419
+ }
7420
+ case "wait_for": {
7421
+ if (!input.selector?.trim() && !input.text?.trim()) {
7422
+ throw new Error("selector or text is required for wait_for");
7423
+ }
7424
+ const timeoutMs = Math.min(
7425
+ Math.max(input.timeout_ms ?? DEFAULT_WAIT_FOR_TIMEOUT_MS, 0),
7426
+ MAX_WAIT_FOR_TIMEOUT_MS
7427
+ );
7428
+ const result = await invokePreviewApi(webview, "waitFor", {
7429
+ selector: input.selector,
7430
+ text: input.text,
7431
+ timeout_ms: timeoutMs
7432
+ });
7433
+ return {
7434
+ action: "wait_for",
7435
+ webview_id: webviewId,
7436
+ selector: input.selector,
7437
+ text: input.text,
7438
+ timeout_ms: timeoutMs,
7439
+ result
7440
+ };
7441
+ }
7442
+ case "get_console_logs": {
7443
+ const result = await invokePreviewApi(webview, "getConsoleLogs", {
7444
+ since_ms: input.since_ms ?? 0,
7445
+ levels: input.levels,
7446
+ clear: input.clear ?? false
7447
+ });
7448
+ return {
7449
+ action: "get_console_logs",
7450
+ webview_id: webviewId,
7451
+ ...typeof result === "object" && result !== null ? result : { result }
7452
+ };
7453
+ }
7454
+ case "screenshot": {
7455
+ const format = input.format ?? "png";
7456
+ const nativeCapture = await captureNativeBrowserScreenshot(webviewId, format);
7457
+ if (nativeCapture.ok) {
7458
+ return {
7459
+ action: "screenshot",
7460
+ webview_id: webviewId,
7461
+ ...nativeCapture.result
7462
+ };
7463
+ }
7464
+ try {
7465
+ const result = await invokePreviewApi(webview, "takeScreenshot", { format });
7466
+ return {
7467
+ action: "screenshot",
7468
+ webview_id: webviewId,
7469
+ capture_method: "svg-foreign-object",
7470
+ native_capture_unavailable: nativeCapture.reason,
7471
+ ...typeof result === "object" && result !== null ? result : { result }
7472
+ };
7473
+ } catch (error) {
7474
+ logBrowserPreviewFailure("screenshot", webviewId, webview, error, {
7475
+ format,
7476
+ capture_method: "svg-foreign-object",
7477
+ native_capture_unavailable: nativeCapture.reason,
7478
+ troubleshooting: "Native macOS capture uses screencapture on the visible preview rect. If native capture fails, grant Screen Recording permission to Tarsk and restart the app. SVG fallback can fail on large third-party pages. Check tarsk-logs.txt for [browser-preview] takeScreenshot failed entries."
7479
+ });
7480
+ throw error;
7481
+ }
7482
+ }
7483
+ default:
7484
+ throw new Error(`Unsupported browser action: ${String(input.action)}`);
7485
+ }
7486
+ }
7487
+ function formatBrowserResult(payload) {
7488
+ if (payload.action === "screenshot") {
7489
+ const display = stripScreenshotPayload(payload);
7490
+ return {
7491
+ text: buildGenerateImageToolText(display),
7492
+ details: payload
7493
+ };
7494
+ }
7495
+ return {
7496
+ text: JSON.stringify(payload, null, 2),
7497
+ details: payload
7498
+ };
7499
+ }
7500
+ function stripScreenshotPayload(payload) {
7501
+ const display = {};
7502
+ for (const [key, value] of Object.entries(payload)) {
7503
+ if (key === "image_data") continue;
7504
+ if (typeof value === "string" || typeof value === "number") {
7505
+ display[key] = value;
7506
+ }
7507
+ }
7508
+ return display;
7509
+ }
7510
+ async function persistScreenshotResult(payload, options) {
7511
+ const imageData = payload.image_data;
7512
+ if (typeof imageData !== "string" || !imageData) {
7513
+ return payload;
7514
+ }
7515
+ const sizeInBytes = decodeBase64ImageData(imageData).length;
7516
+ const { image_data: _removed, ...rest } = payload;
7517
+ if (!options.cwd) {
7518
+ return { ...rest, sizeInBytes };
7519
+ }
7520
+ const format = payload.format === "jpeg" ? "jpeg" : "png";
7521
+ const extension = format === "jpeg" ? "jpg" : "png";
7522
+ const id = Math.random().toString(36).slice(2, 8);
7523
+ const relativePath = buildTmpImagePath(`screenshot-${id}.${extension}`);
7524
+ const saved = await saveToolImage({
7525
+ imageData,
7526
+ cwd: options.cwd,
7527
+ relativePath,
7528
+ threadId: options.threadId,
7529
+ serverOrigin: options.serverOrigin
7530
+ });
7531
+ return {
7532
+ ...rest,
7533
+ savedPath: saved.savedPath,
7534
+ sizeInBytes: saved.sizeInBytes,
7535
+ ...saved.imageUrl ? { imageUrl: saved.imageUrl } : {}
7536
+ };
7537
+ }
7538
+ var BROWSER_TOOL_DESCRIPTION = "Control the in-app Browser. Use to read information from websites \u2014 open_url then snapshot for page content and structure, get_state for URL/title, find_in_page to locate text, evaluate_javascript to extract data, or screenshot for visual reference. Also use for interacting with pages: click, fill, press, hover, wait_for. Actions: open_url, reload, go_back, go_forward, get_state, set_viewport, screenshot, snapshot, click, fill, press, hover, wait_for, get_console_logs, evaluate_javascript, find_in_page, stop_find_in_page. Use snapshot to get @eN element refs, then click/fill/hover by ref. The Browser tab opens automatically. Start the dev server first when testing localhost apps.";
7539
+ function createBrowserTool(options = {}) {
7540
+ const serverOrigin = options.serverOrigin ?? getServerOrigin();
7541
+ return {
7542
+ name: "browser",
7543
+ label: "browser",
7544
+ description: BROWSER_TOOL_DESCRIPTION,
7545
+ parameters: browserSchema,
7546
+ execute: async (toolCallId, input, signal) => {
7547
+ return withAbortSignal(signal, async () => {
7548
+ const electrobun = await loadElectrobunBrowserView();
7549
+ if (!electrobun) {
7550
+ throw new Error(
7551
+ "browser tool is only available in the Tarsk desktop app (Electrobun), not in server-only mode"
7552
+ );
7553
+ }
7554
+ if (isBrowserHostAction(input.action)) {
7555
+ const result2 = await waitForBrowserActionResult(toolCallId, signal);
7556
+ const formatted2 = formatBrowserResult(result2);
7557
+ return {
7558
+ content: [{ type: "text", text: formatted2.text }],
7559
+ details: formatted2.details
7560
+ };
7561
+ }
7562
+ const webviewId = await waitForPreviewWebview(toolCallId, signal);
7563
+ let result = await executeBrowserAction(webviewId, input);
7564
+ if (input.action === "screenshot") {
7565
+ result = await persistScreenshotResult(result, {
7566
+ cwd: options.cwd,
7567
+ threadId: options.threadId,
7568
+ serverOrigin
7569
+ });
7570
+ }
7571
+ const formatted = formatBrowserResult(result);
7572
+ return {
7573
+ content: [{ type: "text", text: formatted.text }],
7574
+ details: typeof formatted.details.imageUrl === "string" ? { imageUrl: formatted.details.imageUrl } : formatted.details
7575
+ };
7576
+ });
7577
+ }
7578
+ };
7579
+ }
7580
+ var browserTool = createBrowserTool();
7581
+
6634
7582
  // src/tools/execute-browser-js.ts
6635
7583
  init_tarsk_debug();
6636
- import { Type as Type16 } from "@sinclair/typebox";
6637
- var runWebWorkerSchema = Type16.Object({
6638
- intention: Type16.String({
7584
+ import { Type as Type17 } from "@sinclair/typebox";
7585
+ var runWebWorkerSchema = Type17.Object({
7586
+ intention: Type17.String({
6639
7587
  description: "A short description of what this code execution is trying to accomplish."
6640
7588
  }),
6641
- code: Type16.String({
7589
+ code: Type17.String({
6642
7590
  description: "The javascript code to execute in browser context(not nodejs). The environment provides an async function `callTool(toolName, params)` which allows calling any of the other available tools by name. callTool always returns `{ text: string | null, error: string | null }`. On success `text` contains the result and `error` is null. On failure `error` contains the error message and `text` is null. Example: `const r = await callTool('read', { path: 'foo.ts' }); if (r.error) return r.error; return r.text;`."
6643
7591
  })
6644
7592
  });
@@ -6685,11 +7633,11 @@ function createRunWebWorkerTool(options) {
6685
7633
  if (signal?.aborted) {
6686
7634
  throw new Error("Operation aborted");
6687
7635
  }
6688
- const result = await new Promise((resolve6, reject) => {
7636
+ const result = await new Promise((resolve7, reject) => {
6689
7637
  const earlyResult = bufferedResults.get(toolCallId);
6690
7638
  if (earlyResult !== void 0) {
6691
7639
  bufferedResults.delete(toolCallId);
6692
- resolve6(earlyResult);
7640
+ resolve7(earlyResult);
6693
7641
  return;
6694
7642
  }
6695
7643
  let timeoutHandle = null;
@@ -6704,7 +7652,7 @@ function createRunWebWorkerTool(options) {
6704
7652
  }
6705
7653
  const wrappedResolve = (value) => {
6706
7654
  cleanup();
6707
- resolve6(value);
7655
+ resolve7(value);
6708
7656
  };
6709
7657
  const wrappedReject = (error) => {
6710
7658
  cleanup();
@@ -6772,7 +7720,7 @@ function looksLikeMissingReturn(code) {
6772
7720
  var runWebWorkerTool = createRunWebWorkerTool();
6773
7721
 
6774
7722
  // src/tools/fetch.ts
6775
- import { Type as Type17 } from "@sinclair/typebox";
7723
+ import { Type as Type18 } from "@sinclair/typebox";
6776
7724
 
6777
7725
  // src/features/web-fetch/content-processor.ts
6778
7726
  import { completeSimple as completeSimple2 } from "@earendil-works/pi-ai";
@@ -6803,7 +7751,7 @@ function runCommandTimed(stepName, command, args2, cwd) {
6803
7751
  let tFirstIo = null;
6804
7752
  let tExit = null;
6805
7753
  logSubprocessMark(scope, stepName, "spawn-call", 0);
6806
- return new Promise((resolve6, reject) => {
7754
+ return new Promise((resolve7, reject) => {
6807
7755
  const proc = spawnProcess(command, args2, { cwd });
6808
7756
  proc.on("spawn", () => {
6809
7757
  tSpawnEvent = Date.now();
@@ -6842,7 +7790,7 @@ function runCommandTimed(stepName, command, args2, cwd) {
6842
7790
  console.log(
6843
7791
  `${GITOPS_TIMING_PREFIX} subprocess ${scope} step=${stepName} summary total=${tClose - t0}ms` + (preSpawnMs !== null ? ` pre-spawn=${preSpawnMs}ms` : "") + (processActiveMs !== null ? ` process-active=${processActiveMs}ms` : "") + (exitToCloseMs !== null ? ` exit-to-close=${exitToCloseMs}ms` : "") + ` cmd=${command} ${args2.join(" ")}`
6844
7792
  );
6845
- resolve6({ code, stdout, stderr });
7793
+ resolve7({ code, stdout, stderr });
6846
7794
  });
6847
7795
  proc.on("error", (error) => {
6848
7796
  logSubprocessMark(scope, stepName, "spawn-error", Date.now() - t0);
@@ -6875,13 +7823,13 @@ function startEventLoopLagMonitor(threadId) {
6875
7823
 
6876
7824
  // src/features/git/git.utils.ts
6877
7825
  import { existsSync as existsSync8, readFileSync as readFileSync3, statSync as statSync5 } from "fs";
6878
- import { isAbsolute as isAbsolute2, normalize as normalize2, resolve, join as join10 } from "path";
7826
+ import { isAbsolute as isAbsolute2, normalize as normalize2, resolve, join as join12 } from "path";
6879
7827
 
6880
7828
  // src/core/paths.ts
6881
- import { join as join9 } from "path";
7829
+ import { join as join11 } from "path";
6882
7830
  import { homedir as homedir4 } from "os";
6883
- var APP_SUPPORT_DIR = join9(homedir4(), "Library", "Application Support", "Tarsk");
6884
- var DATA_DIR = join9(APP_SUPPORT_DIR, "data");
7831
+ var APP_SUPPORT_DIR = join11(homedir4(), "Library", "Application Support", "Tarsk");
7832
+ var DATA_DIR = join11(APP_SUPPORT_DIR, "data");
6885
7833
  function getDataDir() {
6886
7834
  return DATA_DIR;
6887
7835
  }
@@ -7149,7 +8097,7 @@ async function getCurrentBranch(gitRoot) {
7149
8097
  ]);
7150
8098
  return result.stdout.trim();
7151
8099
  }
7152
- return new Promise((resolve6) => {
8100
+ return new Promise((resolve7) => {
7153
8101
  const proc = spawnProcess("git", ["rev-parse", "--abbrev-ref", "HEAD"], { cwd: gitRoot });
7154
8102
  let out = "";
7155
8103
  if (proc.stdout) {
@@ -7158,9 +8106,9 @@ async function getCurrentBranch(gitRoot) {
7158
8106
  });
7159
8107
  }
7160
8108
  proc.on("close", () => {
7161
- resolve6(out.trim());
8109
+ resolve7(out.trim());
7162
8110
  });
7163
- proc.on("error", () => resolve6(""));
8111
+ proc.on("error", () => resolve7(""));
7164
8112
  });
7165
8113
  }
7166
8114
  async function getGitDiff(gitRoot) {
@@ -7237,7 +8185,7 @@ async function getUntrackedFilesDiff(gitRoot) {
7237
8185
  const parts = [];
7238
8186
  const maxFileSize = 1e5;
7239
8187
  for (const relPath of untrackedPaths) {
7240
- const fullPath = join10(gitRoot, relPath);
8188
+ const fullPath = join12(gitRoot, relPath);
7241
8189
  if (!existsSync8(fullPath)) continue;
7242
8190
  try {
7243
8191
  if (statSync5(fullPath).isDirectory()) continue;
@@ -7270,7 +8218,7 @@ async function getGitStatus(gitRoot) {
7270
8218
  ]);
7271
8219
  return result.stdout;
7272
8220
  }
7273
- return new Promise((resolve6) => {
8221
+ return new Promise((resolve7) => {
7274
8222
  const proc = spawnProcess("git", ["status", "--porcelain", "--untracked-files=all"], {
7275
8223
  cwd: gitRoot
7276
8224
  });
@@ -7280,15 +8228,15 @@ async function getGitStatus(gitRoot) {
7280
8228
  out += d.toString();
7281
8229
  });
7282
8230
  }
7283
- proc.on("close", () => resolve6(out));
7284
- proc.on("error", () => resolve6(""));
8231
+ proc.on("close", () => resolve7(out));
8232
+ proc.on("error", () => resolve7(""));
7285
8233
  });
7286
8234
  }
7287
8235
  function runGit(stepName, gitRoot, args2) {
7288
8236
  if (isGitSubprocessTimingEnabled()) {
7289
8237
  return runGitCommandTimed(stepName, gitRoot, args2);
7290
8238
  }
7291
- return new Promise((resolve6) => {
8239
+ return new Promise((resolve7) => {
7292
8240
  const proc = spawnProcess("git", args2, { cwd: gitRoot });
7293
8241
  let stdout = "";
7294
8242
  let stderr = "";
@@ -7302,8 +8250,8 @@ function runGit(stepName, gitRoot, args2) {
7302
8250
  stderr += d.toString();
7303
8251
  });
7304
8252
  }
7305
- proc.on("close", (code) => resolve6({ code, stdout, stderr }));
7306
- proc.on("error", () => resolve6({ code: -1, stdout, stderr }));
8253
+ proc.on("close", (code) => resolve7({ code, stdout, stderr }));
8254
+ proc.on("error", () => resolve7({ code: -1, stdout, stderr }));
7307
8255
  });
7308
8256
  }
7309
8257
  async function getStatusPorcelainV2(gitRoot) {
@@ -7401,7 +8349,7 @@ async function fetchRemoteRefs(gitRoot, refs) {
7401
8349
  await runGit("fetch-remote-refs", gitRoot, ["fetch", "--no-tags", "origin", ...refs]);
7402
8350
  }
7403
8351
  function hasUnpushedCommits(gitRoot, currentBranch) {
7404
- return new Promise((resolve6) => {
8352
+ return new Promise((resolve7) => {
7405
8353
  const proc = spawnProcess("git", ["log", `origin/${currentBranch}..HEAD`, "--oneline"], {
7406
8354
  cwd: gitRoot
7407
8355
  });
@@ -7412,13 +8360,13 @@ function hasUnpushedCommits(gitRoot, currentBranch) {
7412
8360
  });
7413
8361
  }
7414
8362
  proc.on("close", () => {
7415
- resolve6(out.trim().length > 0);
8363
+ resolve7(out.trim().length > 0);
7416
8364
  });
7417
- proc.on("error", () => resolve6(false));
8365
+ proc.on("error", () => resolve7(false));
7418
8366
  });
7419
8367
  }
7420
8368
  function hasCommitsAheadOfDefault(gitRoot, defaultBranch) {
7421
- return new Promise((resolve6) => {
8369
+ return new Promise((resolve7) => {
7422
8370
  const proc = spawnProcess("git", ["rev-list", "--count", `origin/${defaultBranch}..HEAD`], {
7423
8371
  cwd: gitRoot
7424
8372
  });
@@ -7430,12 +8378,12 @@ function hasCommitsAheadOfDefault(gitRoot, defaultBranch) {
7430
8378
  }
7431
8379
  proc.on("close", (code) => {
7432
8380
  if (code === 0) {
7433
- resolve6(parseInt(out.trim(), 10) > 0);
8381
+ resolve7(parseInt(out.trim(), 10) > 0);
7434
8382
  } else {
7435
- resolve6(false);
8383
+ resolve7(false);
7436
8384
  }
7437
8385
  });
7438
- proc.on("error", () => resolve6(false));
8386
+ proc.on("error", () => resolve7(false));
7439
8387
  });
7440
8388
  }
7441
8389
  async function getRemoteOrigin(gitRoot) {
@@ -7450,7 +8398,7 @@ async function getRemoteOrigin(gitRoot) {
7450
8398
  }
7451
8399
  return "";
7452
8400
  }
7453
- return new Promise((resolve6) => {
8401
+ return new Promise((resolve7) => {
7454
8402
  const proc = spawnProcess("git", ["remote", "get-url", "origin"], { cwd: gitRoot });
7455
8403
  let out = "";
7456
8404
  if (proc.stdout) {
@@ -7460,12 +8408,12 @@ async function getRemoteOrigin(gitRoot) {
7460
8408
  }
7461
8409
  proc.on("close", (code) => {
7462
8410
  if (code === 0) {
7463
- resolve6(out.trim());
8411
+ resolve7(out.trim());
7464
8412
  } else {
7465
- resolve6("");
8413
+ resolve7("");
7466
8414
  }
7467
8415
  });
7468
- proc.on("error", () => resolve6(""));
8416
+ proc.on("error", () => resolve7(""));
7469
8417
  });
7470
8418
  }
7471
8419
  async function getGitSyncStatus(gitRoot, branch, options) {
@@ -7473,20 +8421,20 @@ async function getGitSyncStatus(gitRoot, branch, options) {
7473
8421
  console.log(`[git-status] Checking sync status for branch: ${branch}`);
7474
8422
  if (fetchRemote) {
7475
8423
  console.log(`[git-status] Fetching from origin...`);
7476
- await new Promise((resolve6) => {
8424
+ await new Promise((resolve7) => {
7477
8425
  const fetchProc = spawnProcess("git", ["fetch", "origin"], { cwd: gitRoot });
7478
8426
  fetchProc.on("close", () => {
7479
8427
  console.log(`[git-status] \u2713 Fetch completed`);
7480
- resolve6();
8428
+ resolve7();
7481
8429
  });
7482
8430
  fetchProc.on("error", () => {
7483
8431
  console.log(`[git-status] Fetch error (continuing anyway)`);
7484
- resolve6();
8432
+ resolve7();
7485
8433
  });
7486
8434
  });
7487
8435
  }
7488
8436
  console.log(`[git-status] Checking status against remote tracking branch...`);
7489
- return new Promise((resolve6) => {
8437
+ return new Promise((resolve7) => {
7490
8438
  const statusProc = spawnProcess("git", ["status", "-sb"], { cwd: gitRoot });
7491
8439
  let statusOut = "";
7492
8440
  let statusErr = "";
@@ -7506,49 +8454,49 @@ async function getGitSyncStatus(gitRoot, branch, options) {
7506
8454
  const firstLine = output.split("\n")[0] ?? "";
7507
8455
  if (!firstLine.includes(branch)) {
7508
8456
  console.log(`[git-status] Branch name not found in status output, returning 'Up to date'`);
7509
- resolve6("Up to date");
8457
+ resolve7("Up to date");
7510
8458
  return;
7511
8459
  }
7512
8460
  if (!firstLine.includes("...")) {
7513
8461
  console.log(`[git-status] No upstream tracking branch detected, returning 'Up to date'`);
7514
- resolve6("Up to date");
8462
+ resolve7("Up to date");
7515
8463
  return;
7516
8464
  }
7517
8465
  if (firstLine.includes("ahead") && firstLine.includes("behind")) {
7518
8466
  console.log(`[git-status] \u2713 Status: Diverged`);
7519
- resolve6("Diverged");
8467
+ resolve7("Diverged");
7520
8468
  } else if (firstLine.includes("behind")) {
7521
8469
  console.log(`[git-status] \u2713 Status: Behind`);
7522
- resolve6("Behind");
8470
+ resolve7("Behind");
7523
8471
  } else if (firstLine.includes("ahead")) {
7524
8472
  console.log(`[git-status] \u2713 Status: Ahead`);
7525
- resolve6("Ahead");
8473
+ resolve7("Ahead");
7526
8474
  } else {
7527
8475
  console.log(`[git-status] \u2713 Status: Up to date`);
7528
- resolve6("Up to date");
8476
+ resolve7("Up to date");
7529
8477
  }
7530
8478
  });
7531
8479
  statusProc.on("error", () => {
7532
8480
  console.log(`[git-status] Error getting status, defaulting to 'Up to date'`);
7533
- resolve6("Up to date");
8481
+ resolve7("Up to date");
7534
8482
  });
7535
8483
  });
7536
8484
  }
7537
8485
  async function hasRemoteBranch(gitRoot, branch) {
7538
- return new Promise((resolve6) => {
8486
+ return new Promise((resolve7) => {
7539
8487
  const proc = spawnProcess("git", ["rev-parse", "--verify", `origin/${branch}`], {
7540
8488
  cwd: gitRoot
7541
8489
  });
7542
8490
  proc.on("close", (code) => {
7543
- resolve6(code === 0);
8491
+ resolve7(code === 0);
7544
8492
  });
7545
8493
  proc.on("error", () => {
7546
- resolve6(false);
8494
+ resolve7(false);
7547
8495
  });
7548
8496
  });
7549
8497
  }
7550
8498
  function getDefaultBranch(gitRoot) {
7551
- return new Promise((resolve6) => {
8499
+ return new Promise((resolve7) => {
7552
8500
  const proc = spawnProcess("git", ["symbolic-ref", "refs/remotes/origin/HEAD"], {
7553
8501
  cwd: gitRoot
7554
8502
  });
@@ -7561,12 +8509,12 @@ function getDefaultBranch(gitRoot) {
7561
8509
  proc.on("close", (code) => {
7562
8510
  if (code === 0 && out.trim()) {
7563
8511
  const match = out.trim().match(/refs\/remotes\/origin\/(.+)/);
7564
- resolve6(match ? match[1] : null);
8512
+ resolve7(match ? match[1] : null);
7565
8513
  } else {
7566
- resolve6(null);
8514
+ resolve7(null);
7567
8515
  }
7568
8516
  });
7569
- proc.on("error", () => resolve6(null));
8517
+ proc.on("error", () => resolve7(null));
7570
8518
  });
7571
8519
  }
7572
8520
  async function findDefaultBranch(gitRoot) {
@@ -7586,7 +8534,7 @@ async function resolvePrCommitLogRange(gitRoot, baseBranch, currentBranch) {
7586
8534
  }
7587
8535
  async function getPrBranchDiff(gitRoot, baseBranch) {
7588
8536
  const diffRange = await hasRemoteBranch(gitRoot, baseBranch) ? `origin/${baseBranch}...HEAD` : `${baseBranch}...HEAD`;
7589
- return new Promise((resolve6) => {
8537
+ return new Promise((resolve7) => {
7590
8538
  const proc = spawnProcess("git", ["diff", diffRange], { cwd: gitRoot });
7591
8539
  let out = "";
7592
8540
  if (proc.stdout) {
@@ -7594,13 +8542,13 @@ async function getPrBranchDiff(gitRoot, baseBranch) {
7594
8542
  out += d.toString();
7595
8543
  });
7596
8544
  }
7597
- proc.on("close", () => resolve6(out.trim()));
7598
- proc.on("error", () => resolve6(""));
8545
+ proc.on("close", () => resolve7(out.trim()));
8546
+ proc.on("error", () => resolve7(""));
7599
8547
  });
7600
8548
  }
7601
8549
  async function getCommitLog(gitRoot, baseBranch, currentBranch) {
7602
8550
  const range = await resolvePrCommitLogRange(gitRoot, baseBranch, currentBranch);
7603
- return new Promise((resolve6) => {
8551
+ return new Promise((resolve7) => {
7604
8552
  const proc = spawnProcess("git", ["log", "--oneline", range], {
7605
8553
  cwd: gitRoot
7606
8554
  });
@@ -7610,13 +8558,13 @@ async function getCommitLog(gitRoot, baseBranch, currentBranch) {
7610
8558
  out += d.toString();
7611
8559
  });
7612
8560
  }
7613
- proc.on("close", () => resolve6(out.trim()));
7614
- proc.on("error", () => resolve6(""));
8561
+ proc.on("close", () => resolve7(out.trim()));
8562
+ proc.on("error", () => resolve7(""));
7615
8563
  });
7616
8564
  }
7617
8565
  async function getDetailedCommits(gitRoot, baseBranch, currentBranch) {
7618
8566
  const range = await resolvePrCommitLogRange(gitRoot, baseBranch, currentBranch);
7619
- return new Promise((resolve6) => {
8567
+ return new Promise((resolve7) => {
7620
8568
  const proc = spawnProcess("git", ["log", range, "--format=%h %s%n%b%n---"], { cwd: gitRoot });
7621
8569
  let out = "";
7622
8570
  if (proc.stdout) {
@@ -7625,14 +8573,14 @@ async function getDetailedCommits(gitRoot, baseBranch, currentBranch) {
7625
8573
  });
7626
8574
  }
7627
8575
  proc.on("close", () => {
7628
- resolve6(out.length > 2e3 ? out.substring(0, 2e3) + "\n...(more commits)" : out);
8576
+ resolve7(out.length > 2e3 ? out.substring(0, 2e3) + "\n...(more commits)" : out);
7629
8577
  });
7630
- proc.on("error", () => resolve6(""));
8578
+ proc.on("error", () => resolve7(""));
7631
8579
  });
7632
8580
  }
7633
8581
  function getGitLog(gitRoot, limit) {
7634
8582
  return new Promise(
7635
- (resolve6, reject) => {
8583
+ (resolve7, reject) => {
7636
8584
  const proc = spawnProcess(
7637
8585
  "git",
7638
8586
  ["log", "--oneline", "-n", limit.toString(), "--format=%H|%s|%an|%ai"],
@@ -7662,7 +8610,7 @@ function getGitLog(gitRoot, limit) {
7662
8610
  date: parts[3] || ""
7663
8611
  };
7664
8612
  });
7665
- resolve6(parsed);
8613
+ resolve7(parsed);
7666
8614
  } else {
7667
8615
  reject(new Error(err || "Failed to get git log"));
7668
8616
  }
@@ -7672,27 +8620,27 @@ function getGitLog(gitRoot, limit) {
7672
8620
  );
7673
8621
  }
7674
8622
  function stageAllChanges(gitRoot) {
7675
- return new Promise((resolve6, reject) => {
8623
+ return new Promise((resolve7, reject) => {
7676
8624
  const proc = spawnProcess("git", ["add", "-A"], { cwd: gitRoot });
7677
8625
  proc.on("close", (code) => {
7678
- if (code === 0) resolve6();
8626
+ if (code === 0) resolve7();
7679
8627
  else reject(new Error("Failed to stage changes"));
7680
8628
  });
7681
8629
  proc.on("error", reject);
7682
8630
  });
7683
8631
  }
7684
8632
  function commitChanges(gitRoot, message) {
7685
- return new Promise((resolve6, reject) => {
8633
+ return new Promise((resolve7, reject) => {
7686
8634
  const proc = spawnProcess("git", ["commit", "-m", message], { cwd: gitRoot });
7687
8635
  proc.on("close", (code) => {
7688
- if (code === 0) resolve6();
8636
+ if (code === 0) resolve7();
7689
8637
  else reject(new Error("Failed to commit changes"));
7690
8638
  });
7691
8639
  proc.on("error", reject);
7692
8640
  });
7693
8641
  }
7694
8642
  function pushToOrigin(gitRoot, currentBranch) {
7695
- return new Promise((resolve6, reject) => {
8643
+ return new Promise((resolve7, reject) => {
7696
8644
  console.log(`[git-push] Executing: git push -u origin ${currentBranch}`);
7697
8645
  const proc = spawnProcess("git", ["push", "-u", "origin", currentBranch], { cwd: gitRoot });
7698
8646
  let err = "";
@@ -7713,7 +8661,7 @@ function pushToOrigin(gitRoot, currentBranch) {
7713
8661
  console.log(`[git-push] git push exited with code: ${code}`);
7714
8662
  if (code === 0) {
7715
8663
  console.log(`[git-push] \u2713 Push completed successfully`);
7716
- resolve6();
8664
+ resolve7();
7717
8665
  } else {
7718
8666
  console.log(`[git-push] \u2717 Push failed with error: ${err || "Unknown error"}`);
7719
8667
  reject(new Error(err || "Failed to push changes"));
@@ -7728,7 +8676,7 @@ function pushToOrigin(gitRoot, currentBranch) {
7728
8676
  });
7729
8677
  }
7730
8678
  function fetchFromOrigin(gitRoot) {
7731
- return new Promise((resolve6, reject) => {
8679
+ return new Promise((resolve7, reject) => {
7732
8680
  const proc = spawnProcess("git", ["fetch", "origin"], { cwd: gitRoot });
7733
8681
  let err = "";
7734
8682
  if (proc.stderr) {
@@ -7737,14 +8685,14 @@ function fetchFromOrigin(gitRoot) {
7737
8685
  });
7738
8686
  }
7739
8687
  proc.on("close", (code) => {
7740
- if (code === 0) resolve6();
8688
+ if (code === 0) resolve7();
7741
8689
  else reject(new Error(err || "Failed to fetch from origin"));
7742
8690
  });
7743
8691
  proc.on("error", reject);
7744
8692
  });
7745
8693
  }
7746
8694
  function pullFromOrigin(gitRoot) {
7747
- return new Promise((resolve6, reject) => {
8695
+ return new Promise((resolve7, reject) => {
7748
8696
  const proc = spawnProcess("git", ["pull"], { cwd: gitRoot });
7749
8697
  let err = "";
7750
8698
  if (proc.stderr) {
@@ -7753,14 +8701,14 @@ function pullFromOrigin(gitRoot) {
7753
8701
  });
7754
8702
  }
7755
8703
  proc.on("close", (code) => {
7756
- if (code === 0) resolve6();
8704
+ if (code === 0) resolve7();
7757
8705
  else reject(new Error(err || "Failed to pull from origin"));
7758
8706
  });
7759
8707
  proc.on("error", reject);
7760
8708
  });
7761
8709
  }
7762
8710
  function createGitHubRepo(gitRoot, repoName, description, isPrivate) {
7763
- return new Promise((resolve6, reject) => {
8711
+ return new Promise((resolve7, reject) => {
7764
8712
  const args2 = ["repo", "create"];
7765
8713
  if (repoName) {
7766
8714
  args2.push(repoName);
@@ -7791,7 +8739,7 @@ function createGitHubRepo(gitRoot, repoName, description, isPrivate) {
7791
8739
  if (code === 0) {
7792
8740
  invalidateRepoInfoCache(gitRoot);
7793
8741
  const urlMatch = out.match(/https:\/\/[^\s]+/);
7794
- resolve6(urlMatch ? urlMatch[0] : out.trim());
8742
+ resolve7(urlMatch ? urlMatch[0] : out.trim());
7795
8743
  } else {
7796
8744
  reject(new Error(err || "Failed to create repository"));
7797
8745
  }
@@ -7803,7 +8751,7 @@ function createGitHubRepo(gitRoot, repoName, description, isPrivate) {
7803
8751
  });
7804
8752
  }
7805
8753
  function createPullRequest(gitRoot, title, description) {
7806
- return new Promise((resolve6, reject) => {
8754
+ return new Promise((resolve7, reject) => {
7807
8755
  const args2 = ["pr", "create", "--title", title ?? "", "--body", description ?? ""];
7808
8756
  console.log(`[git-create-pr] Executing: gh ${args2.join(" ")}`);
7809
8757
  const proc = spawnProcess("gh", args2, { cwd: gitRoot });
@@ -7828,7 +8776,7 @@ function createPullRequest(gitRoot, title, description) {
7828
8776
  const urlMatch = out.match(/https:\/\/[^\s]+/);
7829
8777
  const prUrl = urlMatch ? urlMatch[0] : out.trim();
7830
8778
  console.log(`[git-create-pr] \u2713 PR URL extracted: ${prUrl}`);
7831
- resolve6(prUrl);
8779
+ resolve7(prUrl);
7832
8780
  } else {
7833
8781
  console.log(`[git-create-pr] \u2717 gh pr create failed with error: ${err || "Unknown error"}`);
7834
8782
  reject(new Error(err || "Failed to create PR"));
@@ -7867,7 +8815,7 @@ async function getPullRequestStatus(gitRoot) {
7867
8815
  }
7868
8816
  throw new Error(result.stderr || "Failed to check PR status");
7869
8817
  }
7870
- return new Promise((resolve6, reject) => {
8818
+ return new Promise((resolve7, reject) => {
7871
8819
  const args2 = ["pr", "view", "--json", "url,state"];
7872
8820
  const proc = spawnProcess("gh", args2, { cwd: gitRoot });
7873
8821
  let out = "";
@@ -7886,7 +8834,7 @@ async function getPullRequestStatus(gitRoot) {
7886
8834
  if (code === 0) {
7887
8835
  try {
7888
8836
  const prData = JSON.parse(out.trim());
7889
- resolve6({
8837
+ resolve7({
7890
8838
  exists: true,
7891
8839
  url: prData.url,
7892
8840
  state: prData.state
@@ -7896,7 +8844,7 @@ async function getPullRequestStatus(gitRoot) {
7896
8844
  }
7897
8845
  } else {
7898
8846
  if (err.includes("no pull requests") || err.includes("not found")) {
7899
- resolve6({ exists: false });
8847
+ resolve7({ exists: false });
7900
8848
  } else {
7901
8849
  reject(new Error(err || "Failed to check PR status"));
7902
8850
  }
@@ -8083,7 +9031,7 @@ function buildCrossHostRedirectResult(redirectUrl) {
8083
9031
 
8084
9032
  // src/features/web-fetch/web-fetch.client.ts
8085
9033
  import { mkdir, writeFile as writeFile2 } from "fs/promises";
8086
- import { join as join11, extname as extname3 } from "path";
9034
+ import { join as join13, extname as extname3 } from "path";
8087
9035
  import { createHash as createHash2 } from "crypto";
8088
9036
 
8089
9037
  // src/features/web-fetch/web-fetch-cache.ts
@@ -8188,7 +9136,7 @@ async function readResponseBody(response, signal) {
8188
9136
  return { body, bytes: body.length };
8189
9137
  }
8190
9138
  async function saveBinaryContent(body, contentType, url) {
8191
- const downloadsDir = join11(getDataDir(), "fetch-downloads");
9139
+ const downloadsDir = join13(getDataDir(), "fetch-downloads");
8192
9140
  await mkdir(downloadsDir, { recursive: true });
8193
9141
  const hash = createHash2("sha256").update(url).digest("hex").slice(0, 12);
8194
9142
  let extension = extname3(new URL(url).pathname);
@@ -8203,7 +9151,7 @@ async function saveBinaryContent(body, contentType, url) {
8203
9151
  if (!extension) {
8204
9152
  extension = ".bin";
8205
9153
  }
8206
- const filePath = join11(downloadsDir, `${hash}${extension}`);
9154
+ const filePath = join13(downloadsDir, `${hash}${extension}`);
8207
9155
  await writeFile2(filePath, body);
8208
9156
  return filePath;
8209
9157
  }
@@ -8364,9 +9312,9 @@ function truncateFetchUrl(url, maxLength = 80) {
8364
9312
  }
8365
9313
 
8366
9314
  // src/tools/fetch.ts
8367
- var fetchSchema = Type17.Object({
8368
- url: Type17.String({ description: "Fully qualified URL to fetch (http or https)" }),
8369
- prompt: Type17.String({
9315
+ var fetchSchema = Type18.Object({
9316
+ url: Type18.String({ description: "Fully qualified URL to fetch (http or https)" }),
9317
+ prompt: Type18.String({
8370
9318
  description: "What to extract or summarize from the page content"
8371
9319
  })
8372
9320
  });
@@ -8446,13 +9394,187 @@ function createFetchTool(options) {
8446
9394
  });
8447
9395
  }
8448
9396
  const details = {
8449
- url: fetchResult.finalUrl,
8450
- bytes: fetchResult.bytes,
8451
- code: fetchResult.code,
8452
- codeText: fetchResult.codeText,
8453
- durationMs: fetchResult.durationMs
9397
+ url: fetchResult.finalUrl,
9398
+ bytes: fetchResult.bytes,
9399
+ code: fetchResult.code,
9400
+ codeText: fetchResult.codeText,
9401
+ durationMs: fetchResult.durationMs
9402
+ };
9403
+ const output = JSON.stringify({ ...details, result });
9404
+ return {
9405
+ content: [{ type: "text", text: output }],
9406
+ details
9407
+ };
9408
+ });
9409
+ }
9410
+ };
9411
+ }
9412
+
9413
+ // src/tools/web-search.ts
9414
+ import { Type as Type19 } from "@sinclair/typebox";
9415
+
9416
+ // src/features/web-search/web-search.client.ts
9417
+ var WEB_SEARCH_API = "https://api.webnative.dev/websearch";
9418
+ function clampNumResults(numResults) {
9419
+ if (numResults === void 0) return 10;
9420
+ return Math.min(100, Math.max(1, Math.round(numResults)));
9421
+ }
9422
+ function normalizeResultItem(raw) {
9423
+ const highlights = Array.isArray(raw.highlights) ? raw.highlights.filter((item) => typeof item === "string") : void 0;
9424
+ return {
9425
+ title: typeof raw.title === "string" ? raw.title : "Untitled",
9426
+ url: typeof raw.url === "string" ? raw.url : typeof raw.id === "string" ? raw.id : "",
9427
+ publishedDate: typeof raw.publishedDate === "string" || raw.publishedDate === null ? raw.publishedDate : void 0,
9428
+ author: typeof raw.author === "string" || raw.author === null ? raw.author : void 0,
9429
+ highlights
9430
+ };
9431
+ }
9432
+ function parseSearchErrorMessage(status, body) {
9433
+ try {
9434
+ const parsed = JSON.parse(body);
9435
+ const message = typeof parsed.error === "string" && parsed.error || typeof parsed.message === "string" && parsed.message;
9436
+ if (message) return `Web search failed (${status}): ${message}`;
9437
+ } catch {
9438
+ }
9439
+ return `Web search failed (${status}): ${body || "Unknown error"}`;
9440
+ }
9441
+ async function searchWeb(options) {
9442
+ const payload = {
9443
+ query: options.query,
9444
+ type: options.type ?? "auto",
9445
+ numResults: clampNumResults(options.numResults),
9446
+ contents: {
9447
+ highlights: true
9448
+ }
9449
+ };
9450
+ if (options.includeDomains?.length) {
9451
+ payload.includeDomains = options.includeDomains;
9452
+ }
9453
+ if (options.excludeDomains?.length) {
9454
+ payload.excludeDomains = options.excludeDomains;
9455
+ }
9456
+ const response = await fetch(WEB_SEARCH_API, {
9457
+ method: "POST",
9458
+ headers: {
9459
+ "Content-Type": "application/json"
9460
+ },
9461
+ body: JSON.stringify(payload),
9462
+ signal: options.signal
9463
+ });
9464
+ const body = await response.text();
9465
+ if (!response.ok) {
9466
+ throw new Error(parseSearchErrorMessage(response.status, body));
9467
+ }
9468
+ let parsed;
9469
+ try {
9470
+ parsed = JSON.parse(body);
9471
+ } catch {
9472
+ throw new Error("Web search failed: invalid JSON response");
9473
+ }
9474
+ const rawResults = Array.isArray(parsed.results) ? parsed.results : [];
9475
+ const results = rawResults.filter((item) => item !== null && typeof item === "object").map(normalizeResultItem);
9476
+ return {
9477
+ requestId: typeof parsed.requestId === "string" ? parsed.requestId : void 0,
9478
+ searchType: typeof parsed.searchType === "string" ? parsed.searchType : void 0,
9479
+ results
9480
+ };
9481
+ }
9482
+
9483
+ // src/tools/web-search.ts
9484
+ var searchTypeSchema = Type19.Union([
9485
+ Type19.Literal("auto"),
9486
+ Type19.Literal("fast"),
9487
+ Type19.Literal("instant"),
9488
+ Type19.Literal("deep-lite"),
9489
+ Type19.Literal("deep"),
9490
+ Type19.Literal("deep-reasoning")
9491
+ ]);
9492
+ var webSearchSchema = Type19.Object({
9493
+ query: Type19.String({
9494
+ description: "Natural language search query (e.g. 'Next.js route handler authentication example')"
9495
+ }),
9496
+ numResults: Type19.Optional(
9497
+ Type19.Number({
9498
+ description: "Number of results to return (1-100, default 10)",
9499
+ minimum: 1,
9500
+ maximum: 100
9501
+ })
9502
+ ),
9503
+ type: Type19.Optional(
9504
+ Type19.Union([searchTypeSchema], {
9505
+ description: "Search depth/latency tradeoff. Use auto (default) for most queries; fast/instant for low latency; deep variants for research."
9506
+ })
9507
+ ),
9508
+ includeDomains: Type19.Optional(
9509
+ Type19.Array(Type19.String(), {
9510
+ description: "Only return results from these domains (e.g. ['docs.github.com', 'nodejs.org'])"
9511
+ })
9512
+ ),
9513
+ excludeDomains: Type19.Optional(
9514
+ Type19.Array(Type19.String(), {
9515
+ description: "Exclude results from these domains"
9516
+ })
9517
+ )
9518
+ });
9519
+ function createWebSearchTool() {
9520
+ return {
9521
+ name: "web_search",
9522
+ label: "web_search",
9523
+ description: "Search the web for current information, documentation, API references, and code examples. Returns titles, URLs, and query-relevant highlights for each result. Use fetch to read a specific URL in full after finding it.",
9524
+ parameters: webSearchSchema,
9525
+ execute: async (_toolCallId, { query, numResults, type, includeDomains, excludeDomains }, signal, onUpdate) => {
9526
+ return withAbortSignal(signal, async (isAborted) => {
9527
+ onUpdate?.({
9528
+ content: [{ type: "text", text: `Searching the web for "${query}"\u2026` }],
9529
+ details: void 0
9530
+ });
9531
+ let response;
9532
+ try {
9533
+ response = await searchWeb({
9534
+ query,
9535
+ numResults,
9536
+ type,
9537
+ includeDomains,
9538
+ excludeDomains,
9539
+ signal
9540
+ });
9541
+ } catch (err) {
9542
+ const message = err instanceof Error ? err.message : String(err);
9543
+ return {
9544
+ content: [{ type: "text", text: message }],
9545
+ details: void 0
9546
+ };
9547
+ }
9548
+ if (isAborted()) {
9549
+ return { content: [], details: void 0 };
9550
+ }
9551
+ const payload = {
9552
+ query,
9553
+ searchType: response.searchType ?? type ?? "auto",
9554
+ requestId: response.requestId,
9555
+ resultCount: response.results.length,
9556
+ results: response.results.map((result) => ({
9557
+ title: result.title,
9558
+ url: result.url,
9559
+ publishedDate: result.publishedDate ?? void 0,
9560
+ author: result.author ?? void 0,
9561
+ highlights: result.highlights ?? []
9562
+ }))
9563
+ };
9564
+ const serialized = JSON.stringify(payload, null, 2);
9565
+ const truncated = truncateHead(serialized, { maxLines: 500, maxBytes: 50 * 1024 });
9566
+ let output = truncated.content;
9567
+ if (truncated.truncated) {
9568
+ output += `
9569
+
9570
+ [Output truncated: showing ${truncated.outputLines}/${truncated.totalLines} lines]`;
9571
+ }
9572
+ const details = {
9573
+ query,
9574
+ searchType: payload.searchType,
9575
+ resultCount: payload.resultCount,
9576
+ requestId: response.requestId
8454
9577
  };
8455
- const output = JSON.stringify({ ...details, result });
8456
9578
  return {
8457
9579
  content: [{ type: "text", text: output }],
8458
9580
  details
@@ -8463,13 +9585,13 @@ function createFetchTool(options) {
8463
9585
  }
8464
9586
 
8465
9587
  // src/tools/tool-search.ts
8466
- import { Type as Type18 } from "@sinclair/typebox";
8467
- var toolSearchSchema = Type18.Object({
8468
- query: Type18.String({
9588
+ import { Type as Type20 } from "@sinclair/typebox";
9589
+ var toolSearchSchema = Type20.Object({
9590
+ query: Type20.String({
8469
9591
  description: 'Search query to find tools. Use "select:name1,name2" to fetch exact tools by name, or keywords to search by description.'
8470
9592
  }),
8471
- max_results: Type18.Optional(
8472
- Type18.Number({
9593
+ max_results: Type20.Optional(
9594
+ Type20.Number({
8473
9595
  description: "Maximum number of results to return (default: 5)"
8474
9596
  })
8475
9597
  )
@@ -8597,6 +9719,7 @@ var toolRegistry = [
8597
9719
  { name: "agent", enabled: true, displayName: "Subagent", activeDisplayName: "Subagent" },
8598
9720
  { name: "ask_user", enabled: true, displayName: "Ask", activeDisplayName: "Asking" },
8599
9721
  { name: "bash", enabled: true, displayName: "Ran", activeDisplayName: "Running" },
9722
+ { name: "browser", enabled: true, displayName: "Browser", activeDisplayName: "Browsing" },
8600
9723
  {
8601
9724
  name: "code_search",
8602
9725
  enabled: false,
@@ -8648,6 +9771,12 @@ var toolRegistry = [
8648
9771
  displayName: "Find Tools",
8649
9772
  activeDisplayName: "Finding Tools"
8650
9773
  },
9774
+ {
9775
+ name: "web_search",
9776
+ enabled: true,
9777
+ displayName: "Web Search",
9778
+ activeDisplayName: "Searching Web"
9779
+ },
8651
9780
  { name: "write", enabled: true, displayName: "Write", activeDisplayName: "Writing" }
8652
9781
  ];
8653
9782
  var registryByName = new Map(toolRegistry.map((entry) => [entry.name, entry]));
@@ -8671,6 +9800,9 @@ function resolveRegistryEntry(toolName) {
8671
9800
  if (lowerName === "fetch" || lowerName.includes("web_fetch")) {
8672
9801
  return registryByName.get("fetch");
8673
9802
  }
9803
+ if (lowerName === "web_search") {
9804
+ return registryByName.get("web_search");
9805
+ }
8674
9806
  if (lowerName.includes("read")) {
8675
9807
  return registryByName.get("read");
8676
9808
  }
@@ -8774,7 +9906,7 @@ var WebFetchPermissionGate = class {
8774
9906
  wildcardOptions: buildWildcardOptions2(normalized)
8775
9907
  };
8776
9908
  this.config.onPermissionRequired?.(request);
8777
- return new Promise((resolve6, reject) => {
9909
+ return new Promise((resolve7, reject) => {
8778
9910
  const cleanup = () => {
8779
9911
  pendingPermissions.delete(toolCallId);
8780
9912
  signal?.removeEventListener("abort", onAbort);
@@ -8792,7 +9924,7 @@ var WebFetchPermissionGate = class {
8792
9924
  pendingPermissions.set(toolCallId, {
8793
9925
  resolve: (decision) => {
8794
9926
  cleanup();
8795
- void this.applyDecision(decision, normalized).then(() => resolve6(decision));
9927
+ void this.applyDecision(decision, normalized).then(() => resolve7(decision));
8796
9928
  },
8797
9929
  reject: (error) => {
8798
9930
  cleanup();
@@ -8932,12 +10064,16 @@ async function createCodingTools(cwd, options) {
8932
10064
  createFetchTool({
8933
10065
  metadataManager: options?.metadataManager,
8934
10066
  webFetchPermissionGate: options?.webFetchPermissionGate
8935
- })
10067
+ }),
10068
+ createWebSearchTool()
8936
10069
  ]);
8937
10070
  if (options?.metadataManager && isToolEnabled("generate_image")) {
8938
10071
  coreTools.push(createGenerateImageTool({ metadataManager: options.metadataManager, cwd }));
8939
10072
  }
8940
- const optionalTools = filterEnabledTools([createFindImagesTool(cwd)]);
10073
+ const optionalTools = filterEnabledTools([
10074
+ createFindImagesTool({ cwd, threadId: options?.threadId }),
10075
+ createBrowserTool({ cwd, threadId: options?.threadId })
10076
+ ]);
8941
10077
  if (options?.skills && options.skills.length > 0) {
8942
10078
  optionalTools.push(
8943
10079
  ...filterEnabledTools([
@@ -9016,7 +10152,7 @@ function createAllTools(cwd, options) {
9016
10152
  });
9017
10153
  }
9018
10154
  if (isToolEnabled("find_images")) {
9019
- tools.find_images = createFindImagesTool(cwd);
10155
+ tools.find_images = createFindImagesTool({ cwd, threadId: options?.threadId });
9020
10156
  }
9021
10157
  if (options?.threadId) {
9022
10158
  for (const [name, tool] of Object.entries(tools)) {
@@ -9050,10 +10186,10 @@ async function* loadCodingToolsWithMcpStatus(cwd, options) {
9050
10186
  if (completed) {
9051
10187
  break;
9052
10188
  }
9053
- await new Promise((resolve6) => {
9054
- notify = resolve6;
10189
+ await new Promise((resolve7) => {
10190
+ notify = resolve7;
9055
10191
  if (completed) {
9056
- resolve6();
10192
+ resolve7();
9057
10193
  }
9058
10194
  });
9059
10195
  notify = null;
@@ -9065,7 +10201,7 @@ async function* loadCodingToolsWithMcpStatus(cwd, options) {
9065
10201
  }
9066
10202
 
9067
10203
  // src/tools/agent-tool.ts
9068
- import { Type as Type19 } from "@sinclair/typebox";
10204
+ import { Type as Type21 } from "@sinclair/typebox";
9069
10205
 
9070
10206
  // src/agent/agent.subagent-executor.ts
9071
10207
  import { Agent } from "@earendil-works/pi-agent-core";
@@ -9183,7 +10319,7 @@ var EventQueue = class {
9183
10319
  content: resultContent,
9184
10320
  isError: event.isError
9185
10321
  };
9186
- if (event.toolName === "generate_image") {
10322
+ if (event.toolName === "generate_image" || event.toolName === "find_images" || event.toolName === "browser") {
9187
10323
  toolResultBlock.content = stripGeneratedImageText(resultContent);
9188
10324
  const details = event.result?.details;
9189
10325
  if (details?.imageUrl) {
@@ -9224,6 +10360,7 @@ var EventQueue = class {
9224
10360
  */
9225
10361
  push(event) {
9226
10362
  this.queue.push(event);
10363
+ this.notify();
9227
10364
  }
9228
10365
  /**
9229
10366
  * Removes and returns the next event from the queue
@@ -9389,8 +10526,8 @@ async function executeSubagent(options) {
9389
10526
  const e = eventQueue.shift();
9390
10527
  if (e) onEvent(e);
9391
10528
  } else {
9392
- await new Promise((resolve6) => {
9393
- eventQueue.setResolver(resolve6);
10529
+ await new Promise((resolve7) => {
10530
+ eventQueue.setResolver(resolve7);
9394
10531
  });
9395
10532
  }
9396
10533
  }
@@ -9414,15 +10551,15 @@ async function executeSubagent(options) {
9414
10551
  }
9415
10552
 
9416
10553
  // src/tools/agent-tool.ts
9417
- var agentToolSchema = Type19.Object({
9418
- prompt: Type19.String({ description: "The task or question for the subagent to work on" }),
9419
- agentName: Type19.Optional(
9420
- Type19.String({
10554
+ var agentToolSchema = Type21.Object({
10555
+ prompt: Type21.String({ description: "The task or question for the subagent to work on" }),
10556
+ agentName: Type21.Optional(
10557
+ Type21.String({
9421
10558
  description: "Name of a defined agent (from AGENT.md) to use. If omitted, a general-purpose subagent is created."
9422
10559
  })
9423
10560
  ),
9424
- description: Type19.Optional(
9425
- Type19.String({
10561
+ description: Type21.Optional(
10562
+ Type21.String({
9426
10563
  description: "A short (3-5 word) label describing what the subagent will do"
9427
10564
  })
9428
10565
  )
@@ -9450,7 +10587,7 @@ function createAgentTool(options) {
9450
10587
  label: "agent",
9451
10588
  description: "Launch a specialized subagent to handle a focused subtask in an isolated context. The subagent runs autonomously and returns its result. Use this to delegate research, analysis, or parallel work without polluting the current context. If agentName is provided, that agent's system prompt and tool restrictions are applied.",
9452
10589
  parameters: agentToolSchema,
9453
- execute: async (_toolCallId, { prompt, agentName, description }) => {
10590
+ execute: async (toolCallId, { prompt, agentName, description }) => {
9454
10591
  const agentDef = agentName ? agents.find((a) => a.name === agentName && !a.disableModelInvocation) : void 0;
9455
10592
  if (agentName && !agentDef) {
9456
10593
  const available = agents.filter((a) => !a.disableModelInvocation).map((a) => a.name).join(", ");
@@ -9474,6 +10611,7 @@ function createAgentTool(options) {
9474
10611
  );
9475
10612
  onEvent({
9476
10613
  type: "subagent_start",
10614
+ toolCallId,
9477
10615
  subagentName: label,
9478
10616
  subagentDescription: agentDef?.description,
9479
10617
  subagentPrompt: prompt
@@ -9490,6 +10628,7 @@ function createAgentTool(options) {
9490
10628
  onEvent: (event) => {
9491
10629
  onEvent({
9492
10630
  type: "subagent_event",
10631
+ toolCallId,
9493
10632
  subagentName: label,
9494
10633
  subagentEvent: event
9495
10634
  });
@@ -9501,6 +10640,7 @@ function createAgentTool(options) {
9501
10640
  console.error(`[agent-tool] Subagent '${label}' failed:`, message);
9502
10641
  onEvent({
9503
10642
  type: "subagent_complete",
10643
+ toolCallId,
9504
10644
  subagentName: label,
9505
10645
  subagentResult: `Error: ${message}`
9506
10646
  });
@@ -9511,6 +10651,7 @@ function createAgentTool(options) {
9511
10651
  }
9512
10652
  onEvent({
9513
10653
  type: "subagent_complete",
10654
+ toolCallId,
9514
10655
  subagentName: label,
9515
10656
  subagentResult: result
9516
10657
  });
@@ -9529,7 +10670,7 @@ import { readFileSync as readFileSync5 } from "fs";
9529
10670
 
9530
10671
  // src/project-analyzer.ts
9531
10672
  import { readFileSync as readFileSync4, existsSync as existsSync9 } from "fs";
9532
- import { join as join12 } from "path";
10673
+ import { join as join14 } from "path";
9533
10674
  var ProjectAnalyzer = class {
9534
10675
  projectPath;
9535
10676
  constructor(projectPath = process.cwd()) {
@@ -9540,7 +10681,7 @@ var ProjectAnalyzer = class {
9540
10681
  return this.generateDescription(info);
9541
10682
  }
9542
10683
  getProjectInfo() {
9543
- const packageJsonPath = join12(this.projectPath, "package.json");
10684
+ const packageJsonPath = join14(this.projectPath, "package.json");
9544
10685
  const info = { description: "" };
9545
10686
  if (existsSync9(packageJsonPath)) {
9546
10687
  const packageJson = JSON.parse(readFileSync4(packageJsonPath, "utf-8"));
@@ -9582,15 +10723,15 @@ var ProjectAnalyzer = class {
9582
10723
  }
9583
10724
  }
9584
10725
  detectBuildTool(_scripts = {}, deps, info) {
9585
- if (deps.vite || existsSync9(join12(this.projectPath, "vite.config.js")) || existsSync9(join12(this.projectPath, "vite.config.ts"))) {
10726
+ if (deps.vite || existsSync9(join14(this.projectPath, "vite.config.js")) || existsSync9(join14(this.projectPath, "vite.config.ts"))) {
9586
10727
  info.buildTool = "Vite";
9587
10728
  return;
9588
10729
  }
9589
- if (deps.webpack || existsSync9(join12(this.projectPath, "webpack.config.js"))) {
10730
+ if (deps.webpack || existsSync9(join14(this.projectPath, "webpack.config.js"))) {
9590
10731
  info.buildTool = "Webpack";
9591
10732
  return;
9592
10733
  }
9593
- if (deps.rollup || existsSync9(join12(this.projectPath, "rollup.config.js"))) {
10734
+ if (deps.rollup || existsSync9(join14(this.projectPath, "rollup.config.js"))) {
9594
10735
  info.buildTool = "Rollup";
9595
10736
  return;
9596
10737
  }
@@ -9640,40 +10781,40 @@ var ProjectAnalyzer = class {
9640
10781
  info.uiLibraries = [...new Set(uiLibs)].filter(Boolean);
9641
10782
  }
9642
10783
  detectProjectType(info) {
9643
- if (existsSync9(join12(this.projectPath, ".xcodeproj")) || existsSync9(join12(this.projectPath, ".xcworkspace")) || existsSync9(join12(this.projectPath, "project.pbxproj"))) {
10784
+ if (existsSync9(join14(this.projectPath, ".xcodeproj")) || existsSync9(join14(this.projectPath, ".xcworkspace")) || existsSync9(join14(this.projectPath, "project.pbxproj"))) {
9644
10785
  info.projectType = "Xcode";
9645
- } else if (existsSync9(join12(this.projectPath, "build.gradle")) || existsSync9(join12(this.projectPath, "build.gradle.kts")) || existsSync9(join12(this.projectPath, "app/build.gradle")) || existsSync9(join12(this.projectPath, "settings.gradle"))) {
10786
+ } else if (existsSync9(join14(this.projectPath, "build.gradle")) || existsSync9(join14(this.projectPath, "build.gradle.kts")) || existsSync9(join14(this.projectPath, "app/build.gradle")) || existsSync9(join14(this.projectPath, "settings.gradle"))) {
9646
10787
  info.projectType = "Android Studio";
9647
- } else if (existsSync9(join12(this.projectPath, "pubspec.yaml"))) {
10788
+ } else if (existsSync9(join14(this.projectPath, "pubspec.yaml"))) {
9648
10789
  info.projectType = "Flutter";
9649
- } else if (existsSync9(join12(this.projectPath, "go.mod"))) {
10790
+ } else if (existsSync9(join14(this.projectPath, "go.mod"))) {
9650
10791
  info.projectType = "Go";
9651
- } else if (existsSync9(join12(this.projectPath, "Cargo.toml"))) {
10792
+ } else if (existsSync9(join14(this.projectPath, "Cargo.toml"))) {
9652
10793
  info.projectType = "Rust";
9653
- } else if (existsSync9(join12(this.projectPath, "requirements.txt")) || existsSync9(join12(this.projectPath, "pyproject.toml")) || existsSync9(join12(this.projectPath, "setup.py"))) {
10794
+ } else if (existsSync9(join14(this.projectPath, "requirements.txt")) || existsSync9(join14(this.projectPath, "pyproject.toml")) || existsSync9(join14(this.projectPath, "setup.py"))) {
9654
10795
  info.projectType = "Python";
9655
- } else if (existsSync9(join12(this.projectPath, "Gemfile"))) {
10796
+ } else if (existsSync9(join14(this.projectPath, "Gemfile"))) {
9656
10797
  info.projectType = "Ruby";
9657
- } else if (existsSync9(join12(this.projectPath, "composer.json"))) {
10798
+ } else if (existsSync9(join14(this.projectPath, "composer.json"))) {
9658
10799
  info.projectType = "PHP";
9659
- } else if (existsSync9(join12(this.projectPath, "pom.xml")) || existsSync9(join12(this.projectPath, "build.xml"))) {
10800
+ } else if (existsSync9(join14(this.projectPath, "pom.xml")) || existsSync9(join14(this.projectPath, "build.xml"))) {
9660
10801
  info.projectType = "Java";
9661
- } else if (existsSync9(join12(this.projectPath, ".csproj")) || existsSync9(join12(this.projectPath, "project.json"))) {
10802
+ } else if (existsSync9(join14(this.projectPath, ".csproj")) || existsSync9(join14(this.projectPath, "project.json"))) {
9662
10803
  info.projectType = ".NET";
9663
10804
  }
9664
10805
  }
9665
10806
  detectPackageManager(info) {
9666
- if (existsSync9(join12(this.projectPath, "bun.lockb"))) {
10807
+ if (existsSync9(join14(this.projectPath, "bun.lockb"))) {
9667
10808
  info.packageManager = "Bun";
9668
- } else if (existsSync9(join12(this.projectPath, "bun.lock"))) {
10809
+ } else if (existsSync9(join14(this.projectPath, "bun.lock"))) {
9669
10810
  info.packageManager = "Bun";
9670
- } else if (existsSync9(join12(this.projectPath, "pnpm-lock.yaml"))) {
10811
+ } else if (existsSync9(join14(this.projectPath, "pnpm-lock.yaml"))) {
9671
10812
  info.packageManager = "pnpm";
9672
- } else if (existsSync9(join12(this.projectPath, "yarn.lock"))) {
10813
+ } else if (existsSync9(join14(this.projectPath, "yarn.lock"))) {
9673
10814
  info.packageManager = "Yarn";
9674
- } else if (existsSync9(join12(this.projectPath, "package-lock.json"))) {
10815
+ } else if (existsSync9(join14(this.projectPath, "package-lock.json"))) {
9675
10816
  info.packageManager = "npm";
9676
- } else if (existsSync9(join12(this.projectPath, "npm-shrinkwrap.json"))) {
10817
+ } else if (existsSync9(join14(this.projectPath, "npm-shrinkwrap.json"))) {
9677
10818
  info.packageManager = "npm";
9678
10819
  }
9679
10820
  }
@@ -9735,8 +10876,8 @@ function analyzeProject(projectPath) {
9735
10876
  }
9736
10877
 
9737
10878
  // src/features/rules/rules.manager.ts
9738
- import { readdir as readdir3, readFile as readFile5 } from "fs/promises";
9739
- import { join as join13, relative as relative4 } from "path";
10879
+ import { readdir as readdir3, readFile as readFile6 } from "fs/promises";
10880
+ import { join as join15, relative as relative4 } from "path";
9740
10881
  import { existsSync as existsSync10 } from "fs";
9741
10882
  import { homedir as homedir5 } from "os";
9742
10883
  function parseRuleFrontmatter(markdown) {
@@ -9780,10 +10921,10 @@ function parseRuleFrontmatter(markdown) {
9780
10921
  return { metadata, content };
9781
10922
  }
9782
10923
  function getGlobalRulesDir() {
9783
- return join13(homedir5(), ".agents", "rules");
10924
+ return join15(homedir5(), ".agents", "rules");
9784
10925
  }
9785
10926
  function getProjectRulesDir(threadPath) {
9786
- return join13(threadPath, ".agents", "rules");
10927
+ return join15(threadPath, ".agents", "rules");
9787
10928
  }
9788
10929
  var RuleManager = class {
9789
10930
  /**
@@ -9816,13 +10957,13 @@ var RuleManager = class {
9816
10957
  try {
9817
10958
  const entries = await readdir3(dir, { withFileTypes: true });
9818
10959
  for (const entry of entries) {
9819
- const fullPath = join13(dir, entry.name);
10960
+ const fullPath = join15(dir, entry.name);
9820
10961
  if (entry.isDirectory()) {
9821
10962
  const subRules = await this.loadRulesFromDir(fullPath, threadPath, scope);
9822
10963
  rules.push(...subRules);
9823
10964
  } else if (entry.isFile() && (entry.name.endsWith(".md") || entry.name.endsWith(".mdc"))) {
9824
10965
  try {
9825
- const fileContent = await readFile5(fullPath, "utf-8");
10966
+ const fileContent = await readFile6(fullPath, "utf-8");
9826
10967
  const { metadata, content } = parseRuleFrontmatter(fileContent);
9827
10968
  const ruleName = entry.name.replace(/\.(md|mdc)$/, "");
9828
10969
  const relativePath = relative4(threadPath, fullPath);
@@ -9951,6 +11092,12 @@ init_tarsk_debug();
9951
11092
  function buildDefaultPrompt(tools) {
9952
11093
  return buildDefaultSystemPrompt(tools.map((tool) => tool.name));
9953
11094
  }
11095
+ function resolveBaseSystemPrompt(tools, overrideOptions) {
11096
+ const base = resolveEffectiveSystemPrompt(buildDefaultPrompt(tools), overrideOptions);
11097
+ return `${base}
11098
+
11099
+ ${formatCurrentDateLine()}`;
11100
+ }
9954
11101
  var PLAN_MODE_INSTRUCTIONS = `
9955
11102
 
9956
11103
  # Plan Mode (ACTIVE)
@@ -10119,7 +11266,7 @@ function resolveEffectiveSystemPrompt(generatedPrompt, options) {
10119
11266
  return generatedPrompt;
10120
11267
  }
10121
11268
  async function loadPromptSections(threadPath, tools, skills, planMode, agents, deferredTools, ralphMode, overrideOptions) {
10122
- let systemPrompt = resolveEffectiveSystemPrompt(buildDefaultPrompt(tools), overrideOptions);
11269
+ let systemPrompt = resolveBaseSystemPrompt(tools, overrideOptions);
10123
11270
  const agentsContent = loadDeveloperContext(threadPath);
10124
11271
  if (agentsContent) {
10125
11272
  systemPrompt += "\n\n# Developer Context\n\n" + agentsContent;
@@ -10201,7 +11348,7 @@ function formatAgentsSection(agents) {
10201
11348
  return section;
10202
11349
  }
10203
11350
  async function loadAgentSystemPrompt(threadPath, tools, skills, planMode, agents, deferredTools, ralphMode, overrideOptions) {
10204
- let prompt = resolveEffectiveSystemPrompt(buildDefaultPrompt(tools), overrideOptions);
11351
+ let prompt = resolveBaseSystemPrompt(tools, overrideOptions);
10205
11352
  const agentsContent = loadDeveloperContext(threadPath);
10206
11353
  if (agentsContent) {
10207
11354
  prompt += "\n\n# Developer Context\n\n" + agentsContent;
@@ -10318,7 +11465,7 @@ var ShellPermissionGate = class {
10318
11465
  wildcardOptions: buildWildcardOptions(command)
10319
11466
  };
10320
11467
  this.config.onPermissionRequired?.(request);
10321
- return new Promise((resolve6, reject) => {
11468
+ return new Promise((resolve7, reject) => {
10322
11469
  const cleanup = () => {
10323
11470
  pendingPermissions2.delete(toolCallId);
10324
11471
  signal?.removeEventListener("abort", onAbort);
@@ -10336,7 +11483,7 @@ var ShellPermissionGate = class {
10336
11483
  pendingPermissions2.set(toolCallId, {
10337
11484
  resolve: (decision, pattern) => {
10338
11485
  cleanup();
10339
- void this.applyDecision(decision, command, pattern).then(() => resolve6(decision));
11486
+ void this.applyDecision(decision, command, pattern).then(() => resolve7(decision));
10340
11487
  },
10341
11488
  reject: (error) => {
10342
11489
  cleanup();
@@ -10765,8 +11912,8 @@ ${userPrompt}`;
10765
11912
  if (eventQueue.length > 0) {
10766
11913
  yield eventQueue.shift();
10767
11914
  } else {
10768
- await new Promise((resolve6) => {
10769
- eventQueue.setResolver(resolve6);
11915
+ await new Promise((resolve7) => {
11916
+ eventQueue.setResolver(resolve7);
10770
11917
  });
10771
11918
  }
10772
11919
  }
@@ -10922,14 +12069,14 @@ var ProcessingStateManagerImpl = class {
10922
12069
 
10923
12070
  // src/core/env-manager.ts
10924
12071
  import { promises as fs } from "fs";
10925
- import { join as join14 } from "path";
12072
+ import { join as join16 } from "path";
10926
12073
  function updateRuntimeEnv(values) {
10927
12074
  for (const [key, value] of Object.entries(values)) {
10928
12075
  process.env[key] = value;
10929
12076
  }
10930
12077
  }
10931
12078
  async function updateEnvFile(keyNames) {
10932
- const envPath = join14(process.cwd(), ".env");
12079
+ const envPath = join16(process.cwd(), ".env");
10933
12080
  let content = "";
10934
12081
  try {
10935
12082
  content = await fs.readFile(envPath, "utf-8");
@@ -10958,7 +12105,7 @@ async function updateEnvFile(keyNames) {
10958
12105
  await fs.writeFile(envPath, newLines.join("\n") + "\n", "utf-8");
10959
12106
  }
10960
12107
  async function readEnvFile() {
10961
- const envPath = join14(process.cwd(), ".env");
12108
+ const envPath = join16(process.cwd(), ".env");
10962
12109
  const envMap = {};
10963
12110
  try {
10964
12111
  const content = await fs.readFile(envPath, "utf-8");
@@ -10981,7 +12128,7 @@ async function readEnvFile() {
10981
12128
  import { Hono } from "hono";
10982
12129
 
10983
12130
  // src/agent/agent-run-guard.ts
10984
- import { randomUUID as randomUUID3 } from "crypto";
12131
+ import { randomUUID as randomUUID4 } from "crypto";
10985
12132
  var DEFAULT_LONG_RUNNING_TURN_LIMIT = 50;
10986
12133
  var LONG_RUNNING_CONFIRMATION_QUESTION = "The agent has been working for a long time. Do you want to continue?";
10987
12134
  var LONG_RUNNING_CONFIRMATION_OPTIONS = ["Yes", "No"];
@@ -11023,7 +12170,7 @@ function shouldPromptForLongRunningConfirmation(state) {
11023
12170
  return state.turnCount >= state.maximumTurnCount;
11024
12171
  }
11025
12172
  function createLongRunningConfirmationRequest(maximumTurnCount) {
11026
- const toolCallId = `long-running-confirmation-${randomUUID3()}`;
12173
+ const toolCallId = `long-running-confirmation-${randomUUID4()}`;
11027
12174
  const event = {
11028
12175
  type: "long_running_confirmation",
11029
12176
  prompt: {
@@ -11049,7 +12196,7 @@ function createLongRunningConfirmationRequest(maximumTurnCount) {
11049
12196
  toolCallId,
11050
12197
  event,
11051
12198
  waitForAnswer: (signal) => {
11052
- return new Promise((resolve6, reject) => {
12199
+ return new Promise((resolve7, reject) => {
11053
12200
  const resolvers = getLongRunningResolvers();
11054
12201
  const cleanup = () => {
11055
12202
  resolvers.delete(toolCallId);
@@ -11072,7 +12219,7 @@ function createLongRunningConfirmationRequest(maximumTurnCount) {
11072
12219
  resolvers.set(toolCallId, {
11073
12220
  resolve: (answer) => {
11074
12221
  cleanup();
11075
- resolve6(answer);
12222
+ resolve7(answer);
11076
12223
  },
11077
12224
  reject: (error) => {
11078
12225
  cleanup();
@@ -11399,11 +12546,11 @@ function createWebFetchPermissionRoutes() {
11399
12546
  import { Hono as Hono4 } from "hono";
11400
12547
 
11401
12548
  // src/features/chat/chat-post.route.ts
11402
- import { randomUUID as randomUUID6 } from "crypto";
12549
+ import { randomUUID as randomUUID7 } from "crypto";
11403
12550
 
11404
12551
  // src/features/skills/skills.manager.ts
11405
- import { readdir as readdir4, readFile as readFile6 } from "fs/promises";
11406
- import { join as join15 } from "path";
12552
+ import { readdir as readdir4, readFile as readFile7 } from "fs/promises";
12553
+ import { join as join17 } from "path";
11407
12554
  import { existsSync as existsSync11 } from "fs";
11408
12555
  import { homedir as homedir6 } from "os";
11409
12556
  function parseFrontmatter(markdown) {
@@ -11451,10 +12598,10 @@ function parseFrontmatter(markdown) {
11451
12598
  return { metadata, content };
11452
12599
  }
11453
12600
  function getGlobalSkillsDir() {
11454
- return join15(homedir6(), ".agents", "skills");
12601
+ return join17(homedir6(), ".agents", "skills");
11455
12602
  }
11456
12603
  function getProjectSkillsDir(threadPath) {
11457
- return join15(threadPath, ".agents", "skills");
12604
+ return join17(threadPath, ".agents", "skills");
11458
12605
  }
11459
12606
  function validateSkillName(name) {
11460
12607
  if (!name || name.length === 0 || name.length > 64) {
@@ -11507,14 +12654,14 @@ var SkillManager = class {
11507
12654
  for (const entry of entries) {
11508
12655
  if (!entry.isDirectory()) continue;
11509
12656
  const skillDirName = entry.name;
11510
- const skillPath = join15(dir, skillDirName);
11511
- const skillFilePath = join15(skillPath, "SKILL.md");
12657
+ const skillPath = join17(dir, skillDirName);
12658
+ const skillFilePath = join17(skillPath, "SKILL.md");
11512
12659
  if (!existsSync11(skillFilePath)) {
11513
12660
  console.warn(`Skipping skill directory ${skillDirName}: SKILL.md not found`);
11514
12661
  continue;
11515
12662
  }
11516
12663
  try {
11517
- const fileContent = await readFile6(skillFilePath, "utf-8");
12664
+ const fileContent = await readFile7(skillFilePath, "utf-8");
11518
12665
  const { metadata, content } = parseFrontmatter(fileContent);
11519
12666
  if (!metadata.name) {
11520
12667
  console.warn(`Skipping skill in ${skillDirName}: missing 'name' in frontmatter`);
@@ -11687,8 +12834,8 @@ async function activateSkills(allSkills, taskDescription, thread, options) {
11687
12834
  }
11688
12835
 
11689
12836
  // src/features/agents/agents.manager.ts
11690
- import { readdir as readdir5, readFile as readFile7 } from "fs/promises";
11691
- import { join as join16 } from "path";
12837
+ import { readdir as readdir5, readFile as readFile8 } from "fs/promises";
12838
+ import { join as join18 } from "path";
11692
12839
  import { existsSync as existsSync12 } from "fs";
11693
12840
  import { homedir as homedir7 } from "os";
11694
12841
  function parseFrontmatter2(markdown) {
@@ -11746,10 +12893,10 @@ function validateDescription2(description) {
11746
12893
  return !!description && description.length > 0 && description.length <= 1024;
11747
12894
  }
11748
12895
  function getGlobalAgentsDir() {
11749
- return join16(homedir7(), ".agents", "agents");
12896
+ return join18(homedir7(), ".agents", "agents");
11750
12897
  }
11751
12898
  function getProjectAgentsDir(threadPath) {
11752
- return join16(threadPath, ".agents", "agents");
12899
+ return join18(threadPath, ".agents", "agents");
11753
12900
  }
11754
12901
  var AgentsManager = class {
11755
12902
  /**
@@ -11781,14 +12928,14 @@ var AgentsManager = class {
11781
12928
  for (const entry of entries) {
11782
12929
  if (!entry.isDirectory()) continue;
11783
12930
  const agentDirName = entry.name;
11784
- const agentPath = join16(dir, agentDirName);
11785
- const agentFilePath = join16(agentPath, "AGENT.md");
12931
+ const agentPath = join18(dir, agentDirName);
12932
+ const agentFilePath = join18(agentPath, "AGENT.md");
11786
12933
  if (!existsSync12(agentFilePath)) {
11787
12934
  console.warn(`[agents] Skipping agent directory ${agentDirName}: AGENT.md not found`);
11788
12935
  continue;
11789
12936
  }
11790
12937
  try {
11791
- const fileContent = await readFile7(agentFilePath, "utf-8");
12938
+ const fileContent = await readFile8(agentFilePath, "utf-8");
11792
12939
  const { metadata, content } = parseFrontmatter2(fileContent);
11793
12940
  if (!metadata.name) {
11794
12941
  console.warn(
@@ -11971,12 +13118,12 @@ function extractAssistantContent(events, fallback) {
11971
13118
 
11972
13119
  // src/features/chat/chat-post.route.ts
11973
13120
  init_database();
11974
- import { readFile as readFile8, unlink } from "fs/promises";
11975
- import { join as join17 } from "path";
13121
+ import { readFile as readFile9, unlink as unlink2 } from "fs/promises";
13122
+ import { join as join19 } from "path";
11976
13123
 
11977
13124
  // src/features/project-todos/project-todos.database.ts
11978
13125
  init_database();
11979
- import { randomUUID as randomUUID4 } from "crypto";
13126
+ import { randomUUID as randomUUID5 } from "crypto";
11980
13127
  function serializeTodo(row) {
11981
13128
  return {
11982
13129
  ...row,
@@ -12001,7 +13148,7 @@ async function getTodoById(db, todoId) {
12001
13148
  }
12002
13149
  async function insertTodo(projectId, title, description) {
12003
13150
  const db = await getDatabase();
12004
- const id = randomUUID4();
13151
+ const id = randomUUID5();
12005
13152
  const now = (/* @__PURE__ */ new Date()).toISOString();
12006
13153
  await db.execute({
12007
13154
  sql: `INSERT INTO project_todos (id, projectId, title, description, status, working, createdAt, updatedAt)
@@ -12123,7 +13270,7 @@ async function invalidateGitStatusCache(db, threadId) {
12123
13270
 
12124
13271
  // src/features/account/account-get-info.route.ts
12125
13272
  init_database();
12126
- import { randomUUID as randomUUID5 } from "crypto";
13273
+ import { randomUUID as randomUUID6 } from "crypto";
12127
13274
  var KEY_BALANCE = "PromptBalance";
12128
13275
  var KEY_VERIFICATION = "PromptBalanceVerification";
12129
13276
  var KEY_PLAN = "Plan";
@@ -12169,7 +13316,7 @@ async function getOrCreateClientReferenceId() {
12169
13316
  if (existing !== null && existing !== void 0) {
12170
13317
  return existing;
12171
13318
  }
12172
- const id = randomUUID5();
13319
+ const id = randomUUID6();
12173
13320
  await setState(db, KEY_CLIENT_REFERENCE_ID, id);
12174
13321
  return id;
12175
13322
  }
@@ -12450,7 +13597,7 @@ async function deserializeProject(db, row) {
12450
13597
  // src/features/chat/chat-post.route.ts
12451
13598
  init_utils();
12452
13599
  function runValidationScript(script, cwd) {
12453
- return new Promise((resolve6) => {
13600
+ return new Promise((resolve7) => {
12454
13601
  const child = spawnShell(script, { cwd, stdio: ["pipe", "pipe", "pipe"] });
12455
13602
  let output = "";
12456
13603
  const stdoutDecoder = new TextDecoder();
@@ -12464,12 +13611,12 @@ function runValidationScript(script, cwd) {
12464
13611
  child.on("close", (code) => {
12465
13612
  output += stdoutDecoder.decode();
12466
13613
  output += stderrDecoder.decode();
12467
- resolve6({ exitCode: code ?? 1, output: output.trim() });
13614
+ resolve7({ exitCode: code ?? 1, output: output.trim() });
12468
13615
  });
12469
13616
  child.on("error", (err) => {
12470
13617
  output += stdoutDecoder.decode();
12471
13618
  output += stderrDecoder.decode();
12472
- resolve6({
13619
+ resolve7({
12473
13620
  exitCode: 1,
12474
13621
  output: `${output}${output ? "\n" : ""}${err.message}`.trim()
12475
13622
  });
@@ -12586,7 +13733,7 @@ async function postChatMessage(c, threadManager, agentExecutor, conversationMana
12586
13733
  }
12587
13734
  let conversationId = thread.currentConversationId;
12588
13735
  if (!conversationId) {
12589
- conversationId = randomUUID6();
13736
+ conversationId = randomUUID7();
12590
13737
  const db2 = await getDatabase();
12591
13738
  await db2.execute({
12592
13739
  sql: "DELETE FROM todos WHERE threadId = ?",
@@ -12896,7 +14043,7 @@ ${result.output}`;
12896
14043
  };
12897
14044
  processingStateManager.pushEvent(threadId, iterationEvent);
12898
14045
  yield iterationEvent;
12899
- const newConversationId = randomUUID6();
14046
+ const newConversationId = randomUUID7();
12900
14047
  await threadManager.updateThread(threadId, {
12901
14048
  currentConversationId: newConversationId
12902
14049
  });
@@ -12988,11 +14135,11 @@ ${result.output}`;
12988
14135
  try {
12989
14136
  const todo = await getTodoByThreadId(db2, threadId);
12990
14137
  if (todo && todo.status === "Plan") {
12991
- const planFilePath = join17(threadPath, `${todo.id}-plan.md`);
14138
+ const planFilePath = join19(threadPath, `${todo.id}-plan.md`);
12992
14139
  try {
12993
- const planContent = await readFile8(planFilePath, "utf-8");
14140
+ const planContent = await readFile9(planFilePath, "utf-8");
12994
14141
  await updateTodo(db2, todo.id, { description: planContent.trim() });
12995
- await unlink(planFilePath);
14142
+ await unlink2(planFilePath);
12996
14143
  } catch {
12997
14144
  }
12998
14145
  }
@@ -13329,7 +14476,7 @@ ${summary}`;
13329
14476
  }
13330
14477
 
13331
14478
  // src/features/chat/chat-delete.route.ts
13332
- import { randomUUID as randomUUID7 } from "crypto";
14479
+ import { randomUUID as randomUUID8 } from "crypto";
13333
14480
  init_database();
13334
14481
  async function deleteChat(c, threadManager) {
13335
14482
  try {
@@ -13351,7 +14498,7 @@ async function deleteChat(c, threadManager) {
13351
14498
  sql: "DELETE FROM todos WHERE threadId = ?",
13352
14499
  args: [threadId]
13353
14500
  });
13354
- const newConversationId = randomUUID7();
14501
+ const newConversationId = randomUUID8();
13355
14502
  await threadManager.updateThread(threadId, { currentConversationId: newConversationId });
13356
14503
  return successResponse(
13357
14504
  c,
@@ -13380,7 +14527,7 @@ function subscribeToChatStream(c, processingStateManager) {
13380
14527
  return c.json({ error: "Thread is not currently processing" }, 404);
13381
14528
  }
13382
14529
  return stream2(c, async (writer) => {
13383
- let resolve6 = null;
14530
+ let resolve7 = null;
13384
14531
  const queue = [];
13385
14532
  let done = false;
13386
14533
  const replay = processingStateManager.getBufferedEvents(threadId).slice();
@@ -13391,18 +14538,18 @@ function subscribeToChatStream(c, processingStateManager) {
13391
14538
  } else {
13392
14539
  queue.push(event);
13393
14540
  }
13394
- if (resolve6) {
13395
- resolve6();
13396
- resolve6 = null;
14541
+ if (resolve7) {
14542
+ resolve7();
14543
+ resolve7 = null;
13397
14544
  }
13398
14545
  });
13399
14546
  const finishedBeforeSubscribe = !processingStateManager.isProcessing(threadId);
13400
14547
  writer.onAbort(() => {
13401
14548
  done = true;
13402
14549
  unsubscribe();
13403
- if (resolve6) {
13404
- resolve6();
13405
- resolve6 = null;
14550
+ if (resolve7) {
14551
+ resolve7();
14552
+ resolve7 = null;
13406
14553
  }
13407
14554
  });
13408
14555
  try {
@@ -13427,7 +14574,7 @@ function subscribeToChatStream(c, processingStateManager) {
13427
14574
  }
13428
14575
  if (!done) {
13429
14576
  await new Promise((r) => {
13430
- resolve6 = r;
14577
+ resolve7 = r;
13431
14578
  });
13432
14579
  }
13433
14580
  }
@@ -13475,10 +14622,10 @@ function createChatRoutes(threadManager, agentExecutor, conversationManager, pro
13475
14622
  init_database();
13476
14623
 
13477
14624
  // src/features/conversations/conversations.database.ts
13478
- import { randomUUID as randomUUID8 } from "crypto";
14625
+ import { randomUUID as randomUUID9 } from "crypto";
13479
14626
  async function insertMessage(db, threadId, conversationId, message, model, attachments, planMode, imageMode, ralphMode, checkpointRef) {
13480
14627
  try {
13481
- const messageId = randomUUID8();
14628
+ const messageId = randomUUID9();
13482
14629
  const timestamp = (/* @__PURE__ */ new Date()).toISOString();
13483
14630
  await db.execute(
13484
14631
  `
@@ -15777,17 +16924,17 @@ async function getOnboardingStatus(c, metadataManager) {
15777
16924
  init_utils();
15778
16925
  async function getGitCheck(c) {
15779
16926
  try {
15780
- const gitInstalled = await new Promise((resolve6) => {
16927
+ const gitInstalled = await new Promise((resolve7) => {
15781
16928
  const proc = spawnProcess("git", ["--version"]);
15782
16929
  let _err = "";
15783
16930
  proc.stderr?.on("data", (d) => {
15784
16931
  _err += d.toString();
15785
16932
  });
15786
16933
  proc.on("close", (code) => {
15787
- resolve6(code === 0);
16934
+ resolve7(code === 0);
15788
16935
  });
15789
16936
  proc.on("error", () => {
15790
- resolve6(false);
16937
+ resolve7(false);
15791
16938
  });
15792
16939
  });
15793
16940
  return c.json({ gitInstalled });
@@ -15821,13 +16968,13 @@ async function postOnboardingReset(c, metadataManager) {
15821
16968
 
15822
16969
  // src/features/onboarding/onboarding-get-recommended-models.route.ts
15823
16970
  import { readFileSync as readFileSync6, existsSync as existsSync13 } from "fs";
15824
- import { join as join18, dirname as dirname4 } from "path";
16971
+ import { join as join20, dirname as dirname4 } from "path";
15825
16972
  import { fileURLToPath } from "url";
15826
16973
  function getRecommendedModelsFilePath() {
15827
16974
  const moduleDir = dirname4(fileURLToPath(import.meta.url));
15828
16975
  const candidates = [
15829
- join18(moduleDir, "../../../recommended-models.txt"),
15830
- join18(moduleDir, "recommended-models.txt")
16976
+ join20(moduleDir, "../../../recommended-models.txt"),
16977
+ join20(moduleDir, "recommended-models.txt")
15831
16978
  ];
15832
16979
  for (const candidate of candidates) {
15833
16980
  if (existsSync13(candidate)) {
@@ -16482,12 +17629,12 @@ async function handleCheckRunning(c, projectManager) {
16482
17629
  }
16483
17630
 
16484
17631
  // src/core/run-command-detector.ts
16485
- import { readFile as readFile9 } from "fs/promises";
16486
- import { join as join19 } from "path";
17632
+ import { readFile as readFile10 } from "fs/promises";
17633
+ import { join as join21 } from "path";
16487
17634
  async function detectPackageManager(projectPath) {
16488
17635
  try {
16489
- const packageJsonPath = join19(projectPath, "package.json");
16490
- const content = await readFile9(packageJsonPath, "utf-8");
17636
+ const packageJsonPath = join21(projectPath, "package.json");
17637
+ const content = await readFile10(packageJsonPath, "utf-8");
16491
17638
  const packageJson = JSON.parse(content);
16492
17639
  if (packageJson.packageManager) {
16493
17640
  const pm = packageJson.packageManager.split("@")[0];
@@ -16502,8 +17649,8 @@ async function detectPackageManager(projectPath) {
16502
17649
  }
16503
17650
  async function detectRunScripts(projectPath) {
16504
17651
  try {
16505
- const packageJsonPath = join19(projectPath, "package.json");
16506
- const content = await readFile9(packageJsonPath, "utf-8");
17652
+ const packageJsonPath = join21(projectPath, "package.json");
17653
+ const content = await readFile10(packageJsonPath, "utf-8");
16507
17654
  const packageJson = JSON.parse(content);
16508
17655
  const scripts = packageJson.scripts ?? {};
16509
17656
  return {
@@ -16548,8 +17695,8 @@ async function suggestRunCommand(projectPath) {
16548
17695
  }
16549
17696
 
16550
17697
  // src/features/projects/projects-package-scripts.route.ts
16551
- import { readFile as readFile10 } from "fs/promises";
16552
- import { join as join20 } from "path";
17698
+ import { readFile as readFile11 } from "fs/promises";
17699
+ import { join as join22 } from "path";
16553
17700
  import { glob } from "glob";
16554
17701
  function formatScriptName(scriptName) {
16555
17702
  return scriptName.replace(/[-:_]/g, " ").replace(/\b\w/g, (l) => l.toUpperCase());
@@ -16577,8 +17724,8 @@ async function findPackageScripts(projectPath) {
16577
17724
  const packageManager = await detectPackageManager(projectPath);
16578
17725
  for (const filePath of packageJsonFiles) {
16579
17726
  try {
16580
- const fullPath = join20(projectPath, filePath);
16581
- const content = await readFile10(fullPath, "utf-8");
17727
+ const fullPath = join22(projectPath, filePath);
17728
+ const content = await readFile11(fullPath, "utf-8");
16582
17729
  const packageJson = JSON.parse(content);
16583
17730
  if (packageJson.scripts) {
16584
17731
  for (const [scriptName] of Object.entries(packageJson.scripts)) {
@@ -16625,8 +17772,8 @@ async function handleGetPackageScripts(c, projectManager) {
16625
17772
  }
16626
17773
 
16627
17774
  // src/features/projects/projects-ai-files.route.ts
16628
- import { readdir as readdir6, readFile as readFile11, writeFile as writeFile3, mkdir as mkdir2, access as access2, rm } from "fs/promises";
16629
- import { join as join21, extname as extname4, basename } from "path";
17775
+ import { readdir as readdir6, readFile as readFile12, writeFile as writeFile3, mkdir as mkdir2, access as access2, rm } from "fs/promises";
17776
+ import { join as join23, extname as extname4, basename } from "path";
16630
17777
  import { existsSync as existsSync14 } from "fs";
16631
17778
  var IGNORED_DIRS = /* @__PURE__ */ new Set([
16632
17779
  ".git",
@@ -16645,8 +17792,8 @@ async function buildFullTree(dirPath, relativeDirPath) {
16645
17792
  try {
16646
17793
  const entries = await readdir6(dirPath, { withFileTypes: true });
16647
17794
  for (const entry of entries) {
16648
- const entryRelPath = join21(relativeDirPath, entry.name);
16649
- const entryAbsPath = join21(dirPath, entry.name);
17795
+ const entryRelPath = join23(relativeDirPath, entry.name);
17796
+ const entryAbsPath = join23(dirPath, entry.name);
16650
17797
  if (entry.isDirectory()) {
16651
17798
  const children = await buildFullTree(entryAbsPath, entryRelPath);
16652
17799
  nodes.push({
@@ -16674,8 +17821,8 @@ async function buildMarkdownFilteredTree(dirPath, relativeDirPath) {
16674
17821
  try {
16675
17822
  const entries = await readdir6(dirPath, { withFileTypes: true });
16676
17823
  for (const entry of entries) {
16677
- const entryRelPath = join21(relativeDirPath, entry.name);
16678
- const entryAbsPath = join21(dirPath, entry.name);
17824
+ const entryRelPath = join23(relativeDirPath, entry.name);
17825
+ const entryAbsPath = join23(dirPath, entry.name);
16679
17826
  if (entry.isDirectory()) {
16680
17827
  if (IGNORED_DIRS.has(entry.name)) continue;
16681
17828
  const children = await buildMarkdownFilteredTree(entryAbsPath, entryRelPath);
@@ -16710,8 +17857,8 @@ async function buildAIFileTree(projectPath) {
16710
17857
  { key: "agents", label: "Agents" }
16711
17858
  ];
16712
17859
  for (const { key, label } of agentsFolders) {
16713
- const relPath = join21(".agents", key);
16714
- const absPath = join21(projectPath, relPath);
17860
+ const relPath = join23(".agents", key);
17861
+ const absPath = join23(projectPath, relPath);
16715
17862
  const exists = existsSync14(absPath);
16716
17863
  if (exists) {
16717
17864
  const children = await buildFullTree(absPath, relPath);
@@ -16746,7 +17893,7 @@ async function buildAIFileTree(projectPath) {
16746
17893
  if (entry.name === ".agents" || entry.name === "AGENTS.md" || IGNORED_DIRS.has(entry.name))
16747
17894
  continue;
16748
17895
  const entryRelPath = entry.name;
16749
- const entryAbsPath = join21(projectPath, entry.name);
17896
+ const entryAbsPath = join23(projectPath, entry.name);
16750
17897
  if (entry.isFile() && MARKDOWN_EXTS.has(extname4(entry.name))) {
16751
17898
  nodes.push({
16752
17899
  id: entryRelPath,
@@ -16773,7 +17920,7 @@ async function buildAIFileTree(projectPath) {
16773
17920
  }
16774
17921
  function validateFilePath(projectPath, filePath) {
16775
17922
  if (!filePath) return null;
16776
- const abs = join21(projectPath, filePath);
17923
+ const abs = join23(projectPath, filePath);
16777
17924
  if (!abs.startsWith(projectPath + "/") && abs !== projectPath) return null;
16778
17925
  return abs;
16779
17926
  }
@@ -16860,7 +18007,7 @@ Links to important documentation, tools, or references.
16860
18007
  } catch {
16861
18008
  return c.json({ error: { code: "NOT_FOUND", message: `File not found: ${filePath}` } }, 404);
16862
18009
  }
16863
- const content = await readFile11(absPath, "utf-8");
18010
+ const content = await readFile12(absPath, "utf-8");
16864
18011
  return c.json({ content, path: filePath });
16865
18012
  } catch (error) {
16866
18013
  return errorResponse(
@@ -16899,7 +18046,7 @@ async function handleSaveAIFile(c, projectManager) {
16899
18046
  if (!absPath) {
16900
18047
  return c.json({ error: { code: "BAD_REQUEST", message: "Invalid file path" } }, 400);
16901
18048
  }
16902
- const parentDir = join21(absPath, "..");
18049
+ const parentDir = join23(absPath, "..");
16903
18050
  await mkdir2(parentDir, { recursive: true });
16904
18051
  await writeFile3(absPath, content, "utf-8");
16905
18052
  return c.json({ success: true, path: filePath });
@@ -16999,10 +18146,10 @@ async function handleCreateSkill(c, projectManager) {
16999
18146
  if (!project) {
17000
18147
  return errorResponse(c, ErrorCodes.PROJECT_NOT_FOUND, `Project not found: ${projectId}`, 404);
17001
18148
  }
17002
- const skillRelPath = join21(".agents", "skills", name);
17003
- const skillAbsPath = join21(project.path, skillRelPath);
17004
- const skillFileRelPath = join21(skillRelPath, "SKILL.md");
17005
- const skillFileAbsPath = join21(skillAbsPath, "SKILL.md");
18149
+ const skillRelPath = join23(".agents", "skills", name);
18150
+ const skillAbsPath = join23(project.path, skillRelPath);
18151
+ const skillFileRelPath = join23(skillRelPath, "SKILL.md");
18152
+ const skillFileAbsPath = join23(skillAbsPath, "SKILL.md");
17006
18153
  if (existsSync14(skillAbsPath)) {
17007
18154
  return c.json(
17008
18155
  { error: { code: "CONFLICT", message: `Skill '${name}' already exists` } },
@@ -17074,11 +18221,14 @@ async function handleGetProjectEnvVars(c, projectManager) {
17074
18221
  return errorResponse(c, ErrorCodes.PROJECT_NOT_FOUND, `Project not found: ${projectId}`, 404);
17075
18222
  }
17076
18223
  const db = await getDatabase();
17077
- const rows = await listProjectEnvVarNames(db, projectId);
17078
- const vars = rows.map((row) => ({
17079
- name: row.name,
17080
- hasValue: true
17081
- }));
18224
+ const rows = await listProjectEnvVars(db, projectId);
18225
+ const vars = await Promise.all(
18226
+ rows.map(async (row) => ({
18227
+ name: row.name,
18228
+ hasValue: true,
18229
+ value: await decryptProviderKey(row.encryptedValue)
18230
+ }))
18231
+ );
17082
18232
  return c.json({ vars });
17083
18233
  } catch (error) {
17084
18234
  return errorResponse(
@@ -17248,7 +18398,7 @@ function createProjectRoutes(projectManager, threadManager) {
17248
18398
 
17249
18399
  // src/features/projects/projects.manager.ts
17250
18400
  init_utils();
17251
- import { join as join24 } from "path";
18401
+ import { join as join26 } from "path";
17252
18402
  import { realpathSync as realpathSync2 } from "fs";
17253
18403
  import { rm as rm4 } from "fs/promises";
17254
18404
 
@@ -17315,13 +18465,13 @@ var ProcessManager = class extends EventEmitter {
17315
18465
  }
17316
18466
  };
17317
18467
  const waitForQueue = () => {
17318
- return new Promise((resolve6) => {
18468
+ return new Promise((resolve7) => {
17319
18469
  const queue = this.queues.get(projectId);
17320
18470
  if (queue && queue.length > 0) {
17321
- resolve6();
18471
+ resolve7();
17322
18472
  } else {
17323
18473
  const res = this.resolvers.get(projectId);
17324
- if (res) res.push(resolve6);
18474
+ if (res) res.push(resolve7);
17325
18475
  }
17326
18476
  });
17327
18477
  };
@@ -17469,14 +18619,14 @@ var ProcessManager = class extends EventEmitter {
17469
18619
 
17470
18620
  // src/features/projects/projects.creator.ts
17471
18621
  init_utils();
17472
- import { randomUUID as randomUUID9 } from "crypto";
17473
- import { basename as basename2, join as join23, relative as relative5 } from "path";
18622
+ import { randomUUID as randomUUID10 } from "crypto";
18623
+ import { basename as basename2, join as join25, relative as relative5 } from "path";
17474
18624
  import { access as access3, mkdir as mkdir4, rm as rm3, stat as stat3, readdir as readdir8 } from "fs/promises";
17475
18625
 
17476
18626
  // src/features/scaffold/scaffold.runner.ts
17477
18627
  init_utils();
17478
18628
  import { existsSync as existsSync15 } from "fs";
17479
- import { join as join22 } from "path";
18629
+ import { join as join24 } from "path";
17480
18630
  import { mkdir as mkdir3, readdir as readdir7, stat as stat2, rename, rm as rm2, writeFile as writeFile4 } from "fs/promises";
17481
18631
  import { createInterface as createInterface2 } from "readline";
17482
18632
 
@@ -18198,7 +19348,7 @@ function loadCatalog() {
18198
19348
  }
18199
19349
  async function writeAgentsMd(projectPath, agentsMd) {
18200
19350
  if (!agentsMd) return;
18201
- const agentsPath = join22(projectPath, "AGENTS.md");
19351
+ const agentsPath = join24(projectPath, "AGENTS.md");
18202
19352
  if (existsSync15(agentsPath)) return;
18203
19353
  await writeFile4(agentsPath, agentsMd, "utf-8");
18204
19354
  }
@@ -18356,7 +19506,7 @@ async function* runCommandStreaming(cwd, command) {
18356
19506
  }
18357
19507
  }
18358
19508
  async function* createScaffoldedProjectStreaming(options) {
18359
- const projectPath = join22(options.parentDir, options.threadId);
19509
+ const projectPath = join24(options.parentDir, options.threadId);
18360
19510
  if (!existsSync15(options.parentDir)) {
18361
19511
  yield {
18362
19512
  type: "result",
@@ -18446,7 +19596,7 @@ async function* createScaffoldedProjectStreaming(options) {
18446
19596
  }
18447
19597
  try {
18448
19598
  const projectName2 = getProjectName(options.projectName);
18449
- const projectSubDir = join22(projectPath, projectName2);
19599
+ const projectSubDir = join24(projectPath, projectName2);
18450
19600
  try {
18451
19601
  const subDirStat = await stat2(projectSubDir);
18452
19602
  if (subDirStat.isDirectory()) {
@@ -18456,8 +19606,8 @@ async function* createScaffoldedProjectStreaming(options) {
18456
19606
  };
18457
19607
  const items = await readdir7(projectSubDir);
18458
19608
  for (const item of items) {
18459
- const oldPath = join22(projectSubDir, item);
18460
- const newPath = join22(projectPath, item);
19609
+ const oldPath = join24(projectSubDir, item);
19610
+ const newPath = join24(projectPath, item);
18461
19611
  await rename(oldPath, newPath);
18462
19612
  }
18463
19613
  await rm2(projectSubDir, { recursive: true, force: true });
@@ -18655,14 +19805,14 @@ var ProjectCreator = class {
18655
19805
  }
18656
19806
  let initialThreadId;
18657
19807
  try {
18658
- const projectId = randomUUID9();
19808
+ const projectId = randomUUID10();
18659
19809
  const existingProjects = await this.metadataManager.loadProjects();
18660
19810
  const existingNames = existingProjects.map((p) => p.name);
18661
19811
  const projectName = this.ensureUniqueProjectName(
18662
19812
  this.deriveProjectName(gitUrl),
18663
19813
  existingNames
18664
19814
  );
18665
- initialThreadId = randomUUID9();
19815
+ initialThreadId = randomUUID10();
18666
19816
  const initialThreadTitle = generateRandomThreadName();
18667
19817
  const baseFolderName = this.toFolderName(this.deriveProjectName(gitUrl));
18668
19818
  if (!baseFolderName) {
@@ -18673,7 +19823,7 @@ var ProjectCreator = class {
18673
19823
  return;
18674
19824
  }
18675
19825
  const uniqueFolderName = await this.ensureUniqueFolderName(baseFolderName);
18676
- const firstThreadPath = join23(this.rootFolder, uniqueFolderName);
19826
+ const firstThreadPath = join25(this.rootFolder, uniqueFolderName);
18677
19827
  this.processingStateManager?.setProcessing(initialThreadId);
18678
19828
  yield {
18679
19829
  type: "progress",
@@ -18811,8 +19961,8 @@ var ProjectCreator = class {
18811
19961
  };
18812
19962
  return;
18813
19963
  }
18814
- const projectId = randomUUID9();
18815
- const initialThreadId = randomUUID9();
19964
+ const projectId = randomUUID10();
19965
+ const initialThreadId = randomUUID10();
18816
19966
  const initialThreadTitle = generateRandomThreadName();
18817
19967
  this.processingStateManager?.setProcessing(initialThreadId);
18818
19968
  try {
@@ -18923,15 +20073,15 @@ var ProjectCreator = class {
18923
20073
  };
18924
20074
  return;
18925
20075
  }
18926
- const projectId = randomUUID9();
18927
- const initialThreadId = randomUUID9();
20076
+ const projectId = randomUUID10();
20077
+ const initialThreadId = randomUUID10();
18928
20078
  const initialThreadTitle = generateRandomThreadName();
18929
20079
  let folderPath;
18930
20080
  let projectMetadataSaved = false;
18931
20081
  this.processingStateManager?.setProcessing(initialThreadId);
18932
20082
  try {
18933
20083
  const uniqueFolderName = await this.ensureUniqueFolderName(folderName);
18934
- folderPath = join23(this.rootFolder, uniqueFolderName);
20084
+ folderPath = join25(this.rootFolder, uniqueFolderName);
18935
20085
  yield {
18936
20086
  type: "progress",
18937
20087
  message: `Creating folder "${uniqueFolderName}"...`,
@@ -19018,7 +20168,7 @@ var ProjectCreator = class {
19018
20168
  return name;
19019
20169
  }
19020
20170
  generateThreadPath(_projectId, threadId) {
19021
- return join23(this.rootFolder, threadId);
20171
+ return join25(this.rootFolder, threadId);
19022
20172
  }
19023
20173
  toFolderName(name) {
19024
20174
  return name.trim().toLowerCase().replace(/[\\/]+/g, " ").replace(/[^a-z0-9._ -]+/g, "").replace(/\s+/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
@@ -19034,7 +20184,7 @@ var ProjectCreator = class {
19034
20184
  async ensureUniqueFolderName(baseFolderName) {
19035
20185
  let candidate = baseFolderName;
19036
20186
  let counter = 1;
19037
- while (await this.folderExists(join23(this.rootFolder, candidate))) {
20187
+ while (await this.folderExists(join25(this.rootFolder, candidate))) {
19038
20188
  candidate = `${baseFolderName}-${counter}`;
19039
20189
  counter++;
19040
20190
  }
@@ -19066,8 +20216,8 @@ var ProjectCreator = class {
19066
20216
  yield { type: "error", message: `Cannot access folder: ${folderPath}` };
19067
20217
  return;
19068
20218
  }
19069
- const projectId = randomUUID9();
19070
- const initialThreadId = randomUUID9();
20219
+ const projectId = randomUUID10();
20220
+ const initialThreadId = randomUUID10();
19071
20221
  const initialThreadTitle = generateRandomThreadName();
19072
20222
  this.processingStateManager?.setProcessing(initialThreadId);
19073
20223
  try {
@@ -19188,19 +20338,19 @@ var ProjectCreator = class {
19188
20338
  }
19189
20339
  async findAllPackageJsonFiles(rootPath, currentPath, setupScripts) {
19190
20340
  try {
19191
- await access3(join23(currentPath, "package.json"));
20341
+ await access3(join25(currentPath, "package.json"));
19192
20342
  const relativePath = relative5(rootPath, currentPath);
19193
20343
  let installCommand = "";
19194
20344
  try {
19195
- await access3(join23(currentPath, "yarn.lock"));
20345
+ await access3(join25(currentPath, "yarn.lock"));
19196
20346
  installCommand = "yarn install";
19197
20347
  } catch {
19198
20348
  try {
19199
- await access3(join23(currentPath, "bun.lock"));
20349
+ await access3(join25(currentPath, "bun.lock"));
19200
20350
  installCommand = "bun install";
19201
20351
  } catch {
19202
20352
  try {
19203
- await access3(join23(currentPath, "pnpm-lock.yaml"));
20353
+ await access3(join25(currentPath, "pnpm-lock.yaml"));
19204
20354
  installCommand = "pnpm install";
19205
20355
  } catch {
19206
20356
  installCommand = "npm install";
@@ -19217,7 +20367,7 @@ var ProjectCreator = class {
19217
20367
  try {
19218
20368
  const entries = await readdir8(currentPath);
19219
20369
  for (const entry of entries) {
19220
- const entryPath = join23(currentPath, entry);
20370
+ const entryPath = join25(currentPath, entry);
19221
20371
  try {
19222
20372
  const stats = await stat3(entryPath);
19223
20373
  if (stats.isDirectory() && !entry.startsWith(".") && entry !== "node_modules") {
@@ -19563,7 +20713,7 @@ var ProjectManagerImpl = class {
19563
20713
  const session = this.terminalSessionManager.getOrCreateSession(threadId, thread.path);
19564
20714
  let workingDir;
19565
20715
  if (cwd) {
19566
- workingDir = cwd.startsWith("/") ? cwd : join24(thread.path, cwd);
20716
+ workingDir = cwd.startsWith("/") ? cwd : join26(thread.path, cwd);
19567
20717
  this.terminalSessionManager.updateWorkingDirectory(threadId, workingDir);
19568
20718
  } else {
19569
20719
  workingDir = session.currentWorkingDirectory;
@@ -19619,7 +20769,7 @@ ___CWD___%s
19619
20769
  if (outputBuffer.length > 0) {
19620
20770
  yield outputBuffer.shift();
19621
20771
  } else {
19622
- await new Promise((resolve6) => setTimeout(resolve6, 50));
20772
+ await new Promise((resolve7) => setTimeout(resolve7, 50));
19623
20773
  }
19624
20774
  }
19625
20775
  }
@@ -20354,8 +21504,8 @@ function createScaffoldRoutes(projectManager) {
20354
21504
  }
20355
21505
 
20356
21506
  // src/features/slash-commands/slash-commands.manager.ts
20357
- import { readdir as readdir9, readFile as readFile12, mkdir as mkdir5, writeFile as writeFile5, unlink as unlink2 } from "fs/promises";
20358
- import { join as join25, basename as basename3, extname as extname5, relative as relative6 } from "path";
21507
+ import { readdir as readdir9, readFile as readFile13, mkdir as mkdir5, writeFile as writeFile5, unlink as unlink3 } from "fs/promises";
21508
+ import { join as join27, basename as basename3, extname as extname5, relative as relative6 } from "path";
20359
21509
  import { existsSync as existsSync16 } from "fs";
20360
21510
  import { homedir as homedir8 } from "os";
20361
21511
  function slugify(filename) {
@@ -20401,10 +21551,10 @@ function parseFrontmatter3(markdown) {
20401
21551
  return { metadata, content };
20402
21552
  }
20403
21553
  function getGlobalCommandsDir() {
20404
- return join25(homedir8(), ".agents", "commands");
21554
+ return join27(homedir8(), ".agents", "commands");
20405
21555
  }
20406
21556
  function getProjectCommandsDir(threadPath) {
20407
- return join25(threadPath, ".agents", "commands");
21557
+ return join27(threadPath, ".agents", "commands");
20408
21558
  }
20409
21559
  var SlashCommandManager = class _SlashCommandManager {
20410
21560
  /**
@@ -20477,8 +21627,8 @@ var SlashCommandManager = class _SlashCommandManager {
20477
21627
  const files = await readdir9(dir);
20478
21628
  for (const file of files) {
20479
21629
  if (!file.endsWith(".md")) continue;
20480
- const filePath = join25(dir, file);
20481
- const fileContent = await readFile12(filePath, "utf-8");
21630
+ const filePath = join27(dir, file);
21631
+ const fileContent = await readFile13(filePath, "utf-8");
20482
21632
  const { metadata, content } = parseFrontmatter3(fileContent);
20483
21633
  const nameWithoutExt = basename3(file, extname5(file));
20484
21634
  const commandName = slugify(nameWithoutExt);
@@ -20512,7 +21662,7 @@ var SlashCommandManager = class _SlashCommandManager {
20512
21662
  await mkdir5(dir, { recursive: true });
20513
21663
  }
20514
21664
  const filename = `${name}.md`;
20515
- const filePath = join25(dir, filename);
21665
+ const filePath = join27(dir, filename);
20516
21666
  if (existsSync16(filePath)) {
20517
21667
  throw new Error(`Command already exists: ${name}`);
20518
21668
  }
@@ -20577,7 +21727,7 @@ var SlashCommandManager = class _SlashCommandManager {
20577
21727
  if (!existsSync16(filePath)) {
20578
21728
  throw new Error(`Command file not found: ${filePath}`);
20579
21729
  }
20580
- await unlink2(filePath);
21730
+ await unlink3(filePath);
20581
21731
  }
20582
21732
  /**
20583
21733
  * Get a specific command by name
@@ -21106,8 +22256,8 @@ function createRuleRoutes(router, projectManager) {
21106
22256
 
21107
22257
  // src/features/threads/threads.manager.ts
21108
22258
  init_utils();
21109
- import { randomUUID as randomUUID10 } from "crypto";
21110
- import { join as join26 } from "path";
22259
+ import { randomUUID as randomUUID11 } from "crypto";
22260
+ import { join as join28 } from "path";
21111
22261
  import { execSync as execSync3 } from "child_process";
21112
22262
  import { rm as rm5, stat as stat4, mkdir as mkdir6 } from "fs/promises";
21113
22263
  init_database();
@@ -21173,7 +22323,7 @@ var ThreadManagerImpl = class {
21173
22323
  } catch {
21174
22324
  await mkdir6(project.path, { recursive: true });
21175
22325
  }
21176
- threadId = randomUUID10();
22326
+ threadId = randomUUID11();
21177
22327
  const threadPath = this.generateThreadPath(projectId, threadId);
21178
22328
  const existingThreads = await this.metadataManager.loadThreads();
21179
22329
  const existingTitles = new Set(
@@ -21497,8 +22647,8 @@ var ThreadManagerImpl = class {
21497
22647
  isDone = true;
21498
22648
  resolveNext();
21499
22649
  });
21500
- const waitForData = () => new Promise((resolve6) => {
21501
- resolveNext = resolve6;
22650
+ const waitForData = () => new Promise((resolve7) => {
22651
+ resolveNext = resolve7;
21502
22652
  });
21503
22653
  while (!isDone || outputQueue.length > 0 || errorQueue.length > 0) {
21504
22654
  if (outputQueue.length > 0) {
@@ -21531,7 +22681,7 @@ var ThreadManagerImpl = class {
21531
22681
  * - 7.4 - THE CLI SHALL ensure each Thread clone is stored in a unique directory path
21532
22682
  */
21533
22683
  generateThreadPath(_projectId, threadId) {
21534
- return join26(this.rootFolder, threadId);
22684
+ return join28(this.rootFolder, threadId);
21535
22685
  }
21536
22686
  /**
21537
22687
  * Generates a thread title if not provided
@@ -21561,7 +22711,7 @@ var ThreadManagerImpl = class {
21561
22711
  if (!thread) {
21562
22712
  throw new Error(`Thread not found: ${threadId}`);
21563
22713
  }
21564
- return new Promise((resolve6, reject) => {
22714
+ return new Promise((resolve7, reject) => {
21565
22715
  const gitProcess = spawnProcess(
21566
22716
  "git",
21567
22717
  ["ls-files", "--cached", "--others", "--exclude-standard"],
@@ -21580,7 +22730,7 @@ var ThreadManagerImpl = class {
21580
22730
  gitProcess.on("close", (code) => {
21581
22731
  if (code === 0) {
21582
22732
  const files = output.split("\n").map((line) => line.trim()).filter((line) => line.length > 0);
21583
- resolve6(files);
22733
+ resolve7(files);
21584
22734
  } else {
21585
22735
  reject(new Error(`git ls-files failed with code ${code}: ${errorOutput}`));
21586
22736
  }
@@ -21724,14 +22874,14 @@ async function handleDeleteThread(c, threadManager) {
21724
22874
  }
21725
22875
 
21726
22876
  // src/core/project-inspector.ts
21727
- import { readFile as readFile13, readdir as readdir10 } from "fs/promises";
22877
+ import { readFile as readFile14, readdir as readdir10 } from "fs/promises";
21728
22878
  import { existsSync as existsSync17 } from "fs";
21729
- import { join as join27, basename as basename4, relative as relative7 } from "path";
22879
+ import { join as join29, basename as basename4, relative as relative7 } from "path";
21730
22880
  import { glob as glob2 } from "glob";
21731
22881
 
21732
22882
  // src/features/project-scripts/project-scripts.database.ts
21733
22883
  init_database();
21734
- import { randomUUID as randomUUID11 } from "crypto";
22884
+ import { randomUUID as randomUUID12 } from "crypto";
21735
22885
  async function getScriptsByProject(db, projectId) {
21736
22886
  const result = await db.execute({
21737
22887
  sql: `SELECT id, projectId, workspace, name, command, friendlyName, updatedAt
@@ -21748,7 +22898,7 @@ async function upsertProjectScripts(projectId, scripts) {
21748
22898
  sql: `INSERT OR REPLACE INTO project_scripts (id, projectId, workspace, name, command, friendlyName, updatedAt)
21749
22899
  VALUES (?, ?, ?, ?, ?, ?, ?)`,
21750
22900
  args: [
21751
- randomUUID11(),
22901
+ randomUUID12(),
21752
22902
  projectId,
21753
22903
  script.workspace,
21754
22904
  script.name,
@@ -21770,13 +22920,13 @@ async function inspectProject(projectPath, projectId) {
21770
22920
  return scripts;
21771
22921
  }
21772
22922
  async function detectPackageManager2(projectPath) {
21773
- if (existsSync17(join27(projectPath, "bun.lockb")) || existsSync17(join27(projectPath, "bun.lock"))) {
22923
+ if (existsSync17(join29(projectPath, "bun.lockb")) || existsSync17(join29(projectPath, "bun.lock"))) {
21774
22924
  return "bun";
21775
22925
  }
21776
- if (existsSync17(join27(projectPath, "pnpm-lock.yaml"))) {
22926
+ if (existsSync17(join29(projectPath, "pnpm-lock.yaml"))) {
21777
22927
  return "pnpm";
21778
22928
  }
21779
- if (existsSync17(join27(projectPath, "yarn.lock"))) {
22929
+ if (existsSync17(join29(projectPath, "yarn.lock"))) {
21780
22930
  return "yarn";
21781
22931
  }
21782
22932
  try {
@@ -21795,8 +22945,8 @@ async function detectPackageManager2(projectPath) {
21795
22945
  return "npm";
21796
22946
  }
21797
22947
  async function detectMonoRepoType(projectPath) {
21798
- const hasPnpmWorkspace = existsSync17(join27(projectPath, "pnpm-workspace.yaml"));
21799
- if (existsSync17(join27(projectPath, "nx.json"))) {
22948
+ const hasPnpmWorkspace = existsSync17(join29(projectPath, "pnpm-workspace.yaml"));
22949
+ if (existsSync17(join29(projectPath, "nx.json"))) {
21800
22950
  return "nx";
21801
22951
  }
21802
22952
  const pkg = await readPackageJson(projectPath);
@@ -21810,10 +22960,10 @@ async function detectMonoRepoType(projectPath) {
21810
22960
  if (hasPnpmWorkspace) {
21811
22961
  return "pnpm";
21812
22962
  }
21813
- if (existsSync17(join27(projectPath, "lerna.json"))) {
22963
+ if (existsSync17(join29(projectPath, "lerna.json"))) {
21814
22964
  return "lerna";
21815
22965
  }
21816
- if (existsSync17(join27(projectPath, "turbo.json"))) {
22966
+ if (existsSync17(join29(projectPath, "turbo.json"))) {
21817
22967
  return "turbo";
21818
22968
  }
21819
22969
  const folderWorkspaces = await detectFolderBasedWorkspaces(projectPath);
@@ -21854,8 +23004,8 @@ async function resolvePackageJsonWorkspaces(projectPath) {
21854
23004
  });
21855
23005
  const workspaces = [];
21856
23006
  for (const folder of folders) {
21857
- const absPath = join27(projectPath, folder);
21858
- if (existsSync17(join27(absPath, "package.json"))) {
23007
+ const absPath = join29(projectPath, folder);
23008
+ if (existsSync17(join29(absPath, "package.json"))) {
21859
23009
  workspaces.push({
21860
23010
  name: basename4(folder),
21861
23011
  folder: absPath,
@@ -21870,12 +23020,12 @@ async function resolvePackageJsonWorkspaces(projectPath) {
21870
23020
  return workspaces;
21871
23021
  }
21872
23022
  async function resolvePnpmWorkspaces(projectPath) {
21873
- const yamlPath = join27(projectPath, "pnpm-workspace.yaml");
23023
+ const yamlPath = join29(projectPath, "pnpm-workspace.yaml");
21874
23024
  if (!existsSync17(yamlPath)) {
21875
23025
  return [{ name: "root", folder: projectPath, relativePath: "." }];
21876
23026
  }
21877
23027
  try {
21878
- const yaml = await readFile13(yamlPath, "utf-8");
23028
+ const yaml = await readFile14(yamlPath, "utf-8");
21879
23029
  const patterns = [];
21880
23030
  for (const line of yaml.split("\n")) {
21881
23031
  const trimmed = line.trim();
@@ -21896,8 +23046,8 @@ async function resolvePnpmWorkspaces(projectPath) {
21896
23046
  });
21897
23047
  const workspaces = [];
21898
23048
  for (const folder of folders) {
21899
- const absPath = join27(projectPath, folder);
21900
- if (existsSync17(join27(absPath, "package.json"))) {
23049
+ const absPath = join29(projectPath, folder);
23050
+ if (existsSync17(join29(absPath, "package.json"))) {
21901
23051
  const wsPkg = await readPackageJson(absPath);
21902
23052
  workspaces.push({
21903
23053
  name: wsPkg?.name ?? basename4(folder),
@@ -21916,12 +23066,12 @@ async function resolvePnpmWorkspaces(projectPath) {
21916
23066
  }
21917
23067
  }
21918
23068
  async function resolveLernaWorkspaces(projectPath) {
21919
- const lernaPath = join27(projectPath, "lerna.json");
23069
+ const lernaPath = join29(projectPath, "lerna.json");
21920
23070
  if (!existsSync17(lernaPath)) {
21921
23071
  return [{ name: "root", folder: projectPath, relativePath: "." }];
21922
23072
  }
21923
23073
  try {
21924
- const content = await readFile13(lernaPath, "utf-8");
23074
+ const content = await readFile14(lernaPath, "utf-8");
21925
23075
  const lerna = JSON.parse(content);
21926
23076
  const patterns = lerna.packages ?? ["packages/*"];
21927
23077
  const folders = await glob2(patterns, {
@@ -21930,8 +23080,8 @@ async function resolveLernaWorkspaces(projectPath) {
21930
23080
  });
21931
23081
  const workspaces = [];
21932
23082
  for (const folder of folders) {
21933
- const absPath = join27(projectPath, folder);
21934
- if (existsSync17(join27(absPath, "package.json"))) {
23083
+ const absPath = join29(projectPath, folder);
23084
+ if (existsSync17(join29(absPath, "package.json"))) {
21935
23085
  const wsPkg = await readPackageJson(absPath);
21936
23086
  workspaces.push({
21937
23087
  name: wsPkg?.name ?? basename4(folder),
@@ -21951,17 +23101,17 @@ async function resolveLernaWorkspaces(projectPath) {
21951
23101
  }
21952
23102
  async function resolveNxWorkspaces(projectPath) {
21953
23103
  const workspaces = [];
21954
- const workspaceJsonPath = join27(projectPath, "workspace.json");
23104
+ const workspaceJsonPath = join29(projectPath, "workspace.json");
21955
23105
  if (existsSync17(workspaceJsonPath)) {
21956
23106
  try {
21957
- const content = await readFile13(workspaceJsonPath, "utf-8");
23107
+ const content = await readFile14(workspaceJsonPath, "utf-8");
21958
23108
  const wsJson = JSON.parse(content);
21959
23109
  for (const [name, value] of Object.entries(wsJson.projects ?? {})) {
21960
23110
  const folder = typeof value === "string" ? value : value.root;
21961
23111
  if (folder) {
21962
23112
  workspaces.push({
21963
23113
  name,
21964
- folder: join27(projectPath, folder),
23114
+ folder: join29(projectPath, folder),
21965
23115
  relativePath: folder
21966
23116
  });
21967
23117
  }
@@ -21976,13 +23126,13 @@ async function resolveNxWorkspaces(projectPath) {
21976
23126
  });
21977
23127
  for (const file of projectJsonFiles) {
21978
23128
  try {
21979
- const content = await readFile13(join27(projectPath, file), "utf-8");
23129
+ const content = await readFile14(join29(projectPath, file), "utf-8");
21980
23130
  const project = JSON.parse(content);
21981
23131
  if (project.name) {
21982
23132
  const folder = file.replace(/\/project\.json$/, "");
21983
23133
  workspaces.push({
21984
23134
  name: project.name,
21985
- folder: join27(projectPath, folder),
23135
+ folder: join29(projectPath, folder),
21986
23136
  relativePath: folder
21987
23137
  });
21988
23138
  }
@@ -21990,15 +23140,15 @@ async function resolveNxWorkspaces(projectPath) {
21990
23140
  }
21991
23141
  }
21992
23142
  if (workspaces.length > 0) return workspaces;
21993
- const appsDir = join27(projectPath, "apps");
23143
+ const appsDir = join29(projectPath, "apps");
21994
23144
  if (existsSync17(appsDir)) {
21995
23145
  const entries = await readdir10(appsDir, { withFileTypes: true });
21996
23146
  for (const entry of entries) {
21997
23147
  if (entry.isDirectory() && !entry.name.startsWith(".")) {
21998
- const folder = join27("apps", entry.name);
23148
+ const folder = join29("apps", entry.name);
21999
23149
  workspaces.push({
22000
23150
  name: entry.name,
22001
- folder: join27(projectPath, folder),
23151
+ folder: join29(projectPath, folder),
22002
23152
  relativePath: folder
22003
23153
  });
22004
23154
  }
@@ -22032,13 +23182,13 @@ async function detectFolderBasedWorkspaces(projectPath) {
22032
23182
  const results = [];
22033
23183
  for (const entry of entries) {
22034
23184
  if (entry.isDirectory() && !entry.name.startsWith(".") && entry.name !== "node_modules" && entry.name !== "dist" && entry.name !== "build") {
22035
- const pkgPath = join27(projectPath, entry.name, "package.json");
23185
+ const pkgPath = join29(projectPath, entry.name, "package.json");
22036
23186
  if (existsSync17(pkgPath)) {
22037
23187
  try {
22038
- const content = await readFile13(pkgPath, "utf-8");
23188
+ const content = await readFile14(pkgPath, "utf-8");
22039
23189
  const pkg = JSON.parse(content);
22040
23190
  if (pkg.dependencies || pkg.devDependencies || pkg.scripts) {
22041
- results.push({ name: entry.name, absPath: join27(projectPath, entry.name) });
23191
+ results.push({ name: entry.name, absPath: join29(projectPath, entry.name) });
22042
23192
  }
22043
23193
  } catch {
22044
23194
  }
@@ -22094,7 +23244,7 @@ function pmRunCommand(packageManager, scriptName) {
22094
23244
  }
22095
23245
  async function readPackageJson(dir) {
22096
23246
  try {
22097
- const content = await readFile13(join27(dir, "package.json"), "utf-8");
23247
+ const content = await readFile14(join29(dir, "package.json"), "utf-8");
22098
23248
  return JSON.parse(content);
22099
23249
  } catch {
22100
23250
  return null;
@@ -22286,8 +23436,8 @@ async function handleListThreadFiles(c, threadManager) {
22286
23436
  }
22287
23437
 
22288
23438
  // src/features/threads/threads-explorer.route.ts
22289
- import { readdir as readdir11, readFile as readFile14, rename as rename2, writeFile as writeFile6, mkdir as mkdir7, rm as rm6, access as access4 } from "fs/promises";
22290
- import { join as join28 } from "path";
23439
+ import { readdir as readdir11, readFile as readFile15, rename as rename2, writeFile as writeFile6, mkdir as mkdir7, rm as rm6, access as access4 } from "fs/promises";
23440
+ import { join as join30 } from "path";
22291
23441
  import { spawn as spawn2 } from "child_process";
22292
23442
  var Utils3 = null;
22293
23443
  var utilsLoaded3 = false;
@@ -22302,7 +23452,7 @@ async function loadUtils3() {
22302
23452
  }
22303
23453
  async function getIgnoredPaths(repoPath, relativePaths) {
22304
23454
  if (relativePaths.length === 0) return /* @__PURE__ */ new Set();
22305
- return new Promise((resolve6) => {
23455
+ return new Promise((resolve7) => {
22306
23456
  const proc = spawn2("git", ["check-ignore", "--stdin"], { cwd: repoPath });
22307
23457
  let output = "";
22308
23458
  proc.stdout?.on("data", (d) => {
@@ -22314,16 +23464,16 @@ async function getIgnoredPaths(repoPath, relativePaths) {
22314
23464
  const ignored = new Set(
22315
23465
  output.split("\n").map((l) => l.trim()).filter(Boolean)
22316
23466
  );
22317
- resolve6(ignored);
23467
+ resolve7(ignored);
22318
23468
  });
22319
- proc.on("error", () => resolve6(/* @__PURE__ */ new Set()));
23469
+ proc.on("error", () => resolve7(/* @__PURE__ */ new Set()));
22320
23470
  });
22321
23471
  }
22322
23472
  async function listExplorerEntriesRecursive(rootPath, currentPath, relativePath) {
22323
23473
  const dirents = await readdir11(currentPath, { withFileTypes: true });
22324
23474
  const isRoot = relativePath.length === 0;
22325
23475
  const entries = dirents.filter((d) => !(isRoot && d.name === ".git") && d.name !== "node_modules").map((d) => {
22326
- const entryPath = relativePath ? join28(relativePath, d.name) : d.name;
23476
+ const entryPath = relativePath ? join30(relativePath, d.name) : d.name;
22327
23477
  return {
22328
23478
  name: d.name,
22329
23479
  type: d.isDirectory() ? "folder" : "file",
@@ -22335,7 +23485,7 @@ async function listExplorerEntriesRecursive(rootPath, currentPath, relativePath)
22335
23485
  });
22336
23486
  const nestedEntries = await Promise.all(
22337
23487
  entries.filter((entry) => entry.type === "folder").map(
22338
- (entry) => listExplorerEntriesRecursive(rootPath, join28(rootPath, entry.path), entry.path)
23488
+ (entry) => listExplorerEntriesRecursive(rootPath, join30(rootPath, entry.path), entry.path)
22339
23489
  )
22340
23490
  );
22341
23491
  return [...entries, ...nestedEntries.flat()];
@@ -22371,7 +23521,7 @@ async function handleListThreadExplorerDir(c, threadManager) {
22371
23521
  const entries = recursive ? await listExplorerEntriesRecursive(thread.path, absDir, queryPath) : dirents.filter((d) => !(!queryPath && d.name === ".git")).map((d) => ({
22372
23522
  name: d.name,
22373
23523
  type: d.isDirectory() ? "folder" : "file",
22374
- path: queryPath ? join28(queryPath, d.name) : d.name
23524
+ path: queryPath ? join30(queryPath, d.name) : d.name
22375
23525
  })).sort((a, b) => {
22376
23526
  if (a.type !== b.type) return a.type === "folder" ? -1 : 1;
22377
23527
  return a.name.localeCompare(b.name);
@@ -22517,7 +23667,7 @@ async function handleCreateExplorerFile(c, threadManager) {
22517
23667
  absParent = validated;
22518
23668
  }
22519
23669
  await mkdir7(absParent, { recursive: true });
22520
- await writeFile6(join28(absParent, name), "", "utf-8");
23670
+ await writeFile6(join30(absParent, name), "", "utf-8");
22521
23671
  const relPath = dirPath ? `${dirPath}/${name}` : name;
22522
23672
  return c.json({ success: true, path: relPath });
22523
23673
  } catch (error) {
@@ -22559,7 +23709,7 @@ async function handleGetExplorerMedia(c, threadManager) {
22559
23709
  }
22560
23710
  const ext = getFileExtension(filePath);
22561
23711
  const contentType = MEDIA_MIME_TYPES[ext] ?? "application/octet-stream";
22562
- const data = await readFile14(absPath);
23712
+ const data = await readFile15(absPath);
22563
23713
  return new Response(data, {
22564
23714
  headers: {
22565
23715
  "Content-Type": contentType,
@@ -22609,7 +23759,7 @@ async function handleCreateExplorerFolder(c, threadManager) {
22609
23759
  }
22610
23760
  absParent = validated;
22611
23761
  }
22612
- await mkdir7(join28(absParent, name), { recursive: true });
23762
+ await mkdir7(join30(absParent, name), { recursive: true });
22613
23763
  const relPath = dirPath ? `${dirPath}/${name}` : name;
22614
23764
  return c.json({ success: true, path: relPath });
22615
23765
  } catch (error) {
@@ -22782,13 +23932,13 @@ async function handleFixComments(c, threadManager) {
22782
23932
  }
22783
23933
 
22784
23934
  // src/features/threads/threads-ai-files.route.ts
22785
- import { readFile as readFile15, writeFile as writeFile8, mkdir as mkdir9, access as access5, rm as rm7, stat as stat5 } from "fs/promises";
22786
- import { join as join30 } from "path";
23935
+ import { readFile as readFile16, writeFile as writeFile8, mkdir as mkdir9, access as access5, rm as rm7, stat as stat5 } from "fs/promises";
23936
+ import { join as join32 } from "path";
22787
23937
  import { existsSync as existsSync18 } from "fs";
22788
23938
 
22789
23939
  // src/features/git/git-download-folder.ts
22790
23940
  import { mkdir as mkdir8, writeFile as writeFile7 } from "fs/promises";
22791
- import { join as join29, dirname as dirname5 } from "path";
23941
+ import { join as join31, dirname as dirname5 } from "path";
22792
23942
  async function downloadGithubFolder(repoUrl, srcPath, destPath, options = {}) {
22793
23943
  const { token, ref } = options;
22794
23944
  const match = repoUrl.replace(/\.git$/, "").match(/github\.com\/([^/]+)\/([^/]+)/);
@@ -22824,7 +23974,7 @@ async function downloadGithubFolder(repoUrl, srcPath, destPath, options = {}) {
22824
23974
  await Promise.all(
22825
23975
  files.map(async (file) => {
22826
23976
  const relativePath = file.path.slice(prefix.length);
22827
- const localPath = join29(destPath, relativePath);
23977
+ const localPath = join31(destPath, relativePath);
22828
23978
  await mkdir8(dirname5(localPath), { recursive: true });
22829
23979
  const rawUrl = `https://raw.githubusercontent.com/${owner}/${repo}/${refParam}/${file.path}`;
22830
23980
  const fileRes = await fetch(rawUrl, { headers });
@@ -22942,7 +24092,7 @@ Links to important documentation, tools, or references.
22942
24092
  400
22943
24093
  );
22944
24094
  }
22945
- const content = await readFile15(absPath, "utf-8");
24095
+ const content = await readFile16(absPath, "utf-8");
22946
24096
  return c.json({ content, path: filePath });
22947
24097
  } catch (error) {
22948
24098
  return errorResponse(
@@ -22981,7 +24131,7 @@ async function handleSaveThreadAIFile(c, threadManager) {
22981
24131
  if (!absPath) {
22982
24132
  return c.json({ error: { code: "BAD_REQUEST", message: "Invalid file path" } }, 400);
22983
24133
  }
22984
- const parentDir = join30(absPath, "..");
24134
+ const parentDir = join32(absPath, "..");
22985
24135
  await mkdir9(parentDir, { recursive: true });
22986
24136
  await writeFile8(absPath, content, "utf-8");
22987
24137
  return c.json({ success: true, path: filePath });
@@ -23070,10 +24220,10 @@ async function handleCreateThreadAgent(c, threadManager) {
23070
24220
  if (!thread) {
23071
24221
  return errorResponse(c, ErrorCodes.THREAD_NOT_FOUND, `Thread not found: ${threadId}`, 404);
23072
24222
  }
23073
- const agentRelPath = join30(".agents", "agents", name);
23074
- const agentAbsPath = join30(thread.path, agentRelPath);
23075
- const agentFileRelPath = join30(agentRelPath, "AGENT.md");
23076
- const agentFileAbsPath = join30(agentAbsPath, "AGENT.md");
24223
+ const agentRelPath = join32(".agents", "agents", name);
24224
+ const agentAbsPath = join32(thread.path, agentRelPath);
24225
+ const agentFileRelPath = join32(agentRelPath, "AGENT.md");
24226
+ const agentFileAbsPath = join32(agentAbsPath, "AGENT.md");
23077
24227
  if (existsSync18(agentAbsPath)) {
23078
24228
  return c.json(
23079
24229
  { error: { code: "CONFLICT", message: `Agent '${name}' already exists` } },
@@ -23140,10 +24290,10 @@ async function handleCreateThreadSkill(c, threadManager) {
23140
24290
  if (!thread) {
23141
24291
  return errorResponse(c, ErrorCodes.THREAD_NOT_FOUND, `Thread not found: ${threadId}`, 404);
23142
24292
  }
23143
- const skillRelPath = join30(".agents", "skills", name);
23144
- const skillAbsPath = join30(thread.path, skillRelPath);
23145
- const skillFileRelPath = join30(skillRelPath, "SKILL.md");
23146
- const skillFileAbsPath = join30(skillAbsPath, "SKILL.md");
24293
+ const skillRelPath = join32(".agents", "skills", name);
24294
+ const skillAbsPath = join32(thread.path, skillRelPath);
24295
+ const skillFileRelPath = join32(skillRelPath, "SKILL.md");
24296
+ const skillFileAbsPath = join32(skillAbsPath, "SKILL.md");
23147
24297
  if (existsSync18(skillAbsPath)) {
23148
24298
  return c.json(
23149
24299
  { error: { code: "CONFLICT", message: `Skill '${name}' already exists` } },
@@ -23274,7 +24424,7 @@ var ScriptProcessManager = class {
23274
24424
  if (outputBuffer.length > 0) {
23275
24425
  yield outputBuffer.shift();
23276
24426
  } else {
23277
- await new Promise((resolve6) => setTimeout(resolve6, 50));
24427
+ await new Promise((resolve7) => setTimeout(resolve7, 50));
23278
24428
  }
23279
24429
  }
23280
24430
  } finally {
@@ -23608,7 +24758,7 @@ var GitManagerImpl = class {
23608
24758
  yield event;
23609
24759
  }
23610
24760
  } else {
23611
- await new Promise((resolve6) => setTimeout(resolve6, 10));
24761
+ await new Promise((resolve7) => setTimeout(resolve7, 10));
23612
24762
  }
23613
24763
  }
23614
24764
  if (processError) {
@@ -23671,7 +24821,7 @@ var GitManagerImpl = class {
23671
24821
  * @returns true if the branch exists, false otherwise
23672
24822
  */
23673
24823
  async checkBranchExists(repoPath, branchName) {
23674
- return new Promise((resolve6) => {
24824
+ return new Promise((resolve7) => {
23675
24825
  const gitProcess = spawnProcess(
23676
24826
  "git",
23677
24827
  ["show-ref", "--verify", "--quiet", `refs/heads/${branchName}`],
@@ -23680,10 +24830,10 @@ var GitManagerImpl = class {
23680
24830
  }
23681
24831
  );
23682
24832
  gitProcess.on("close", (code) => {
23683
- resolve6(code === 0);
24833
+ resolve7(code === 0);
23684
24834
  });
23685
24835
  gitProcess.on("error", () => {
23686
- resolve6(false);
24836
+ resolve7(false);
23687
24837
  });
23688
24838
  });
23689
24839
  }
@@ -23741,7 +24891,7 @@ var GitManagerImpl = class {
23741
24891
  yield event;
23742
24892
  }
23743
24893
  } else {
23744
- await new Promise((resolve6) => setTimeout(resolve6, 10));
24894
+ await new Promise((resolve7) => setTimeout(resolve7, 10));
23745
24895
  }
23746
24896
  }
23747
24897
  if (processError) {
@@ -23797,7 +24947,7 @@ import { Hono as Hono13 } from "hono";
23797
24947
  // src/features/git/git-user.route.ts
23798
24948
  init_utils();
23799
24949
  function readGitConfigValue(key) {
23800
- return new Promise((resolve6) => {
24950
+ return new Promise((resolve7) => {
23801
24951
  const proc = spawnProcess("git", ["config", key]);
23802
24952
  let out = "";
23803
24953
  proc.stdout?.on("data", (d) => {
@@ -23805,12 +24955,12 @@ function readGitConfigValue(key) {
23805
24955
  });
23806
24956
  proc.on("close", (code) => {
23807
24957
  if (code === 0) {
23808
- resolve6(out.trim());
24958
+ resolve7(out.trim());
23809
24959
  } else {
23810
- resolve6("");
24960
+ resolve7("");
23811
24961
  }
23812
24962
  });
23813
- proc.on("error", () => resolve6(""));
24963
+ proc.on("error", () => resolve7(""));
23814
24964
  });
23815
24965
  }
23816
24966
  async function gitUserHandler(c) {
@@ -23855,14 +25005,14 @@ async function gitStatusHandler(c, metadataManager) {
23855
25005
  400
23856
25006
  );
23857
25007
  }
23858
- const { hasChanges, changedFilesCount } = await new Promise((resolve6) => {
25008
+ const { hasChanges, changedFilesCount } = await new Promise((resolve7) => {
23859
25009
  getGitStatus(gitRoot).then((statusOutput) => {
23860
25010
  const lines = statusOutput.trim().split("\n").filter((line) => line.length > 0);
23861
- resolve6({
25011
+ resolve7({
23862
25012
  hasChanges: lines.length > 0,
23863
25013
  changedFilesCount: lines.length
23864
25014
  });
23865
- }).catch(() => resolve6({ hasChanges: false, changedFilesCount: 0 }));
25015
+ }).catch(() => resolve7({ hasChanges: false, changedFilesCount: 0 }));
23866
25016
  });
23867
25017
  const currentBranch = await getCurrentBranch(gitRoot);
23868
25018
  const hasUnpushed = await hasUnpushedCommits(gitRoot, currentBranch);
@@ -23929,15 +25079,26 @@ async function gitDiffHandler(c, metadataManager) {
23929
25079
  let oldContent = "";
23930
25080
  let newContent = "";
23931
25081
  const hunks = [];
25082
+ const isBinary = getExplorerFileMediaType(file.path) !== null;
25083
+ if (isBinary) {
25084
+ filesWithDiff.push({
25085
+ ...file,
25086
+ oldContent: "",
25087
+ newContent: "",
25088
+ isBinary: true,
25089
+ hunks: []
25090
+ });
25091
+ continue;
25092
+ }
23932
25093
  if (file.status === "deleted") {
23933
- const content = await new Promise((resolve6) => {
25094
+ const content = await new Promise((resolve7) => {
23934
25095
  const proc = spawnProcess("git", ["show", `HEAD:${file.path}`], { cwd: gitRoot });
23935
25096
  let out = "";
23936
25097
  proc.stdout?.on("data", (d) => {
23937
25098
  out += d.toString();
23938
25099
  });
23939
- proc.on("close", () => resolve6(out));
23940
- proc.on("error", () => resolve6(""));
25100
+ proc.on("close", () => resolve7(out));
25101
+ proc.on("error", () => resolve7(""));
23941
25102
  });
23942
25103
  oldContent = content;
23943
25104
  } else if (file.status === "added" && file.path.startsWith("(new)")) {
@@ -23947,7 +25108,7 @@ async function gitDiffHandler(c, metadataManager) {
23947
25108
  newContent = "";
23948
25109
  }
23949
25110
  } else {
23950
- const diffOutput = await new Promise((resolve6) => {
25111
+ const diffOutput = await new Promise((resolve7) => {
23951
25112
  const proc = spawnProcess(
23952
25113
  "git",
23953
25114
  file.status === "added" ? ["diff", "--cached", "--", file.path] : ["diff", "HEAD", "--", file.path],
@@ -23957,21 +25118,21 @@ async function gitDiffHandler(c, metadataManager) {
23957
25118
  proc.stdout?.on("data", (d) => {
23958
25119
  out += d.toString();
23959
25120
  });
23960
- proc.on("close", () => resolve6(out));
23961
- proc.on("error", () => resolve6(""));
25121
+ proc.on("close", () => resolve7(out));
25122
+ proc.on("error", () => resolve7(""));
23962
25123
  });
23963
25124
  const lines = diffOutput.split("\n");
23964
25125
  let currentHunk = null;
23965
25126
  let oldLineNum = 0;
23966
25127
  let newLineNum = 0;
23967
- const oldContentOutput = await new Promise((resolve6) => {
25128
+ const oldContentOutput = await new Promise((resolve7) => {
23968
25129
  const proc = spawnProcess("git", ["show", `HEAD:${file.path}`], { cwd: gitRoot });
23969
25130
  let out = "";
23970
25131
  proc.stdout?.on("data", (d) => {
23971
25132
  out += d.toString();
23972
25133
  });
23973
- proc.on("close", () => resolve6(out));
23974
- proc.on("error", () => resolve6(""));
25134
+ proc.on("close", () => resolve7(out));
25135
+ proc.on("error", () => resolve7(""));
23975
25136
  });
23976
25137
  oldContent = oldContentOutput;
23977
25138
  try {
@@ -24028,6 +25189,7 @@ async function gitDiffHandler(c, metadataManager) {
24028
25189
  ...file,
24029
25190
  oldContent,
24030
25191
  newContent,
25192
+ isBinary: false,
24031
25193
  hunks
24032
25194
  });
24033
25195
  }
@@ -24807,7 +25969,7 @@ function sanitizeBranchName(name) {
24807
25969
  return name.trim().toLowerCase().replace(/\s+/g, "-").replace(/[^a-zA-Z0-9\-_/.]/g, "").replace(/^[/.]+|[/.]+$/g, "").replace(/-+/g, "-") || "branch";
24808
25970
  }
24809
25971
  function checkBranchExists(gitRoot, branchName) {
24810
- return new Promise((resolve6) => {
25972
+ return new Promise((resolve7) => {
24811
25973
  const proc = spawnProcess(
24812
25974
  "git",
24813
25975
  ["show-ref", "--verify", "--quiet", `refs/heads/${branchName}`],
@@ -24816,15 +25978,15 @@ function checkBranchExists(gitRoot, branchName) {
24816
25978
  }
24817
25979
  );
24818
25980
  proc.on("close", (code) => {
24819
- resolve6(code === 0);
25981
+ resolve7(code === 0);
24820
25982
  });
24821
25983
  proc.on("error", () => {
24822
- resolve6(false);
25984
+ resolve7(false);
24823
25985
  });
24824
25986
  });
24825
25987
  }
24826
25988
  function createAndCheckoutBranch(gitRoot, branchName) {
24827
- return new Promise((resolve6, reject) => {
25989
+ return new Promise((resolve7, reject) => {
24828
25990
  const proc = spawnProcess("git", ["checkout", "-b", branchName], {
24829
25991
  cwd: gitRoot
24830
25992
  });
@@ -24836,7 +25998,7 @@ function createAndCheckoutBranch(gitRoot, branchName) {
24836
25998
  }
24837
25999
  proc.on("close", (code) => {
24838
26000
  if (code === 0) {
24839
- resolve6();
26001
+ resolve7();
24840
26002
  } else {
24841
26003
  reject(new Error(err || `Failed to create branch "${branchName}"`));
24842
26004
  }
@@ -24929,7 +26091,7 @@ function sanitizeBranchName2(name) {
24929
26091
  return name.trim().toLowerCase().replace(/\s+/g, "-").replace(/[^a-zA-Z0-9\-_/.]/g, "").replace(/^[/.]+|[/.]+$/g, "").replace(/-+/g, "-") || "branch";
24930
26092
  }
24931
26093
  function checkBranchExists2(gitRoot, branchName) {
24932
- return new Promise((resolve6) => {
26094
+ return new Promise((resolve7) => {
24933
26095
  const proc = spawnProcess(
24934
26096
  "git",
24935
26097
  ["show-ref", "--verify", "--quiet", `refs/heads/${branchName}`],
@@ -24938,16 +26100,16 @@ function checkBranchExists2(gitRoot, branchName) {
24938
26100
  }
24939
26101
  );
24940
26102
  proc.on("close", (code) => {
24941
- resolve6(code === 0);
26103
+ resolve7(code === 0);
24942
26104
  });
24943
26105
  proc.on("error", () => {
24944
- resolve6(false);
26106
+ resolve7(false);
24945
26107
  });
24946
26108
  });
24947
26109
  }
24948
26110
  function createAndCheckoutBranch2(gitRoot, branchName, baseBranch) {
24949
26111
  const args2 = baseBranch ? ["checkout", "-b", branchName, `origin/${baseBranch}`] : ["checkout", "-b", branchName];
24950
- return new Promise((resolve6, reject) => {
26112
+ return new Promise((resolve7, reject) => {
24951
26113
  const proc = spawnProcess("git", args2, {
24952
26114
  cwd: gitRoot
24953
26115
  });
@@ -24959,7 +26121,7 @@ function createAndCheckoutBranch2(gitRoot, branchName, baseBranch) {
24959
26121
  }
24960
26122
  proc.on("close", (code) => {
24961
26123
  if (code === 0) {
24962
- resolve6();
26124
+ resolve7();
24963
26125
  } else {
24964
26126
  reject(new Error(err || `Failed to create branch "${branchName}"`));
24965
26127
  }
@@ -25074,14 +26236,14 @@ async function gitSyncBranchHandler(c, metadataManager) {
25074
26236
  );
25075
26237
  }
25076
26238
  const { spawnProcess: spawnProcess2 } = await Promise.resolve().then(() => (init_utils(), utils_exports));
25077
- return new Promise((resolve6) => {
26239
+ return new Promise((resolve7) => {
25078
26240
  console.log(`[sync-branch] Checking branch status...`);
25079
26241
  const statusProc = spawnProcess2("git", ["status", "-sb"], { cwd: gitRoot });
25080
26242
  let statusOutput = "";
25081
26243
  statusProc.on("close", (statusCode) => {
25082
26244
  if (statusCode !== 0) {
25083
26245
  console.log(`[sync-branch] Failed to get git status: code ${statusCode}`);
25084
- resolve6(
26246
+ resolve7(
25085
26247
  c.json(
25086
26248
  {
25087
26249
  error: "Failed to check git status",
@@ -25115,7 +26277,7 @@ async function gitSyncBranchHandler(c, metadataManager) {
25115
26277
  pushProc.on("close", (pushCode) => {
25116
26278
  if (pushCode !== 0) {
25117
26279
  console.log(`[sync-branch] Failed to push commits: code ${pushCode}`);
25118
- resolve6(
26280
+ resolve7(
25119
26281
  c.json(
25120
26282
  {
25121
26283
  error: `Failed to push commits before sync: ${pushErr || pushOut}`,
@@ -25133,7 +26295,7 @@ async function gitSyncBranchHandler(c, metadataManager) {
25133
26295
  console.log(
25134
26296
  `[sync-branch] \u2717 Push process error: ${error instanceof Error ? error.message : String(error)}`
25135
26297
  );
25136
- resolve6(
26298
+ resolve7(
25137
26299
  c.json(
25138
26300
  {
25139
26301
  error: `Failed to push commits: ${error instanceof Error ? error.message : String(error)}`
@@ -25161,7 +26323,7 @@ async function gitSyncBranchHandler(c, metadataManager) {
25161
26323
  console.log(
25162
26324
  `[sync-branch] \u2717 Status process error: ${error instanceof Error ? error.message : String(error)}`
25163
26325
  );
25164
- resolve6(
26326
+ resolve7(
25165
26327
  c.json(
25166
26328
  {
25167
26329
  error: `Failed to check git status: ${error instanceof Error ? error.message : String(error)}`
@@ -25176,7 +26338,7 @@ async function gitSyncBranchHandler(c, metadataManager) {
25176
26338
  configProc.on("close", (configCode) => {
25177
26339
  if (configCode !== 0) {
25178
26340
  console.log(`[sync-branch] Failed to configure git: code ${configCode}`);
25179
- resolve6(
26341
+ resolve7(
25180
26342
  c.json(
25181
26343
  {
25182
26344
  error: "Failed to configure git pull strategy",
@@ -25210,7 +26372,7 @@ async function gitSyncBranchHandler(c, metadataManager) {
25210
26372
  if (code === 0) {
25211
26373
  const message = currentBranch === defaultBranch ? `Successfully pulled latest changes from origin/${defaultBranch}` : `Successfully synced ${currentBranch} with ${defaultBranch}`;
25212
26374
  console.log(`[sync-branch] \u2713 ${message}`);
25213
- resolve6(
26375
+ resolve7(
25214
26376
  c.json({
25215
26377
  success: true,
25216
26378
  message,
@@ -25221,7 +26383,7 @@ async function gitSyncBranchHandler(c, metadataManager) {
25221
26383
  } else {
25222
26384
  console.log(`[sync-branch] \u2717 Failed to sync with exit code ${code}`);
25223
26385
  console.log(`[sync-branch] Error output: ${err || out}`);
25224
- resolve6(
26386
+ resolve7(
25225
26387
  c.json(
25226
26388
  {
25227
26389
  error: `Failed to sync with ${defaultBranch}: ${err || out}`,
@@ -25236,7 +26398,7 @@ async function gitSyncBranchHandler(c, metadataManager) {
25236
26398
  console.log(
25237
26399
  `[sync-branch] \u2717 Process error: ${error instanceof Error ? error.message : String(error)}`
25238
26400
  );
25239
- resolve6(
26401
+ resolve7(
25240
26402
  c.json(
25241
26403
  {
25242
26404
  error: `Failed to sync branch: ${error instanceof Error ? error.message : String(error)}`
@@ -25250,7 +26412,7 @@ async function gitSyncBranchHandler(c, metadataManager) {
25250
26412
  console.log(
25251
26413
  `[sync-branch] \u2717 Config process error: ${error instanceof Error ? error.message : String(error)}`
25252
26414
  );
25253
- resolve6(
26415
+ resolve7(
25254
26416
  c.json(
25255
26417
  {
25256
26418
  error: `Failed to configure git: ${error instanceof Error ? error.message : String(error)}`
@@ -25271,7 +26433,7 @@ async function gitSyncBranchHandler(c, metadataManager) {
25271
26433
  // src/features/git/git-list-branches.route.ts
25272
26434
  init_utils();
25273
26435
  function listLocalBranches(gitRoot) {
25274
- return new Promise((resolve6) => {
26436
+ return new Promise((resolve7) => {
25275
26437
  const proc = spawnProcess("git", ["branch", "--format=%(refname:short)"], { cwd: gitRoot });
25276
26438
  let out = "";
25277
26439
  if (proc.stdout) {
@@ -25281,9 +26443,9 @@ function listLocalBranches(gitRoot) {
25281
26443
  }
25282
26444
  proc.on("close", () => {
25283
26445
  const branches = out.split("\n").map((b) => b.trim()).filter(Boolean);
25284
- resolve6(branches);
26446
+ resolve7(branches);
25285
26447
  });
25286
- proc.on("error", () => resolve6([]));
26448
+ proc.on("error", () => resolve7([]));
25287
26449
  });
25288
26450
  }
25289
26451
  async function gitListBranchesHandler(c, metadataManager) {
@@ -25315,7 +26477,7 @@ async function gitListBranchesHandler(c, metadataManager) {
25315
26477
  // src/features/git/git-checkout-branch.route.ts
25316
26478
  init_utils();
25317
26479
  function checkoutBranch(gitRoot, branchName) {
25318
- return new Promise((resolve6, reject) => {
26480
+ return new Promise((resolve7, reject) => {
25319
26481
  const proc = spawnProcess("git", ["checkout", branchName], { cwd: gitRoot });
25320
26482
  let err = "";
25321
26483
  if (proc.stderr) {
@@ -25325,7 +26487,7 @@ function checkoutBranch(gitRoot, branchName) {
25325
26487
  }
25326
26488
  proc.on("close", (code) => {
25327
26489
  if (code === 0) {
25328
- resolve6();
26490
+ resolve7();
25329
26491
  } else {
25330
26492
  reject(new Error(err.trim() || `Failed to checkout branch "${branchName}"`));
25331
26493
  }
@@ -25375,7 +26537,7 @@ import { unlinkSync } from "fs";
25375
26537
  import { resolve as resolve5 } from "path";
25376
26538
  init_utils();
25377
26539
  function runGit2(args2, cwd) {
25378
- return new Promise((resolve6, reject) => {
26540
+ return new Promise((resolve7, reject) => {
25379
26541
  const proc = spawnProcess("git", args2, { cwd });
25380
26542
  let out = "";
25381
26543
  let err = "";
@@ -25386,7 +26548,7 @@ function runGit2(args2, cwd) {
25386
26548
  err += d.toString();
25387
26549
  });
25388
26550
  proc.on("close", (code) => {
25389
- if (code === 0) resolve6(out.trim());
26551
+ if (code === 0) resolve7(out.trim());
25390
26552
  else reject(new Error(err.trim() || `git ${args2[0]} failed with code ${code}`));
25391
26553
  });
25392
26554
  proc.on("error", reject);
@@ -25438,7 +26600,7 @@ async function gitRevertFileHandler(c, metadataManager) {
25438
26600
  // src/features/git/git-checkpoint.route.ts
25439
26601
  init_utils();
25440
26602
  function stashWithLabel(gitRoot, label) {
25441
- return new Promise((resolve6, reject) => {
26603
+ return new Promise((resolve7, reject) => {
25442
26604
  const proc = spawnProcess("git", ["stash", "push", "--include-untracked", "-m", label], {
25443
26605
  cwd: gitRoot
25444
26606
  });
@@ -25458,7 +26620,7 @@ function stashWithLabel(gitRoot, label) {
25458
26620
  if (code === 0) {
25459
26621
  const nothingSaved = out.includes("No local changes to save");
25460
26622
  if (nothingSaved) {
25461
- resolve6(false);
26623
+ resolve7(false);
25462
26624
  return;
25463
26625
  }
25464
26626
  const apply = spawnProcess("git", ["stash", "apply", "--index"], { cwd: gitRoot });
@@ -25470,7 +26632,7 @@ function stashWithLabel(gitRoot, label) {
25470
26632
  }
25471
26633
  apply.on("close", (applyCode) => {
25472
26634
  if (applyCode === 0) {
25473
- resolve6(true);
26635
+ resolve7(true);
25474
26636
  } else {
25475
26637
  reject(new Error(applyErr || "Failed to re-apply stash after checkpoint"));
25476
26638
  }
@@ -25519,7 +26681,7 @@ async function gitCheckpointHandler(c, metadataManager) {
25519
26681
  // src/features/git/git-checkpoint-restore.route.ts
25520
26682
  init_utils();
25521
26683
  function listStashes(gitRoot) {
25522
- return new Promise((resolve6, reject) => {
26684
+ return new Promise((resolve7, reject) => {
25523
26685
  const proc = spawnProcess("git", ["stash", "list", "--format=%gd %s"], { cwd: gitRoot });
25524
26686
  let out = "";
25525
26687
  let err = "";
@@ -25543,18 +26705,18 @@ function listStashes(gitRoot) {
25543
26705
  if (!match) return null;
25544
26706
  return { index: parseInt(match[1], 10), label: match[2].trim() };
25545
26707
  }).filter((e) => e !== null);
25546
- resolve6(entries);
26708
+ resolve7(entries);
25547
26709
  });
25548
26710
  proc.on("error", reject);
25549
26711
  });
25550
26712
  }
25551
26713
  function discardWorkingChanges(gitRoot) {
25552
- return new Promise((resolve6, reject) => {
26714
+ return new Promise((resolve7, reject) => {
25553
26715
  const proc = spawnProcess("git", ["checkout", "--", "."], { cwd: gitRoot });
25554
26716
  proc.on("close", () => {
25555
26717
  const clean = spawnProcess("git", ["clean", "-fd"], { cwd: gitRoot });
25556
26718
  clean.on("close", (code) => {
25557
- if (code === 0) resolve6();
26719
+ if (code === 0) resolve7();
25558
26720
  else reject(new Error("Failed to clean working tree"));
25559
26721
  });
25560
26722
  clean.on("error", reject);
@@ -25563,7 +26725,7 @@ function discardWorkingChanges(gitRoot) {
25563
26725
  });
25564
26726
  }
25565
26727
  function applyStash(gitRoot, index) {
25566
- return new Promise((resolve6, reject) => {
26728
+ return new Promise((resolve7, reject) => {
25567
26729
  const proc = spawnProcess("git", ["stash", "apply", `stash@{${index}}`], { cwd: gitRoot });
25568
26730
  let err = "";
25569
26731
  if (proc.stderr) {
@@ -25572,7 +26734,7 @@ function applyStash(gitRoot, index) {
25572
26734
  });
25573
26735
  }
25574
26736
  proc.on("close", (code) => {
25575
- if (code === 0) resolve6();
26737
+ if (code === 0) resolve7();
25576
26738
  else reject(new Error(err || "Failed to apply stash"));
25577
26739
  });
25578
26740
  proc.on("error", reject);
@@ -25616,6 +26778,175 @@ async function gitCheckpointRestoreHandler(c, metadataManager) {
25616
26778
  }
25617
26779
  }
25618
26780
 
26781
+ // src/features/git/git-rename-branch.route.ts
26782
+ init_utils();
26783
+ function sanitizeBranchName3(name) {
26784
+ if (!name || typeof name !== "string") {
26785
+ return "";
26786
+ }
26787
+ return name.trim().toLowerCase().replace(/\s+/g, "-").replace(/[^a-zA-Z0-9\-_/.]/g, "").replace(/^[/.]+|[/.]+$/g, "").replace(/-+/g, "-") || "";
26788
+ }
26789
+ function checkBranchExists3(gitRoot, branchName) {
26790
+ return new Promise((resolve7) => {
26791
+ const proc = spawnProcess(
26792
+ "git",
26793
+ ["show-ref", "--verify", "--quiet", `refs/heads/${branchName}`],
26794
+ {
26795
+ cwd: gitRoot
26796
+ }
26797
+ );
26798
+ proc.on("close", (code) => {
26799
+ resolve7(code === 0);
26800
+ });
26801
+ proc.on("error", () => {
26802
+ resolve7(false);
26803
+ });
26804
+ });
26805
+ }
26806
+ function renameBranch(gitRoot, newBranchName) {
26807
+ return new Promise((resolve7, reject) => {
26808
+ const proc = spawnProcess("git", ["branch", "-m", newBranchName], {
26809
+ cwd: gitRoot
26810
+ });
26811
+ let err = "";
26812
+ if (proc.stderr) {
26813
+ proc.stderr.on("data", (d) => {
26814
+ err += d.toString();
26815
+ });
26816
+ }
26817
+ proc.on("close", (code) => {
26818
+ if (code === 0) {
26819
+ resolve7();
26820
+ } else {
26821
+ reject(new Error(err || `Failed to rename branch to "${newBranchName}"`));
26822
+ }
26823
+ });
26824
+ proc.on("error", reject);
26825
+ });
26826
+ }
26827
+ async function gitRenameBranchHandler(c, metadataManager) {
26828
+ try {
26829
+ const threadId = c.req.param("threadId");
26830
+ const thread = await metadataManager.loadThreads().then((threads2) => threads2.find((t) => t.id === threadId));
26831
+ if (!thread) {
26832
+ return c.json({ error: "Thread not found" }, 404);
26833
+ }
26834
+ const repoPath = thread.path;
26835
+ if (!repoPath) {
26836
+ return c.json({ error: "Thread path not found" }, 404);
26837
+ }
26838
+ const body = await c.req.json().catch(() => ({}));
26839
+ const rawName = body.newBranchName?.trim();
26840
+ if (!rawName) {
26841
+ return c.json({ error: "newBranchName is required" }, 400);
26842
+ }
26843
+ const newBranchName = sanitizeBranchName3(rawName);
26844
+ if (!newBranchName) {
26845
+ return c.json({ error: "Invalid branch name" }, 400);
26846
+ }
26847
+ const absolutePath = resolveThreadPath(repoPath);
26848
+ let gitRoot;
26849
+ try {
26850
+ gitRoot = await getGitRoot(absolutePath);
26851
+ } catch {
26852
+ return c.json({ error: `Path is not a git repository: ${absolutePath}` }, 400);
26853
+ }
26854
+ const branchExists = await checkBranchExists3(gitRoot, newBranchName);
26855
+ if (branchExists) {
26856
+ return c.json({ error: `Branch "${newBranchName}" already exists` }, 409);
26857
+ }
26858
+ await renameBranch(gitRoot, newBranchName);
26859
+ const threads = await metadataManager.loadThreads();
26860
+ const threadIndex = threads.findIndex((t) => t.id === threadId);
26861
+ if (threadIndex !== -1) {
26862
+ threads[threadIndex].currentBranch = newBranchName;
26863
+ await metadataManager.saveThreads(threads);
26864
+ }
26865
+ return c.json({ success: true, branchName: newBranchName });
26866
+ } catch (error) {
26867
+ const message = error instanceof Error ? error.message : "Failed to rename branch";
26868
+ return c.json({ error: message }, 500);
26869
+ }
26870
+ }
26871
+
26872
+ // src/features/git/git-media.route.ts
26873
+ import { readFileSync as readFileSync8, existsSync as existsSync23 } from "fs";
26874
+ import { resolve as resolve6 } from "path";
26875
+ import { spawn as spawn3 } from "child_process";
26876
+ function spawnBinary(command, args2, cwd) {
26877
+ return new Promise((resolve7, reject) => {
26878
+ const proc = spawn3(command, args2, { cwd });
26879
+ const chunks = [];
26880
+ proc.stdout?.on("data", (d) => {
26881
+ chunks.push(Buffer.from(d));
26882
+ });
26883
+ proc.on("close", (code) => {
26884
+ if (code === 0) {
26885
+ resolve7(Buffer.concat(chunks));
26886
+ } else {
26887
+ reject(new Error(`Process exited with code ${code}`));
26888
+ }
26889
+ });
26890
+ proc.on("error", reject);
26891
+ });
26892
+ }
26893
+ async function gitMediaHandler(c, metadataManager) {
26894
+ try {
26895
+ const threadId = c.req.param("threadId");
26896
+ const filePath = c.req.query("path");
26897
+ const version = c.req.query("version");
26898
+ if (!threadId) {
26899
+ return c.json({ error: "Thread ID is required" }, 400);
26900
+ }
26901
+ if (!filePath) {
26902
+ return c.json({ error: "path query parameter is required" }, 400);
26903
+ }
26904
+ if (version !== "old" && version !== "new") {
26905
+ return c.json({ error: "version query parameter must be 'old' or 'new'" }, 400);
26906
+ }
26907
+ const thread = await metadataManager.loadThreads().then((threads) => threads.find((t) => t.id === threadId));
26908
+ if (!thread) {
26909
+ return c.json({ error: "Thread not found" }, 404);
26910
+ }
26911
+ const repoPath = thread.path;
26912
+ if (!repoPath) {
26913
+ return c.json({ error: "Thread path not found" }, 404);
26914
+ }
26915
+ const absolutePath = resolveThreadPath(repoPath);
26916
+ let gitRoot;
26917
+ try {
26918
+ gitRoot = await getGitRoot(absolutePath);
26919
+ } catch {
26920
+ return c.json({ error: `Path is not a git repository: ${absolutePath}` }, 400);
26921
+ }
26922
+ const ext = getFileExtension(filePath);
26923
+ const contentType = EXPLORER_MEDIA_MIME_TYPES[ext] ?? "application/octet-stream";
26924
+ let data;
26925
+ if (version === "old") {
26926
+ try {
26927
+ data = await spawnBinary("git", ["show", `HEAD:${filePath}`], gitRoot);
26928
+ } catch {
26929
+ return c.json({ error: `File not found in HEAD: ${filePath}` }, 404);
26930
+ }
26931
+ } else {
26932
+ const absFilePath = resolve6(gitRoot, filePath);
26933
+ if (!existsSync23(absFilePath)) {
26934
+ return c.json({ error: `File not found in working directory: ${filePath}` }, 404);
26935
+ }
26936
+ data = readFileSync8(absFilePath);
26937
+ }
26938
+ return new Response(data.buffer, {
26939
+ headers: {
26940
+ "Content-Type": contentType,
26941
+ "Cross-Origin-Resource-Policy": "cross-origin"
26942
+ }
26943
+ });
26944
+ } catch (error) {
26945
+ const message = error instanceof Error ? error.message : "Failed to get git media";
26946
+ return c.json({ error: message }, 500);
26947
+ }
26948
+ }
26949
+
25619
26950
  // src/features/git/git.routes.ts
25620
26951
  function createGitRoutes(metadataManager) {
25621
26952
  const router = new Hono13();
@@ -25638,6 +26969,9 @@ function createGitRoutes(metadataManager) {
25638
26969
  router.get("/diff/:threadId", async (c) => {
25639
26970
  return gitDiffHandler(c, metadataManager);
25640
26971
  });
26972
+ router.get("/media/:threadId", async (c) => {
26973
+ return gitMediaHandler(c, metadataManager);
26974
+ });
25641
26975
  router.post("/generate-commit-message/:threadId", async (c) => {
25642
26976
  return gitGenerateCommitMessageHandler(c, metadataManager);
25643
26977
  });
@@ -25702,6 +27036,11 @@ function createGitRoutes(metadataManager) {
25702
27036
  await invalidateGitStatusCache(db, c.req.param("threadId"));
25703
27037
  return gitCheckoutBranchHandler(c, metadataManager);
25704
27038
  });
27039
+ router.post("/rename-branch/:threadId", async (c) => {
27040
+ const db = await getDatabase();
27041
+ await invalidateGitStatusCache(db, c.req.param("threadId"));
27042
+ return gitRenameBranchHandler(c, metadataManager);
27043
+ });
25705
27044
  router.post("/revert-file/:threadId", async (c) => {
25706
27045
  const db = await getDatabase();
25707
27046
  await invalidateGitStatusCache(db, c.req.param("threadId"));
@@ -25757,8 +27096,8 @@ function createUpdateRoutes(updater) {
25757
27096
 
25758
27097
  // src/features/mcp/mcp.routes.ts
25759
27098
  import { Hono as Hono15 } from "hono";
25760
- import { readFile as readFile16, writeFile as writeFile9, mkdir as mkdir11, access as access6 } from "fs/promises";
25761
- import { join as join31, dirname as dirname7 } from "path";
27099
+ import { readFile as readFile17, writeFile as writeFile9, mkdir as mkdir11, access as access6 } from "fs/promises";
27100
+ import { join as join33, dirname as dirname7 } from "path";
25762
27101
 
25763
27102
  // src/features/mcp/mcp.popular.json
25764
27103
  var mcp_popular_default = [
@@ -25880,10 +27219,10 @@ var mcp_popular_default = [
25880
27219
  var MCP_CONFIG_PATHS2 = [".agents/mcp.json", "mcp.json"];
25881
27220
  async function readMCPConfig(projectPath) {
25882
27221
  for (const configPath of MCP_CONFIG_PATHS2) {
25883
- const fullPath = join31(projectPath, configPath);
27222
+ const fullPath = join33(projectPath, configPath);
25884
27223
  try {
25885
27224
  await access6(fullPath);
25886
- const content = await readFile16(fullPath, "utf-8");
27225
+ const content = await readFile17(fullPath, "utf-8");
25887
27226
  const config = JSON.parse(content);
25888
27227
  return { config, filePath: fullPath };
25889
27228
  } catch {
@@ -25894,7 +27233,7 @@ async function readMCPConfig(projectPath) {
25894
27233
  }
25895
27234
  async function writeMCPConfig(projectPath, config) {
25896
27235
  const existing = await readMCPConfig(projectPath);
25897
- const targetPath = existing?.filePath ?? join31(projectPath, ".agents/mcp.json");
27236
+ const targetPath = existing?.filePath ?? join33(projectPath, ".agents/mcp.json");
25898
27237
  await mkdir11(dirname7(targetPath), { recursive: true });
25899
27238
  await writeFile9(targetPath, JSON.stringify(config, null, 2), "utf-8");
25900
27239
  }
@@ -26310,8 +27649,8 @@ function getLocalNetworkAddresses() {
26310
27649
  }
26311
27650
 
26312
27651
  // src/core/server-bind-mode-store.ts
26313
- import { existsSync as existsSync23, mkdirSync as mkdirSync2, readFileSync as readFileSync8, writeFileSync } from "fs";
26314
- import { join as join32 } from "path";
27652
+ import { existsSync as existsSync24, mkdirSync as mkdirSync2, readFileSync as readFileSync9, writeFileSync } from "fs";
27653
+ import { join as join34 } from "path";
26315
27654
 
26316
27655
  // src/core/server-bind-mode.ts
26317
27656
  var DEFAULT_SERVER_BIND_MODE = "local";
@@ -26323,16 +27662,16 @@ function getServerBindHostname(mode) {
26323
27662
  }
26324
27663
 
26325
27664
  // src/core/server-bind-mode-store.ts
26326
- var SERVER_BIND_MODE_FILE = join32(DATA_DIR, "server-bind-mode.json");
27665
+ var SERVER_BIND_MODE_FILE = join34(DATA_DIR, "server-bind-mode.json");
26327
27666
  function hasServerBindModeFile() {
26328
- return existsSync23(SERVER_BIND_MODE_FILE);
27667
+ return existsSync24(SERVER_BIND_MODE_FILE);
26329
27668
  }
26330
27669
  function readServerBindMode() {
26331
27670
  try {
26332
- if (!existsSync23(SERVER_BIND_MODE_FILE)) {
27671
+ if (!existsSync24(SERVER_BIND_MODE_FILE)) {
26333
27672
  return DEFAULT_SERVER_BIND_MODE;
26334
27673
  }
26335
- const raw = readFileSync8(SERVER_BIND_MODE_FILE, "utf8");
27674
+ const raw = readFileSync9(SERVER_BIND_MODE_FILE, "utf8");
26336
27675
  const parsed = JSON.parse(raw);
26337
27676
  return normalizeServerBindMode(parsed.serverBindMode);
26338
27677
  } catch {
@@ -26656,7 +27995,8 @@ async function clipboardWriteImage(c) {
26656
27995
  pngData[i] = binary.charCodeAt(i);
26657
27996
  }
26658
27997
  } else if (imageUrl) {
26659
- const res = await fetch(imageUrl);
27998
+ const fetchUrl2 = resolveAbsoluteImageUrl(imageUrl, new URL(c.req.url).origin);
27999
+ const res = await fetch(fetchUrl2);
26660
28000
  if (!res.ok) {
26661
28001
  return c.json({ error: `Failed to fetch image: ${res.status}` }, 400);
26662
28002
  }
@@ -26673,7 +28013,7 @@ async function clipboardWriteImage(c) {
26673
28013
  }
26674
28014
 
26675
28015
  // src/features/image/image-save.route.ts
26676
- import { join as join33 } from "path";
28016
+ import { join as join35 } from "path";
26677
28017
  var Utils5 = null;
26678
28018
  var utilsLoaded5 = false;
26679
28019
  async function loadUtils5() {
@@ -26701,7 +28041,8 @@ async function saveImage(c) {
26701
28041
  data[i] = binary.charCodeAt(i);
26702
28042
  }
26703
28043
  } else if (imageUrl) {
26704
- const res = await fetch(imageUrl);
28044
+ const fetchUrl2 = resolveAbsoluteImageUrl(imageUrl, new URL(c.req.url).origin);
28045
+ const res = await fetch(fetchUrl2);
26705
28046
  if (!res.ok) {
26706
28047
  return c.json({ error: `Failed to fetch image: ${res.status}` }, 400);
26707
28048
  }
@@ -26710,7 +28051,7 @@ async function saveImage(c) {
26710
28051
  return c.json({ error: "imageUrl or imageData is required" }, 400);
26711
28052
  }
26712
28053
  const name = filename ?? `generated-image-${Date.now()}.png`;
26713
- const savePath = join33(Utils5.paths.downloads, name);
28054
+ const savePath = join35(Utils5.paths.downloads, name);
26714
28055
  await Bun.write(savePath, data);
26715
28056
  return c.json({ success: true, path: savePath });
26716
28057
  } catch (error) {
@@ -26802,7 +28143,7 @@ function createBrowserJsRoutes(threadManager, projectManager, processingStateMan
26802
28143
  });
26803
28144
  }
26804
28145
  }) : void 0;
26805
- const tools = createAllTools(thread.path, { shellPermissionGate });
28146
+ const tools = createAllTools(thread.path, { shellPermissionGate, threadId });
26806
28147
  const tool = tools[toolName];
26807
28148
  if (!tool) {
26808
28149
  return errorResponse(c, ErrorCodes.INVALID_REQUEST, `Tool ${toolName} not found`, 404);
@@ -26818,8 +28159,79 @@ function createBrowserJsRoutes(threadManager, projectManager, processingStateMan
26818
28159
  return router;
26819
28160
  }
26820
28161
 
26821
- // src/features/voice-model/voice-model.routes.ts
28162
+ // src/features/browser/browser.routes.ts
26822
28163
  import { Hono as Hono23 } from "hono";
28164
+ function createBrowserRoutes() {
28165
+ const router = new Hono23();
28166
+ router.post("/ready", async (c) => {
28167
+ try {
28168
+ const body = await c.req.json();
28169
+ const { toolCallId, webviewId } = body;
28170
+ if (!toolCallId || typeof toolCallId !== "string") {
28171
+ return errorResponse(
28172
+ c,
28173
+ ErrorCodes.INVALID_REQUEST,
28174
+ "toolCallId is required and must be a string",
28175
+ 400
28176
+ );
28177
+ }
28178
+ if (typeof webviewId !== "number" || !Number.isFinite(webviewId)) {
28179
+ return errorResponse(
28180
+ c,
28181
+ ErrorCodes.INVALID_REQUEST,
28182
+ "webviewId is required and must be a number",
28183
+ 400
28184
+ );
28185
+ }
28186
+ submitBrowserWebviewReady(toolCallId, webviewId);
28187
+ return c.json({ success: true });
28188
+ } catch (error) {
28189
+ return errorResponse(
28190
+ c,
28191
+ ErrorCodes.REQUEST_PARSE_ERROR,
28192
+ "Failed to parse request body",
28193
+ 400,
28194
+ error instanceof Error ? error.message : String(error)
28195
+ );
28196
+ }
28197
+ });
28198
+ router.post("/action-result", async (c) => {
28199
+ try {
28200
+ const body = await c.req.json();
28201
+ const { toolCallId, result } = body;
28202
+ if (!toolCallId || typeof toolCallId !== "string") {
28203
+ return errorResponse(
28204
+ c,
28205
+ ErrorCodes.INVALID_REQUEST,
28206
+ "toolCallId is required and must be a string",
28207
+ 400
28208
+ );
28209
+ }
28210
+ if (!result || typeof result !== "object" || Array.isArray(result)) {
28211
+ return errorResponse(
28212
+ c,
28213
+ ErrorCodes.INVALID_REQUEST,
28214
+ "result is required and must be an object",
28215
+ 400
28216
+ );
28217
+ }
28218
+ submitBrowserActionResult(toolCallId, result);
28219
+ return c.json({ success: true });
28220
+ } catch (error) {
28221
+ return errorResponse(
28222
+ c,
28223
+ ErrorCodes.REQUEST_PARSE_ERROR,
28224
+ "Failed to parse request body",
28225
+ 400,
28226
+ error instanceof Error ? error.message : String(error)
28227
+ );
28228
+ }
28229
+ });
28230
+ return router;
28231
+ }
28232
+
28233
+ // src/features/voice-model/voice-model.routes.ts
28234
+ import { Hono as Hono24 } from "hono";
26823
28235
  var VOICE_MODEL_URLS = {
26824
28236
  default: "https://install.tarsk.io/voice-models/ggml-tiny.en.bin",
26825
28237
  tiny: "https://install.tarsk.io/voice-models/ggml-tiny.en-q5_1.bin"
@@ -26831,7 +28243,7 @@ function getVoiceModelUrl(model) {
26831
28243
  return VOICE_MODEL_URLS.default;
26832
28244
  }
26833
28245
  function createVoiceModelRoutes() {
26834
- const router = new Hono23();
28246
+ const router = new Hono24();
26835
28247
  router.get("/download", async (c) => {
26836
28248
  const selectedModel = c.req.query("model");
26837
28249
  const modelUrl = getVoiceModelUrl(selectedModel);
@@ -26872,9 +28284,9 @@ function createVoiceModelRoutes() {
26872
28284
  }
26873
28285
 
26874
28286
  // src/features/logs/logs.routes.ts
26875
- import { Hono as Hono24 } from "hono";
28287
+ import { Hono as Hono25 } from "hono";
26876
28288
  function createLogsRoutes() {
26877
- const router = new Hono24();
28289
+ const router = new Hono25();
26878
28290
  router.post("/", async (c) => {
26879
28291
  let body = {};
26880
28292
  try {
@@ -26912,11 +28324,11 @@ function createLogsRoutes() {
26912
28324
 
26913
28325
  // src/features/user-tasks/user-tasks.routes.ts
26914
28326
  init_database();
26915
- import { Hono as Hono25 } from "hono";
28327
+ import { Hono as Hono26 } from "hono";
26916
28328
 
26917
28329
  // src/features/user-tasks/user-tasks.database.ts
26918
28330
  init_database();
26919
- import { randomUUID as randomUUID12 } from "crypto";
28331
+ import { randomUUID as randomUUID13 } from "crypto";
26920
28332
  async function getUserTasksByThread(db, threadId) {
26921
28333
  const result = await db.execute({
26922
28334
  sql: `SELECT id, threadId, content, createdAt, updatedAt
@@ -26929,7 +28341,7 @@ async function getUserTasksByThread(db, threadId) {
26929
28341
  }
26930
28342
  async function insertUserTask(threadId, content) {
26931
28343
  const db = await getDatabase();
26932
- const id = randomUUID12();
28344
+ const id = randomUUID13();
26933
28345
  const now = (/* @__PURE__ */ new Date()).toISOString();
26934
28346
  await db.execute({
26935
28347
  sql: `INSERT INTO user_tasks (id, threadId, content, createdAt, updatedAt)
@@ -26953,7 +28365,7 @@ async function deleteUserTaskById(db, id) {
26953
28365
 
26954
28366
  // src/features/user-tasks/user-tasks.routes.ts
26955
28367
  function createUserTaskRoutes() {
26956
- const router = new Hono25();
28368
+ const router = new Hono26();
26957
28369
  router.get("/:threadId/user-tasks", async (c) => {
26958
28370
  const threadId = c.req.param("threadId");
26959
28371
  const db = await getDatabase();
@@ -26993,7 +28405,7 @@ async function startTarskServer(options) {
26993
28405
  async function startTarskServerInternal(options) {
26994
28406
  const { isDebug: isDebug2, publicDir: publicDirOverride } = options;
26995
28407
  const port = isDebug2 ? 462 : process.env.PORT ? parseInt(process.env.PORT) : 641;
26996
- const app = new Hono26();
28408
+ const app = new Hono27();
26997
28409
  app.use("/*", cors());
26998
28410
  app.use("/*", async (c, next) => {
26999
28411
  c.header("Cross-Origin-Opener-Policy", "same-origin");
@@ -27086,6 +28498,7 @@ async function startTarskServerInternal(options) {
27086
28498
  "/api/browser-js",
27087
28499
  createBrowserJsRoutes(threadManager, projectManager, processingStateManager)
27088
28500
  );
28501
+ app.route("/api/browser", createBrowserRoutes());
27089
28502
  app.route("/api/voice-model", createVoiceModelRoutes());
27090
28503
  app.route("/api/logs", createLogsRoutes());
27091
28504
  app.route("/api/update", createUpdateRoutes(options.updater));
@@ -27157,10 +28570,10 @@ async function listenForTarskServer(options) {
27157
28570
  hostname: options.hostname,
27158
28571
  port: options.port
27159
28572
  });
27160
- return new Promise((resolve6, reject) => {
28573
+ return new Promise((resolve7, reject) => {
27161
28574
  server.once("error", (error) => {
27162
28575
  if (error.code === "EADDRINUSE") {
27163
- resolve6(false);
28576
+ resolve7(false);
27164
28577
  return;
27165
28578
  }
27166
28579
  startPromise = null;
@@ -27168,7 +28581,7 @@ async function listenForTarskServer(options) {
27168
28581
  });
27169
28582
  function onListening() {
27170
28583
  options.onListening();
27171
- resolve6(true);
28584
+ resolve7(true);
27172
28585
  }
27173
28586
  if (options.hostname) {
27174
28587
  server.listen(options.port, options.hostname, onListening);