@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.
- package/README.md +4 -2
- package/dist/{chunk-5QMA2YBY.cjs → chunk-6X7EYQLS.cjs} +782 -114
- package/dist/chunk-6X7EYQLS.cjs.map +1 -0
- package/dist/{chunk-NDSL7NPN.js → chunk-D3UZNLZO.js} +769 -71
- package/dist/chunk-D3UZNLZO.js.map +1 -0
- package/dist/{chunk-Q3SOMX26.js → chunk-EQ5CXH44.js} +772 -111
- package/dist/chunk-EQ5CXH44.js.map +1 -0
- package/dist/chunk-LOYIGOBZ.js +54 -0
- package/dist/chunk-LOYIGOBZ.js.map +1 -0
- package/dist/{chunk-OHXW2YM6.js → chunk-MEJHTQJM.js} +289 -166
- package/dist/chunk-MEJHTQJM.js.map +1 -0
- package/dist/chunk-NYZD3THB.cjs +1521 -0
- package/dist/chunk-NYZD3THB.cjs.map +1 -0
- package/dist/chunk-OZO7D77N.cjs +59 -0
- package/dist/chunk-OZO7D77N.cjs.map +1 -0
- package/dist/{chunk-JF5AJQMU.cjs → chunk-Z45DHTDX.cjs} +291 -170
- package/dist/chunk-Z45DHTDX.cjs.map +1 -0
- package/dist/cli/index.cjs +47 -39
- package/dist/cli/index.cjs.map +1 -1
- package/dist/cli/index.js +22 -14
- package/dist/cli/index.js.map +1 -1
- package/dist/{index-DPsZ1zat.d.ts → index-Cw3SfEAB.d.ts} +20 -34
- package/dist/{index-RTPmFjMp.d.cts → index-D2Qntkn_.d.cts} +20 -34
- package/dist/index.cjs +125 -89
- package/dist/index.d.cts +62 -22
- package/dist/index.d.ts +62 -22
- package/dist/index.js +4 -4
- package/dist/models/index.cjs +19 -15
- package/dist/models/index.d.cts +55 -6
- package/dist/models/index.d.ts +55 -6
- package/dist/models/index.js +2 -2
- package/dist/tools/index.cjs +53 -61
- package/dist/tools/index.d.cts +3 -3
- package/dist/tools/index.d.ts +3 -3
- package/dist/tools/index.js +2 -2
- package/dist/{types-C0aX_Qdp.d.cts → types-CWPAYWzr.d.cts} +307 -61
- package/dist/{types-C0aX_Qdp.d.ts → types-CWPAYWzr.d.ts} +307 -61
- package/package.json +25 -14
- package/dist/chunk-5QMA2YBY.cjs.map +0 -1
- package/dist/chunk-CNSGZVRN.cjs +0 -152
- package/dist/chunk-CNSGZVRN.cjs.map +0 -1
- package/dist/chunk-JF5AJQMU.cjs.map +0 -1
- package/dist/chunk-NDSL7NPN.js.map +0 -1
- package/dist/chunk-OHXW2YM6.js.map +0 -1
- package/dist/chunk-Q3SOMX26.js.map +0 -1
- package/dist/chunk-WH3APNQ5.js +0 -147
- package/dist/chunk-WH3APNQ5.js.map +0 -1
- package/dist/chunk-X35MHWXE.cjs +0 -817
- package/dist/chunk-X35MHWXE.cjs.map +0 -1
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { zodToJsonSchema } from './chunk-
|
|
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
|
-
|
|
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
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
)
|
|
406
|
-
|
|
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
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
this.
|
|
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
|
|
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
|
-
-
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
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
|
|
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
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
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
|
|
1964
|
-
var
|
|
1965
|
-
|
|
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: `
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
-
|
|
1980
|
-
|
|
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
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
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 ({
|
|
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
|
|
2070
|
-
const icon =
|
|
2071
|
-
lines.push(`[${icon}]
|
|
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 =
|
|
2074
|
-
const inProgress =
|
|
2075
|
-
const completed =
|
|
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: `
|
|
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
|
|
2084
|
-
return [
|
|
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
|
-
...
|
|
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,
|
|
2707
|
-
//# sourceMappingURL=chunk-
|
|
2708
|
-
//# sourceMappingURL=chunk-
|
|
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
|