@visorcraft/idlehands 1.4.6 → 2.0.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/dist/agent/constants.js +12 -0
- package/dist/agent/constants.js.map +1 -0
- package/dist/agent/errors.js +8 -0
- package/dist/agent/errors.js.map +1 -0
- package/dist/agent/exec-helpers.js +105 -0
- package/dist/agent/exec-helpers.js.map +1 -0
- package/dist/agent/model-pick.js +21 -0
- package/dist/agent/model-pick.js.map +1 -0
- package/dist/agent/session-utils.js +63 -0
- package/dist/agent/session-utils.js.map +1 -0
- package/dist/agent/subagent-context.js +78 -0
- package/dist/agent/subagent-context.js.map +1 -0
- package/dist/agent/tool-loop-guard.js.map +1 -1
- package/dist/agent/tool-policy.js +54 -0
- package/dist/agent/tool-policy.js.map +1 -0
- package/dist/agent/tools-schema.js +281 -0
- package/dist/agent/tools-schema.js.map +1 -0
- package/dist/agent.js +136 -630
- package/dist/agent.js.map +1 -1
- package/dist/anton/controller.js +21 -136
- package/dist/anton/controller.js.map +1 -1
- package/dist/anton/lint-baseline.js +64 -0
- package/dist/anton/lint-baseline.js.map +1 -0
- package/dist/anton/preflight.js.map +1 -1
- package/dist/anton/prompt.js +71 -71
- package/dist/anton/reporter.js.map +1 -1
- package/dist/anton/runtime-ready.js +120 -0
- package/dist/anton/runtime-ready.js.map +1 -0
- package/dist/anton/session.js +1 -1
- package/dist/anton/session.js.map +1 -1
- package/dist/anton/verifier-utils.js +148 -0
- package/dist/anton/verifier-utils.js.map +1 -0
- package/dist/anton/verifier.js +26 -227
- package/dist/anton/verifier.js.map +1 -1
- package/dist/bot/anton-auto-pin.js +12 -0
- package/dist/bot/anton-auto-pin.js.map +1 -0
- package/dist/bot/anton-commands.js +137 -0
- package/dist/bot/anton-commands.js.map +1 -0
- package/dist/bot/anton-run.js +144 -0
- package/dist/bot/anton-run.js.map +1 -0
- package/dist/bot/anton-status-format.js +18 -0
- package/dist/bot/anton-status-format.js.map +1 -0
- package/dist/bot/basic-commands.js +114 -0
- package/dist/bot/basic-commands.js.map +1 -0
- package/dist/bot/command-format.js.map +1 -1
- package/dist/bot/command-logic.js +8 -728
- package/dist/bot/command-logic.js.map +1 -1
- package/dist/bot/commands.js +18 -1
- package/dist/bot/commands.js.map +1 -1
- package/dist/bot/discord-anton-autopin.js +29 -0
- package/dist/bot/discord-anton-autopin.js.map +1 -0
- package/dist/bot/discord-anton.js +45 -0
- package/dist/bot/discord-anton.js.map +1 -0
- package/dist/bot/discord-commands.js +20 -52
- package/dist/bot/discord-commands.js.map +1 -1
- package/dist/bot/discord-result.js +9 -0
- package/dist/bot/discord-result.js.map +1 -0
- package/dist/bot/discord-routing.js.map +1 -1
- package/dist/bot/discord.js +42 -12
- package/dist/bot/discord.js.map +1 -1
- package/dist/bot/escalation-commands.js +145 -0
- package/dist/bot/escalation-commands.js.map +1 -0
- package/dist/bot/escalation.js.map +1 -1
- package/dist/bot/git-status-command.js +28 -0
- package/dist/bot/git-status-command.js.map +1 -0
- package/dist/bot/model-endpoint.js +25 -0
- package/dist/bot/model-endpoint.js.map +1 -0
- package/dist/bot/session-history.js +61 -0
- package/dist/bot/session-history.js.map +1 -0
- package/dist/bot/session-settings.js +89 -0
- package/dist/bot/session-settings.js.map +1 -0
- package/dist/bot/telegram-commands.js +15 -7
- package/dist/bot/telegram-commands.js.map +1 -1
- package/dist/bot/telegram.js +13 -28
- package/dist/bot/telegram.js.map +1 -1
- package/dist/cli/agent-turn.js +8 -2
- package/dist/cli/agent-turn.js.map +1 -1
- package/dist/cli/commands/anton.js +6 -1
- package/dist/cli/commands/anton.js.map +1 -1
- package/dist/cli/commands/model.js +1 -3
- package/dist/cli/commands/model.js.map +1 -1
- package/dist/cli/commands/project.js +1 -1
- package/dist/cli/commands/project.js.map +1 -1
- package/dist/cli/commands/secrets.js +1 -1
- package/dist/cli/commands/secrets.js.map +1 -1
- package/dist/cli/commands/session.js +22 -12
- package/dist/cli/commands/session.js.map +1 -1
- package/dist/cli/guided-onboarding.js +20 -0
- package/dist/cli/guided-onboarding.js.map +1 -0
- package/dist/cli/runtime-cmds.js +8 -133
- package/dist/cli/runtime-cmds.js.map +1 -1
- package/dist/cli/runtime-common.js +35 -0
- package/dist/cli/runtime-common.js.map +1 -0
- package/dist/cli/runtime-detect.js +12 -0
- package/dist/cli/runtime-detect.js.map +1 -0
- package/dist/cli/runtime-host-command.js +7 -0
- package/dist/cli/runtime-host-command.js.map +1 -0
- package/dist/cli/runtime-probe-defaults.js +63 -0
- package/dist/cli/runtime-probe-defaults.js.map +1 -0
- package/dist/cli/runtime-scan-ports.js +30 -0
- package/dist/cli/runtime-scan-ports.js.map +1 -0
- package/dist/cli/setup-bot-step.js +51 -0
- package/dist/cli/setup-bot-step.js.map +1 -0
- package/dist/cli/setup-runtime-forms.js +214 -0
- package/dist/cli/setup-runtime-forms.js.map +1 -0
- package/dist/cli/setup-style.js +8 -0
- package/dist/cli/setup-style.js.map +1 -0
- package/dist/cli/setup-ui.js +146 -0
- package/dist/cli/setup-ui.js.map +1 -0
- package/dist/cli/setup.js +11 -449
- package/dist/cli/setup.js.map +1 -1
- package/dist/client/error-utils.js +37 -0
- package/dist/client/error-utils.js.map +1 -0
- package/dist/client/pressure.js +77 -0
- package/dist/client/pressure.js.map +1 -0
- package/dist/client.js +24 -122
- package/dist/client.js.map +1 -1
- package/dist/config.js +31 -14
- package/dist/config.js.map +1 -1
- package/dist/git.js +8 -2
- package/dist/git.js.map +1 -1
- package/dist/hooks/types.js.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/progress/message-edit-scheduler.js.map +1 -1
- package/dist/progress/turn-progress.js.map +1 -1
- package/dist/runtime/executor.js +4 -1
- package/dist/runtime/executor.js.map +1 -1
- package/dist/runtime/health.js.map +1 -1
- package/dist/runtime/host-runner.js.map +1 -1
- package/dist/safety.js +3 -2
- package/dist/safety.js.map +1 -1
- package/dist/shared/config-utils.js.map +1 -1
- package/dist/tools/exec-core.js +252 -0
- package/dist/tools/exec-core.js.map +1 -0
- package/dist/tools/exec-pty.js +89 -0
- package/dist/tools/exec-pty.js.map +1 -0
- package/dist/tools/exec-utils.js +94 -0
- package/dist/tools/exec-utils.js.map +1 -0
- package/dist/tools/file-discovery.js +144 -0
- package/dist/tools/file-discovery.js.map +1 -0
- package/dist/tools/file-mutations.js +326 -0
- package/dist/tools/file-mutations.js.map +1 -0
- package/dist/tools/file-read.js +133 -0
- package/dist/tools/file-read.js.map +1 -0
- package/dist/tools/patch-apply.js +168 -0
- package/dist/tools/patch-apply.js.map +1 -0
- package/dist/tools/path-safety.js.map +1 -1
- package/dist/tools/replay-utils.js +25 -0
- package/dist/tools/replay-utils.js.map +1 -0
- package/dist/tools/search-utils.js +55 -0
- package/dist/tools/search-utils.js.map +1 -0
- package/dist/tools/sys-notes.js +34 -0
- package/dist/tools/sys-notes.js.map +1 -0
- package/dist/tools/text-utils.js +164 -0
- package/dist/tools/text-utils.js.map +1 -0
- package/dist/tools/undo.js +1 -1
- package/dist/tools/undo.js.map +1 -1
- package/dist/tools/vault-tools.js +36 -0
- package/dist/tools/vault-tools.js.map +1 -0
- package/dist/tools.js +19 -1460
- package/dist/tools.js.map +1 -1
- package/dist/tui/controller.js +5 -2
- package/dist/tui/controller.js.map +1 -1
- package/dist/tui/render.js.map +1 -1
- package/dist/utils.js +2 -2
- package/dist/utils.js.map +1 -1
- package/dist/vault.js +1 -1
- package/dist/vault.js.map +1 -1
- package/dist/watchdog.js +1 -3
- package/dist/watchdog.js.map +1 -1
- package/package.json +2 -1
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import fs from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { shellEscape } from '../utils.js';
|
|
4
|
+
import { resolvePath, redactPath } from './path-safety.js';
|
|
5
|
+
import { globishMatch, hasRg } from './search-utils.js';
|
|
6
|
+
import { ToolError } from './tool-error.js';
|
|
7
|
+
export async function listDirTool(ctx, args) {
|
|
8
|
+
const p = resolvePath(ctx, args?.path ?? '.');
|
|
9
|
+
const recursive = Boolean(args?.recursive);
|
|
10
|
+
const maxEntries = Math.min(args?.max_entries ? Number(args.max_entries) : 200, 500);
|
|
11
|
+
if (!p)
|
|
12
|
+
throw new Error('list_dir: missing path');
|
|
13
|
+
const absCwd = path.resolve(ctx.cwd);
|
|
14
|
+
const lines = [];
|
|
15
|
+
let count = 0;
|
|
16
|
+
async function walk(dir, depth) {
|
|
17
|
+
if (count >= maxEntries)
|
|
18
|
+
return;
|
|
19
|
+
const ents = await fs.readdir(dir, { withFileTypes: true }).catch((e) => {
|
|
20
|
+
throw new Error(`list_dir: cannot read ${dir}: ${e?.message ?? String(e)}`);
|
|
21
|
+
});
|
|
22
|
+
for (const ent of ents) {
|
|
23
|
+
if (count >= maxEntries)
|
|
24
|
+
return;
|
|
25
|
+
const full = path.join(dir, ent.name);
|
|
26
|
+
const st = await fs.lstat(full).catch(() => null);
|
|
27
|
+
const kind = ent.isDirectory() ? 'dir' : ent.isSymbolicLink() ? 'link' : 'file';
|
|
28
|
+
lines.push(`${kind}\t${st?.size ?? 0}\t${redactPath(full, absCwd)}`);
|
|
29
|
+
count++;
|
|
30
|
+
if (recursive && ent.isDirectory() && depth < 3) {
|
|
31
|
+
await walk(full, depth + 1);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
await walk(p, 0);
|
|
36
|
+
if (count >= maxEntries)
|
|
37
|
+
lines.push(`[truncated after ${maxEntries} entries]`);
|
|
38
|
+
if (!lines.length)
|
|
39
|
+
return `[empty directory: ${redactPath(p, absCwd)}]`;
|
|
40
|
+
return lines.join('\n');
|
|
41
|
+
}
|
|
42
|
+
export async function searchFilesTool(ctx, args, execFn) {
|
|
43
|
+
const root = resolvePath(ctx, args?.path ?? '.');
|
|
44
|
+
const pattern = typeof args?.pattern === 'string' ? args.pattern : undefined;
|
|
45
|
+
const include = typeof args?.include === 'string' ? args.include : undefined;
|
|
46
|
+
const maxResults = Math.min(args?.max_results ? Number(args.max_results) : 50, 100);
|
|
47
|
+
if (!root)
|
|
48
|
+
throw new Error('search_files: missing path');
|
|
49
|
+
if (!pattern)
|
|
50
|
+
throw new Error('search_files: missing pattern');
|
|
51
|
+
const absCwd = path.resolve(ctx.cwd);
|
|
52
|
+
if (await hasRg()) {
|
|
53
|
+
const cmd = ['rg', '-n', '--no-heading', '--color', 'never', pattern, root];
|
|
54
|
+
if (include)
|
|
55
|
+
cmd.splice(1, 0, '-g', include);
|
|
56
|
+
try {
|
|
57
|
+
const rawJson = await execFn(ctx, { command: cmd.map(shellEscape).join(' '), timeout: 30 });
|
|
58
|
+
const parsed = JSON.parse(rawJson);
|
|
59
|
+
if (parsed.rc === 1 && !parsed.out?.trim()) {
|
|
60
|
+
return `No matches for pattern \"${pattern}\" in ${root}. STOP — do NOT read files individually to search. Try a broader regex pattern, different keywords, or use exec: grep -rn \"keyword\" ${root}`;
|
|
61
|
+
}
|
|
62
|
+
if (parsed.rc < 2) {
|
|
63
|
+
const rgOutput = parsed.out ?? '';
|
|
64
|
+
if (rgOutput) {
|
|
65
|
+
const lines = rgOutput.split(/\r?\n/).filter(Boolean).slice(0, maxResults);
|
|
66
|
+
if (lines.length >= maxResults)
|
|
67
|
+
lines.push(`[truncated after ${maxResults} results]`);
|
|
68
|
+
const redactedLines = lines.map((line) => {
|
|
69
|
+
const colonIdx = line.indexOf(':');
|
|
70
|
+
if (colonIdx === -1)
|
|
71
|
+
return line;
|
|
72
|
+
const filePath = line.substring(0, colonIdx);
|
|
73
|
+
const rest = line.substring(colonIdx + 1);
|
|
74
|
+
return redactPath(filePath, absCwd) + ':' + rest;
|
|
75
|
+
});
|
|
76
|
+
return redactedLines.join('\n');
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
catch {
|
|
81
|
+
// fall through
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
let re;
|
|
85
|
+
try {
|
|
86
|
+
re = new RegExp(pattern);
|
|
87
|
+
}
|
|
88
|
+
catch (e) {
|
|
89
|
+
throw new ToolError('invalid_args', `search_files: invalid regex pattern: ${e?.message ?? String(e)}`, false, 'Escape regex metacharacters (\\\\, [, ], (, ), +, *, ?). If you intended literal text, use an escaped/literal pattern.');
|
|
90
|
+
}
|
|
91
|
+
const out = [];
|
|
92
|
+
async function walk(dir, depth) {
|
|
93
|
+
if (out.length >= maxResults)
|
|
94
|
+
return;
|
|
95
|
+
const ents = await fs.readdir(dir, { withFileTypes: true }).catch(() => []);
|
|
96
|
+
for (const ent of ents) {
|
|
97
|
+
if (out.length >= maxResults)
|
|
98
|
+
return;
|
|
99
|
+
const full = path.join(dir, ent.name);
|
|
100
|
+
if (ent.isDirectory()) {
|
|
101
|
+
if (ent.name === 'node_modules' ||
|
|
102
|
+
ent.name === '.git' ||
|
|
103
|
+
ent.name === 'dist' ||
|
|
104
|
+
ent.name === 'build')
|
|
105
|
+
continue;
|
|
106
|
+
if (depth < 6)
|
|
107
|
+
await walk(full, depth + 1);
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
110
|
+
if (!ent.isFile())
|
|
111
|
+
continue;
|
|
112
|
+
if (include && !globishMatch(ent.name, include))
|
|
113
|
+
continue;
|
|
114
|
+
const rawBuf = await fs.readFile(full).catch(() => null);
|
|
115
|
+
if (!rawBuf)
|
|
116
|
+
continue;
|
|
117
|
+
let isBinary = false;
|
|
118
|
+
for (let bi = 0; bi < Math.min(rawBuf.length, 512); bi++) {
|
|
119
|
+
if (rawBuf[bi] === 0) {
|
|
120
|
+
isBinary = true;
|
|
121
|
+
break;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
if (isBinary)
|
|
125
|
+
continue;
|
|
126
|
+
const buf = rawBuf.toString('utf8');
|
|
127
|
+
const lines = buf.split(/\r?\n/);
|
|
128
|
+
for (let i = 0; i < lines.length; i++) {
|
|
129
|
+
if (re.test(lines[i])) {
|
|
130
|
+
out.push(`${redactPath(full, absCwd)}:${i + 1}:${lines[i]}`);
|
|
131
|
+
if (out.length >= maxResults)
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
await walk(root, 0);
|
|
138
|
+
if (out.length >= maxResults)
|
|
139
|
+
out.push(`[truncated after ${maxResults} results]`);
|
|
140
|
+
if (!out.length)
|
|
141
|
+
return `No matches for pattern \"${pattern}\" in ${redactPath(root, absCwd)}.`;
|
|
142
|
+
return out.join('\n');
|
|
143
|
+
}
|
|
144
|
+
//# sourceMappingURL=file-discovery.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"file-discovery.js","sourceRoot":"","sources":["../../src/tools/file-discovery.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
import fs from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { checkPathSafety } from '../safety.js';
|
|
4
|
+
import { checkCwdWarning, enforceMutationWithinCwd, redactPath, resolvePath, } from './path-safety.js';
|
|
5
|
+
import { checkpointReplay } from './replay-utils.js';
|
|
6
|
+
import { bigramSimilarity } from './search-utils.js';
|
|
7
|
+
import { snapshotBeforeEdit } from './sys-notes.js';
|
|
8
|
+
import { mutationReadback } from './text-utils.js';
|
|
9
|
+
import { atomicWrite, backupFile } from './undo.js';
|
|
10
|
+
export async function writeFileTool(ctx, args) {
|
|
11
|
+
const p = resolvePath(ctx, args?.path);
|
|
12
|
+
const absCwd = path.resolve(ctx.cwd);
|
|
13
|
+
const redactedPath = redactPath(p, absCwd);
|
|
14
|
+
const raw = args?.content;
|
|
15
|
+
const contentWasObject = raw != null && typeof raw === 'object';
|
|
16
|
+
const content = typeof raw === 'string' ? raw : contentWasObject ? JSON.stringify(raw, null, 2) : undefined;
|
|
17
|
+
if (contentWasObject) {
|
|
18
|
+
console.warn(`[write_file] Warning: content for "${args?.path}" arrived as ${typeof raw} — auto-serialized to JSON string. If this was intentional (e.g. package.json), the write succeeded.`);
|
|
19
|
+
}
|
|
20
|
+
if (!p)
|
|
21
|
+
throw new Error('write_file: missing path');
|
|
22
|
+
if (content == null)
|
|
23
|
+
throw new Error('write_file: missing content (got ' + typeof raw + ')');
|
|
24
|
+
const overwrite = Boolean(args?.overwrite ?? args?.force);
|
|
25
|
+
enforceMutationWithinCwd('write_file', p, ctx);
|
|
26
|
+
const cwdWarning = checkCwdWarning('write_file', p, ctx);
|
|
27
|
+
const pathVerdict = checkPathSafety(p);
|
|
28
|
+
if (pathVerdict.tier === 'forbidden')
|
|
29
|
+
throw new Error(`write_file: ${pathVerdict.reason}`);
|
|
30
|
+
if (pathVerdict.tier === 'cautious' && !ctx.noConfirm) {
|
|
31
|
+
if (ctx.confirm) {
|
|
32
|
+
const ok = await ctx.confirm(pathVerdict.prompt || `Write to ${redactedPath}?`, {
|
|
33
|
+
tool: 'write_file',
|
|
34
|
+
args: { path: p },
|
|
35
|
+
});
|
|
36
|
+
if (!ok)
|
|
37
|
+
throw new Error(`write_file: cancelled by user (${pathVerdict.reason})`);
|
|
38
|
+
}
|
|
39
|
+
else {
|
|
40
|
+
throw new Error(`write_file: blocked (${pathVerdict.reason}) without --no-confirm/--yolo`);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
const existingStat = await fs.stat(p).catch(() => null);
|
|
44
|
+
if (existingStat?.isFile() && existingStat.size > 0 && !overwrite) {
|
|
45
|
+
throw new Error(`write_file: refusing to overwrite existing non-empty file ${redactedPath} without explicit overwrite=true (or force=true). ` +
|
|
46
|
+
`Use edit_range/apply_patch for surgical edits, or set overwrite=true for intentional full-file replacement.`);
|
|
47
|
+
}
|
|
48
|
+
if (ctx.dryRun) {
|
|
49
|
+
const mode = existingStat?.isFile()
|
|
50
|
+
? existingStat.size > 0
|
|
51
|
+
? 'overwrite'
|
|
52
|
+
: 'update-empty'
|
|
53
|
+
: 'create';
|
|
54
|
+
return `dry-run: would write ${p} (${Buffer.byteLength(content, 'utf8')} bytes, mode=${mode}${overwrite ? ', explicit-overwrite' : ''})${cwdWarning}`;
|
|
55
|
+
}
|
|
56
|
+
if (ctx.mode === 'sys' && ctx.vault)
|
|
57
|
+
await snapshotBeforeEdit(ctx.vault, p).catch(() => { });
|
|
58
|
+
const beforeBuf = await fs.readFile(p).catch(() => Buffer.from(''));
|
|
59
|
+
await backupFile(p, ctx);
|
|
60
|
+
await atomicWrite(p, content);
|
|
61
|
+
ctx.onMutation?.(p);
|
|
62
|
+
const replayNote = await checkpointReplay(ctx, {
|
|
63
|
+
op: 'write_file',
|
|
64
|
+
filePath: p,
|
|
65
|
+
before: beforeBuf,
|
|
66
|
+
after: Buffer.from(content, 'utf8'),
|
|
67
|
+
});
|
|
68
|
+
const contentLines = content.split(/\r?\n/);
|
|
69
|
+
const readback = contentLines.length <= 40
|
|
70
|
+
? mutationReadback(content, 0, contentLines.length)
|
|
71
|
+
: mutationReadback(content, 0, 20) +
|
|
72
|
+
'\n...\n' +
|
|
73
|
+
mutationReadback(content, contentLines.length - 10, contentLines.length);
|
|
74
|
+
return `wrote ${redactedPath} (${Buffer.byteLength(content, 'utf8')} bytes)${replayNote}${cwdWarning}${readback}`;
|
|
75
|
+
}
|
|
76
|
+
export async function insertFileTool(ctx, args) {
|
|
77
|
+
const p = resolvePath(ctx, args?.path);
|
|
78
|
+
const absCwd = path.resolve(ctx.cwd);
|
|
79
|
+
const redactedPath = redactPath(p, absCwd);
|
|
80
|
+
const line = Number(args?.line);
|
|
81
|
+
const rawText = args?.text;
|
|
82
|
+
const text = typeof rawText === 'string'
|
|
83
|
+
? rawText
|
|
84
|
+
: rawText != null && typeof rawText === 'object'
|
|
85
|
+
? JSON.stringify(rawText, null, 2)
|
|
86
|
+
: undefined;
|
|
87
|
+
if (!p)
|
|
88
|
+
throw new Error('insert_file: missing path');
|
|
89
|
+
if (!Number.isFinite(line))
|
|
90
|
+
throw new Error('insert_file: missing/invalid line');
|
|
91
|
+
if (text == null)
|
|
92
|
+
throw new Error('insert_file: missing text (got ' + typeof rawText + ')');
|
|
93
|
+
enforceMutationWithinCwd('insert_file', p, ctx);
|
|
94
|
+
const pathVerdict = checkPathSafety(p);
|
|
95
|
+
if (pathVerdict.tier === 'forbidden')
|
|
96
|
+
throw new Error(`insert_file: ${pathVerdict.reason}`);
|
|
97
|
+
if (pathVerdict.tier === 'cautious' && !ctx.noConfirm) {
|
|
98
|
+
if (ctx.confirm) {
|
|
99
|
+
const ok = await ctx.confirm(pathVerdict.prompt || `Insert into ${redactedPath}?`, {
|
|
100
|
+
tool: 'insert_file',
|
|
101
|
+
args: { path: p },
|
|
102
|
+
});
|
|
103
|
+
if (!ok)
|
|
104
|
+
throw new Error(`insert_file: cancelled by user (${pathVerdict.reason})`);
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
throw new Error(`insert_file: blocked (${pathVerdict.reason}) without --no-confirm/--yolo`);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
if (ctx.dryRun)
|
|
111
|
+
return `dry-run: would insert into ${redactedPath} at line=${line} (${Buffer.byteLength(text, 'utf8')} bytes)`;
|
|
112
|
+
if (ctx.mode === 'sys' && ctx.vault)
|
|
113
|
+
await snapshotBeforeEdit(ctx.vault, p).catch(() => { });
|
|
114
|
+
const beforeText = await fs.readFile(p, 'utf8').catch(() => '');
|
|
115
|
+
const eol = beforeText.includes('\r\n') ? '\r\n' : '\n';
|
|
116
|
+
if (beforeText === '') {
|
|
117
|
+
const out = text;
|
|
118
|
+
await backupFile(p, ctx);
|
|
119
|
+
await atomicWrite(p, out);
|
|
120
|
+
ctx.onMutation?.(p);
|
|
121
|
+
const replayNote = await checkpointReplay(ctx, {
|
|
122
|
+
op: 'insert_file',
|
|
123
|
+
filePath: p,
|
|
124
|
+
before: Buffer.from(beforeText, 'utf8'),
|
|
125
|
+
after: Buffer.from(out, 'utf8'),
|
|
126
|
+
});
|
|
127
|
+
const cwdWarning = checkCwdWarning('insert_file', p, ctx);
|
|
128
|
+
const readback = mutationReadback(out, 0, out.split(/\r?\n/).length);
|
|
129
|
+
return `inserted into ${redactedPath} at 0${replayNote}${cwdWarning}${readback}`;
|
|
130
|
+
}
|
|
131
|
+
const lines = beforeText.split(/\r?\n/);
|
|
132
|
+
let idx;
|
|
133
|
+
if (line === -1)
|
|
134
|
+
idx = lines.length;
|
|
135
|
+
else
|
|
136
|
+
idx = Math.max(0, Math.min(lines.length, line));
|
|
137
|
+
if (line === -1 && lines.length > 0 && lines[lines.length - 1] === '')
|
|
138
|
+
idx = lines.length - 1;
|
|
139
|
+
const insertLines = text.split(/\r?\n/);
|
|
140
|
+
lines.splice(idx, 0, ...insertLines);
|
|
141
|
+
const out = lines.join(eol);
|
|
142
|
+
await backupFile(p, ctx);
|
|
143
|
+
await atomicWrite(p, out);
|
|
144
|
+
ctx.onMutation?.(p);
|
|
145
|
+
const replayNote = await checkpointReplay(ctx, {
|
|
146
|
+
op: 'insert_file',
|
|
147
|
+
filePath: p,
|
|
148
|
+
before: Buffer.from(beforeText, 'utf8'),
|
|
149
|
+
after: Buffer.from(out, 'utf8'),
|
|
150
|
+
});
|
|
151
|
+
const cwdWarning = checkCwdWarning('insert_file', p, ctx);
|
|
152
|
+
const insertEndLine = idx + insertLines.length;
|
|
153
|
+
const readback = mutationReadback(out, idx, insertEndLine);
|
|
154
|
+
return `inserted into ${redactedPath} at ${idx}${replayNote}${cwdWarning}${readback}`;
|
|
155
|
+
}
|
|
156
|
+
export async function editFileTool(ctx, args) {
|
|
157
|
+
const p = resolvePath(ctx, args?.path);
|
|
158
|
+
const absCwd = path.resolve(ctx.cwd);
|
|
159
|
+
const redactedPath = redactPath(p, absCwd);
|
|
160
|
+
const rawOld = args?.old_text;
|
|
161
|
+
const oldText = typeof rawOld === 'string'
|
|
162
|
+
? rawOld
|
|
163
|
+
: rawOld != null && typeof rawOld === 'object'
|
|
164
|
+
? JSON.stringify(rawOld, null, 2)
|
|
165
|
+
: undefined;
|
|
166
|
+
const rawNew = args?.new_text;
|
|
167
|
+
const newText = typeof rawNew === 'string'
|
|
168
|
+
? rawNew
|
|
169
|
+
: rawNew != null && typeof rawNew === 'object'
|
|
170
|
+
? JSON.stringify(rawNew, null, 2)
|
|
171
|
+
: undefined;
|
|
172
|
+
const replaceAll = Boolean(args?.replace_all);
|
|
173
|
+
if (!p)
|
|
174
|
+
throw new Error('edit_file: missing path');
|
|
175
|
+
if (oldText == null)
|
|
176
|
+
throw new Error('edit_file: missing old_text');
|
|
177
|
+
if (newText == null)
|
|
178
|
+
throw new Error('edit_file: missing new_text');
|
|
179
|
+
enforceMutationWithinCwd('edit_file', p, ctx);
|
|
180
|
+
const pathVerdict = checkPathSafety(p);
|
|
181
|
+
if (pathVerdict.tier === 'forbidden')
|
|
182
|
+
throw new Error(`edit_file: ${pathVerdict.reason}`);
|
|
183
|
+
if (pathVerdict.tier === 'cautious' && !ctx.noConfirm) {
|
|
184
|
+
if (ctx.confirm) {
|
|
185
|
+
const ok = await ctx.confirm(pathVerdict.prompt || `Edit ${redactedPath}?`, {
|
|
186
|
+
tool: 'edit_file',
|
|
187
|
+
args: { path: p, old_text: oldText, new_text: newText },
|
|
188
|
+
});
|
|
189
|
+
if (!ok)
|
|
190
|
+
throw new Error(`edit_file: cancelled by user (${pathVerdict.reason})`);
|
|
191
|
+
}
|
|
192
|
+
else {
|
|
193
|
+
throw new Error(`edit_file: blocked (${pathVerdict.reason}) without --no-confirm/--yolo`);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
if (ctx.mode === 'sys' && ctx.vault)
|
|
197
|
+
await snapshotBeforeEdit(ctx.vault, p).catch(() => { });
|
|
198
|
+
const cur = await fs.readFile(p, 'utf8').catch((e) => {
|
|
199
|
+
throw new Error(`edit_file: cannot read ${redactedPath}: ${e?.message ?? String(e)}`);
|
|
200
|
+
});
|
|
201
|
+
const idx = cur.indexOf(oldText);
|
|
202
|
+
if (idx === -1) {
|
|
203
|
+
const normalize = (s) => s.replace(/\s+/g, ' ').trim().toLowerCase();
|
|
204
|
+
const needle = normalize(oldText);
|
|
205
|
+
const curLines = cur.split(/\r?\n/);
|
|
206
|
+
const needleLines = oldText.split(/\r?\n/).length;
|
|
207
|
+
let bestScore = 0;
|
|
208
|
+
let bestLine = -1;
|
|
209
|
+
let bestText = '';
|
|
210
|
+
for (let i = 0; i < curLines.length; i++) {
|
|
211
|
+
const windowEnd = Math.min(curLines.length, i + needleLines);
|
|
212
|
+
const window = curLines.slice(i, windowEnd).join('\n');
|
|
213
|
+
const score = bigramSimilarity(needle, normalize(window));
|
|
214
|
+
if (score > bestScore) {
|
|
215
|
+
bestScore = score;
|
|
216
|
+
bestLine = i + 1;
|
|
217
|
+
bestText = window;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
let hint = '';
|
|
221
|
+
if (bestScore > 0.3 && bestLine > 0) {
|
|
222
|
+
const preview = bestText.length > 600 ? bestText.slice(0, 600) + '…' : bestText;
|
|
223
|
+
hint = `\nClosest match at line ${bestLine} (${Math.round(bestScore * 100)}% similarity):\n${preview}`;
|
|
224
|
+
}
|
|
225
|
+
else if (!cur.trim()) {
|
|
226
|
+
hint = `\nFile is empty.`;
|
|
227
|
+
}
|
|
228
|
+
else {
|
|
229
|
+
hint = `\nFile head (first 400 chars):\n${cur.slice(0, 400)}`;
|
|
230
|
+
}
|
|
231
|
+
throw new Error(`edit_file: old_text not found in ${redactedPath}. Re-read the file and retry with exact text.${hint}`);
|
|
232
|
+
}
|
|
233
|
+
const next = replaceAll
|
|
234
|
+
? cur.split(oldText).join(newText)
|
|
235
|
+
: cur.slice(0, idx) + newText + cur.slice(idx + oldText.length);
|
|
236
|
+
if (ctx.dryRun)
|
|
237
|
+
return `dry-run: would edit ${redactedPath} (replace_all=${replaceAll})`;
|
|
238
|
+
await backupFile(p, ctx);
|
|
239
|
+
await atomicWrite(p, next);
|
|
240
|
+
ctx.onMutation?.(p);
|
|
241
|
+
const replayNote = await checkpointReplay(ctx, {
|
|
242
|
+
op: 'edit_file',
|
|
243
|
+
filePath: p,
|
|
244
|
+
before: Buffer.from(cur, 'utf8'),
|
|
245
|
+
after: Buffer.from(next, 'utf8'),
|
|
246
|
+
});
|
|
247
|
+
const cwdWarning = checkCwdWarning('edit_file', p, ctx);
|
|
248
|
+
const editStartLine = next.slice(0, idx).split(/\r?\n/).length - 1;
|
|
249
|
+
const editEndLine = editStartLine + newText.split(/\r?\n/).length;
|
|
250
|
+
const readback = mutationReadback(next, editStartLine, editEndLine);
|
|
251
|
+
return `edited ${redactedPath} (replace_all=${replaceAll})${replayNote}${cwdWarning}${readback}`;
|
|
252
|
+
}
|
|
253
|
+
export async function editRangeTool(ctx, args) {
|
|
254
|
+
const p = resolvePath(ctx, args?.path);
|
|
255
|
+
const startLine = Number(args?.start_line);
|
|
256
|
+
const endLine = Number(args?.end_line);
|
|
257
|
+
const rawReplacement = args?.replacement;
|
|
258
|
+
const replacement = typeof rawReplacement === 'string'
|
|
259
|
+
? rawReplacement
|
|
260
|
+
: rawReplacement != null && typeof rawReplacement === 'object'
|
|
261
|
+
? JSON.stringify(rawReplacement, null, 2)
|
|
262
|
+
: undefined;
|
|
263
|
+
if (!p)
|
|
264
|
+
throw new Error('edit_range: missing path');
|
|
265
|
+
if (!Number.isFinite(startLine) || startLine < 1)
|
|
266
|
+
throw new Error('edit_range: missing/invalid start_line');
|
|
267
|
+
if (!Number.isFinite(endLine) || endLine < startLine)
|
|
268
|
+
throw new Error('edit_range: missing/invalid end_line');
|
|
269
|
+
if (replacement == null)
|
|
270
|
+
throw new Error('edit_range: missing replacement (got ' + typeof rawReplacement + ')');
|
|
271
|
+
const hasLiteralEscapedNewlines = replacement.includes('\\n');
|
|
272
|
+
const hasRealNewlines = replacement.includes('\n') || replacement.includes('\r');
|
|
273
|
+
if (hasLiteralEscapedNewlines && !hasRealNewlines) {
|
|
274
|
+
throw new Error('edit_range: replacement appears double-escaped (contains literal "\\n" sequences). ' +
|
|
275
|
+
'Resend replacement with REAL newline characters (multi-line string), not escaped backslash-n text.');
|
|
276
|
+
}
|
|
277
|
+
enforceMutationWithinCwd('edit_range', p, ctx);
|
|
278
|
+
const pathVerdict = checkPathSafety(p);
|
|
279
|
+
if (pathVerdict.tier === 'forbidden')
|
|
280
|
+
throw new Error(`edit_range: ${pathVerdict.reason}`);
|
|
281
|
+
if (pathVerdict.tier === 'cautious' && !ctx.noConfirm) {
|
|
282
|
+
if (ctx.confirm) {
|
|
283
|
+
const ok = await ctx.confirm(pathVerdict.prompt || `Edit range in ${p}?`, {
|
|
284
|
+
tool: 'edit_range',
|
|
285
|
+
args: { path: p, start_line: startLine, end_line: endLine },
|
|
286
|
+
});
|
|
287
|
+
if (!ok)
|
|
288
|
+
throw new Error(`edit_range: cancelled by user (${pathVerdict.reason})`);
|
|
289
|
+
}
|
|
290
|
+
else {
|
|
291
|
+
throw new Error(`edit_range: blocked (${pathVerdict.reason}) without --no-confirm/--yolo`);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
if (ctx.dryRun)
|
|
295
|
+
return `dry-run: would edit_range ${p} lines ${startLine}-${endLine} (${Buffer.byteLength(replacement, 'utf8')} bytes)`;
|
|
296
|
+
if (ctx.mode === 'sys' && ctx.vault)
|
|
297
|
+
await snapshotBeforeEdit(ctx.vault, p).catch(() => { });
|
|
298
|
+
const beforeText = await fs.readFile(p, 'utf8').catch((e) => {
|
|
299
|
+
throw new Error(`edit_range: cannot read ${p}: ${e?.message ?? String(e)}`);
|
|
300
|
+
});
|
|
301
|
+
const eol = beforeText.includes('\r\n') ? '\r\n' : '\n';
|
|
302
|
+
const lines = beforeText.split(/\r?\n/);
|
|
303
|
+
if (startLine > lines.length)
|
|
304
|
+
throw new Error(`edit_range: start_line ${startLine} out of range (file has ${lines.length} lines)`);
|
|
305
|
+
if (endLine > lines.length)
|
|
306
|
+
throw new Error(`edit_range: end_line ${endLine} out of range (file has ${lines.length} lines)`);
|
|
307
|
+
const startIdx = startLine - 1;
|
|
308
|
+
const deleteCount = endLine - startLine + 1;
|
|
309
|
+
const replacementLines = replacement === '' ? [] : replacement.split(/\r?\n/);
|
|
310
|
+
lines.splice(startIdx, deleteCount, ...replacementLines);
|
|
311
|
+
const out = lines.join(eol);
|
|
312
|
+
await backupFile(p, ctx);
|
|
313
|
+
await atomicWrite(p, out);
|
|
314
|
+
ctx.onMutation?.(p);
|
|
315
|
+
const replayNote = await checkpointReplay(ctx, {
|
|
316
|
+
op: 'edit_range',
|
|
317
|
+
filePath: p,
|
|
318
|
+
before: Buffer.from(beforeText, 'utf8'),
|
|
319
|
+
after: Buffer.from(out, 'utf8'),
|
|
320
|
+
});
|
|
321
|
+
const cwdWarning = checkCwdWarning('edit_range', p, ctx);
|
|
322
|
+
const rangeEndLine = startIdx + replacementLines.length;
|
|
323
|
+
const readback = mutationReadback(out, startIdx, rangeEndLine);
|
|
324
|
+
return `edited ${p} lines ${startLine}-${endLine}${replayNote}${cwdWarning}${readback}`;
|
|
325
|
+
}
|
|
326
|
+
//# sourceMappingURL=file-mutations.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"file-mutations.js","sourceRoot":"","sources":["../../src/tools/file-mutations.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import fs from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { resolvePath, redactPath } from './path-safety.js';
|
|
4
|
+
import { guessMimeType, truncateBytes } from './text-utils.js';
|
|
5
|
+
import { ToolError } from './tool-error.js';
|
|
6
|
+
export async function readFileTool(ctx, args) {
|
|
7
|
+
const p = resolvePath(ctx, args?.path);
|
|
8
|
+
const absCwd = path.resolve(ctx.cwd);
|
|
9
|
+
const redactedPath = redactPath(p, absCwd);
|
|
10
|
+
const offset = args?.offset != null ? Number(args.offset) : undefined;
|
|
11
|
+
const rawLimit = args?.limit != null ? Number(args.limit) : undefined;
|
|
12
|
+
let limit = Number.isFinite(rawLimit) && rawLimit > 0
|
|
13
|
+
? Math.max(1, Math.floor(rawLimit))
|
|
14
|
+
: 200;
|
|
15
|
+
if (ctx.maxReadLines != null && ctx.maxReadLines > 0) {
|
|
16
|
+
limit = Math.min(limit, ctx.maxReadLines);
|
|
17
|
+
}
|
|
18
|
+
const search = typeof args?.search === 'string' ? args.search : undefined;
|
|
19
|
+
const rawContext = args?.context != null ? Number(args.context) : undefined;
|
|
20
|
+
const context = Number.isFinite(rawContext) && rawContext >= 0
|
|
21
|
+
? Math.max(0, Math.min(200, Math.floor(rawContext)))
|
|
22
|
+
: 10;
|
|
23
|
+
const formatRaw = typeof args?.format === 'string' ? args.format.trim().toLowerCase() : 'numbered';
|
|
24
|
+
const format = formatRaw === 'plain' || formatRaw === 'numbered' || formatRaw === 'sparse'
|
|
25
|
+
? formatRaw
|
|
26
|
+
: 'numbered';
|
|
27
|
+
const rawMaxBytes = args?.max_bytes != null ? Number(args.max_bytes) : undefined;
|
|
28
|
+
const maxBytes = Number.isFinite(rawMaxBytes) && rawMaxBytes > 0
|
|
29
|
+
? Math.min(256 * 1024, Math.max(256, Math.floor(rawMaxBytes)))
|
|
30
|
+
: 20 * 1024;
|
|
31
|
+
if (!p)
|
|
32
|
+
throw new Error('read_file: missing path');
|
|
33
|
+
try {
|
|
34
|
+
const stat = await fs.stat(p);
|
|
35
|
+
if (stat.isDirectory()) {
|
|
36
|
+
return `read_file: "${redactedPath}" is a directory, not a file. Use list_dir to see its contents, or search_files to find specific code.`;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
// let readFile error path handle ENOENT/etc
|
|
41
|
+
}
|
|
42
|
+
const buf = await fs.readFile(p).catch((e) => {
|
|
43
|
+
throw new Error(`read_file: cannot read ${p}: ${e?.message ?? String(e)}`);
|
|
44
|
+
});
|
|
45
|
+
for (let i = 0; i < Math.min(buf.length, 512); i++) {
|
|
46
|
+
if (buf[i] === 0) {
|
|
47
|
+
const mimeGuess = guessMimeType(p, buf);
|
|
48
|
+
return `[binary file, ${buf.length} bytes, detected type: ${mimeGuess}]`;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
const text = buf.toString('utf8');
|
|
52
|
+
if (!text)
|
|
53
|
+
return `# ${p}\n[file is empty (0 bytes)]`;
|
|
54
|
+
const lines = text.split(/\r?\n/);
|
|
55
|
+
let start = 1;
|
|
56
|
+
let end = Math.min(lines.length, limit);
|
|
57
|
+
let matchLines = [];
|
|
58
|
+
if (search) {
|
|
59
|
+
matchLines = [];
|
|
60
|
+
for (let i = 0; i < lines.length; i++) {
|
|
61
|
+
if (lines[i].includes(search))
|
|
62
|
+
matchLines.push(i + 1);
|
|
63
|
+
}
|
|
64
|
+
if (!matchLines.length) {
|
|
65
|
+
return truncateBytes(`# ${p}\n# search not found: ${JSON.stringify(search)}\n# file has ${lines.length} lines`, maxBytes).text;
|
|
66
|
+
}
|
|
67
|
+
const firstIdx = matchLines[0];
|
|
68
|
+
start = Math.max(1, firstIdx - context);
|
|
69
|
+
end = Math.min(lines.length, firstIdx + context);
|
|
70
|
+
if (end - start + 1 > limit) {
|
|
71
|
+
const half = Math.floor(limit / 2);
|
|
72
|
+
start = Math.max(1, firstIdx - half);
|
|
73
|
+
end = Math.min(lines.length, start + limit - 1);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
else if (offset && offset >= 1) {
|
|
77
|
+
start = Math.max(1, Math.floor(offset));
|
|
78
|
+
end = Math.min(lines.length, start + limit - 1);
|
|
79
|
+
}
|
|
80
|
+
const matchSet = new Set(matchLines);
|
|
81
|
+
const out = [];
|
|
82
|
+
out.push(`# ${p} (lines ${start}-${end} of ${lines.length})`);
|
|
83
|
+
if (search) {
|
|
84
|
+
const shown = matchLines.slice(0, 20);
|
|
85
|
+
out.push(`# matches at lines: ${shown.join(', ')}${matchLines.length > shown.length ? ' …' : ''}`);
|
|
86
|
+
}
|
|
87
|
+
const renderNumbered = (ln, body) => `${ln}| ${body}`;
|
|
88
|
+
for (let ln = start; ln <= end; ln++) {
|
|
89
|
+
const body = lines[ln - 1] ?? '';
|
|
90
|
+
if (format === 'plain') {
|
|
91
|
+
out.push(body);
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
if (format === 'numbered') {
|
|
95
|
+
out.push(renderNumbered(ln, body));
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
const isAnchor = ln === start || ln === end || (ln - start) % 10 === 0;
|
|
99
|
+
if (isAnchor || matchSet.has(ln))
|
|
100
|
+
out.push(renderNumbered(ln, body));
|
|
101
|
+
else
|
|
102
|
+
out.push(body);
|
|
103
|
+
}
|
|
104
|
+
if (end < lines.length)
|
|
105
|
+
out.push(`# ... (${lines.length - end} more lines)`);
|
|
106
|
+
return truncateBytes(out.join('\n'), maxBytes).text;
|
|
107
|
+
}
|
|
108
|
+
export async function readFilesTool(ctx, args) {
|
|
109
|
+
const reqs = Array.isArray(args?.requests) ? args.requests : [];
|
|
110
|
+
if (!reqs.length) {
|
|
111
|
+
throw new ToolError('invalid_args', 'read_files: missing requests[]', false, 'Provide requests as an array of {path, limit,...} objects.');
|
|
112
|
+
}
|
|
113
|
+
const parts = [];
|
|
114
|
+
let failures = 0;
|
|
115
|
+
for (let i = 0; i < reqs.length; i++) {
|
|
116
|
+
const r = reqs[i];
|
|
117
|
+
const p = typeof r?.path === 'string' ? r.path : `request[${i}]`;
|
|
118
|
+
try {
|
|
119
|
+
parts.push(await readFileTool(ctx, r));
|
|
120
|
+
}
|
|
121
|
+
catch (e) {
|
|
122
|
+
failures++;
|
|
123
|
+
const te = ToolError.fromError(e, 'internal');
|
|
124
|
+
parts.push(`[file:${p}] ERROR: code=${te.code} msg=${te.message}`);
|
|
125
|
+
}
|
|
126
|
+
parts.push('');
|
|
127
|
+
}
|
|
128
|
+
if (failures > 0) {
|
|
129
|
+
parts.push(`# read_files completed with partial failures: ${failures}/${reqs.length}`);
|
|
130
|
+
}
|
|
131
|
+
return parts.join('\n');
|
|
132
|
+
}
|
|
133
|
+
//# sourceMappingURL=file-read.js.map
|