@code-yeongyu/senpi 2026.11.15-2 → 2026.11.15-3
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/CHANGELOG.md +19 -0
- package/dist/bundle/chunks/{chunk-Q4RJ4WLE.js → chunk-225TDKTA.js} +4 -4
- package/dist/bundle/chunks/{chunk-QSU2456D.js → chunk-3KR3CDGM.js} +1 -1
- package/dist/bundle/chunks/{chunk-AOTHPTMU.js → chunk-5GCZFK4X.js} +2 -2
- package/dist/bundle/chunks/{chunk-BMADOJHZ.js → chunk-5QIFH624.js} +1 -1
- package/dist/bundle/chunks/{chunk-THYIV26N.js → chunk-GQEXLLWJ.js} +1 -1
- package/dist/bundle/chunks/{chunk-3RILB2OD.js → chunk-OJOPOUAY.js} +9 -9
- package/dist/bundle/chunks/{chunk-64FJ4QQJ.js → chunk-PPCAJQDJ.js} +1 -1
- package/dist/bundle/chunks/{chunk-74HCKLRH.js → chunk-TEXE4HT6.js} +1 -1
- package/dist/bundle/chunks/{chunk-RRFDLGX6.js → chunk-TNYFCGI7.js} +156 -156
- package/dist/bundle/chunks/{cli-main-GNQY2LTU.js → cli-main-CV3ODX4A.js} +1 -1
- package/dist/bundle/chunks/{hand-entry-D3DNH6XL.js → hand-entry-2NN7ZTY6.js} +1 -1
- package/dist/bundle/chunks/{interactive-mode-ATBPY4M5.js → interactive-mode-TGHZWM2T.js} +1 -1
- package/dist/bundle/chunks/{main-OMFVSMUR.js → main-B7WEY4JW.js} +1 -1
- package/dist/bundle/chunks/{multi-session-host-ACQ5N4UB.js → multi-session-host-53YLL2OD.js} +1 -1
- package/dist/bundle/chunks/{package-manager-cli-2VMRV4HL.js → package-manager-cli-2U54AVKW.js} +1 -1
- package/dist/bundle/chunks/{role-BTH5PVME.js → role-4IXZZAL4.js} +1 -1
- package/dist/bundle/chunks/{rpc-mode-XUK5SYKH.js → rpc-mode-RRYXENML.js} +1 -1
- package/dist/bundle/chunks/{runtime-session-VO3X4OE3.js → runtime-session-KKHEYZOH.js} +1 -1
- package/dist/bundle/chunks/{session-control-endpoint-Y5EIFFPY.js → session-control-endpoint-HVULSIUR.js} +1 -1
- package/dist/bundle/chunks/session-worker.js +331 -331
- package/dist/bundle/cli.js +2 -2
- package/dist/bundle/index.js +1 -1
- package/dist/bundle/rpc-entry.js +1 -1
- package/dist/bundle/runtime-manifest.json +1 -1
- package/dist/core/agent-session.d.ts +6 -0
- package/dist/core/agent-session.js +34 -3
- package/dist/core/extensions/builtin/anthropic-subscription/auth-lane.d.ts +2 -0
- package/dist/core/extensions/builtin/anthropic-subscription/auth-lane.js +1 -0
- package/dist/core/extensions/builtin/anthropic-subscription/failover.d.ts +2 -0
- package/dist/core/extensions/builtin/anthropic-subscription/failover.js +22 -4
- package/dist/core/extensions/builtin/anthropic-subscription/index.js +7 -2
- package/dist/core/extensions/builtin/anthropic-subscription/limit-recheck.d.ts +15 -0
- package/dist/core/extensions/builtin/anthropic-subscription/limit-recheck.js +55 -0
- package/dist/core/extensions/builtin/anthropic-subscription/session-stream.d.ts +1 -0
- package/dist/core/extensions/builtin/anthropic-subscription/session-stream.js +1 -0
- package/dist/core/extensions/builtin/anthropic-subscription/stream-guidance.js +8 -5
- package/dist/core/extensions/builtin/anthropic-subscription/stream.d.ts +1 -1
- package/dist/core/extensions/builtin/anthropic-subscription/stream.js +3 -1
- package/dist/core/extensions/builtin/terminal/extension-state.js +15 -0
- package/dist/core/extensions/builtin/terminal/notify.d.ts +2 -1
- package/dist/core/extensions/builtin/terminal/notify.js +15 -6
- package/dist/core/extensions/builtin/terminal/session-bundle.d.ts +5 -1
- package/dist/core/extensions/builtin/terminal/session-bundle.js +41 -1
- package/dist/core/extensions/builtin/terminal/tools/context.d.ts +4 -0
- package/dist/core/extensions/loader.js +5 -0
- package/dist/core/extensions/runner.d.ts +1 -0
- package/dist/core/extensions/runner.js +5 -0
- package/dist/core/extensions/types.d.ts +7 -0
- package/dist/core/run-background-jobs.d.ts +33 -0
- package/dist/core/run-background-jobs.js +47 -0
- package/dist/core/tools/bash-core.d.ts +50 -0
- package/dist/core/tools/bash-core.js +169 -0
- package/dist/core/tools/bash-schema.d.ts +48 -0
- package/dist/core/tools/bash-schema.js +34 -0
- package/dist/core/tools/bash.d.ts +4 -70
- package/dist/core/tools/bash.js +32 -409
- package/dist/core/tools/coding-tools-core.d.ts +24 -0
- package/dist/core/tools/coding-tools-core.js +14 -0
- package/dist/core/tools/edit-core.d.ts +56 -0
- package/dist/core/tools/edit-core.js +175 -0
- package/dist/core/tools/edit-diff.d.ts +0 -5
- package/dist/core/tools/edit-diff.js +0 -32
- package/dist/core/tools/edit-preview.d.ts +7 -0
- package/dist/core/tools/edit-preview.js +34 -0
- package/dist/core/tools/edit.d.ts +5 -43
- package/dist/core/tools/edit.js +10 -158
- package/dist/core/tools/encoding.d.ts +10 -0
- package/dist/core/tools/encoding.js +58 -0
- package/dist/core/tools/file-tool-host.d.ts +25 -0
- package/dist/core/tools/file-tool-host.js +109 -0
- package/dist/core/tools/fs-error-code.d.ts +2 -0
- package/dist/core/tools/fs-error-code.js +15 -0
- package/dist/core/tools/machine-output-spill.d.ts +11 -0
- package/dist/core/tools/machine-output-spill.js +65 -0
- package/dist/core/tools/machine.d.ts +4 -24
- package/dist/core/tools/machine.js +4 -44
- package/dist/core/tools/output-accumulator-core.d.ts +84 -0
- package/dist/core/tools/output-accumulator-core.js +200 -0
- package/dist/core/tools/output-accumulator.d.ts +9 -61
- package/dist/core/tools/output-accumulator.js +90 -265
- package/dist/core/tools/path-utils.js +3 -48
- package/dist/core/tools/posix-signals.d.ts +3 -0
- package/dist/core/tools/posix-signals.js +113 -0
- package/dist/core/tools/read-core.d.ts +64 -0
- package/dist/core/tools/read-core.js +180 -0
- package/dist/core/tools/read-text.d.ts +23 -0
- package/dist/core/tools/read-text.js +90 -0
- package/dist/core/tools/read.d.ts +6 -47
- package/dist/core/tools/read.js +13 -218
- package/dist/core/tools/renderers/bash.d.ts +0 -1
- package/dist/core/tools/renderers/bash.js +0 -1
- package/dist/core/tools/renderers/edit.d.ts +1 -1
- package/dist/core/tools/renderers/edit.js +1 -1
- package/dist/core/tools/secret-output-redactor.js +2 -1
- package/dist/core/tools/shell-operations.d.ts +53 -0
- package/dist/core/tools/shell-operations.js +115 -0
- package/dist/core/tools/shell-output.d.ts +20 -0
- package/dist/core/tools/shell-output.js +152 -0
- package/dist/core/tools/tail-window.js +5 -7
- package/dist/core/tools/tool-machine.d.ts +30 -0
- package/dist/core/tools/tool-machine.js +47 -0
- package/dist/core/tools/truncate.js +15 -14
- package/dist/core/tools/write-core.d.ts +47 -0
- package/dist/core/tools/write-core.js +108 -0
- package/dist/core/tools/write-result.d.ts +0 -1
- package/dist/core/tools/write-result.js +0 -13
- package/dist/core/tools/write.d.ts +6 -31
- package/dist/core/tools/write.js +25 -77
- package/dist/core/turn-origin.d.ts +4 -2
- package/dist/core/turn-origin.js +15 -8
- package/dist/modes/interactive/session-control-session-commands.js +1 -1
- package/dist/modes/rpc/connection-handler.js +2 -2
- package/dist/modes/rpc/effect-commands.d.ts +1 -4
- package/dist/modes/rpc/method-classes-schema-session.d.ts +1 -0
- package/dist/modes/rpc/method-classes-schema-session.js +5 -1
- package/dist/modes/rpc/method-classes-schema.d.ts +1 -0
- package/dist/modes/rpc/pause-command-validation.js +2 -0
- package/dist/modes/rpc/rpc-mutation-response-types.d.ts +2 -4
- package/dist/modes/rpc/rpc-session-command-types.d.ts +1 -0
- package/dist/modes/rpc/session-control-actions-data.d.ts +7 -3
- package/dist/modes/rpc/session-control-actions-data.js +12 -6
- package/dist/tools-core.d.ts +14 -0
- package/dist/tools-core.js +9 -0
- package/dist/utils/image-mime.d.ts +4 -0
- package/dist/utils/image-mime.js +99 -0
- package/dist/utils/mime.d.ts +1 -1
- package/dist/utils/mime.js +2 -97
- package/docs/extensions.md +7 -1
- package/docs/rpc.md +14 -0
- package/package.json +11 -7
- package/vendor/senpi-machine-ops/files/local-files.js +34 -5
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import { Type } from "typebox";
|
|
2
|
+
import { splitBom } from "../../utils/text.js";
|
|
3
|
+
import { applyEditsToNormalizedContent, detectLineEnding, generateDiffString, generateUnifiedPatch, normalizeToLF, restoreLineEndings, } from "./edit-diff.js";
|
|
4
|
+
import { decodeUtf8, encodeUtf8 } from "./encoding.js";
|
|
5
|
+
import { fsErrorCode } from "./fs-error-code.js";
|
|
6
|
+
import { inProcessFileMutationQueue, lexicalCanonicalPath, resolvePosixToolPath, } from "./file-tool-host.js";
|
|
7
|
+
import { wrapToolDefinition } from "./tool-definition-wrapper.js";
|
|
8
|
+
const replaceEditSchema = Type.Object({
|
|
9
|
+
oldText: Type.String({
|
|
10
|
+
description: "Exact text for one targeted replacement. It must be unique in the original file and must not overlap with any other edits[].oldText in the same call.",
|
|
11
|
+
}),
|
|
12
|
+
newText: Type.String({ description: "Replacement text for this targeted edit." }),
|
|
13
|
+
}, {});
|
|
14
|
+
const editSchema = Type.Object({
|
|
15
|
+
path: Type.String({ description: "Path to the file to edit (relative or absolute)" }),
|
|
16
|
+
edits: Type.Array(replaceEditSchema, {
|
|
17
|
+
description: "One or more targeted replacements. Each edit is matched against the original file, not incrementally. Do not include overlapping or nested edits. If two changes touch the same block or nearby lines, merge them into one edit instead.",
|
|
18
|
+
}),
|
|
19
|
+
}, {});
|
|
20
|
+
export const editToolSystemPromptContribution = {
|
|
21
|
+
snippet: "Make precise file edits with exact text replacement, including multiple disjoint edits in one call",
|
|
22
|
+
guidelines: ["All changes to one file -> one edit call."],
|
|
23
|
+
fullGuidelines: [
|
|
24
|
+
"Use edit for precise changes (edits[].oldText must match exactly)",
|
|
25
|
+
"When changing multiple separate locations in one file, use one edit call with multiple entries in edits[] instead of multiple edit calls",
|
|
26
|
+
"Each edits[].oldText is matched against the original file, not after earlier edits are applied. Do not emit overlapping or nested edits. Merge nearby changes into one edit.",
|
|
27
|
+
"Keep edits[].oldText as small as possible while still being unique in the file. Do not pad with large unchanged regions.",
|
|
28
|
+
],
|
|
29
|
+
};
|
|
30
|
+
function isSingleEditInput(value) {
|
|
31
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
const edit = value;
|
|
35
|
+
return typeof edit.oldText === "string" && typeof edit.newText === "string";
|
|
36
|
+
}
|
|
37
|
+
/** The default edit operations over the shared operation layer; the tool already holds the file's mutation slot. */
|
|
38
|
+
function machineEditOperations(machine, signal) {
|
|
39
|
+
const ctx = machine.context(signal, "caller");
|
|
40
|
+
return {
|
|
41
|
+
readFile: async (path) => (await machine.ops.files.read({ path }, ctx)).bytes,
|
|
42
|
+
writeFile: async (path, content) => {
|
|
43
|
+
await machine.ops.files.write({ path, bytes: encodeUtf8(content) }, ctx);
|
|
44
|
+
},
|
|
45
|
+
access: async (path) => {
|
|
46
|
+
await machine.ops.files.stat({ path, access: "readwrite" }, ctx);
|
|
47
|
+
},
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
function prepareEditArguments(input) {
|
|
51
|
+
if (!input || typeof input !== "object") {
|
|
52
|
+
return input;
|
|
53
|
+
}
|
|
54
|
+
const args = input;
|
|
55
|
+
// Some models (Opus 4.6, GLM-5.1) send edits as a JSON string instead of an array.
|
|
56
|
+
// Others send a single edit object instead of a one-element edits array.
|
|
57
|
+
if (typeof args.edits === "string") {
|
|
58
|
+
try {
|
|
59
|
+
const parsed = JSON.parse(args.edits);
|
|
60
|
+
if (Array.isArray(parsed)) {
|
|
61
|
+
args.edits = parsed;
|
|
62
|
+
}
|
|
63
|
+
else if (isSingleEditInput(parsed)) {
|
|
64
|
+
args.edits = [parsed];
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
catch { }
|
|
68
|
+
}
|
|
69
|
+
else if (isSingleEditInput(args.edits)) {
|
|
70
|
+
args.edits = [args.edits];
|
|
71
|
+
}
|
|
72
|
+
const legacy = args;
|
|
73
|
+
if (typeof legacy.oldText !== "string" || typeof legacy.newText !== "string") {
|
|
74
|
+
return args;
|
|
75
|
+
}
|
|
76
|
+
const edits = Array.isArray(legacy.edits) ? [...legacy.edits] : [];
|
|
77
|
+
edits.push({ oldText: legacy.oldText, newText: legacy.newText });
|
|
78
|
+
const { oldText: _oldText, newText: _newText, ...rest } = legacy;
|
|
79
|
+
return { ...rest, edits };
|
|
80
|
+
}
|
|
81
|
+
function validateEditInput(input) {
|
|
82
|
+
if (!Array.isArray(input.edits) || input.edits.length === 0) {
|
|
83
|
+
throw new Error("Edit tool input is invalid. edits must contain at least one replacement.");
|
|
84
|
+
}
|
|
85
|
+
return { path: input.path, edits: input.edits };
|
|
86
|
+
}
|
|
87
|
+
export function createCoreEditToolDefinition(cwd, options) {
|
|
88
|
+
const machine = options.machine;
|
|
89
|
+
const filesystemPolicy = options.filesystemPolicy;
|
|
90
|
+
const resolvePath = options.resolvePath ?? resolvePosixToolPath;
|
|
91
|
+
const withMutationQueue = options.withMutationQueue ?? inProcessFileMutationQueue;
|
|
92
|
+
const canonicalizePath = options.canonicalizePath ?? lexicalCanonicalPath;
|
|
93
|
+
return {
|
|
94
|
+
name: "edit",
|
|
95
|
+
label: "edit",
|
|
96
|
+
description: "Edit a single file using exact text replacement. Every edits[].oldText must match a unique, non-overlapping region of the original file. If two changes affect the same block or nearby lines, merge them into one edit instead of emitting overlapping edits. Do not include large unchanged regions just to connect distant changes.",
|
|
97
|
+
promptSnippet: editToolSystemPromptContribution.snippet,
|
|
98
|
+
promptGuidelines: [...editToolSystemPromptContribution.guidelines],
|
|
99
|
+
fullPromptGuidelines: [...editToolSystemPromptContribution.fullGuidelines],
|
|
100
|
+
parameters: editSchema,
|
|
101
|
+
constrainedSampling: { type: "json_schema", strict: "prefer" },
|
|
102
|
+
renderShell: "self",
|
|
103
|
+
prepareArguments: prepareEditArguments,
|
|
104
|
+
async execute(_toolCallId, input, signal, _onUpdate, ctx) {
|
|
105
|
+
const { path, edits } = validateEditInput(input);
|
|
106
|
+
const admitted = machine.begin("files.edit", { path, edits }, signal);
|
|
107
|
+
if (admitted !== undefined)
|
|
108
|
+
await admitted;
|
|
109
|
+
const ops = options.operations ?? machineEditOperations(machine, signal);
|
|
110
|
+
const absolutePath = resolvePath(path, ctx?.cwd || cwd);
|
|
111
|
+
return withMutationQueue(absolutePath, async () => {
|
|
112
|
+
// Do not reject from an abort event listener here: that would release the
|
|
113
|
+
// mutation queue while an in-flight filesystem operation may still finish.
|
|
114
|
+
// Checking signal.aborted after each await observes the same aborts while
|
|
115
|
+
// keeping the queue locked until the current operation has settled.
|
|
116
|
+
const throwIfAborted = () => {
|
|
117
|
+
if (signal?.aborted)
|
|
118
|
+
throw new Error("Operation aborted");
|
|
119
|
+
};
|
|
120
|
+
throwIfAborted();
|
|
121
|
+
if (filesystemPolicy) {
|
|
122
|
+
const decision = await filesystemPolicy({
|
|
123
|
+
operation: "write",
|
|
124
|
+
canonicalPath: await canonicalizePath(absolutePath),
|
|
125
|
+
toolName: "edit",
|
|
126
|
+
});
|
|
127
|
+
if (!decision.allow)
|
|
128
|
+
throw new Error(decision.reason);
|
|
129
|
+
throwIfAborted();
|
|
130
|
+
}
|
|
131
|
+
// Check if file exists.
|
|
132
|
+
try {
|
|
133
|
+
await ops.access(absolutePath);
|
|
134
|
+
}
|
|
135
|
+
catch (error) {
|
|
136
|
+
throwIfAborted();
|
|
137
|
+
const errorMessage = error instanceof Error && "code" in error
|
|
138
|
+
? `Error code: ${fsErrorCode(error) ?? error.code}`
|
|
139
|
+
: String(error);
|
|
140
|
+
throw new Error(`Could not edit file: ${path}. ${errorMessage}.`);
|
|
141
|
+
}
|
|
142
|
+
throwIfAborted();
|
|
143
|
+
// Read the file.
|
|
144
|
+
const buffer = await ops.readFile(absolutePath);
|
|
145
|
+
const rawContent = decodeUtf8(buffer);
|
|
146
|
+
throwIfAborted();
|
|
147
|
+
// Strip BOM before matching. The model will not include an invisible BOM in oldText.
|
|
148
|
+
const { bom, text: content } = splitBom(rawContent);
|
|
149
|
+
const originalEnding = detectLineEnding(content);
|
|
150
|
+
const normalizedContent = normalizeToLF(content);
|
|
151
|
+
const { baseContent, newContent } = applyEditsToNormalizedContent(normalizedContent, edits, path);
|
|
152
|
+
throwIfAborted();
|
|
153
|
+
const finalContent = bom + restoreLineEndings(newContent, originalEnding);
|
|
154
|
+
await ops.writeFile(absolutePath, finalContent);
|
|
155
|
+
throwIfAborted();
|
|
156
|
+
const diffResult = generateDiffString(baseContent, newContent);
|
|
157
|
+
const patch = generateUnifiedPatch(path, baseContent, newContent);
|
|
158
|
+
return {
|
|
159
|
+
content: [
|
|
160
|
+
{
|
|
161
|
+
type: "text",
|
|
162
|
+
text: `Successfully replaced ${edits.length} block(s) in ${path}.`,
|
|
163
|
+
},
|
|
164
|
+
],
|
|
165
|
+
details: { diff: diffResult.diff, patch, firstChangedLine: diffResult.firstChangedLine },
|
|
166
|
+
};
|
|
167
|
+
});
|
|
168
|
+
},
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
export function createCoreEditTool(cwd, options) {
|
|
172
|
+
return wrapToolDefinition(createCoreEditToolDefinition(cwd, options));
|
|
173
|
+
}
|
|
174
|
+
export { editSchema };
|
|
175
|
+
//# sourceMappingURL=edit-core.js.map
|
|
@@ -87,10 +87,5 @@ export interface EditDiffResult {
|
|
|
87
87
|
export interface EditDiffError {
|
|
88
88
|
error: string;
|
|
89
89
|
}
|
|
90
|
-
/**
|
|
91
|
-
* Compute the diff for one or more edit operations without applying them.
|
|
92
|
-
* Used for preview rendering in the TUI before the tool executes.
|
|
93
|
-
*/
|
|
94
|
-
export declare function computeEditsDiff(path: string, edits: Edit[], cwd: string): Promise<EditDiffResult | EditDiffError>;
|
|
95
90
|
export {};
|
|
96
91
|
//# sourceMappingURL=edit-diff.d.ts.map
|
|
@@ -2,10 +2,6 @@
|
|
|
2
2
|
* Shared diff computation utilities for the edit and similar tools.
|
|
3
3
|
*/
|
|
4
4
|
import * as Diff from "diff";
|
|
5
|
-
import { constants } from "fs";
|
|
6
|
-
import { access, readFile } from "fs/promises";
|
|
7
|
-
import { splitBom } from "../../utils/text.js";
|
|
8
|
-
import { resolveToCwd } from "./path-utils.js";
|
|
9
5
|
import { createUnifiedPatch } from "./unified-diff.js";
|
|
10
6
|
export function detectLineEnding(content) {
|
|
11
7
|
const crlfIdx = content.indexOf("\r\n");
|
|
@@ -381,32 +377,4 @@ export function generateDiffString(oldContent, newContent, contextLines = 4) {
|
|
|
381
377
|
}
|
|
382
378
|
return { diff: output.join("\n"), firstChangedLine };
|
|
383
379
|
}
|
|
384
|
-
/**
|
|
385
|
-
* Compute the diff for one or more edit operations without applying them.
|
|
386
|
-
* Used for preview rendering in the TUI before the tool executes.
|
|
387
|
-
*/
|
|
388
|
-
export async function computeEditsDiff(path, edits, cwd) {
|
|
389
|
-
const absolutePath = resolveToCwd(path, cwd);
|
|
390
|
-
try {
|
|
391
|
-
// Check if file exists and is readable
|
|
392
|
-
try {
|
|
393
|
-
await access(absolutePath, constants.R_OK);
|
|
394
|
-
}
|
|
395
|
-
catch (error) {
|
|
396
|
-
const errorMessage = error instanceof Error && "code" in error ? `Error code: ${error.code}` : String(error);
|
|
397
|
-
return { error: `Could not edit file: ${path}. ${errorMessage}.` };
|
|
398
|
-
}
|
|
399
|
-
// Read the file
|
|
400
|
-
const rawContent = await readFile(absolutePath, "utf-8");
|
|
401
|
-
// Strip BOM before matching (LLM won't include invisible BOM in oldText)
|
|
402
|
-
const { text: content } = splitBom(rawContent);
|
|
403
|
-
const normalizedContent = normalizeToLF(content);
|
|
404
|
-
const { baseContent, newContent } = applyEditsToNormalizedContent(normalizedContent, edits, path);
|
|
405
|
-
// Generate the diff
|
|
406
|
-
return generateDiffString(baseContent, newContent);
|
|
407
|
-
}
|
|
408
|
-
catch (err) {
|
|
409
|
-
return { error: err instanceof Error ? err.message : String(err) };
|
|
410
|
-
}
|
|
411
|
-
}
|
|
412
380
|
//# sourceMappingURL=edit-diff.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { type Edit, type EditDiffError, type EditDiffResult } from "./edit-diff.ts";
|
|
2
|
+
/**
|
|
3
|
+
* Compute the diff for one or more edit operations without applying them.
|
|
4
|
+
* Used for preview rendering in the TUI before the tool executes.
|
|
5
|
+
*/
|
|
6
|
+
export declare function computeEditsDiff(path: string, edits: Edit[], cwd: string): Promise<EditDiffResult | EditDiffError>;
|
|
7
|
+
//# sourceMappingURL=edit-preview.d.ts.map
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { constants } from "node:fs";
|
|
2
|
+
import { access, readFile } from "node:fs/promises";
|
|
3
|
+
import { splitBom } from "../../utils/text.js";
|
|
4
|
+
import { applyEditsToNormalizedContent, generateDiffString, normalizeToLF, } from "./edit-diff.js";
|
|
5
|
+
import { resolveToCwd } from "./path-utils.js";
|
|
6
|
+
/**
|
|
7
|
+
* Compute the diff for one or more edit operations without applying them.
|
|
8
|
+
* Used for preview rendering in the TUI before the tool executes.
|
|
9
|
+
*/
|
|
10
|
+
export async function computeEditsDiff(path, edits, cwd) {
|
|
11
|
+
const absolutePath = resolveToCwd(path, cwd);
|
|
12
|
+
try {
|
|
13
|
+
// Check if file exists and is readable
|
|
14
|
+
try {
|
|
15
|
+
await access(absolutePath, constants.R_OK);
|
|
16
|
+
}
|
|
17
|
+
catch (error) {
|
|
18
|
+
const errorMessage = error instanceof Error && "code" in error ? `Error code: ${error.code}` : String(error);
|
|
19
|
+
return { error: `Could not edit file: ${path}. ${errorMessage}.` };
|
|
20
|
+
}
|
|
21
|
+
// Read the file
|
|
22
|
+
const rawContent = await readFile(absolutePath, "utf-8");
|
|
23
|
+
// Strip BOM before matching (LLM won't include invisible BOM in oldText)
|
|
24
|
+
const { text: content } = splitBom(rawContent);
|
|
25
|
+
const normalizedContent = normalizeToLF(content);
|
|
26
|
+
const { baseContent, newContent } = applyEditsToNormalizedContent(normalizedContent, edits, path);
|
|
27
|
+
// Generate the diff
|
|
28
|
+
return generateDiffString(baseContent, newContent);
|
|
29
|
+
}
|
|
30
|
+
catch (err) {
|
|
31
|
+
return { error: err instanceof Error ? err.message : String(err) };
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=edit-preview.js.map
|
|
@@ -1,50 +1,12 @@
|
|
|
1
1
|
import type { AgentTool } from "@earendil-works/pi-agent-core";
|
|
2
|
-
import
|
|
3
|
-
import type
|
|
4
|
-
import { type ToolMachine } from "./machine.ts";
|
|
2
|
+
import type { ToolDefinition } from "../extensions/types.ts";
|
|
3
|
+
import { type CoreEditToolOptions, type EditToolDetails, type editSchema } from "./edit-core.ts";
|
|
5
4
|
import { type EditRenderState } from "./renderers/edit.ts";
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
edits: Type.TArray<Type.TObject<{
|
|
9
|
-
oldText: Type.TString;
|
|
10
|
-
newText: Type.TString;
|
|
11
|
-
}>>;
|
|
12
|
-
}>;
|
|
13
|
-
export declare const editToolSystemPromptContribution: {
|
|
14
|
-
readonly snippet: "Make precise file edits with exact text replacement, including multiple disjoint edits in one call";
|
|
15
|
-
readonly guidelines: readonly ["All changes to one file -> one edit call."];
|
|
16
|
-
readonly fullGuidelines: readonly ["Use edit for precise changes (edits[].oldText must match exactly)", "When changing multiple separate locations in one file, use one edit call with multiple entries in edits[] instead of multiple edit calls", "Each edits[].oldText is matched against the original file, not after earlier edits are applied. Do not emit overlapping or nested edits. Merge nearby changes into one edit.", "Keep edits[].oldText as small as possible while still being unique in the file. Do not pad with large unchanged regions."];
|
|
17
|
-
};
|
|
18
|
-
export type EditToolInput = Static<typeof editSchema>;
|
|
19
|
-
export interface EditToolDetails {
|
|
20
|
-
/** Display-oriented diff of the changes made */
|
|
21
|
-
diff: string;
|
|
22
|
-
/** Standard unified patch of the changes made */
|
|
23
|
-
patch: string;
|
|
24
|
-
/** Line number of the first change in the new file (for editor navigation) */
|
|
25
|
-
firstChangedLine?: number;
|
|
26
|
-
}
|
|
27
|
-
/**
|
|
28
|
-
* Pluggable operations for the edit tool.
|
|
29
|
-
* Override these to delegate file editing to remote systems (for example SSH).
|
|
30
|
-
*/
|
|
31
|
-
export interface EditOperations {
|
|
32
|
-
/** Read file contents as a Buffer */
|
|
33
|
-
readFile: (absolutePath: string) => Promise<Buffer>;
|
|
34
|
-
/** Write content to a file */
|
|
35
|
-
writeFile: (absolutePath: string, content: string) => Promise<void>;
|
|
36
|
-
/** Check if file is readable and writable (throw if not) */
|
|
37
|
-
access: (absolutePath: string) => Promise<void>;
|
|
38
|
-
}
|
|
39
|
-
export interface EditToolOptions {
|
|
40
|
-
/** Custom operations for file editing. Default: local filesystem */
|
|
41
|
-
operations?: EditOperations;
|
|
42
|
-
/** Extension-registered filesystem policy checker. */
|
|
43
|
-
filesystemPolicy?: FilesystemPolicyChecker;
|
|
5
|
+
export { type EditOperations, type EditToolDetails, type EditToolInput, editToolSystemPromptContribution, } from "./edit-core.ts";
|
|
6
|
+
export interface EditToolOptions extends Pick<CoreEditToolOptions, "operations" | "filesystemPolicy"> {
|
|
44
7
|
/** Operation layer and policy gate. Default: the process-wide local machine. */
|
|
45
|
-
machine?:
|
|
8
|
+
machine?: CoreEditToolOptions["machine"];
|
|
46
9
|
}
|
|
47
10
|
export declare function createEditToolDefinition(cwd: string, options?: EditToolOptions): ToolDefinition<typeof editSchema, EditToolDetails | undefined, EditRenderState>;
|
|
48
11
|
export declare function createEditTool(cwd: string, options?: EditToolOptions): AgentTool<typeof editSchema>;
|
|
49
|
-
export {};
|
|
50
12
|
//# sourceMappingURL=edit.d.ts.map
|
package/dist/core/tools/edit.js
CHANGED
|
@@ -1,169 +1,21 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { splitBom } from "../../utils/text.js";
|
|
3
|
-
import { applyEditsToNormalizedContent, detectLineEnding, generateDiffString, generateUnifiedPatch, normalizeToLF, restoreLineEndings, } from "./edit-diff.js";
|
|
1
|
+
import { createCoreEditToolDefinition, } from "./edit-core.js";
|
|
4
2
|
import { withFileMutationQueue } from "./file-mutation-queue.js";
|
|
5
3
|
import { canonicalizeFilesystemPath } from "./filesystem-policy.js";
|
|
6
|
-
import {
|
|
4
|
+
import { defaultToolMachine } from "./machine.js";
|
|
7
5
|
import { resolveToCwd } from "./path-utils.js";
|
|
8
6
|
import { editRenderers } from "./renderers/edit.js";
|
|
9
7
|
import { wrapToolDefinition } from "./tool-definition-wrapper.js";
|
|
10
|
-
|
|
11
|
-
oldText: Type.String({
|
|
12
|
-
description: "Exact text for one targeted replacement. It must be unique in the original file and must not overlap with any other edits[].oldText in the same call.",
|
|
13
|
-
}),
|
|
14
|
-
newText: Type.String({ description: "Replacement text for this targeted edit." }),
|
|
15
|
-
}, {});
|
|
16
|
-
const editSchema = Type.Object({
|
|
17
|
-
path: Type.String({ description: "Path to the file to edit (relative or absolute)" }),
|
|
18
|
-
edits: Type.Array(replaceEditSchema, {
|
|
19
|
-
description: "One or more targeted replacements. Each edit is matched against the original file, not incrementally. Do not include overlapping or nested edits. If two changes touch the same block or nearby lines, merge them into one edit instead.",
|
|
20
|
-
}),
|
|
21
|
-
}, {});
|
|
22
|
-
export const editToolSystemPromptContribution = {
|
|
23
|
-
snippet: "Make precise file edits with exact text replacement, including multiple disjoint edits in one call",
|
|
24
|
-
guidelines: ["All changes to one file -> one edit call."],
|
|
25
|
-
fullGuidelines: [
|
|
26
|
-
"Use edit for precise changes (edits[].oldText must match exactly)",
|
|
27
|
-
"When changing multiple separate locations in one file, use one edit call with multiple entries in edits[] instead of multiple edit calls",
|
|
28
|
-
"Each edits[].oldText is matched against the original file, not after earlier edits are applied. Do not emit overlapping or nested edits. Merge nearby changes into one edit.",
|
|
29
|
-
"Keep edits[].oldText as small as possible while still being unique in the file. Do not pad with large unchanged regions.",
|
|
30
|
-
],
|
|
31
|
-
};
|
|
32
|
-
function isSingleEditInput(value) {
|
|
33
|
-
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
34
|
-
return false;
|
|
35
|
-
}
|
|
36
|
-
const edit = value;
|
|
37
|
-
return typeof edit.oldText === "string" && typeof edit.newText === "string";
|
|
38
|
-
}
|
|
39
|
-
/** The default edit operations over the shared operation layer; the tool already holds the file's mutation slot. */
|
|
40
|
-
function machineEditOperations(machine, signal) {
|
|
41
|
-
const ctx = machine.context(signal, "caller");
|
|
42
|
-
return {
|
|
43
|
-
readFile: async (path) => asBuffer((await machine.ops.files.read({ path }, ctx)).bytes),
|
|
44
|
-
writeFile: async (path, content) => {
|
|
45
|
-
await machine.ops.files.write({ path, bytes: Buffer.from(content, "utf-8") }, ctx);
|
|
46
|
-
},
|
|
47
|
-
access: async (path) => {
|
|
48
|
-
await machine.ops.files.stat({ path, access: "readwrite" }, ctx);
|
|
49
|
-
},
|
|
50
|
-
};
|
|
51
|
-
}
|
|
52
|
-
function prepareEditArguments(input) {
|
|
53
|
-
if (!input || typeof input !== "object") {
|
|
54
|
-
return input;
|
|
55
|
-
}
|
|
56
|
-
const args = input;
|
|
57
|
-
// Some models (Opus 4.6, GLM-5.1) send edits as a JSON string instead of an array.
|
|
58
|
-
// Others send a single edit object instead of a one-element edits array.
|
|
59
|
-
if (typeof args.edits === "string") {
|
|
60
|
-
try {
|
|
61
|
-
const parsed = JSON.parse(args.edits);
|
|
62
|
-
if (Array.isArray(parsed)) {
|
|
63
|
-
args.edits = parsed;
|
|
64
|
-
}
|
|
65
|
-
else if (isSingleEditInput(parsed)) {
|
|
66
|
-
args.edits = [parsed];
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
catch { }
|
|
70
|
-
}
|
|
71
|
-
else if (isSingleEditInput(args.edits)) {
|
|
72
|
-
args.edits = [args.edits];
|
|
73
|
-
}
|
|
74
|
-
const legacy = args;
|
|
75
|
-
if (typeof legacy.oldText !== "string" || typeof legacy.newText !== "string") {
|
|
76
|
-
return args;
|
|
77
|
-
}
|
|
78
|
-
const edits = Array.isArray(legacy.edits) ? [...legacy.edits] : [];
|
|
79
|
-
edits.push({ oldText: legacy.oldText, newText: legacy.newText });
|
|
80
|
-
const { oldText: _oldText, newText: _newText, ...rest } = legacy;
|
|
81
|
-
return { ...rest, edits };
|
|
82
|
-
}
|
|
83
|
-
function validateEditInput(input) {
|
|
84
|
-
if (!Array.isArray(input.edits) || input.edits.length === 0) {
|
|
85
|
-
throw new Error("Edit tool input is invalid. edits must contain at least one replacement.");
|
|
86
|
-
}
|
|
87
|
-
return { path: input.path, edits: input.edits };
|
|
88
|
-
}
|
|
8
|
+
export { editToolSystemPromptContribution, } from "./edit-core.js";
|
|
89
9
|
export function createEditToolDefinition(cwd, options) {
|
|
90
|
-
const machine = options?.machine ?? defaultToolMachine();
|
|
91
|
-
const filesystemPolicy = options?.filesystemPolicy;
|
|
92
10
|
return {
|
|
93
11
|
...(options?.operations !== undefined || options?.filesystemPolicy !== undefined ? { serverPinned: true } : {}),
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
constrainedSampling: { type: "json_schema", strict: "prefer" },
|
|
102
|
-
renderShell: "self",
|
|
103
|
-
prepareArguments: prepareEditArguments,
|
|
104
|
-
async execute(_toolCallId, input, signal, _onUpdate, ctx) {
|
|
105
|
-
const { path, edits } = validateEditInput(input);
|
|
106
|
-
const admitted = machine.begin("files.edit", { path, edits }, signal);
|
|
107
|
-
if (admitted !== undefined)
|
|
108
|
-
await admitted;
|
|
109
|
-
const ops = options?.operations ?? machineEditOperations(machine, signal);
|
|
110
|
-
const absolutePath = resolveToCwd(path, ctx?.cwd || cwd);
|
|
111
|
-
return withFileMutationQueue(absolutePath, async () => {
|
|
112
|
-
// Do not reject from an abort event listener here: that would release the
|
|
113
|
-
// mutation queue while an in-flight filesystem operation may still finish.
|
|
114
|
-
// Checking signal.aborted after each await observes the same aborts while
|
|
115
|
-
// keeping the queue locked until the current operation has settled.
|
|
116
|
-
const throwIfAborted = () => {
|
|
117
|
-
if (signal?.aborted)
|
|
118
|
-
throw new Error("Operation aborted");
|
|
119
|
-
};
|
|
120
|
-
throwIfAborted();
|
|
121
|
-
if (filesystemPolicy) {
|
|
122
|
-
const decision = await filesystemPolicy({
|
|
123
|
-
operation: "write",
|
|
124
|
-
canonicalPath: await canonicalizeFilesystemPath(absolutePath),
|
|
125
|
-
toolName: "edit",
|
|
126
|
-
});
|
|
127
|
-
if (!decision.allow)
|
|
128
|
-
throw new Error(decision.reason);
|
|
129
|
-
throwIfAborted();
|
|
130
|
-
}
|
|
131
|
-
// Check if file exists.
|
|
132
|
-
try {
|
|
133
|
-
await ops.access(absolutePath);
|
|
134
|
-
}
|
|
135
|
-
catch (error) {
|
|
136
|
-
throwIfAborted();
|
|
137
|
-
const errorMessage = error instanceof Error && "code" in error ? `Error code: ${error.code}` : String(error);
|
|
138
|
-
throw new Error(`Could not edit file: ${path}. ${errorMessage}.`);
|
|
139
|
-
}
|
|
140
|
-
throwIfAborted();
|
|
141
|
-
// Read the file.
|
|
142
|
-
const buffer = await ops.readFile(absolutePath);
|
|
143
|
-
const rawContent = buffer.toString("utf-8");
|
|
144
|
-
throwIfAborted();
|
|
145
|
-
// Strip BOM before matching. The model will not include an invisible BOM in oldText.
|
|
146
|
-
const { bom, text: content } = splitBom(rawContent);
|
|
147
|
-
const originalEnding = detectLineEnding(content);
|
|
148
|
-
const normalizedContent = normalizeToLF(content);
|
|
149
|
-
const { baseContent, newContent } = applyEditsToNormalizedContent(normalizedContent, edits, path);
|
|
150
|
-
throwIfAborted();
|
|
151
|
-
const finalContent = bom + restoreLineEndings(newContent, originalEnding);
|
|
152
|
-
await ops.writeFile(absolutePath, finalContent);
|
|
153
|
-
throwIfAborted();
|
|
154
|
-
const diffResult = generateDiffString(baseContent, newContent);
|
|
155
|
-
const patch = generateUnifiedPatch(path, baseContent, newContent);
|
|
156
|
-
return {
|
|
157
|
-
content: [
|
|
158
|
-
{
|
|
159
|
-
type: "text",
|
|
160
|
-
text: `Successfully replaced ${edits.length} block(s) in ${path}.`,
|
|
161
|
-
},
|
|
162
|
-
],
|
|
163
|
-
details: { diff: diffResult.diff, patch, firstChangedLine: diffResult.firstChangedLine },
|
|
164
|
-
};
|
|
165
|
-
});
|
|
166
|
-
},
|
|
12
|
+
...createCoreEditToolDefinition(cwd, {
|
|
13
|
+
...options,
|
|
14
|
+
machine: options?.machine ?? defaultToolMachine(),
|
|
15
|
+
resolvePath: resolveToCwd,
|
|
16
|
+
withMutationQueue: withFileMutationQueue,
|
|
17
|
+
canonicalizePath: canonicalizeFilesystemPath,
|
|
18
|
+
}),
|
|
167
19
|
...editRenderers,
|
|
168
20
|
};
|
|
169
21
|
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/** Bytes `text` takes as UTF-8; a lone surrogate counts as its 3-byte replacement character, as `Buffer.byteLength` does. */
|
|
2
|
+
export declare function utf8ByteLength(text: string): number;
|
|
3
|
+
export declare function encodeUtf8(text: string): Uint8Array;
|
|
4
|
+
/** Decodes like `Buffer#toString("utf-8")`: invalid sequences become U+FFFD and a leading BOM is kept. */
|
|
5
|
+
export declare function decodeUtf8(bytes: Uint8Array): string;
|
|
6
|
+
export declare function concatBytes(chunks: readonly Uint8Array[]): Uint8Array;
|
|
7
|
+
export declare function encodeBase64(bytes: Uint8Array): string;
|
|
8
|
+
/** Decodes base64url text to UTF-8 like `Buffer.from(text, "base64url")`: a dangling final character is ignored. */
|
|
9
|
+
export declare function decodeBase64UrlUtf8(text: string): string;
|
|
10
|
+
//# sourceMappingURL=encoding.d.ts.map
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
// UTF-8 and base64 byte work without the Node `Buffer` global, so the tool cores also run on hosts that lack it.
|
|
2
|
+
const encoder = new TextEncoder();
|
|
3
|
+
/** Bytes `text` takes as UTF-8; a lone surrogate counts as its 3-byte replacement character, as `Buffer.byteLength` does. */
|
|
4
|
+
export function utf8ByteLength(text) {
|
|
5
|
+
let bytes = 0;
|
|
6
|
+
for (let index = 0; index < text.length; index++) {
|
|
7
|
+
const code = text.charCodeAt(index);
|
|
8
|
+
if (code < 0x80)
|
|
9
|
+
bytes += 1;
|
|
10
|
+
else if (code < 0x800)
|
|
11
|
+
bytes += 2;
|
|
12
|
+
else if (code >= 0xd800 && code <= 0xdbff && index + 1 < text.length) {
|
|
13
|
+
const next = text.charCodeAt(index + 1);
|
|
14
|
+
if (next >= 0xdc00 && next <= 0xdfff) {
|
|
15
|
+
bytes += 4;
|
|
16
|
+
index++;
|
|
17
|
+
}
|
|
18
|
+
else
|
|
19
|
+
bytes += 3;
|
|
20
|
+
}
|
|
21
|
+
else
|
|
22
|
+
bytes += 3;
|
|
23
|
+
}
|
|
24
|
+
return bytes;
|
|
25
|
+
}
|
|
26
|
+
export function encodeUtf8(text) {
|
|
27
|
+
return encoder.encode(text);
|
|
28
|
+
}
|
|
29
|
+
/** Decodes like `Buffer#toString("utf-8")`: invalid sequences become U+FFFD and a leading BOM is kept. */
|
|
30
|
+
export function decodeUtf8(bytes) {
|
|
31
|
+
return new TextDecoder("utf-8", { ignoreBOM: true }).decode(bytes);
|
|
32
|
+
}
|
|
33
|
+
export function concatBytes(chunks) {
|
|
34
|
+
const total = chunks.reduce((sum, chunk) => sum + chunk.length, 0);
|
|
35
|
+
const joined = new Uint8Array(total);
|
|
36
|
+
let offset = 0;
|
|
37
|
+
for (const chunk of chunks) {
|
|
38
|
+
joined.set(chunk, offset);
|
|
39
|
+
offset += chunk.length;
|
|
40
|
+
}
|
|
41
|
+
return joined;
|
|
42
|
+
}
|
|
43
|
+
const BASE64_CHUNK = 0x8000;
|
|
44
|
+
export function encodeBase64(bytes) {
|
|
45
|
+
let binary = "";
|
|
46
|
+
for (let start = 0; start < bytes.length; start += BASE64_CHUNK) {
|
|
47
|
+
binary += String.fromCharCode(...bytes.subarray(start, start + BASE64_CHUNK));
|
|
48
|
+
}
|
|
49
|
+
return btoa(binary);
|
|
50
|
+
}
|
|
51
|
+
/** Decodes base64url text to UTF-8 like `Buffer.from(text, "base64url")`: a dangling final character is ignored. */
|
|
52
|
+
export function decodeBase64UrlUtf8(text) {
|
|
53
|
+
const usable = text.length % 4 === 1 ? text.slice(0, -1) : text;
|
|
54
|
+
const base64 = usable.replaceAll("-", "+").replaceAll("_", "/");
|
|
55
|
+
const binary = atob(base64.padEnd(Math.ceil(base64.length / 4) * 4, "="));
|
|
56
|
+
return decodeUtf8(Uint8Array.from(binary, (char) => char.charCodeAt(0)));
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=encoding.js.map
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/** Resolves a model-given path against the working directory. */
|
|
2
|
+
export type ToolPathResolver = (path: string, cwd: string) => string;
|
|
3
|
+
/** Serializes the mutations of one file. */
|
|
4
|
+
export type FileMutationQueue = <T>(absolutePath: string, fn: () => Promise<T>) => Promise<T>;
|
|
5
|
+
/** The path an extension filesystem policy judges. */
|
|
6
|
+
export type ToolPathCanonicalizer = (absolutePath: string) => Promise<string>;
|
|
7
|
+
/**
|
|
8
|
+
* POSIX resolution as `path.posix.resolve` does it, after the same input clean-up the local resolver applies (Unicode
|
|
9
|
+
* spaces, a leading `@`, `file://` URLs). `~` is not expanded: the host's home directory is unknown here.
|
|
10
|
+
*/
|
|
11
|
+
export declare const resolvePosixToolPath: ToolPathResolver;
|
|
12
|
+
export declare function posixDirname(path: string): string;
|
|
13
|
+
/** Serializes mutations per resolved path string within this isolate. */
|
|
14
|
+
export declare const inProcessFileMutationQueue: FileMutationQueue;
|
|
15
|
+
export declare const lexicalCanonicalPath: ToolPathCanonicalizer;
|
|
16
|
+
export declare function tryMacOSScreenshotPath(filePath: string): string;
|
|
17
|
+
export declare function tryNFDVariant(filePath: string): string;
|
|
18
|
+
export declare function tryCurlyQuoteVariant(filePath: string): string;
|
|
19
|
+
export declare function unquotedPath(filePath: string): string | undefined;
|
|
20
|
+
/**
|
|
21
|
+
* The read tool's path: the resolved path when it exists, else the first existing spelling variant (quoted,
|
|
22
|
+
* macOS screenshot AM/PM, NFD, curly quote), else the resolved path.
|
|
23
|
+
*/
|
|
24
|
+
export declare function resolveReadPathVariants(filePath: string, cwd: string, resolve: ToolPathResolver, exists: (path: string) => Promise<boolean>): Promise<string>;
|
|
25
|
+
//# sourceMappingURL=file-tool-host.d.ts.map
|