@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,11 +1,11 @@
1
1
  #!/usr/bin/env node
2
- import { zodToJsonSchema } from './chunk-WH3APNQ5.js';
2
+ import { zodToJsonSchema } from './chunk-LOYIGOBZ.js';
3
3
  import { mkdir, writeFile, readFile } from 'fs/promises';
4
- import { join, dirname } from 'path';
4
+ import { join, dirname, win32 } from 'path';
5
5
  import { homedir } from 'os';
6
- import { z } from 'zod';
7
6
  import iconv from 'iconv-lite';
8
7
  import fg from 'fast-glob';
8
+ import { z } from 'zod';
9
9
  import { analyse } from 'chardet';
10
10
  import { execSync, spawn } from 'child_process';
11
11
  import { existsSync } from 'fs';
@@ -223,16 +223,20 @@ var OutputHandler = class {
223
223
  function createOutputHandler(userBasePath) {
224
224
  return new OutputHandler(userBasePath);
225
225
  }
226
+
227
+ // src/tools/registry.ts
226
228
  var ToolRegistry = class {
227
229
  tools = /* @__PURE__ */ new Map();
228
230
  categories = /* @__PURE__ */ new Map();
229
231
  outputHandler;
230
232
  hookManager = null;
231
233
  executionPolicy;
234
+ hookObserver;
232
235
  constructor(config) {
233
236
  const enableOutputHandler = config?.enableOutputHandler !== false;
234
237
  this.outputHandler = enableOutputHandler ? createOutputHandler(config?.userBasePath) : null;
235
238
  this.executionPolicy = config?.executionPolicy;
239
+ this.hookObserver = config?.hookObserver;
236
240
  }
237
241
  /**
238
242
  * 工具名是否在 {@link ToolExecutionPolicy.disallowedTools} 中(无策略时为 false)。
@@ -299,6 +303,14 @@ var ToolRegistry = class {
299
303
  ...extra
300
304
  };
301
305
  }
306
+ hookObserverCtx(eventType, name, options) {
307
+ return {
308
+ eventType,
309
+ toolName: name,
310
+ toolCallId: options?.toolCallId,
311
+ projectDir: options?.projectDir
312
+ };
313
+ }
302
314
  /**
303
315
  * 注册工具
304
316
  */
@@ -369,6 +381,11 @@ var ToolRegistry = class {
369
381
  }
370
382
  const tool = this.tools.get(name);
371
383
  if (!tool) {
384
+ if (hookMgr) {
385
+ this.hookObserver?.onHookStart?.(
386
+ this.hookObserverCtx("postToolUseFailure", name, options)
387
+ );
388
+ }
372
389
  const ctx = this.buildHookContext("postToolUseFailure", name, rawArgsObj, options, {
373
390
  errorMessage: `Tool "${name}" not found`,
374
391
  failureKind: "tool_error"
@@ -381,32 +398,56 @@ var ToolRegistry = class {
381
398
  }
382
399
  let workingInput = rawArgsObj;
383
400
  try {
384
- workingInput = tool.parameters.parse(args);
401
+ const initial = tool.parameters.safeParse(args);
402
+ if (!initial.success) {
403
+ const msg = `Invalid arguments for tool "${name}": ${initial.error.issues.map((i) => i.message).join(", ")}`;
404
+ if (hookMgr) {
405
+ this.hookObserver?.onHookStart?.(
406
+ this.hookObserverCtx("postToolUseFailure", name, options)
407
+ );
408
+ }
409
+ await hookMgr?.executePostToolUseFailure(
410
+ this.buildHookContext("postToolUseFailure", name, rawArgsObj, options, {
411
+ errorMessage: msg,
412
+ failureKind: "validation"
413
+ })
414
+ );
415
+ return { content: msg, isError: true };
416
+ }
417
+ workingInput = initial.data;
385
418
  if (hookMgr) {
419
+ this.hookObserver?.onHookStart?.(
420
+ this.hookObserverCtx("preToolUse", name, options)
421
+ );
386
422
  const pre = await hookMgr.executePreToolUse(
387
423
  this.buildHookContext("preToolUse", name, workingInput, options)
388
424
  );
425
+ this.hookObserver?.onHookDecision?.({
426
+ ...this.hookObserverCtx("preToolUse", name, options),
427
+ allowed: pre.allowed,
428
+ reason: pre.reason
429
+ });
389
430
  if (!pre.allowed) {
390
431
  return {
391
432
  content: pre.reason ?? "Blocked by hook",
392
433
  isError: true
393
434
  };
394
435
  }
395
- try {
396
- workingInput = tool.parameters.parse(pre.updatedInput ?? workingInput);
397
- } catch (err) {
398
- if (err instanceof z.ZodError) {
399
- const msg = `Invalid arguments after hook merge for tool "${name}": ${err.errors.map((e) => e.message).join(", ")}`;
400
- await hookMgr.executePostToolUseFailure(
401
- this.buildHookContext("postToolUseFailure", name, workingInput, options, {
402
- errorMessage: msg,
403
- failureKind: "validation"
404
- })
405
- );
406
- return { content: msg, isError: true };
407
- }
408
- throw err;
436
+ const merged = tool.parameters.safeParse(pre.updatedInput ?? workingInput);
437
+ if (!merged.success) {
438
+ const msg = `Invalid arguments after hook merge for tool "${name}": ${merged.error.issues.map((i) => i.message).join(", ")}`;
439
+ this.hookObserver?.onHookStart?.(
440
+ this.hookObserverCtx("postToolUseFailure", name, options)
441
+ );
442
+ await hookMgr.executePostToolUseFailure(
443
+ this.buildHookContext("postToolUseFailure", name, workingInput, options, {
444
+ errorMessage: msg,
445
+ failureKind: "validation"
446
+ })
447
+ );
448
+ return { content: msg, isError: true };
409
449
  }
450
+ workingInput = merged.data;
410
451
  }
411
452
  const handlerArgs = workingInput;
412
453
  const executionContext = {
@@ -417,6 +458,11 @@ var ToolRegistry = class {
417
458
  const result = await tool.handler(handlerArgs, executionContext);
418
459
  const toolResultRaw = result;
419
460
  if (result.isError) {
461
+ if (hookMgr) {
462
+ this.hookObserver?.onHookStart?.(
463
+ this.hookObserverCtx("postToolUseFailure", name, options)
464
+ );
465
+ }
420
466
  await hookMgr?.executePostToolUseFailure(
421
467
  this.buildHookContext("postToolUseFailure", name, workingInput, options, {
422
468
  errorMessage: result.content,
@@ -434,6 +480,11 @@ var ToolRegistry = class {
434
480
  { args: handlerArgs }
435
481
  );
436
482
  }
483
+ if (hookMgr) {
484
+ this.hookObserver?.onHookStart?.(
485
+ this.hookObserverCtx("postToolUse", name, options)
486
+ );
487
+ }
437
488
  await hookMgr?.executePostToolUse(
438
489
  this.buildHookContext("postToolUse", name, workingInput, options, {
439
490
  toolResultRaw,
@@ -442,20 +493,12 @@ var ToolRegistry = class {
442
493
  );
443
494
  return finalResult;
444
495
  } catch (error) {
445
- if (error instanceof z.ZodError) {
446
- const msg2 = `Invalid arguments for tool "${name}": ${error.errors.map((e) => e.message).join(", ")}`;
447
- await hookMgr?.executePostToolUseFailure(
448
- this.buildHookContext("postToolUseFailure", name, rawArgsObj, options, {
449
- errorMessage: msg2,
450
- failureKind: "validation"
451
- })
496
+ const msg = `Error executing tool "${name}": ${error instanceof Error ? error.message : String(error)}`;
497
+ if (hookMgr) {
498
+ this.hookObserver?.onHookStart?.(
499
+ this.hookObserverCtx("postToolUseFailure", name, options)
452
500
  );
453
- return {
454
- content: msg2,
455
- isError: true
456
- };
457
501
  }
458
- const msg = `Error executing tool "${name}": ${error instanceof Error ? error.message : String(error)}`;
459
502
  await hookMgr?.executePostToolUseFailure(
460
503
  this.buildHookContext("postToolUseFailure", name, workingInput, options, {
461
504
  errorMessage: msg,
@@ -802,10 +845,94 @@ var MAX_LINE_LENGTH = 2e3;
802
845
  var MAX_LINE_SUFFIX = `... (line truncated to ${MAX_LINE_LENGTH} chars)`;
803
846
  var MAX_BYTES = 50 * 1024;
804
847
  var MAX_BYTES_LABEL = `${MAX_BYTES / 1024} KB`;
848
+ var EDIT_MAX_FILE_BYTES = 1024 ** 3;
849
+ function detectDominantEol(content) {
850
+ let crlf = 0;
851
+ for (let i = 0; i < content.length - 1; i++) {
852
+ if (content[i] === "\r" && content[i + 1] === "\n") {
853
+ crlf++;
854
+ }
855
+ }
856
+ let nl = 0;
857
+ for (let i = 0; i < content.length; i++) {
858
+ if (content[i] === "\n") {
859
+ nl++;
860
+ }
861
+ }
862
+ const bareLf = nl - crlf;
863
+ if (crlf > bareLf) {
864
+ return "\r\n";
865
+ }
866
+ return "\n";
867
+ }
868
+ function normalizeNewStringEols(text, eol) {
869
+ const unified = text.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
870
+ if (eol === "\n") {
871
+ return unified;
872
+ }
873
+ return unified.replace(/\n/g, "\r\n");
874
+ }
875
+ function buildNeedleCandidates(oldString, dominantEol) {
876
+ const out = [oldString];
877
+ if (dominantEol === "\r\n") {
878
+ if (!oldString.includes("\r")) {
879
+ const v = oldString.replace(/\n/g, "\r\n");
880
+ if (v !== oldString) {
881
+ out.push(v);
882
+ }
883
+ }
884
+ } else {
885
+ if (oldString.includes("\r\n")) {
886
+ const v = oldString.replace(/\r\n/g, "\n");
887
+ if (v !== oldString) {
888
+ out.push(v);
889
+ }
890
+ } else if (oldString.includes("\r")) {
891
+ const v = oldString.replace(/\r/g, "\n");
892
+ if (v !== oldString) {
893
+ out.push(v);
894
+ }
895
+ }
896
+ }
897
+ return out;
898
+ }
899
+ function countOccurrences(haystack, needle) {
900
+ if (needle.length === 0) {
901
+ return 0;
902
+ }
903
+ let n = 0;
904
+ let from = 0;
905
+ let i = 0;
906
+ while ((i = haystack.indexOf(needle, from)) !== -1) {
907
+ n++;
908
+ from = i + needle.length;
909
+ }
910
+ return n;
911
+ }
912
+ function replaceNonOverlapping(content, needle, replacement, replaceAll) {
913
+ if (!replaceAll) {
914
+ const i2 = content.indexOf(needle);
915
+ if (i2 === -1) {
916
+ throw new Error("Edit: needle not found after resolution (internal inconsistency)");
917
+ }
918
+ return content.slice(0, i2) + replacement + content.slice(i2 + needle.length);
919
+ }
920
+ let out = "";
921
+ let from = 0;
922
+ let i = 0;
923
+ while ((i = content.indexOf(needle, from)) !== -1) {
924
+ out += content.slice(from, i) + replacement;
925
+ from = i + needle.length;
926
+ }
927
+ out += content.slice(from);
928
+ return out;
929
+ }
805
930
  var readFileTool = createTool({
806
931
  name: "Read",
807
932
  category: "filesystem",
808
- description: `Reads a file from the local filesystem. You can access any file directly by using this tool.
933
+ description: `Reads human-readable text from the local filesystem: source code, configuration, logs, Markdown, and structured text (JSON, XML, YAML, etc.).
934
+
935
+ 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.
809
936
 
810
937
  Usage:
811
938
  - The file_path parameter must be an absolute path, not a relative path
@@ -814,10 +941,12 @@ Usage:
814
941
  - Results are returned using cat -n style, with line numbers starting at 1
815
942
  - Lines longer than 2000 characters are truncated
816
943
  - Use the offset and limit parameters to read specific line ranges of large files
817
- - If you read a file that exists but has empty contents you will receive an error message
944
+ - An empty file (no lines) returns successfully with no numbered lines and a suffix noting zero total lines\u2014it is not an error
818
945
  - 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)`,
819
946
  parameters: z.object({
820
- file_path: z.string().describe("The absolute path to the file to read"),
947
+ file_path: z.string().describe(
948
+ "Absolute path to a text or text-decodable source file (not binary formats such as images, xlsx, or pdf)."
949
+ ),
821
950
  encoding: z.string().optional().describe(
822
951
  '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).'
823
952
  ),
@@ -988,7 +1117,9 @@ var editTool = createTool({
988
1117
 
989
1118
  Usage:
990
1119
  - 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
1120
+ - Files at or above 1 GiB cannot be edited with this tool (use another workflow for huge files)
991
1121
  - 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
1122
+ - 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)
992
1123
  - ALWAYS prefer editing existing files in the codebase. NEVER write new files unless explicitly required
993
1124
  - Only use emojis if the user explicitly requests it. Avoid adding emojis to files unless asked
994
1125
  - 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
@@ -996,7 +1127,7 @@ Usage:
996
1127
  - For non-UTF-8 files, set encoding to the same value you used with Read (default is utf8)`,
997
1128
  parameters: z.object({
998
1129
  file_path: z.string().describe("The absolute path to the file to modify"),
999
- old_string: z.string().describe("The text to replace"),
1130
+ old_string: z.string().min(1).describe("The text to replace (non-empty)"),
1000
1131
  new_string: z.string().describe("The text to replace it with (must be different from old_string)"),
1001
1132
  replace_all: z.boolean().default(false).describe("Replace all occurrences of old_string (default false)"),
1002
1133
  encoding: z.string().optional().describe(
@@ -1018,25 +1149,46 @@ Usage:
1018
1149
  isError: true
1019
1150
  };
1020
1151
  }
1152
+ const fs = await import('fs/promises');
1153
+ const stat = await fs.stat(file_path);
1154
+ if (!stat.isFile()) {
1155
+ return {
1156
+ content: `Error: ${file_path} is not a file`,
1157
+ isError: true
1158
+ };
1159
+ }
1160
+ if (stat.size >= EDIT_MAX_FILE_BYTES) {
1161
+ return {
1162
+ 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.`,
1163
+ isError: true
1164
+ };
1165
+ }
1021
1166
  const content = await readFileAsUnicodeString(file_path, normalized);
1022
- if (!content.includes(old_string)) {
1167
+ const dominantEol = detectDominantEol(content);
1168
+ const candidates = buildNeedleCandidates(old_string, dominantEol);
1169
+ let needle = null;
1170
+ for (const c of candidates) {
1171
+ if (countOccurrences(content, c) > 0) {
1172
+ needle = c;
1173
+ break;
1174
+ }
1175
+ }
1176
+ if (needle === null) {
1023
1177
  return {
1024
1178
  content: `old_string not found in ${file_path}`,
1025
1179
  isError: true
1026
1180
  };
1027
1181
  }
1028
- if (!replace_all) {
1029
- const occurrences2 = content.split(old_string).length - 1;
1030
- if (occurrences2 > 1) {
1031
- return {
1032
- content: `Found ${occurrences2} matches for old_string. Provide more context to make it unique, or set replace_all to true.`,
1033
- isError: true
1034
- };
1035
- }
1182
+ const occurrences = countOccurrences(content, needle);
1183
+ if (!replace_all && occurrences > 1) {
1184
+ return {
1185
+ content: `Found ${occurrences} matches for old_string. Provide more context to make it unique, or set replace_all to true.`,
1186
+ isError: true
1187
+ };
1036
1188
  }
1037
- const newContent = replace_all ? content.replaceAll(old_string, new_string) : content.replace(old_string, new_string);
1189
+ const normalizedNew = normalizeNewStringEols(new_string, dominantEol);
1190
+ const newContent = replaceNonOverlapping(content, needle, normalizedNew, replace_all);
1038
1191
  await writeFileFromUnicodeString(file_path, newContent, normalized);
1039
- const occurrences = replace_all ? content.split(old_string).length - 1 : 1;
1040
1192
  return {
1041
1193
  content: `Successfully edited ${file_path} (${occurrences} replacement${occurrences > 1 ? "s" : ""})`
1042
1194
  };
@@ -1118,6 +1270,53 @@ function findInPath(executable) {
1118
1270
  return null;
1119
1271
  }
1120
1272
  }
1273
+ function findGitBashNextToGitExe(gitExe) {
1274
+ if (!existsSync(gitExe)) {
1275
+ return null;
1276
+ }
1277
+ const dir = win32.dirname(gitExe);
1278
+ const siblingBash = win32.join(dir, "bash.exe");
1279
+ if (existsSync(siblingBash)) {
1280
+ return siblingBash;
1281
+ }
1282
+ let walk = dir;
1283
+ for (let i = 0; i < 4; i++) {
1284
+ const bashPath = win32.join(walk, "bin", "bash.exe");
1285
+ if (existsSync(bashPath)) {
1286
+ return bashPath;
1287
+ }
1288
+ const parent = win32.dirname(walk);
1289
+ if (parent === walk) {
1290
+ break;
1291
+ }
1292
+ walk = parent;
1293
+ }
1294
+ return null;
1295
+ }
1296
+ function findBashViaGitInstall() {
1297
+ const gitExe = findInPath("git");
1298
+ if (!gitExe) {
1299
+ return null;
1300
+ }
1301
+ return findGitBashNextToGitExe(gitExe);
1302
+ }
1303
+ function findGitBashInDefaultInstallDirs() {
1304
+ const drives = ["C", "D", "E"];
1305
+ const relatives = [
1306
+ ["Program Files", "Git", "bin", "bash.exe"],
1307
+ ["Program Files (x86)", "Git", "bin", "bash.exe"]
1308
+ ];
1309
+ for (const drive of drives) {
1310
+ const root = `${drive}:\\`;
1311
+ for (const parts of relatives) {
1312
+ const p = win32.join(root, ...parts);
1313
+ if (existsSync(p)) {
1314
+ return p;
1315
+ }
1316
+ }
1317
+ }
1318
+ return null;
1319
+ }
1121
1320
  function getShellPath() {
1122
1321
  if (cachedShellPath !== null) {
1123
1322
  return cachedShellPath;
@@ -1128,15 +1327,15 @@ function getShellPath() {
1128
1327
  cachedShellPath = bashPath;
1129
1328
  return bashPath;
1130
1329
  }
1131
- const gitBashPaths = [
1132
- "C:\\Program Files\\Git\\bin\\bash.exe",
1133
- "C:\\Program Files (x86)\\Git\\bin\\bash.exe"
1134
- ];
1135
- for (const path of gitBashPaths) {
1136
- if (existsSync(path)) {
1137
- cachedShellPath = path;
1138
- return path;
1139
- }
1330
+ const viaGit = findBashViaGitInstall();
1331
+ if (viaGit) {
1332
+ cachedShellPath = viaGit;
1333
+ return viaGit;
1334
+ }
1335
+ const fromDirs = findGitBashInDefaultInstallDirs();
1336
+ if (fromDirs) {
1337
+ cachedShellPath = fromDirs;
1338
+ return fromDirs;
1140
1339
  }
1141
1340
  const pwshPath = findInPath("pwsh");
1142
1341
  if (pwshPath) {
@@ -1960,128 +2159,52 @@ var webSearchTool = createTool({
1960
2159
  function getWebTools() {
1961
2160
  return [webFetchTool, webSearchTool];
1962
2161
  }
1963
- var tasks = /* @__PURE__ */ new Map();
1964
- var nextId = 1;
1965
- var taskCreateTool = createTool({
1966
- name: "TaskCreate",
2162
+ var todoStatusEnum = z.enum(["pending", "in_progress", "completed"]);
2163
+ var todoWriteTool = createTool({
2164
+ name: "TodoWrite",
1967
2165
  category: "planning",
1968
- 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.
2166
+ description: `MUST USE for multi-step tasks. Creates a structured task list to track progress. Call this tool FIRST before executing any complex task.
1969
2167
 
1970
- ## When to Use This Tool
2168
+ 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).
1971
2169
 
1972
- - Complex multi-step tasks - When a task requires 3 or more distinct steps or actions
1973
- - Non-trivial and complex tasks - Tasks that require careful planning or multiple operations
1974
- - User explicitly requests todo list - When the user directly asks you to use the task list
1975
- - User provides multiple tasks - When users provide a list of things to be done
2170
+ **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.
1976
2171
 
1977
- ## When NOT to Use
2172
+ **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.
1978
2173
 
1979
- - There is only a single, straightforward task
1980
- - The task is trivial and tracking it provides no organizational benefit
1981
- - The task can be completed in less than 3 trivial steps
1982
-
1983
- All tasks are created with status pending.`,
2174
+ Triggers: multi-step tasks, multiple files/components, user provides task list.
2175
+ Skip: single simple task, informational questions.`,
1984
2176
  parameters: z.object({
1985
- subject: z.string().describe("A brief title for the task"),
1986
- description: z.string().describe("What needs to be done"),
1987
- activeForm: z.string().optional().describe('Present continuous form shown in spinner when in_progress (e.g., "Running tests")')
1988
- }),
1989
- handler: async ({ subject, description, activeForm }) => {
1990
- const id = String(nextId++);
1991
- const task = {
1992
- id,
1993
- subject,
1994
- description,
1995
- status: "pending",
1996
- ...activeForm && { activeForm }
1997
- };
1998
- tasks.set(id, task);
1999
- return {
2000
- content: `Task created [${id}]: ${subject}`,
2001
- metadata: { task }
2002
- };
2003
- }
2004
- });
2005
- var taskUpdateTool = createTool({
2006
- name: "TaskUpdate",
2007
- category: "planning",
2008
- description: `Use this tool to update a task in the task list.
2009
-
2010
- ## When to Use This Tool
2011
-
2012
- **Mark tasks as completed:**
2013
- - When you have completed the work described in a task
2014
- - IMPORTANT: Always mark your assigned tasks as completed when you finish them
2015
-
2016
- **Delete tasks:**
2017
- - When a task is no longer relevant or was created in error
2018
-
2019
- ## Status Workflow
2020
-
2021
- Status progresses: pending \u2192 in_progress \u2192 completed`,
2022
- parameters: z.object({
2023
- taskId: z.string().describe("The ID of the task to update"),
2024
- status: z.enum(["pending", "in_progress", "completed", "deleted"]).optional().describe("New status for the task"),
2025
- subject: z.string().optional().describe("New subject for the task"),
2026
- description: z.string().optional().describe("New description for the task"),
2027
- activeForm: z.string().optional().describe("Present continuous form shown in spinner when in_progress")
2177
+ todos: z.array(
2178
+ z.object({
2179
+ content: z.string().describe('Brief description of the task in imperative form (e.g., "Run tests")'),
2180
+ activeForm: z.string().optional().describe(
2181
+ 'Optional present-continuous label for in-progress UI (e.g., "Running tests"). Omit if not needed.'
2182
+ ),
2183
+ status: todoStatusEnum.describe("Task status: pending, in_progress, or completed")
2184
+ })
2185
+ ).min(1).describe(
2186
+ "Full updated todo list. Refresh this whenever progress or the plan changes. When finishing the multi-step work, all entries should be completed."
2187
+ )
2028
2188
  }),
2029
- handler: async ({ taskId, status, subject, description, activeForm }) => {
2030
- const task = tasks.get(taskId);
2031
- if (!task) {
2032
- return {
2033
- content: `Task ${taskId} not found`,
2034
- isError: true
2035
- };
2036
- }
2037
- if (status === "deleted") {
2038
- tasks.delete(taskId);
2039
- return { content: `Task ${taskId} deleted` };
2040
- }
2041
- if (status) task.status = status;
2042
- if (subject) task.subject = subject;
2043
- if (description) task.description = description;
2044
- if (activeForm) task.activeForm = activeForm;
2045
- const icon = task.status === "completed" ? "x" : task.status === "in_progress" ? ">" : " ";
2046
- return {
2047
- content: `Task [${taskId}] updated: [${icon}] ${task.subject}`,
2048
- metadata: { task }
2049
- };
2050
- }
2051
- });
2052
- var taskListTool = createTool({
2053
- name: "TaskList",
2054
- category: "planning",
2055
- description: `Use this tool to list all tasks in the task list.
2056
-
2057
- ## Output
2058
-
2059
- Returns a summary of each task:
2060
- - id: Task identifier (use with TaskUpdate)
2061
- - subject: Brief description of the task
2062
- - status: pending, in_progress, or completed`,
2063
- parameters: z.object({}),
2064
- handler: async () => {
2065
- if (tasks.size === 0) {
2066
- return { content: "No tasks in the list." };
2067
- }
2189
+ handler: async ({ todos }) => {
2068
2190
  const lines = [];
2069
- for (const [, task] of tasks) {
2070
- const icon = task.status === "completed" ? "x" : task.status === "in_progress" ? ">" : " ";
2071
- lines.push(`[${icon}] [${task.id}] ${task.subject}`);
2191
+ for (const todo of todos) {
2192
+ const icon = todo.status === "completed" ? "x" : todo.status === "in_progress" ? ">" : " ";
2193
+ lines.push(`[${icon}] ${todo.content}`);
2072
2194
  }
2073
- const pending = [...tasks.values()].filter((t) => t.status === "pending").length;
2074
- const inProgress = [...tasks.values()].filter((t) => t.status === "in_progress").length;
2075
- const completed = [...tasks.values()].filter((t) => t.status === "completed").length;
2195
+ const pending = todos.filter((todo) => todo.status === "pending").length;
2196
+ const inProgress = todos.filter((todo) => todo.status === "in_progress").length;
2197
+ const completed = todos.filter((todo) => todo.status === "completed").length;
2076
2198
  return {
2077
- content: `Tasks (${completed} completed, ${inProgress} in progress, ${pending} pending):
2199
+ content: `Task list updated (${completed} completed, ${inProgress} in progress, ${pending} pending):
2078
2200
 
2079
- ${lines.join("\n")}`
2201
+ ${lines.join("\n")}`,
2202
+ metadata: { todos }
2080
2203
  };
2081
2204
  }
2082
2205
  });
2083
- function getTaskTools() {
2084
- return [taskCreateTool, taskUpdateTool, taskListTool];
2206
+ function getPlanningTools() {
2207
+ return [todoWriteTool];
2085
2208
  }
2086
2209
  var questionsSchema = z.object({
2087
2210
  questions: z.array(
@@ -2292,7 +2415,7 @@ function getAllBuiltinTools(skillRegistry, interactionOptions) {
2292
2415
  ...getShellTools(),
2293
2416
  ...getGrepTools(),
2294
2417
  ...getWebTools(),
2295
- ...getTaskTools(),
2418
+ ...getPlanningTools(),
2296
2419
  ...getInteractionTools(interactionOptions),
2297
2420
  ...getSubagentTools(),
2298
2421
  ...getSkillTools(skillRegistry)
@@ -2703,6 +2826,6 @@ function createFunctionHook(config) {
2703
2826
  };
2704
2827
  }
2705
2828
 
2706
- export { DEFAULT_GREP_HEAD_LIMIT, FileStorageStrategy, HookManager, MAX_LINE_LENGTH2 as MAX_LINE_LENGTH, OUTPUT_CONFIG, OutputHandler, PaginationHintStrategy, SmartTruncateStrategy, ToolRegistry, agentTool, bashTool, buildHookEnv, createAgentTool, createAskUserQuestionTool, createFunctionHook, createOutputHandler, createSkillTool, createTool, editTool, formatAnswerSummary, formatAskUserQuestionPrompt, formatEnvironmentSection, getAllBuiltinTools, getEnvironmentInfo, getFileSystemTools, getGlobalRegistry, getGrepTools, getInteractionTools, getSafeBuiltinTools, getShellTools, getSkillTools, getSubagentTools, getTaskTools, getWebTools, globTool, grepTool, loadHooksSettingsFromProject, loadHooksSettingsFromUser, matchTool, mergeCommandHookLayers, parseHooksSettingsFile, questionTool, readFileTool, subagentRequestSchema, taskCreateTool, taskListTool, taskUpdateTool, truncateMatchLineForDisplay, webFetchTool, webSearchTool, writeFileTool };
2707
- //# sourceMappingURL=chunk-OHXW2YM6.js.map
2708
- //# sourceMappingURL=chunk-OHXW2YM6.js.map
2829
+ export { DEFAULT_GREP_HEAD_LIMIT, FileStorageStrategy, HookManager, MAX_LINE_LENGTH2 as MAX_LINE_LENGTH, OUTPUT_CONFIG, OutputHandler, PaginationHintStrategy, SmartTruncateStrategy, ToolRegistry, agentTool, bashTool, buildHookEnv, createAgentTool, createAskUserQuestionTool, createFunctionHook, createOutputHandler, createSkillTool, createTool, editTool, formatAnswerSummary, formatAskUserQuestionPrompt, formatEnvironmentSection, getAllBuiltinTools, getEnvironmentInfo, getFileSystemTools, getGlobalRegistry, getGrepTools, getInteractionTools, getPlanningTools, getSafeBuiltinTools, getShellTools, getSkillTools, getSubagentTools, getWebTools, globTool, grepTool, loadHooksSettingsFromProject, loadHooksSettingsFromUser, matchTool, mergeCommandHookLayers, parseHooksSettingsFile, questionTool, readFileTool, subagentRequestSchema, todoWriteTool, truncateMatchLineForDisplay, webFetchTool, webSearchTool, writeFileTool };
2830
+ //# sourceMappingURL=chunk-MEJHTQJM.js.map
2831
+ //# sourceMappingURL=chunk-MEJHTQJM.js.map