nolo-cli 0.11.0 → 0.12.0-alpha.1

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 (123) hide show
  1. package/{_executeModel-ZEDPX6OF.js → _executeModel-MWAY74HA.js} +51 -50
  2. package/{action-AA4IJ5K2.js → action-RAXNQQKC.js} +8 -9
  3. package/{addContentAction-QYVO4SYP.js → addContentAction-ZHI7TVV3.js} +7 -9
  4. package/{addDialogAgentAction-WVVKT5WQ.js → addDialogAgentAction-JWXPKXNC.js} +9 -10
  5. package/{agentDeleteCommand-RDPGYIWF.js → agentDeleteCommand-LXR5A5FV.js} +38 -28
  6. package/{agentEmailCommands-2HJJKS5F.js → agentEmailCommands-DXRAP7BB.js} +64 -62
  7. package/{agentListCommands-HFEVV3ZW.js → agentListCommands-ZFMS5ZLY.js} +127 -31
  8. package/{agentMachineCommands-25ZOENJU.js → agentMachineCommands-ZJLYVV52.js} +37 -26
  9. package/{agentPullCommand-GOL2S4W2.js → agentPullCommand-KRBRIFIY.js} +7 -7
  10. package/{agentRecordCommands-GTCOAM3L.js → agentRecordCommands-DFS626TB.js} +56 -54
  11. package/{agentRunCommand-YBS3JDLD.js → agentRunCommand-GWBIE2TS.js} +59 -59
  12. package/{agentRunControl-K4VKNEHF.js → agentRunControl-IX4F5W2C.js} +3 -3
  13. package/{agentRuntimeLocal-UQBMQI2M.js → agentRuntimeLocal-YNZSHMZC.js} +30 -15
  14. package/{agentSlice-6EKOBCCS.js → agentSlice-T2PXOEPQ.js} +8 -9
  15. package/{authCommand-O2OWACN4.js → authCommand-DMW336GT.js} +15 -15
  16. package/{authCommands-H7365TZP.js → authCommands-GH5T6RYV.js} +6 -6
  17. package/{chunk-7D3JY6C2.js → chunk-3HHTVM6G.js} +64 -5
  18. package/{chunk-AFON4QUU.js → chunk-3QKO4EHV.js} +3 -3
  19. package/{chunk-4SOKXIW4.js → chunk-43UD5ZP7.js} +8 -11
  20. package/{chunk-S3GS2PRL.js → chunk-4BTHUYMY.js} +597 -68
  21. package/{chunk-SV5MWASH.js → chunk-4EH7AR52.js} +12 -12
  22. package/{chunk-FXXSGWAJ.js → chunk-4GBNEA5B.js} +1 -1
  23. package/{chunk-PXZAAB4L.js → chunk-4LG74DRG.js} +4 -4
  24. package/{chunk-2FTTDCD4.js → chunk-4ON5AV72.js} +2 -1
  25. package/{chunk-IWX4UW2E.js → chunk-524BDL33.js} +89 -8
  26. package/{chunk-X2S7JTIG.js → chunk-5ITRDSHY.js} +1240 -136
  27. package/{chunk-ISNCLGUZ.js → chunk-5NBHQD4D.js} +4 -3
  28. package/{chunk-GBDSZATH.js → chunk-6LMFFG3K.js} +3 -3
  29. package/{chunk-OUI3ZHYD.js → chunk-6OVCRC72.js} +1 -1
  30. package/{chunk-CKOJQDKR.js → chunk-7JJCGV7N.js} +146 -3
  31. package/{chunk-MQI77IRB.js → chunk-7MYHLZ3F.js} +2 -2
  32. package/{chunk-5MOUAAMK.js → chunk-AM53Q2SD.js} +194 -2564
  33. package/chunk-BYXBJQAS.js +0 -0
  34. package/{chunk-LAX2JSTX.js → chunk-DE5BA4O6.js} +20 -2
  35. package/{chunk-BX6U3ETE.js → chunk-DRMGJFGQ.js} +293 -89
  36. package/{chunk-KJR5KAUQ.js → chunk-EBH7G34M.js} +20 -7
  37. package/{chunk-E4HNJSHC.js → chunk-G3B356GF.js} +16 -18
  38. package/{chunk-KQQVGRA3.js → chunk-GDBCUWQ3.js} +35 -29
  39. package/{chunk-Q3UKJVU4.js → chunk-GPZM7QH6.js} +10 -10
  40. package/chunk-IKXL2HZ3.js +70 -0
  41. package/{chunk-QNMFBBZK.js → chunk-IVAOVLM7.js} +1 -1
  42. package/{chunk-EVZKZF32.js → chunk-JUN3HFMN.js} +182 -75
  43. package/{chunk-NEM67FX7.js → chunk-JZDIW766.js} +1 -1
  44. package/{chunk-TACHTH5D.js → chunk-LHSJMY5V.js} +18 -18
  45. package/{chunk-XYZA7S7V.js → chunk-MEVCDATU.js} +6 -6
  46. package/{chunk-YOCHZD7D.js → chunk-NCGFB7NN.js} +5 -3
  47. package/{chunk-O26GI2JP.js → chunk-NITP2JDG.js} +39 -8
  48. package/{chunk-YPRZSPF3.js → chunk-OCDEWRE2.js} +1 -1
  49. package/{chunk-PNEW2Z3T.js → chunk-OHEKYYJF.js} +7 -7
  50. package/chunk-OOIN7HSO.js +146 -0
  51. package/{chunk-DWGAEFJL.js → chunk-OPKKB25T.js} +1 -1
  52. package/{chunk-AEZAORLG.js → chunk-QCA3EPYK.js} +5 -5
  53. package/{chunk-C4FTUXVF.js → chunk-QLUFR47U.js} +11 -1
  54. package/{chunk-C2MDQQ4O.js → chunk-RNQ5BLR2.js} +1 -1
  55. package/{chunk-L53FGHA4.js → chunk-RSEUHW6T.js} +1 -1
  56. package/{chunk-2W2VYVI5.js → chunk-RZ735TLX.js} +527 -176
  57. package/chunk-S3UTETIC.js +4885 -0
  58. package/chunk-S73OEOHH.js +2286 -0
  59. package/{chunk-YTBMA7NO.js → chunk-TSKWE74T.js} +58 -8
  60. package/{chunk-5OIFWVX7.js → chunk-UG3WOFU6.js} +1 -1
  61. package/{chunk-4ZDM73SD.js → chunk-V2J635L5.js} +1 -1
  62. package/{chunk-IPYUUY4E.js → chunk-WPHL6ALY.js} +1 -1
  63. package/{chunk-3NSIQZZ6.js → chunk-WTYKCU4W.js} +9 -9
  64. package/{chunk-LP65VRCI.js → chunk-WWU275LU.js} +6 -6
  65. package/{chunk-XFOLQLDN.js → chunk-XHQWJUW4.js} +2 -2
  66. package/{chunk-D6OQHXVG.js → chunk-YDCZVUFP.js} +7 -7
  67. package/{chunk-PXQN7IBQ.js → chunk-YTQLJYLB.js} +2 -2
  68. package/{chunk-ZEYDOXHF.js → chunk-ZPFRLGIZ.js} +1467 -869
  69. package/{chunk-ZPRK73XY.js → chunk-ZR4VHQ5D.js} +14 -13
  70. package/cliEnvHelpers-VYC2OPTB.js +30 -0
  71. package/{createAgentAutomationAction-LTI5QU35.js → createAgentAutomationAction-G32GKDGV.js} +25 -26
  72. package/{createDialogAction-OJJ7L3L3.js → createDialogAction-TISANRZK.js} +26 -27
  73. package/{createPageAction-5MXWVRGY.js → createPageAction-O7KOYNV4.js} +26 -27
  74. package/{dbSlice-RQFQQVHT.js → dbSlice-WDP2ZZHK.js} +6 -7
  75. package/{deleteFile-7XP5BGC7.js → deleteFile-M7UNIUOO.js} +4 -4
  76. package/{dialogCommands-JTJ3RPPW.js → dialogCommands-6JPB3YQY.js} +33 -23
  77. package/{dialogSlice-OWROKURJ.js → dialogSlice-PRHHYSP5.js} +26 -27
  78. package/{docCreateCommands-WJBEW2V7.js → docCreateCommands-75E6TZNN.js} +33 -23
  79. package/{docDeleteCommands-BVEHJOY5.js → docDeleteCommands-EJHLN7LW.js} +34 -25
  80. package/{docReadCommands-NEWOZIJV.js → docReadCommands-RJYBC4FS.js} +33 -23
  81. package/{docStore-3DXRYBMD.js → docStore-H5VWKT5D.js} +25 -26
  82. package/{docUpdateCommands-C7WHT7UU.js → docUpdateCommands-MG4CNISV.js} +33 -23
  83. package/fetchMessages-KTNM7QAR.js +1 -1
  84. package/{getPricing-3E6UNPKL.js → getPricing-6TOZXPAU.js} +7 -3
  85. package/{handleSendMessageAction-B5PRR7BE.js → handleSendMessageAction-QTUGICSZ.js} +29 -30
  86. package/index.js +77 -76
  87. package/{localDialogRead-YSKFFRTT.js → localDialogRead-2VRI24F4.js} +1 -1
  88. package/{machineCommands-IIIREAEK.js → machineCommands-MAV7M3TK.js} +63 -61
  89. package/{machineWsRunDispatch-3SIU6ILW.js → machineWsRunDispatch-ZOEGTKHC.js} +55 -53
  90. package/{memoryCommands-O4MIPIWQ.js → memoryCommands-AFMES3LN.js} +5 -5
  91. package/{models-4OPOOLFS.js → models-LSCRJJ6P.js} +2 -2
  92. package/package.json +1 -1
  93. package/{read-QJNKRROM.js → read-2MIMRBZ2.js} +4 -4
  94. package/{readDocTool-H6X6KO6X.js → readDocTool-QHVGQHUJ.js} +1 -1
  95. package/{readlineWorkspace-HGP3I3OC.js → readlineWorkspace-SWQKC7ZS.js} +1113 -678
  96. package/{removeDialogAgentAction-TCT35SOF.js → removeDialogAgentAction-GTX5AN54.js} +13 -13
  97. package/{runAgentClientLoop-EFM4PTVI.js → runAgentClientLoop-P25HACXX.js} +47 -47
  98. package/{runtimeDoctorCommands-ID43Z7VX.js → runtimeDoctorCommands-QNF3J3UV.js} +6 -6
  99. package/{setDialogExtraReferencesAction-CGYLZMFH.js → setDialogExtraReferencesAction-WFQ63KYO.js} +9 -10
  100. package/{setPrimaryDialogAgentAction-F6BFQDJU.js → setPrimaryDialogAgentAction-P57FXTSU.js} +13 -13
  101. package/{settingSlice-2I5HMG47.js → settingSlice-CK5YZGNI.js} +7 -8
  102. package/{skillDiscovery-UGSQGX2U.js → skillDiscovery-ZYHSX7VD.js} +3 -3
  103. package/{spaceCommands-MT7BWCIL.js → spaceCommands-6O5EFEM5.js} +37 -29
  104. package/{spaceSlice-BS5TSW3W.js → spaceSlice-4EXTYXS4.js} +28 -27
  105. package/{streamAgentChatTurn-I3VBFNR3.js → streamAgentChatTurn-Q3FJAHG2.js} +222 -128
  106. package/{toolApiClient-C7P7YSE4.js → toolApiClient-QI3YBNWB.js} +2 -2
  107. package/{tools-XSD2BOEN.js → tools-AHNQE4GP.js} +34 -34
  108. package/{tuiSummaryLlmCaller-32NRHTT2.js → tuiSummaryLlmCaller-TXLF4MEI.js} +8 -6
  109. package/{updateCommands-VOSPKXFF.js → updateCommands-XZDOOYFH.js} +13 -5
  110. package/{updateDialogTitleAction-7FYNJOGP.js → updateDialogTitleAction-XZDVLKVX.js} +29 -29
  111. package/chunk-5MMAK75Q.js +0 -15
  112. package/chunk-AZPCASRE.js +0 -36
  113. package/chunk-BBYVOSU2.js +0 -792
  114. package/chunk-DCOA4QSN.js +0 -105
  115. package/chunk-UAD2WT54.js +0 -64
  116. package/{chunk-JK7VW2FN.js → chunk-5MIDPSSK.js} +3 -3
  117. package/{chunk-5CS33IBL.js → chunk-PDL2AV3E.js} +3 -3
  118. package/{chunk-5MMZUWSG.js → chunk-TOILAPLH.js} +5 -5
  119. package/{chunk-OOM6SJNR.js → chunk-TSUJOM6N.js} +6 -6
  120. package/{db-6QED3ZVO.js → db-HCJ5PRB5.js} +3 -3
  121. package/{offlineMarxistsAgentCommand-BCPJITJL.js → offlineMarxistsAgentCommand-5OOWZLJI.js} +3 -3
  122. package/{tableCommands-M3DJUETS.js → tableCommands-E4SQWDOX.js} +6 -6
  123. package/{understanding-ZOESBLF3.js → understanding-3CUQQ6SV.js} +3 -3
File without changes
@@ -1,10 +1,13 @@
1
1
  import {
2
2
  listUserRecordsFromServers,
3
3
  readLiveDbRecordAfterTombstoneMerge
4
- } from "./chunk-DWGAEFJL.js";
4
+ } from "./chunk-OPKKB25T.js";
5
5
  import {
6
6
  agentRecordHasConfiguredCredential
7
- } from "./chunk-AEZAORLG.js";
7
+ } from "./chunk-QCA3EPYK.js";
8
+ import {
9
+ toSafeAgentSummary
10
+ } from "./chunk-OOIN7HSO.js";
8
11
 
9
12
  // packages/cli/agentListHelpers.ts
10
13
  function parseAgentRecordId(privateKey, explicitId) {
@@ -34,6 +37,11 @@ function normalizeListedAgent(record) {
34
37
  publicKey: keys.publicKey,
35
38
  name: typeof record?.name === "string" && record.name ? record.name : "(unnamed)",
36
39
  model: typeof record?.model === "string" && record.model ? record.model : "-",
40
+ ...typeof record?.handle === "string" && record.handle ? { handle: record.handle } : {},
41
+ ...typeof record?.introduction === "string" && record.introduction ? { introduction: record.introduction } : typeof record?.description === "string" && record.description ? { introduction: record.description } : {},
42
+ ...typeof record?.provider === "string" && record.provider ? { provider: record.provider } : {},
43
+ ...typeof record?.inputPrice === "number" && Number.isFinite(record.inputPrice) ? { inputPrice: record.inputPrice } : {},
44
+ ...typeof record?.outputPrice === "number" && Number.isFinite(record.outputPrice) ? { outputPrice: record.outputPrice } : {},
37
45
  updatedAt: typeof record?.updatedAt === "string" || typeof record?.updatedAt === "number" ? record.updatedAt : typeof record?.createdAt === "string" || typeof record?.createdAt === "number" ? record.createdAt : typeof record?.created === "string" || typeof record?.created === "number" ? record.created : null,
38
46
  isPublicFlag: !!record?.isPublic,
39
47
  publicRecordExists: false,
@@ -46,6 +54,13 @@ function normalizeListedAgent(record) {
46
54
  ...typeof record?.cliProvider === "string" && record.cliProvider ? { cliProvider: record.cliProvider } : {}
47
55
  };
48
56
  }
57
+ function toSafeListedAgentSummary(agent, options) {
58
+ const summary = toSafeAgentSummary(agent, options);
59
+ if (agent.publicRecordExists === false) {
60
+ delete summary.publicKey;
61
+ }
62
+ return summary;
63
+ }
49
64
  function sortListedAgents(agents) {
50
65
  agents.sort((a, b) => {
51
66
  const ta = a.updatedAt == null ? 0 : new Date(a.updatedAt).getTime();
@@ -57,6 +72,7 @@ function sortListedAgents(agents) {
57
72
  function parseAgentListArgs(args) {
58
73
  return {
59
74
  wantJson: args.includes("--json"),
75
+ wantSafe: args.includes("--safe"),
60
76
  publicOnly: args.includes("--public-only"),
61
77
  idsOnly: args.includes("--ids-only")
62
78
  };
@@ -182,6 +198,8 @@ async function decorateAgentsWithPublicStatusAcrossServers(args) {
182
198
  }
183
199
 
184
200
  export {
201
+ normalizeListedAgent,
202
+ toSafeListedAgentSummary,
185
203
  parseAgentListArgs,
186
204
  listLocalCachedAgents,
187
205
  listRemoteAgents,
@@ -3,25 +3,28 @@ import {
3
3
  finalizeAccumulatedToolCalls,
4
4
  flushChatCompletionStream,
5
5
  readSseFrames,
6
- throwIfChatCompletionStreamFailed,
7
- toOpenAiCompatibleMessages
8
- } from "./chunk-ZPRK73XY.js";
6
+ throwIfChatCompletionStreamFailed
7
+ } from "./chunk-ZR4VHQ5D.js";
9
8
  import {
10
9
  parseToolArgumentsJson
11
- } from "./chunk-S3GS2PRL.js";
10
+ } from "./chunk-4BTHUYMY.js";
12
11
  import {
13
12
  createThinkParserState,
14
13
  extractThinkContent
15
14
  } from "./chunk-OPZYGHUC.js";
16
15
  import {
17
16
  buildProviderAuthHeaders,
18
- buildProviderExecutionPlan
19
- } from "./chunk-YTBMA7NO.js";
17
+ buildProviderExecutionPlan,
18
+ toOpenAiCompatibleMessages
19
+ } from "./chunk-TSKWE74T.js";
20
20
  import {
21
21
  canonicalizeToolName,
22
22
  createGlob,
23
23
  resolveExecutableOnPath
24
- } from "./chunk-4SOKXIW4.js";
24
+ } from "./chunk-43UD5ZP7.js";
25
+ import {
26
+ toErrorMessage
27
+ } from "./chunk-HNMXYYJR.js";
25
28
  import {
26
29
  asRecordOrEmpty
27
30
  } from "./chunk-KX6YBIHG.js";
@@ -38,17 +41,14 @@ import {
38
41
  asTrimmedLowercaseString
39
42
  } from "./chunk-QSJ3HFEF.js";
40
43
  import {
41
- toErrorMessage
42
- } from "./chunk-HNMXYYJR.js";
44
+ isRecord
45
+ } from "./chunk-KUG52IAP.js";
43
46
  import {
44
47
  asTrimmedString
45
48
  } from "./chunk-3EUW3WEO.js";
46
49
  import {
47
50
  asOptionalPositiveFiniteNumber
48
51
  } from "./chunk-U6VC4MU4.js";
49
- import {
50
- isRecord
51
- } from "./chunk-KUG52IAP.js";
52
52
  import {
53
53
  normalizeServerOrigin
54
54
  } from "./chunk-CF7PE763.js";
@@ -389,10 +389,16 @@ function dialogMessageRecordToAgentRuntimeMessage(record) {
389
389
  return {
390
390
  role: record.role,
391
391
  content: record.content ?? "",
392
+ ...record.contextReference !== void 0 ? { context_reference: record.contextReference } : {},
392
393
  ...typeof record.reasoning_content === "string" ? { reasoning_content: record.reasoning_content } : {},
393
394
  ...typeof record.toolCallId === "string" ? { tool_call_id: record.toolCallId } : {},
394
395
  ...Array.isArray(record.tool_calls) ? { tool_calls: record.tool_calls } : {},
395
- ...typeof record.toolName === "string" ? { toolName: record.toolName } : {}
396
+ ...typeof record.toolName === "string" ? { toolName: record.toolName } : {},
397
+ ...(() => {
398
+ const raw = record.createdAt;
399
+ const ms = typeof raw === "number" ? raw : typeof raw === "string" ? Date.parse(raw) : NaN;
400
+ return Number.isFinite(ms) ? { createdAt: ms } : {};
401
+ })()
396
402
  };
397
403
  }
398
404
 
@@ -469,6 +475,7 @@ function buildDialogMessageWriteOps(args) {
469
475
  dialogId: args.dialogId,
470
476
  role: message.role,
471
477
  content: message.content ?? "",
478
+ ...message.context_reference !== void 0 ? { contextReference: message.context_reference } : {},
472
479
  // 持久化思维链(reasoning):空轮/异常排查的关键证据,
473
480
  // 回读由 dialogMessageRecordToAgentRuntimeMessage 还原。
474
481
  ...typeof message.reasoning_content === "string" ? { reasoning_content: message.reasoning_content } : {},
@@ -1338,7 +1345,7 @@ function buildSearchWorkspaceParameters(variant) {
1338
1345
  const path = buildWorkspacePathProperty();
1339
1346
  const includeIgnored = {
1340
1347
  type: "boolean",
1341
- description: "When true, search files ignored by .gitignore such as .tmp. Defaults to false; .git and node_modules remain excluded."
1348
+ description: "When true, search files ignored by .gitignore such as .tmp. Defaults to false. Build artifacts (dist/, *.tsbuildinfo, *.min.js, etc.) are always excluded regardless of this flag. .git and node_modules remain excluded."
1342
1349
  };
1343
1350
  const exclude = {
1344
1351
  type: "array",
@@ -1701,6 +1708,24 @@ var EXEC_SHELL_TIMEOUT_ENV = "NOLO_EXEC_SHELL_TIMEOUT_MS";
1701
1708
  var EXEC_SHELL_DETACH_ENV = "NOLO_EXEC_SHELL_DETACH_MS";
1702
1709
  var DEFAULT_EXEC_SHELL_DETACH_MS = 12e4;
1703
1710
  var DEFAULT_LOCAL_API_ORIGIN = "http://127.0.0.1:38123";
1711
+ var ALWAYS_EXCLUDED_GLOBS = [
1712
+ "*.tsbuildinfo",
1713
+ "dist/**",
1714
+ "build/**",
1715
+ "out/**",
1716
+ "coverage/**",
1717
+ ".next/**",
1718
+ ".turbo/**",
1719
+ "public/assets/**",
1720
+ "*.min.js",
1721
+ "*.min.css",
1722
+ "*.map",
1723
+ "*.lock",
1724
+ "bun.lockb",
1725
+ "*.ldb"
1726
+ ];
1727
+ var DEFAULT_SEARCH_MAX_RESULTS = 200;
1728
+ var SEARCH_OUTPUT_CHAR_LIMIT = 2e4;
1704
1729
  function readTrimmedString(value) {
1705
1730
  return asOptionalTrimmedString(value);
1706
1731
  }
@@ -2046,8 +2071,29 @@ function sliceReadFileContent(args) {
2046
2071
  truncated: startLine > 1 || effectiveEndLine < totalLines
2047
2072
  };
2048
2073
  }
2049
- function countExactTextOccurrences(args) {
2050
- return args.content.split(args.oldText).length - 1;
2074
+ function buildEolTolerantPattern(oldTextLf) {
2075
+ const escaped = oldTextLf.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
2076
+ const pattern = escaped.replace(/\n/g, "\\r?\\n");
2077
+ return new RegExp(pattern, "g");
2078
+ }
2079
+ function countAndReplaceEolTolerant(args) {
2080
+ const oldTextLf = normalizeEolToLf(args.oldText);
2081
+ const newTextLf = normalizeEolToLf(args.newText);
2082
+ const pattern = buildEolTolerantPattern(oldTextLf);
2083
+ let count = 0;
2084
+ const result = args.content.replace(pattern, (match) => {
2085
+ count++;
2086
+ const eol = match.includes("\r\n") ? "\r\n" : "\n";
2087
+ return convertEol(newTextLf, eol);
2088
+ });
2089
+ return { count, result };
2090
+ }
2091
+ function normalizeEolToLf(text) {
2092
+ return text.replace(/\r\n/g, "\n");
2093
+ }
2094
+ function convertEol(text, eol) {
2095
+ const lf = normalizeEolToLf(text);
2096
+ return eol === "\r\n" ? lf.replace(/\n/g, "\r\n") : lf;
2051
2097
  }
2052
2098
  function pluralizeReplacement(count) {
2053
2099
  return count === 1 ? "replacement" : "replacements";
@@ -2068,8 +2114,8 @@ function requireWorkspaceGlobPattern(args) {
2068
2114
  if (!pattern) throw new Error("globFiles requires a non-empty pattern.");
2069
2115
  return pattern;
2070
2116
  }
2071
- function readWorkspaceMaxResults(args) {
2072
- if (args.maxResults === void 0) return void 0;
2117
+ function readWorkspaceMaxResults(args, defaultValue) {
2118
+ if (args.maxResults === void 0) return defaultValue;
2073
2119
  const value = Number(args.maxResults);
2074
2120
  if (!Number.isInteger(value) || value < 1) {
2075
2121
  throw new Error("maxResults must be a positive integer.");
@@ -2245,6 +2291,29 @@ function waitForNodeProcessExit(proc) {
2245
2291
  });
2246
2292
  });
2247
2293
  }
2294
+ function isSpawnFailureError(error) {
2295
+ if (!error || typeof error !== "object") return false;
2296
+ const err = error;
2297
+ if (err.code === "ENOENT") return true;
2298
+ const message = toErrorMessage(error).toLowerCase();
2299
+ return message.includes("enoent") || message.includes("executable not found") || message.includes("spawn") && message.includes("not found");
2300
+ }
2301
+ function spawnFailedCommandResult(error, outputLimit) {
2302
+ const stderr = `spawn failed: ${toErrorMessage(error)}
2303
+ `;
2304
+ return {
2305
+ stdout: "",
2306
+ stderr,
2307
+ exitCode: 127,
2308
+ timedOut: false,
2309
+ spawnFailed: true,
2310
+ content: truncateToolOutput(
2311
+ [`stderr:
2312
+ ${stderr.trim()}`, "exitCode: 127"].filter(Boolean).join("\n\n"),
2313
+ outputLimit
2314
+ )
2315
+ };
2316
+ }
2248
2317
  function resolveRipgrepBinary(env = process.env) {
2249
2318
  const fromEnv = asOptionalTrimmedString(env.NOLO_BUNDLED_RG);
2250
2319
  if (fromEnv) {
@@ -2255,6 +2324,9 @@ function resolveRipgrepBinary(env = process.env) {
2255
2324
  }
2256
2325
  return resolveExecutableOnPath("rg");
2257
2326
  }
2327
+ function resolveGrepBinary() {
2328
+ return resolveExecutableOnPath("grep");
2329
+ }
2258
2330
  async function runWorkspaceCommand(args) {
2259
2331
  const timeoutMs = asOptionalPositiveFiniteNumber(args.timeoutMs);
2260
2332
  const detached = process.platform !== "win32";
@@ -2339,7 +2411,21 @@ async function runWorkspaceCommand(args) {
2339
2411
  }
2340
2412
  if (abortPromise) racers.push(abortPromise);
2341
2413
  if (detachPromise) racers.push(detachPromise);
2342
- const raceWinner = await Promise.race(racers);
2414
+ let raceWinner;
2415
+ try {
2416
+ raceWinner = await Promise.race(racers);
2417
+ } catch (error) {
2418
+ if (timeout) clearTimeout(timeout);
2419
+ if (detachTimer) clearTimeout(detachTimer);
2420
+ if (abortListener && args.abortSignal) {
2421
+ args.abortSignal.removeEventListener("abort", abortListener);
2422
+ }
2423
+ detachSignalCleanup();
2424
+ if (isSpawnFailureError(error)) {
2425
+ return spawnFailedCommandResult(error, args.outputLimit);
2426
+ }
2427
+ throw error;
2428
+ }
2343
2429
  if (timeout) clearTimeout(timeout);
2344
2430
  if (detachTimer) clearTimeout(detachTimer);
2345
2431
  if (abortListener && args.abortSignal) {
@@ -2456,12 +2542,40 @@ async function runWorkspaceCommandLimitedLines(args) {
2456
2542
  ]);
2457
2543
  } catch (error) {
2458
2544
  const settled2 = await exitSettled;
2459
- if (!settled2.ok) throw settled2.error;
2545
+ if (!settled2.ok) {
2546
+ return {
2547
+ stdout: "",
2548
+ stderr: `spawn failed: ${toErrorMessage(settled2.error)}
2549
+ `,
2550
+ exitCode: 127,
2551
+ limitedByMaxResults: false,
2552
+ spawnFailed: true
2553
+ };
2554
+ }
2555
+ if (isSpawnFailureError(error)) {
2556
+ return {
2557
+ stdout: "",
2558
+ stderr: `spawn failed: ${toErrorMessage(error)}
2559
+ `,
2560
+ exitCode: 127,
2561
+ limitedByMaxResults: false,
2562
+ spawnFailed: true
2563
+ };
2564
+ }
2460
2565
  throw error;
2461
2566
  }
2462
2567
  if (!limitedByMaxResults && pending.trim()) lines.push(pending);
2463
2568
  const settled = await exitSettled;
2464
- if (!settled.ok) throw settled.error;
2569
+ if (!settled.ok) {
2570
+ return {
2571
+ stdout: "",
2572
+ stderr: `spawn failed: ${toErrorMessage(settled.error)}
2573
+ `,
2574
+ exitCode: 127,
2575
+ limitedByMaxResults: false,
2576
+ spawnFailed: true
2577
+ };
2578
+ }
2465
2579
  const exitCode = limitedByMaxResults ? 0 : settled.code;
2466
2580
  const stderr = await stderrPromise;
2467
2581
  return {
@@ -2590,13 +2704,16 @@ async function editFileTool(args) {
2590
2704
  ...args.confirmExternalFileAccess ? { confirmExternalFileAccess: args.confirmExternalFileAccess } : {}
2591
2705
  });
2592
2706
  const content = await readFile(absolutePath, "utf8");
2593
- const replacementCount = countExactTextOccurrences({ content, oldText });
2707
+ const { count: replacementCount, result: nextContent } = countAndReplaceEolTolerant({
2708
+ content,
2709
+ oldText,
2710
+ newText
2711
+ });
2594
2712
  if (replacementCount !== expectedReplacements) {
2595
2713
  throw new Error(
2596
- `editFile expected ${expectedReplacements} ${pluralizeReplacement(expectedReplacements)} but found ${replacementCount} in ${requestedPath}.`
2714
+ `editFile expected ${expectedReplacements} ${pluralizeReplacement(expectedReplacements)} but found ${replacementCount} in ${requestedPath} (matched after EOL normalization; verify the exact text).`
2597
2715
  );
2598
2716
  }
2599
- const nextContent = content.split(oldText).join(newText);
2600
2717
  await writeFile(absolutePath, nextContent, "utf8");
2601
2718
  const relativePath = normalizeWorkspaceRelativePath({
2602
2719
  workspaceRoot: resolve(args.workspaceRoot),
@@ -2693,7 +2810,7 @@ async function searchFilesTool(args) {
2693
2810
  const parsed = parseWorkspaceToolArguments(args.call.arguments);
2694
2811
  const query = requireWorkspaceSearchQuery(parsed);
2695
2812
  const exclude = readWorkspaceExcludeGlobs(parsed);
2696
- const maxResults = readWorkspaceMaxResults(parsed);
2813
+ const maxResults = readWorkspaceMaxResults(parsed, DEFAULT_SEARCH_MAX_RESULTS);
2697
2814
  const contextLines = readWorkspaceContextLines(parsed);
2698
2815
  const literal = parsed.literal === true;
2699
2816
  const caseSensitive = parsed.caseSensitive === false ? false : true;
@@ -2708,8 +2825,8 @@ async function searchFilesTool(args) {
2708
2825
  workspaceRoot: resolve(args.workspaceRoot),
2709
2826
  targetPath: searchPath
2710
2827
  });
2711
- const rgBinary = resolveRipgrepBinary();
2712
- const grepBinary = resolveExecutableOnPath("grep") || "grep";
2828
+ const rgBinary = (args.resolveRipgrepBinary ?? resolveRipgrepBinary)();
2829
+ const grepBinary = (args.resolveGrepBinary ?? resolveGrepBinary)();
2713
2830
  const rgCommand = rgBinary ? [
2714
2831
  rgBinary,
2715
2832
  "--line-number",
@@ -2723,29 +2840,26 @@ async function searchFilesTool(args) {
2723
2840
  "!node_modules",
2724
2841
  "--glob",
2725
2842
  "!.git",
2843
+ ...ALWAYS_EXCLUDED_GLOBS.flatMap((g) => ["--glob", `!${g}`]),
2726
2844
  ...exclude.flatMap((excludePattern) => ["--glob", `!${excludePattern}`]),
2727
2845
  "--",
2728
2846
  query,
2729
2847
  relativeSearchPath
2730
2848
  ] : null;
2731
- const grepCommand = [
2732
- grepBinary,
2733
- "-R",
2734
- "-n",
2735
- "-I",
2736
- ...literal ? ["-F"] : [],
2737
- ...caseSensitive ? [] : ["-i"],
2738
- ...contextLines !== void 0 ? ["-C", String(contextLines)] : [],
2739
- "--exclude-dir=node_modules",
2740
- "--exclude-dir=.git",
2741
- ...exclude.flatMap((excludePattern) => ["--exclude", excludePattern]),
2742
- query,
2743
- relativeSearchPath
2849
+ const grepExcludeArgs = [
2850
+ ...ALWAYS_EXCLUDED_GLOBS.flatMap((g) => {
2851
+ if (!g.endsWith("/**")) return ["--exclude", g];
2852
+ const dir = g.replace(/\/\*\*$/, "");
2853
+ return dir.includes("/") ? [] : [`--exclude-dir=${dir}`];
2854
+ }),
2855
+ ...exclude.flatMap((excludePattern) => ["--exclude", excludePattern])
2744
2856
  ];
2857
+ let searchEngine = "js";
2858
+ let binariesUnavailable = !rgBinary && !grepBinary;
2745
2859
  const result = await (async () => {
2746
2860
  if (rgCommand) {
2747
2861
  try {
2748
- return maxResults && !contextLines ? await runWorkspaceCommandLimitedLines({
2862
+ const rgResult = maxResults && !contextLines ? await runWorkspaceCommandLimitedLines({
2749
2863
  workspaceRoot: args.workspaceRoot,
2750
2864
  command: rgCommand,
2751
2865
  maxLines: maxResults
@@ -2753,35 +2867,66 @@ async function searchFilesTool(args) {
2753
2867
  workspaceRoot: args.workspaceRoot,
2754
2868
  command: rgCommand
2755
2869
  });
2870
+ if (!rgResult.spawnFailed) {
2871
+ searchEngine = "ripgrep";
2872
+ return rgResult;
2873
+ }
2756
2874
  } catch {
2757
2875
  }
2758
2876
  }
2759
- try {
2760
- return maxResults && !contextLines ? await runWorkspaceCommandLimitedLines({
2761
- workspaceRoot: args.workspaceRoot,
2762
- command: grepCommand,
2763
- maxLines: maxResults
2764
- }) : await runWorkspaceCommand({
2765
- workspaceRoot: args.workspaceRoot,
2766
- command: grepCommand
2767
- });
2768
- } catch {
2769
- return {
2770
- stdout: "",
2771
- stderr: "",
2772
- exitCode: 0,
2773
- limitedByMaxResults: false
2774
- };
2877
+ if (grepBinary) {
2878
+ const grepCommand = [
2879
+ grepBinary,
2880
+ "-R",
2881
+ "-n",
2882
+ "-I",
2883
+ ...literal ? ["-F"] : [],
2884
+ ...caseSensitive ? [] : ["-i"],
2885
+ ...contextLines !== void 0 ? ["-C", String(contextLines)] : [],
2886
+ "--exclude-dir=node_modules",
2887
+ "--exclude-dir=.git",
2888
+ ...grepExcludeArgs,
2889
+ query,
2890
+ relativeSearchPath
2891
+ ];
2892
+ try {
2893
+ const grepResult = maxResults && !contextLines ? await runWorkspaceCommandLimitedLines({
2894
+ workspaceRoot: args.workspaceRoot,
2895
+ command: grepCommand,
2896
+ maxLines: maxResults
2897
+ }) : await runWorkspaceCommand({
2898
+ workspaceRoot: args.workspaceRoot,
2899
+ command: grepCommand
2900
+ });
2901
+ if (!grepResult.spawnFailed) {
2902
+ searchEngine = "grep";
2903
+ return grepResult;
2904
+ }
2905
+ binariesUnavailable = true;
2906
+ } catch {
2907
+ binariesUnavailable = true;
2908
+ }
2909
+ } else {
2910
+ binariesUnavailable = true;
2775
2911
  }
2912
+ return {
2913
+ stdout: "",
2914
+ stderr: "",
2915
+ exitCode: 0,
2916
+ limitedByMaxResults: false,
2917
+ spawnFailed: true
2918
+ };
2776
2919
  })();
2777
2920
  let outputLines = result.stdout.split(/\r?\n/).map((line) => line.trimEnd().replace(/^\.\//, "")).filter(Boolean);
2921
+ outputLines = dropAlwaysExcludedLines(outputLines);
2778
2922
  let limitedByMaxResults = "limitedByMaxResults" in result && result.limitedByMaxResults === true;
2779
- if (outputLines.length === 0 && result.exitCode === 0 && (includeIgnored || !await hasRootGitignore(args.workspaceRoot))) {
2923
+ const forceJsFallback = binariesUnavailable || result.spawnFailed === true;
2924
+ if (outputLines.length === 0 && (forceJsFallback || result.exitCode === 0 && (includeIgnored || !await hasRootGitignore(args.workspaceRoot)))) {
2780
2925
  const fallback = await scanWorkspaceTextMatches({
2781
2926
  workspaceRoot: args.workspaceRoot,
2782
2927
  relativeSearchPath,
2783
2928
  query,
2784
- exclude,
2929
+ exclude: [...ALWAYS_EXCLUDED_GLOBS, ...exclude],
2785
2930
  maxResults,
2786
2931
  literal,
2787
2932
  caseSensitive,
@@ -2789,6 +2934,7 @@ async function searchFilesTool(args) {
2789
2934
  });
2790
2935
  outputLines = fallback.lines;
2791
2936
  limitedByMaxResults = fallback.limitedByMaxResults;
2937
+ searchEngine = "js";
2792
2938
  }
2793
2939
  if (contextLines && outputLines.length > 0) {
2794
2940
  const limited = limitSearchOutputByMatches(outputLines, maxResults);
@@ -2800,8 +2946,11 @@ async function searchFilesTool(args) {
2800
2946
  const match = line.match(/^(.*?):\d+:/);
2801
2947
  return match?.[1] ? [match[1]] : [];
2802
2948
  })));
2949
+ const rawContent = outputLines.join("\n");
2950
+ const content = truncateToolOutput(rawContent, SEARCH_OUTPUT_CHAR_LIMIT);
2951
+ const truncatedByByteLimit = content.length !== rawContent.length;
2803
2952
  return {
2804
- content: outputLines.join("\n"),
2953
+ content,
2805
2954
  metadata: {
2806
2955
  query,
2807
2956
  path: requestedPath,
@@ -2814,10 +2963,12 @@ async function searchFilesTool(args) {
2814
2963
  count: outputLines.length,
2815
2964
  matchCount: matchLines.length,
2816
2965
  matchedFiles,
2817
- truncated: limitedByMaxResults,
2966
+ truncated: limitedByMaxResults || truncatedByByteLimit,
2818
2967
  limitedByMaxResults,
2968
+ ...truncatedByByteLimit ? { truncatedByByteLimit: true } : {},
2819
2969
  ...maxResults ? { maxResults } : {},
2820
2970
  exitCode: result.exitCode,
2971
+ searchEngine,
2821
2972
  ...extractActivity(parsed) ? { activity: extractActivity(parsed) } : {}
2822
2973
  }
2823
2974
  };
@@ -2848,6 +2999,14 @@ async function filterRootGitignoredFiles(args) {
2848
2999
  const globs = patterns.map((pattern) => createGlob(pattern));
2849
3000
  return args.files.filter((file) => !globs.some((glob) => glob.match(file)));
2850
3001
  }
3002
+ function dropAlwaysExcludedLines(lines) {
3003
+ const globs = ALWAYS_EXCLUDED_GLOBS.map((pattern) => createGlob(pattern));
3004
+ return lines.filter((line) => {
3005
+ const filePath = line.match(/^(.*?):\d+[:-]/)?.[1];
3006
+ if (!filePath) return true;
3007
+ return !globs.some((glob) => glob.match(filePath));
3008
+ });
3009
+ }
2851
3010
  function scanWorkspaceGlobFiles(args) {
2852
3011
  const pathPrefix = args.relativeSearchPath === "." ? "" : `${args.relativeSearchPath.replace(/\/+$/, "")}/`;
2853
3012
  return Array.from(createGlob(args.pattern).scanSync({
@@ -2940,38 +3099,76 @@ async function globFilesTool(args) {
2940
3099
  workspaceRoot: resolve(args.workspaceRoot),
2941
3100
  targetPath: searchPath
2942
3101
  });
2943
- const command = [
2944
- "rg",
2945
- "--files",
2946
- "--hidden",
2947
- ...includeIgnored ? ["--no-ignore"] : [],
2948
- "--glob",
2949
- pattern,
2950
- "--glob",
2951
- "!node_modules",
2952
- "--glob",
2953
- "!.git",
2954
- ...exclude.flatMap((excludePattern) => ["--glob", `!${excludePattern}`]),
2955
- ...relativeSearchPath === "." ? [] : [relativeSearchPath]
2956
- ];
2957
- const result = await runWorkspaceCommand({
2958
- workspaceRoot: args.workspaceRoot,
2959
- command
2960
- });
2961
3102
  const pathPrefix = relativeSearchPath === "." ? "" : `${relativeSearchPath.replace(/\/+$/, "")}/`;
2962
- let files = result.stdout.split(/\r?\n/).map((line) => line.trim().replace(/^\.\//, "")).filter(Boolean).filter((line) => !pathPrefix || line === relativeSearchPath || line.startsWith(pathPrefix)).sort((left, right) => left.localeCompare(right));
2963
- files = await filterRootGitignoredFiles({
2964
- workspaceRoot: args.workspaceRoot,
2965
- files,
2966
- includeIgnored
2967
- });
2968
- if (files.length === 0 && (includeIgnored || !await hasRootGitignore(args.workspaceRoot))) {
3103
+ const rgBinary = (args.resolveRipgrepBinary ?? resolveRipgrepBinary)();
3104
+ let searchEngine = "js";
3105
+ let exitCode = 0;
3106
+ let files = [];
3107
+ if (rgBinary) {
3108
+ const command = [
3109
+ rgBinary,
3110
+ "--files",
3111
+ "--hidden",
3112
+ ...includeIgnored ? ["--no-ignore"] : [],
3113
+ "--glob",
3114
+ pattern,
3115
+ "--glob",
3116
+ "!node_modules",
3117
+ "--glob",
3118
+ "!.git",
3119
+ ...exclude.flatMap((excludePattern) => ["--glob", `!${excludePattern}`]),
3120
+ ...relativeSearchPath === "." ? [] : [relativeSearchPath]
3121
+ ];
3122
+ const result = await runWorkspaceCommand({
3123
+ workspaceRoot: args.workspaceRoot,
3124
+ command
3125
+ });
3126
+ exitCode = result.exitCode;
3127
+ if (result.spawnFailed) {
3128
+ files = scanWorkspaceGlobFiles({
3129
+ workspaceRoot: args.workspaceRoot,
3130
+ pattern,
3131
+ relativeSearchPath,
3132
+ exclude
3133
+ });
3134
+ files = await filterRootGitignoredFiles({
3135
+ workspaceRoot: args.workspaceRoot,
3136
+ files,
3137
+ includeIgnored
3138
+ });
3139
+ searchEngine = "js";
3140
+ } else {
3141
+ files = result.stdout.split(/\r?\n/).map((line) => line.trim().replace(/^\.\//, "")).filter(Boolean).filter((line) => !pathPrefix || line === relativeSearchPath || line.startsWith(pathPrefix)).sort((left, right) => left.localeCompare(right));
3142
+ files = await filterRootGitignoredFiles({
3143
+ workspaceRoot: args.workspaceRoot,
3144
+ files,
3145
+ includeIgnored
3146
+ });
3147
+ if (files.length === 0 && (includeIgnored || !await hasRootGitignore(args.workspaceRoot))) {
3148
+ files = scanWorkspaceGlobFiles({
3149
+ workspaceRoot: args.workspaceRoot,
3150
+ pattern,
3151
+ relativeSearchPath,
3152
+ exclude
3153
+ });
3154
+ searchEngine = "js";
3155
+ } else {
3156
+ searchEngine = "ripgrep";
3157
+ }
3158
+ }
3159
+ } else {
2969
3160
  files = scanWorkspaceGlobFiles({
2970
3161
  workspaceRoot: args.workspaceRoot,
2971
3162
  pattern,
2972
3163
  relativeSearchPath,
2973
3164
  exclude
2974
3165
  });
3166
+ files = await filterRootGitignoredFiles({
3167
+ workspaceRoot: args.workspaceRoot,
3168
+ files,
3169
+ includeIgnored
3170
+ });
3171
+ searchEngine = "js";
2975
3172
  }
2976
3173
  const commandLimitedByMaxResults = false;
2977
3174
  const totalCount = commandLimitedByMaxResults ? void 0 : files.length;
@@ -2992,7 +3189,8 @@ async function globFilesTool(args) {
2992
3189
  truncated: limitedByMaxResults,
2993
3190
  limitedByMaxResults,
2994
3191
  ...maxResults ? { maxResults } : {},
2995
- exitCode: result.exitCode,
3192
+ exitCode,
3193
+ searchEngine,
2996
3194
  ...activity ? { activity } : {}
2997
3195
  }
2998
3196
  };
@@ -3196,6 +3394,10 @@ async function listProcessesTool(args) {
3196
3394
  }
3197
3395
  function createLocalWorkspaceToolExecutors(args) {
3198
3396
  const fileAccess = args.confirmExternalFileAccess ? { confirmExternalFileAccess: args.confirmExternalFileAccess } : {};
3397
+ const searchBinaryResolvers = {
3398
+ ...args.resolveRipgrepBinary ? { resolveRipgrepBinary: args.resolveRipgrepBinary } : {},
3399
+ ...args.resolveGrepBinary ? { resolveGrepBinary: args.resolveGrepBinary } : {}
3400
+ };
3199
3401
  return {
3200
3402
  editFile: (call) => editFileTool({
3201
3403
  call,
@@ -3205,7 +3407,8 @@ function createLocalWorkspaceToolExecutors(args) {
3205
3407
  globFiles: (call) => globFilesTool({
3206
3408
  call,
3207
3409
  workspaceRoot: args.workspaceRoot,
3208
- ...fileAccess
3410
+ ...fileAccess,
3411
+ ...args.resolveRipgrepBinary ? { resolveRipgrepBinary: args.resolveRipgrepBinary } : {}
3209
3412
  }),
3210
3413
  listFiles: (call) => listFilesTool({
3211
3414
  call,
@@ -3225,7 +3428,8 @@ function createLocalWorkspaceToolExecutors(args) {
3225
3428
  searchFiles: (call) => searchFilesTool({
3226
3429
  call,
3227
3430
  workspaceRoot: args.workspaceRoot,
3228
- ...fileAccess
3431
+ ...fileAccess,
3432
+ ...searchBinaryResolvers
3229
3433
  }),
3230
3434
  captureVisualState: (call) => captureVisualStateTool({
3231
3435
  call,