@ddlqhd/agent-sdk 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/README.md +4 -2
  2. package/dist/{chunk-5QMA2YBY.cjs → chunk-6X7EYQLS.cjs} +782 -114
  3. package/dist/chunk-6X7EYQLS.cjs.map +1 -0
  4. package/dist/{chunk-NDSL7NPN.js → chunk-D3UZNLZO.js} +769 -71
  5. package/dist/chunk-D3UZNLZO.js.map +1 -0
  6. package/dist/{chunk-Q3SOMX26.js → chunk-EQ5CXH44.js} +772 -111
  7. package/dist/chunk-EQ5CXH44.js.map +1 -0
  8. package/dist/chunk-LOYIGOBZ.js +54 -0
  9. package/dist/chunk-LOYIGOBZ.js.map +1 -0
  10. package/dist/{chunk-OHXW2YM6.js → chunk-MEJHTQJM.js} +289 -166
  11. package/dist/chunk-MEJHTQJM.js.map +1 -0
  12. package/dist/chunk-NYZD3THB.cjs +1521 -0
  13. package/dist/chunk-NYZD3THB.cjs.map +1 -0
  14. package/dist/chunk-OZO7D77N.cjs +59 -0
  15. package/dist/chunk-OZO7D77N.cjs.map +1 -0
  16. package/dist/{chunk-JF5AJQMU.cjs → chunk-Z45DHTDX.cjs} +291 -170
  17. package/dist/chunk-Z45DHTDX.cjs.map +1 -0
  18. package/dist/cli/index.cjs +47 -39
  19. package/dist/cli/index.cjs.map +1 -1
  20. package/dist/cli/index.js +22 -14
  21. package/dist/cli/index.js.map +1 -1
  22. package/dist/{index-DPsZ1zat.d.ts → index-Cw3SfEAB.d.ts} +20 -34
  23. package/dist/{index-RTPmFjMp.d.cts → index-D2Qntkn_.d.cts} +20 -34
  24. package/dist/index.cjs +125 -89
  25. package/dist/index.d.cts +62 -22
  26. package/dist/index.d.ts +62 -22
  27. package/dist/index.js +4 -4
  28. package/dist/models/index.cjs +19 -15
  29. package/dist/models/index.d.cts +55 -6
  30. package/dist/models/index.d.ts +55 -6
  31. package/dist/models/index.js +2 -2
  32. package/dist/tools/index.cjs +53 -61
  33. package/dist/tools/index.d.cts +3 -3
  34. package/dist/tools/index.d.ts +3 -3
  35. package/dist/tools/index.js +2 -2
  36. package/dist/{types-C0aX_Qdp.d.cts → types-CWPAYWzr.d.cts} +307 -61
  37. package/dist/{types-C0aX_Qdp.d.ts → types-CWPAYWzr.d.ts} +307 -61
  38. package/package.json +25 -14
  39. package/dist/chunk-5QMA2YBY.cjs.map +0 -1
  40. package/dist/chunk-CNSGZVRN.cjs +0 -152
  41. package/dist/chunk-CNSGZVRN.cjs.map +0 -1
  42. package/dist/chunk-JF5AJQMU.cjs.map +0 -1
  43. package/dist/chunk-NDSL7NPN.js.map +0 -1
  44. package/dist/chunk-OHXW2YM6.js.map +0 -1
  45. package/dist/chunk-Q3SOMX26.js.map +0 -1
  46. package/dist/chunk-WH3APNQ5.js +0 -147
  47. package/dist/chunk-WH3APNQ5.js.map +0 -1
  48. package/dist/chunk-X35MHWXE.cjs +0 -817
  49. package/dist/chunk-X35MHWXE.cjs.map +0 -1
@@ -1,13 +1,13 @@
1
1
  #!/usr/bin/env node
2
2
  'use strict';
3
3
 
4
- var chunkCNSGZVRN_cjs = require('./chunk-CNSGZVRN.cjs');
4
+ var chunkOZO7D77N_cjs = require('./chunk-OZO7D77N.cjs');
5
5
  var promises = require('fs/promises');
6
6
  var path = require('path');
7
7
  var os = require('os');
8
- var zod = require('zod');
9
8
  var iconv = require('iconv-lite');
10
9
  var fg = require('fast-glob');
10
+ var zod = require('zod');
11
11
  var chardet = require('chardet');
12
12
  var child_process = require('child_process');
13
13
  var fs = require('fs');
@@ -253,16 +253,20 @@ var OutputHandler = class {
253
253
  function createOutputHandler(userBasePath) {
254
254
  return new OutputHandler(userBasePath);
255
255
  }
256
+
257
+ // src/tools/registry.ts
256
258
  var ToolRegistry = class {
257
259
  tools = /* @__PURE__ */ new Map();
258
260
  categories = /* @__PURE__ */ new Map();
259
261
  outputHandler;
260
262
  hookManager = null;
261
263
  executionPolicy;
264
+ hookObserver;
262
265
  constructor(config) {
263
266
  const enableOutputHandler = config?.enableOutputHandler !== false;
264
267
  this.outputHandler = enableOutputHandler ? createOutputHandler(config?.userBasePath) : null;
265
268
  this.executionPolicy = config?.executionPolicy;
269
+ this.hookObserver = config?.hookObserver;
266
270
  }
267
271
  /**
268
272
  * 工具名是否在 {@link ToolExecutionPolicy.disallowedTools} 中(无策略时为 false)。
@@ -329,6 +333,14 @@ var ToolRegistry = class {
329
333
  ...extra
330
334
  };
331
335
  }
336
+ hookObserverCtx(eventType, name, options) {
337
+ return {
338
+ eventType,
339
+ toolName: name,
340
+ toolCallId: options?.toolCallId,
341
+ projectDir: options?.projectDir
342
+ };
343
+ }
332
344
  /**
333
345
  * 注册工具
334
346
  */
@@ -399,6 +411,11 @@ var ToolRegistry = class {
399
411
  }
400
412
  const tool = this.tools.get(name);
401
413
  if (!tool) {
414
+ if (hookMgr) {
415
+ this.hookObserver?.onHookStart?.(
416
+ this.hookObserverCtx("postToolUseFailure", name, options)
417
+ );
418
+ }
402
419
  const ctx = this.buildHookContext("postToolUseFailure", name, rawArgsObj, options, {
403
420
  errorMessage: `Tool "${name}" not found`,
404
421
  failureKind: "tool_error"
@@ -411,32 +428,56 @@ var ToolRegistry = class {
411
428
  }
412
429
  let workingInput = rawArgsObj;
413
430
  try {
414
- workingInput = tool.parameters.parse(args);
431
+ const initial = tool.parameters.safeParse(args);
432
+ if (!initial.success) {
433
+ const msg = `Invalid arguments for tool "${name}": ${initial.error.issues.map((i) => i.message).join(", ")}`;
434
+ if (hookMgr) {
435
+ this.hookObserver?.onHookStart?.(
436
+ this.hookObserverCtx("postToolUseFailure", name, options)
437
+ );
438
+ }
439
+ await hookMgr?.executePostToolUseFailure(
440
+ this.buildHookContext("postToolUseFailure", name, rawArgsObj, options, {
441
+ errorMessage: msg,
442
+ failureKind: "validation"
443
+ })
444
+ );
445
+ return { content: msg, isError: true };
446
+ }
447
+ workingInput = initial.data;
415
448
  if (hookMgr) {
449
+ this.hookObserver?.onHookStart?.(
450
+ this.hookObserverCtx("preToolUse", name, options)
451
+ );
416
452
  const pre = await hookMgr.executePreToolUse(
417
453
  this.buildHookContext("preToolUse", name, workingInput, options)
418
454
  );
455
+ this.hookObserver?.onHookDecision?.({
456
+ ...this.hookObserverCtx("preToolUse", name, options),
457
+ allowed: pre.allowed,
458
+ reason: pre.reason
459
+ });
419
460
  if (!pre.allowed) {
420
461
  return {
421
462
  content: pre.reason ?? "Blocked by hook",
422
463
  isError: true
423
464
  };
424
465
  }
425
- try {
426
- workingInput = tool.parameters.parse(pre.updatedInput ?? workingInput);
427
- } catch (err) {
428
- if (err instanceof zod.z.ZodError) {
429
- const msg = `Invalid arguments after hook merge for tool "${name}": ${err.errors.map((e) => e.message).join(", ")}`;
430
- await hookMgr.executePostToolUseFailure(
431
- this.buildHookContext("postToolUseFailure", name, workingInput, options, {
432
- errorMessage: msg,
433
- failureKind: "validation"
434
- })
435
- );
436
- return { content: msg, isError: true };
437
- }
438
- throw err;
466
+ const merged = tool.parameters.safeParse(pre.updatedInput ?? workingInput);
467
+ if (!merged.success) {
468
+ const msg = `Invalid arguments after hook merge for tool "${name}": ${merged.error.issues.map((i) => i.message).join(", ")}`;
469
+ this.hookObserver?.onHookStart?.(
470
+ this.hookObserverCtx("postToolUseFailure", name, options)
471
+ );
472
+ await hookMgr.executePostToolUseFailure(
473
+ this.buildHookContext("postToolUseFailure", name, workingInput, options, {
474
+ errorMessage: msg,
475
+ failureKind: "validation"
476
+ })
477
+ );
478
+ return { content: msg, isError: true };
439
479
  }
480
+ workingInput = merged.data;
440
481
  }
441
482
  const handlerArgs = workingInput;
442
483
  const executionContext = {
@@ -447,6 +488,11 @@ var ToolRegistry = class {
447
488
  const result = await tool.handler(handlerArgs, executionContext);
448
489
  const toolResultRaw = result;
449
490
  if (result.isError) {
491
+ if (hookMgr) {
492
+ this.hookObserver?.onHookStart?.(
493
+ this.hookObserverCtx("postToolUseFailure", name, options)
494
+ );
495
+ }
450
496
  await hookMgr?.executePostToolUseFailure(
451
497
  this.buildHookContext("postToolUseFailure", name, workingInput, options, {
452
498
  errorMessage: result.content,
@@ -464,6 +510,11 @@ var ToolRegistry = class {
464
510
  { args: handlerArgs }
465
511
  );
466
512
  }
513
+ if (hookMgr) {
514
+ this.hookObserver?.onHookStart?.(
515
+ this.hookObserverCtx("postToolUse", name, options)
516
+ );
517
+ }
467
518
  await hookMgr?.executePostToolUse(
468
519
  this.buildHookContext("postToolUse", name, workingInput, options, {
469
520
  toolResultRaw,
@@ -472,20 +523,12 @@ var ToolRegistry = class {
472
523
  );
473
524
  return finalResult;
474
525
  } catch (error) {
475
- if (error instanceof zod.z.ZodError) {
476
- const msg2 = `Invalid arguments for tool "${name}": ${error.errors.map((e) => e.message).join(", ")}`;
477
- await hookMgr?.executePostToolUseFailure(
478
- this.buildHookContext("postToolUseFailure", name, rawArgsObj, options, {
479
- errorMessage: msg2,
480
- failureKind: "validation"
481
- })
526
+ const msg = `Error executing tool "${name}": ${error instanceof Error ? error.message : String(error)}`;
527
+ if (hookMgr) {
528
+ this.hookObserver?.onHookStart?.(
529
+ this.hookObserverCtx("postToolUseFailure", name, options)
482
530
  );
483
- return {
484
- content: msg2,
485
- isError: true
486
- };
487
531
  }
488
- const msg = `Error executing tool "${name}": ${error instanceof Error ? error.message : String(error)}`;
489
532
  await hookMgr?.executePostToolUseFailure(
490
533
  this.buildHookContext("postToolUseFailure", name, workingInput, options, {
491
534
  errorMessage: msg,
@@ -505,7 +548,7 @@ var ToolRegistry = class {
505
548
  return this.getAll().map((tool) => ({
506
549
  name: tool.name,
507
550
  description: tool.description,
508
- parameters: chunkCNSGZVRN_cjs.zodToJsonSchema(tool.parameters)
551
+ parameters: chunkOZO7D77N_cjs.zodToJsonSchema(tool.parameters)
509
552
  }));
510
553
  }
511
554
  /**
@@ -561,7 +604,7 @@ var ToolRegistry = class {
561
604
  return this.getAll().map((tool) => ({
562
605
  name: tool.name,
563
606
  description: tool.description,
564
- parameters: chunkCNSGZVRN_cjs.zodToJsonSchema(tool.parameters)
607
+ parameters: chunkOZO7D77N_cjs.zodToJsonSchema(tool.parameters)
565
608
  }));
566
609
  }
567
610
  };
@@ -832,10 +875,94 @@ var MAX_LINE_LENGTH = 2e3;
832
875
  var MAX_LINE_SUFFIX = `... (line truncated to ${MAX_LINE_LENGTH} chars)`;
833
876
  var MAX_BYTES = 50 * 1024;
834
877
  var MAX_BYTES_LABEL = `${MAX_BYTES / 1024} KB`;
878
+ var EDIT_MAX_FILE_BYTES = 1024 ** 3;
879
+ function detectDominantEol(content) {
880
+ let crlf = 0;
881
+ for (let i = 0; i < content.length - 1; i++) {
882
+ if (content[i] === "\r" && content[i + 1] === "\n") {
883
+ crlf++;
884
+ }
885
+ }
886
+ let nl = 0;
887
+ for (let i = 0; i < content.length; i++) {
888
+ if (content[i] === "\n") {
889
+ nl++;
890
+ }
891
+ }
892
+ const bareLf = nl - crlf;
893
+ if (crlf > bareLf) {
894
+ return "\r\n";
895
+ }
896
+ return "\n";
897
+ }
898
+ function normalizeNewStringEols(text, eol) {
899
+ const unified = text.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
900
+ if (eol === "\n") {
901
+ return unified;
902
+ }
903
+ return unified.replace(/\n/g, "\r\n");
904
+ }
905
+ function buildNeedleCandidates(oldString, dominantEol) {
906
+ const out = [oldString];
907
+ if (dominantEol === "\r\n") {
908
+ if (!oldString.includes("\r")) {
909
+ const v = oldString.replace(/\n/g, "\r\n");
910
+ if (v !== oldString) {
911
+ out.push(v);
912
+ }
913
+ }
914
+ } else {
915
+ if (oldString.includes("\r\n")) {
916
+ const v = oldString.replace(/\r\n/g, "\n");
917
+ if (v !== oldString) {
918
+ out.push(v);
919
+ }
920
+ } else if (oldString.includes("\r")) {
921
+ const v = oldString.replace(/\r/g, "\n");
922
+ if (v !== oldString) {
923
+ out.push(v);
924
+ }
925
+ }
926
+ }
927
+ return out;
928
+ }
929
+ function countOccurrences(haystack, needle) {
930
+ if (needle.length === 0) {
931
+ return 0;
932
+ }
933
+ let n = 0;
934
+ let from = 0;
935
+ let i = 0;
936
+ while ((i = haystack.indexOf(needle, from)) !== -1) {
937
+ n++;
938
+ from = i + needle.length;
939
+ }
940
+ return n;
941
+ }
942
+ function replaceNonOverlapping(content, needle, replacement, replaceAll) {
943
+ if (!replaceAll) {
944
+ const i2 = content.indexOf(needle);
945
+ if (i2 === -1) {
946
+ throw new Error("Edit: needle not found after resolution (internal inconsistency)");
947
+ }
948
+ return content.slice(0, i2) + replacement + content.slice(i2 + needle.length);
949
+ }
950
+ let out = "";
951
+ let from = 0;
952
+ let i = 0;
953
+ while ((i = content.indexOf(needle, from)) !== -1) {
954
+ out += content.slice(from, i) + replacement;
955
+ from = i + needle.length;
956
+ }
957
+ out += content.slice(from);
958
+ return out;
959
+ }
835
960
  var readFileTool = createTool({
836
961
  name: "Read",
837
962
  category: "filesystem",
838
- description: `Reads a file from the local filesystem. You can access any file directly by using this tool.
963
+ description: `Reads human-readable text from the local filesystem: source code, configuration, logs, Markdown, and structured text (JSON, XML, YAML, etc.).
964
+
965
+ Do NOT use Read for binary formats. This tool decodes the file as text; for binaries the result is garbage or misleading. Examples: images (png, jpg, gif, webp, ico), Office/OpenXML (xlsx, docx, pptx), PDF, audio/video, archives (zip, gz, etc.), and compiled binaries. Prefer format-specific tooling, a small script or library in the user's environment, or ask the user for an exported/plain-text view.
839
966
 
840
967
  Usage:
841
968
  - The file_path parameter must be an absolute path, not a relative path
@@ -844,10 +971,12 @@ Usage:
844
971
  - Results are returned using cat -n style, with line numbers starting at 1
845
972
  - Lines longer than 2000 characters are truncated
846
973
  - Use the offset and limit parameters to read specific line ranges of large files
847
- - If you read a file that exists but has empty contents you will receive an error message
974
+ - An empty file (no lines) returns successfully with no numbered lines and a suffix noting zero total lines\u2014it is not an error
848
975
  - Text encoding is detected automatically from the file (BOM, UTF-8 validity, charset analysis). You rarely need to set encoding; use it only to override detection (e.g. gbk, gb18030, cp936 maps to gbk)`,
849
976
  parameters: zod.z.object({
850
- file_path: zod.z.string().describe("The absolute path to the file to read"),
977
+ file_path: zod.z.string().describe(
978
+ "Absolute path to a text or text-decodable source file (not binary formats such as images, xlsx, or pdf)."
979
+ ),
851
980
  encoding: zod.z.string().optional().describe(
852
981
  'Optional. Omit or use "auto" for automatic detection (default). Set only to force a specific encoding (utf8, gbk, gb18030, latin1, etc.; cp936 is treated as gbk).'
853
982
  ),
@@ -1018,7 +1147,9 @@ var editTool = createTool({
1018
1147
 
1019
1148
  Usage:
1020
1149
  - You must use the Read tool at least once in the conversation before editing. This tool will error if you attempt an edit without reading the file
1150
+ - Files at or above 1 GiB cannot be edited with this tool (use another workflow for huge files)
1021
1151
  - When editing text from Read tool output, ensure you preserve the exact indentation (tabs/spaces) as it appears AFTER the line number prefix. The line number prefix format is: line number + tab. Everything after that is the actual file content to match. Never include any part of the line number prefix in the old_string or new_string
1152
+ - old_string may use \\n line breaks; CRLF files are matched and new_string is rewritten to the file's dominant line ending style (\\r\\n vs \\n)
1022
1153
  - ALWAYS prefer editing existing files in the codebase. NEVER write new files unless explicitly required
1023
1154
  - Only use emojis if the user explicitly requests it. Avoid adding emojis to files unless asked
1024
1155
  - The edit will FAIL if old_string is not unique in the file. Either provide a larger string with more surrounding context to make it unique or use replace_all to change every instance of old_string
@@ -1026,7 +1157,7 @@ Usage:
1026
1157
  - For non-UTF-8 files, set encoding to the same value you used with Read (default is utf8)`,
1027
1158
  parameters: zod.z.object({
1028
1159
  file_path: zod.z.string().describe("The absolute path to the file to modify"),
1029
- old_string: zod.z.string().describe("The text to replace"),
1160
+ old_string: zod.z.string().min(1).describe("The text to replace (non-empty)"),
1030
1161
  new_string: zod.z.string().describe("The text to replace it with (must be different from old_string)"),
1031
1162
  replace_all: zod.z.boolean().default(false).describe("Replace all occurrences of old_string (default false)"),
1032
1163
  encoding: zod.z.string().optional().describe(
@@ -1048,25 +1179,46 @@ Usage:
1048
1179
  isError: true
1049
1180
  };
1050
1181
  }
1182
+ const fs = await import('fs/promises');
1183
+ const stat = await fs.stat(file_path);
1184
+ if (!stat.isFile()) {
1185
+ return {
1186
+ content: `Error: ${file_path} is not a file`,
1187
+ isError: true
1188
+ };
1189
+ }
1190
+ if (stat.size >= EDIT_MAX_FILE_BYTES) {
1191
+ return {
1192
+ content: `Error: file is too large to edit (${stat.size} bytes). Maximum size is ${EDIT_MAX_FILE_BYTES} bytes (1 GiB). Use a different tool or split the work.`,
1193
+ isError: true
1194
+ };
1195
+ }
1051
1196
  const content = await readFileAsUnicodeString(file_path, normalized);
1052
- if (!content.includes(old_string)) {
1197
+ const dominantEol = detectDominantEol(content);
1198
+ const candidates = buildNeedleCandidates(old_string, dominantEol);
1199
+ let needle = null;
1200
+ for (const c of candidates) {
1201
+ if (countOccurrences(content, c) > 0) {
1202
+ needle = c;
1203
+ break;
1204
+ }
1205
+ }
1206
+ if (needle === null) {
1053
1207
  return {
1054
1208
  content: `old_string not found in ${file_path}`,
1055
1209
  isError: true
1056
1210
  };
1057
1211
  }
1058
- if (!replace_all) {
1059
- const occurrences2 = content.split(old_string).length - 1;
1060
- if (occurrences2 > 1) {
1061
- return {
1062
- content: `Found ${occurrences2} matches for old_string. Provide more context to make it unique, or set replace_all to true.`,
1063
- isError: true
1064
- };
1065
- }
1212
+ const occurrences = countOccurrences(content, needle);
1213
+ if (!replace_all && occurrences > 1) {
1214
+ return {
1215
+ content: `Found ${occurrences} matches for old_string. Provide more context to make it unique, or set replace_all to true.`,
1216
+ isError: true
1217
+ };
1066
1218
  }
1067
- const newContent = replace_all ? content.replaceAll(old_string, new_string) : content.replace(old_string, new_string);
1219
+ const normalizedNew = normalizeNewStringEols(new_string, dominantEol);
1220
+ const newContent = replaceNonOverlapping(content, needle, normalizedNew, replace_all);
1068
1221
  await writeFileFromUnicodeString(file_path, newContent, normalized);
1069
- const occurrences = replace_all ? content.split(old_string).length - 1 : 1;
1070
1222
  return {
1071
1223
  content: `Successfully edited ${file_path} (${occurrences} replacement${occurrences > 1 ? "s" : ""})`
1072
1224
  };
@@ -1148,6 +1300,53 @@ function findInPath(executable) {
1148
1300
  return null;
1149
1301
  }
1150
1302
  }
1303
+ function findGitBashNextToGitExe(gitExe) {
1304
+ if (!fs.existsSync(gitExe)) {
1305
+ return null;
1306
+ }
1307
+ const dir = path.win32.dirname(gitExe);
1308
+ const siblingBash = path.win32.join(dir, "bash.exe");
1309
+ if (fs.existsSync(siblingBash)) {
1310
+ return siblingBash;
1311
+ }
1312
+ let walk = dir;
1313
+ for (let i = 0; i < 4; i++) {
1314
+ const bashPath = path.win32.join(walk, "bin", "bash.exe");
1315
+ if (fs.existsSync(bashPath)) {
1316
+ return bashPath;
1317
+ }
1318
+ const parent = path.win32.dirname(walk);
1319
+ if (parent === walk) {
1320
+ break;
1321
+ }
1322
+ walk = parent;
1323
+ }
1324
+ return null;
1325
+ }
1326
+ function findBashViaGitInstall() {
1327
+ const gitExe = findInPath("git");
1328
+ if (!gitExe) {
1329
+ return null;
1330
+ }
1331
+ return findGitBashNextToGitExe(gitExe);
1332
+ }
1333
+ function findGitBashInDefaultInstallDirs() {
1334
+ const drives = ["C", "D", "E"];
1335
+ const relatives = [
1336
+ ["Program Files", "Git", "bin", "bash.exe"],
1337
+ ["Program Files (x86)", "Git", "bin", "bash.exe"]
1338
+ ];
1339
+ for (const drive of drives) {
1340
+ const root = `${drive}:\\`;
1341
+ for (const parts of relatives) {
1342
+ const p = path.win32.join(root, ...parts);
1343
+ if (fs.existsSync(p)) {
1344
+ return p;
1345
+ }
1346
+ }
1347
+ }
1348
+ return null;
1349
+ }
1151
1350
  function getShellPath() {
1152
1351
  if (cachedShellPath !== null) {
1153
1352
  return cachedShellPath;
@@ -1158,15 +1357,15 @@ function getShellPath() {
1158
1357
  cachedShellPath = bashPath;
1159
1358
  return bashPath;
1160
1359
  }
1161
- const gitBashPaths = [
1162
- "C:\\Program Files\\Git\\bin\\bash.exe",
1163
- "C:\\Program Files (x86)\\Git\\bin\\bash.exe"
1164
- ];
1165
- for (const path of gitBashPaths) {
1166
- if (fs.existsSync(path)) {
1167
- cachedShellPath = path;
1168
- return path;
1169
- }
1360
+ const viaGit = findBashViaGitInstall();
1361
+ if (viaGit) {
1362
+ cachedShellPath = viaGit;
1363
+ return viaGit;
1364
+ }
1365
+ const fromDirs = findGitBashInDefaultInstallDirs();
1366
+ if (fromDirs) {
1367
+ cachedShellPath = fromDirs;
1368
+ return fromDirs;
1170
1369
  }
1171
1370
  const pwshPath = findInPath("pwsh");
1172
1371
  if (pwshPath) {
@@ -1990,128 +2189,52 @@ var webSearchTool = createTool({
1990
2189
  function getWebTools() {
1991
2190
  return [webFetchTool, webSearchTool];
1992
2191
  }
1993
- var tasks = /* @__PURE__ */ new Map();
1994
- var nextId = 1;
1995
- var taskCreateTool = createTool({
1996
- name: "TaskCreate",
2192
+ var todoStatusEnum = zod.z.enum(["pending", "in_progress", "completed"]);
2193
+ var todoWriteTool = createTool({
2194
+ name: "TodoWrite",
1997
2195
  category: "planning",
1998
- description: `Use this tool to create a structured task list for your current coding session. This helps you track progress, organize complex tasks, and demonstrate thoroughness to the user.
2196
+ description: `MUST USE for multi-step tasks. Creates a structured task list to track progress. Call this tool FIRST before executing any complex task.
1999
2197
 
2000
- ## When to Use This Tool
2198
+ Any number of tasks may be "in_progress" when work runs in parallel. Zero in_progress is fine (e.g. all pending or all completed).
2001
2199
 
2002
- - Complex multi-step tasks - When a task requires 3 or more distinct steps or actions
2003
- - Non-trivial and complex tasks - Tasks that require careful planning or multiple operations
2004
- - User explicitly requests todo list - When the user directly asks you to use the task list
2005
- - User provides multiple tasks - When users provide a list of things to be done
2200
+ **Before you finish your assistant response for a multi-step request**, call \`TodoWrite\` again so **every** item in the list has status \`completed\`, unless you are explicitly pausing mid-work for a follow-up turn. Do not leave dangling \`pending\` or \`in_progress\` items when the work for this request is done.
2006
2201
 
2007
- ## When NOT to Use
2202
+ **Replanning:** If execution shows the earlier plan was wrong, incomplete, or no longer fits (wrong order, missing steps, obsolete items), call \`TodoWrite\` anytime with an updated full list\u2014add, remove, merge, or rewrite steps as needed.
2008
2203
 
2009
- - There is only a single, straightforward task
2010
- - The task is trivial and tracking it provides no organizational benefit
2011
- - The task can be completed in less than 3 trivial steps
2012
-
2013
- All tasks are created with status pending.`,
2204
+ Triggers: multi-step tasks, multiple files/components, user provides task list.
2205
+ Skip: single simple task, informational questions.`,
2014
2206
  parameters: zod.z.object({
2015
- subject: zod.z.string().describe("A brief title for the task"),
2016
- description: zod.z.string().describe("What needs to be done"),
2017
- activeForm: zod.z.string().optional().describe('Present continuous form shown in spinner when in_progress (e.g., "Running tests")')
2018
- }),
2019
- handler: async ({ subject, description, activeForm }) => {
2020
- const id = String(nextId++);
2021
- const task = {
2022
- id,
2023
- subject,
2024
- description,
2025
- status: "pending",
2026
- ...activeForm && { activeForm }
2027
- };
2028
- tasks.set(id, task);
2029
- return {
2030
- content: `Task created [${id}]: ${subject}`,
2031
- metadata: { task }
2032
- };
2033
- }
2034
- });
2035
- var taskUpdateTool = createTool({
2036
- name: "TaskUpdate",
2037
- category: "planning",
2038
- description: `Use this tool to update a task in the task list.
2039
-
2040
- ## When to Use This Tool
2041
-
2042
- **Mark tasks as completed:**
2043
- - When you have completed the work described in a task
2044
- - IMPORTANT: Always mark your assigned tasks as completed when you finish them
2045
-
2046
- **Delete tasks:**
2047
- - When a task is no longer relevant or was created in error
2048
-
2049
- ## Status Workflow
2050
-
2051
- Status progresses: pending \u2192 in_progress \u2192 completed`,
2052
- parameters: zod.z.object({
2053
- taskId: zod.z.string().describe("The ID of the task to update"),
2054
- status: zod.z.enum(["pending", "in_progress", "completed", "deleted"]).optional().describe("New status for the task"),
2055
- subject: zod.z.string().optional().describe("New subject for the task"),
2056
- description: zod.z.string().optional().describe("New description for the task"),
2057
- activeForm: zod.z.string().optional().describe("Present continuous form shown in spinner when in_progress")
2207
+ todos: zod.z.array(
2208
+ zod.z.object({
2209
+ content: zod.z.string().describe('Brief description of the task in imperative form (e.g., "Run tests")'),
2210
+ activeForm: zod.z.string().optional().describe(
2211
+ 'Optional present-continuous label for in-progress UI (e.g., "Running tests"). Omit if not needed.'
2212
+ ),
2213
+ status: todoStatusEnum.describe("Task status: pending, in_progress, or completed")
2214
+ })
2215
+ ).min(1).describe(
2216
+ "Full updated todo list. Refresh this whenever progress or the plan changes. When finishing the multi-step work, all entries should be completed."
2217
+ )
2058
2218
  }),
2059
- handler: async ({ taskId, status, subject, description, activeForm }) => {
2060
- const task = tasks.get(taskId);
2061
- if (!task) {
2062
- return {
2063
- content: `Task ${taskId} not found`,
2064
- isError: true
2065
- };
2066
- }
2067
- if (status === "deleted") {
2068
- tasks.delete(taskId);
2069
- return { content: `Task ${taskId} deleted` };
2070
- }
2071
- if (status) task.status = status;
2072
- if (subject) task.subject = subject;
2073
- if (description) task.description = description;
2074
- if (activeForm) task.activeForm = activeForm;
2075
- const icon = task.status === "completed" ? "x" : task.status === "in_progress" ? ">" : " ";
2076
- return {
2077
- content: `Task [${taskId}] updated: [${icon}] ${task.subject}`,
2078
- metadata: { task }
2079
- };
2080
- }
2081
- });
2082
- var taskListTool = createTool({
2083
- name: "TaskList",
2084
- category: "planning",
2085
- description: `Use this tool to list all tasks in the task list.
2086
-
2087
- ## Output
2088
-
2089
- Returns a summary of each task:
2090
- - id: Task identifier (use with TaskUpdate)
2091
- - subject: Brief description of the task
2092
- - status: pending, in_progress, or completed`,
2093
- parameters: zod.z.object({}),
2094
- handler: async () => {
2095
- if (tasks.size === 0) {
2096
- return { content: "No tasks in the list." };
2097
- }
2219
+ handler: async ({ todos }) => {
2098
2220
  const lines = [];
2099
- for (const [, task] of tasks) {
2100
- const icon = task.status === "completed" ? "x" : task.status === "in_progress" ? ">" : " ";
2101
- lines.push(`[${icon}] [${task.id}] ${task.subject}`);
2221
+ for (const todo of todos) {
2222
+ const icon = todo.status === "completed" ? "x" : todo.status === "in_progress" ? ">" : " ";
2223
+ lines.push(`[${icon}] ${todo.content}`);
2102
2224
  }
2103
- const pending = [...tasks.values()].filter((t) => t.status === "pending").length;
2104
- const inProgress = [...tasks.values()].filter((t) => t.status === "in_progress").length;
2105
- const completed = [...tasks.values()].filter((t) => t.status === "completed").length;
2225
+ const pending = todos.filter((todo) => todo.status === "pending").length;
2226
+ const inProgress = todos.filter((todo) => todo.status === "in_progress").length;
2227
+ const completed = todos.filter((todo) => todo.status === "completed").length;
2106
2228
  return {
2107
- content: `Tasks (${completed} completed, ${inProgress} in progress, ${pending} pending):
2229
+ content: `Task list updated (${completed} completed, ${inProgress} in progress, ${pending} pending):
2108
2230
 
2109
- ${lines.join("\n")}`
2231
+ ${lines.join("\n")}`,
2232
+ metadata: { todos }
2110
2233
  };
2111
2234
  }
2112
2235
  });
2113
- function getTaskTools() {
2114
- return [taskCreateTool, taskUpdateTool, taskListTool];
2236
+ function getPlanningTools() {
2237
+ return [todoWriteTool];
2115
2238
  }
2116
2239
  var questionsSchema = zod.z.object({
2117
2240
  questions: zod.z.array(
@@ -2322,7 +2445,7 @@ function getAllBuiltinTools(skillRegistry, interactionOptions) {
2322
2445
  ...getShellTools(),
2323
2446
  ...getGrepTools(),
2324
2447
  ...getWebTools(),
2325
- ...getTaskTools(),
2448
+ ...getPlanningTools(),
2326
2449
  ...getInteractionTools(interactionOptions),
2327
2450
  ...getSubagentTools(),
2328
2451
  ...getSkillTools(skillRegistry)
@@ -2761,11 +2884,11 @@ exports.getFileSystemTools = getFileSystemTools;
2761
2884
  exports.getGlobalRegistry = getGlobalRegistry;
2762
2885
  exports.getGrepTools = getGrepTools;
2763
2886
  exports.getInteractionTools = getInteractionTools;
2887
+ exports.getPlanningTools = getPlanningTools;
2764
2888
  exports.getSafeBuiltinTools = getSafeBuiltinTools;
2765
2889
  exports.getShellTools = getShellTools;
2766
2890
  exports.getSkillTools = getSkillTools;
2767
2891
  exports.getSubagentTools = getSubagentTools;
2768
- exports.getTaskTools = getTaskTools;
2769
2892
  exports.getWebTools = getWebTools;
2770
2893
  exports.globTool = globTool;
2771
2894
  exports.grepTool = grepTool;
@@ -2777,12 +2900,10 @@ exports.parseHooksSettingsFile = parseHooksSettingsFile;
2777
2900
  exports.questionTool = questionTool;
2778
2901
  exports.readFileTool = readFileTool;
2779
2902
  exports.subagentRequestSchema = subagentRequestSchema;
2780
- exports.taskCreateTool = taskCreateTool;
2781
- exports.taskListTool = taskListTool;
2782
- exports.taskUpdateTool = taskUpdateTool;
2903
+ exports.todoWriteTool = todoWriteTool;
2783
2904
  exports.truncateMatchLineForDisplay = truncateMatchLineForDisplay;
2784
2905
  exports.webFetchTool = webFetchTool;
2785
2906
  exports.webSearchTool = webSearchTool;
2786
2907
  exports.writeFileTool = writeFileTool;
2787
- //# sourceMappingURL=chunk-JF5AJQMU.cjs.map
2788
- //# sourceMappingURL=chunk-JF5AJQMU.cjs.map
2908
+ //# sourceMappingURL=chunk-Z45DHTDX.cjs.map
2909
+ //# sourceMappingURL=chunk-Z45DHTDX.cjs.map