@visorcraft/idlehands 1.1.16 → 1.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/dist/agent/formatting.js +30 -13
- package/dist/agent/formatting.js.map +1 -1
- package/dist/agent/review-artifact.js +12 -8
- package/dist/agent/review-artifact.js.map +1 -1
- package/dist/agent/tool-calls.js +62 -21
- package/dist/agent/tool-calls.js.map +1 -1
- package/dist/agent/tool-loop-detection.js +310 -0
- package/dist/agent/tool-loop-detection.js.map +1 -0
- package/dist/agent/tool-loop-guard.js +235 -0
- package/dist/agent/tool-loop-guard.js.map +1 -0
- package/dist/agent.js +585 -144
- package/dist/agent.js.map +1 -1
- package/dist/anton/controller.js +46 -30
- package/dist/anton/controller.js.map +1 -1
- package/dist/anton/lock.js +5 -1
- package/dist/anton/lock.js.map +1 -1
- package/dist/anton/parser.js +18 -19
- package/dist/anton/parser.js.map +1 -1
- package/dist/anton/prompt.js +42 -11
- package/dist/anton/prompt.js.map +1 -1
- package/dist/anton/reporter.js.map +1 -1
- package/dist/anton/session.js.map +1 -1
- package/dist/anton/verifier.js +3 -5
- package/dist/anton/verifier.js.map +1 -1
- package/dist/bench/compare.js +53 -20
- package/dist/bench/compare.js.map +1 -1
- package/dist/bench/openclaw.js +4 -4
- package/dist/bench/openclaw.js.map +1 -1
- package/dist/bench/report.js +11 -3
- package/dist/bench/report.js.map +1 -1
- package/dist/bench/runner.js +20 -14
- package/dist/bench/runner.js.map +1 -1
- package/dist/bot/commands.js +69 -26
- package/dist/bot/commands.js.map +1 -1
- package/dist/bot/confirm-discord.js +32 -9
- package/dist/bot/confirm-discord.js.map +1 -1
- package/dist/bot/confirm-telegram.js +26 -10
- package/dist/bot/confirm-telegram.js.map +1 -1
- package/dist/bot/dir-guard.js +18 -3
- package/dist/bot/dir-guard.js.map +1 -1
- package/dist/bot/discord-routing.js +28 -4
- package/dist/bot/discord-routing.js.map +1 -1
- package/dist/bot/discord-streaming.js +3 -3
- package/dist/bot/discord-streaming.js.map +1 -1
- package/dist/bot/discord.js +93 -37
- package/dist/bot/discord.js.map +1 -1
- package/dist/bot/escalation.js +124 -0
- package/dist/bot/escalation.js.map +1 -0
- package/dist/bot/format.js +2 -5
- package/dist/bot/format.js.map +1 -1
- package/dist/bot/session-manager.js +17 -6
- package/dist/bot/session-manager.js.map +1 -1
- package/dist/bot/telegram.js +92 -29
- package/dist/bot/telegram.js.map +1 -1
- package/dist/cli/agent-turn.js +10 -4
- package/dist/cli/agent-turn.js.map +1 -1
- package/dist/cli/args.js +51 -9
- package/dist/cli/args.js.map +1 -1
- package/dist/cli/bot.js +19 -9
- package/dist/cli/bot.js.map +1 -1
- package/dist/cli/build-repl-context.js +60 -26
- package/dist/cli/build-repl-context.js.map +1 -1
- package/dist/cli/command-registry.js.map +1 -1
- package/dist/cli/commands/anton.js +5 -3
- package/dist/cli/commands/anton.js.map +1 -1
- package/dist/cli/commands/editing.js +27 -12
- package/dist/cli/commands/editing.js.map +1 -1
- package/dist/cli/commands/model.js +16 -7
- package/dist/cli/commands/model.js.map +1 -1
- package/dist/cli/commands/project.js +52 -17
- package/dist/cli/commands/project.js.map +1 -1
- package/dist/cli/commands/runtime.js +1 -1
- package/dist/cli/commands/runtime.js.map +1 -1
- package/dist/cli/commands/secrets.js +279 -0
- package/dist/cli/commands/secrets.js.map +1 -0
- package/dist/cli/commands/session.js +57 -2
- package/dist/cli/commands/session.js.map +1 -1
- package/dist/cli/commands/tools.js +3 -1
- package/dist/cli/commands/tools.js.map +1 -1
- package/dist/cli/commands/trifecta.js +1 -1
- package/dist/cli/commands/trifecta.js.map +1 -1
- package/dist/cli/commands/tui.js.map +1 -1
- package/dist/cli/init.js +50 -16
- package/dist/cli/init.js.map +1 -1
- package/dist/cli/input.js +25 -7
- package/dist/cli/input.js.map +1 -1
- package/dist/cli/oneshot.js +31 -19
- package/dist/cli/oneshot.js.map +1 -1
- package/dist/cli/repl-dispatch.js +10 -6
- package/dist/cli/repl-dispatch.js.map +1 -1
- package/dist/cli/runtime-cmds.js +110 -46
- package/dist/cli/runtime-cmds.js.map +1 -1
- package/dist/cli/service.js +3 -3
- package/dist/cli/service.js.map +1 -1
- package/dist/cli/session-state.js +12 -5
- package/dist/cli/session-state.js.map +1 -1
- package/dist/cli/setup.js +86 -33
- package/dist/cli/setup.js.map +1 -1
- package/dist/cli/shell.js +4 -4
- package/dist/cli/shell.js.map +1 -1
- package/dist/cli/status.js +56 -12
- package/dist/cli/status.js.map +1 -1
- package/dist/client.js +40 -21
- package/dist/client.js.map +1 -1
- package/dist/commands.js +1 -1
- package/dist/commands.js.map +1 -1
- package/dist/config.js +171 -15
- package/dist/config.js.map +1 -1
- package/dist/confirm/auto.js.map +1 -1
- package/dist/confirm/headless.js +13 -2
- package/dist/confirm/headless.js.map +1 -1
- package/dist/confirm/terminal.js +1 -5
- package/dist/confirm/terminal.js.map +1 -1
- package/dist/context.js +9 -3
- package/dist/context.js.map +1 -1
- package/dist/git.js +56 -61
- package/dist/git.js.map +1 -1
- package/dist/harnesses.js +137 -37
- package/dist/harnesses.js.map +1 -1
- package/dist/history.js +12 -4
- package/dist/history.js.map +1 -1
- package/dist/hooks/index.js +2 -2
- package/dist/hooks/index.js.map +1 -1
- package/dist/hooks/loader.js +6 -5
- package/dist/hooks/loader.js.map +1 -1
- package/dist/hooks/manager.js.map +1 -1
- package/dist/hooks/plugins/example-console.js.map +1 -1
- package/dist/hooks/scaffold.js +8 -6
- package/dist/hooks/scaffold.js.map +1 -1
- package/dist/index.js +120 -66
- package/dist/index.js.map +1 -1
- package/dist/indexer.js +6 -18
- package/dist/indexer.js.map +1 -1
- package/dist/jsonrpc.js.map +1 -1
- package/dist/lens.js +38 -16
- package/dist/lens.js.map +1 -1
- package/dist/lsp.js +60 -24
- package/dist/lsp.js.map +1 -1
- package/dist/markdown.js +6 -6
- package/dist/markdown.js.map +1 -1
- package/dist/mcp.js +15 -6
- package/dist/mcp.js.map +1 -1
- package/dist/model-customization.js +7 -3
- package/dist/model-customization.js.map +1 -1
- package/dist/progress/message-edit-scheduler.js +15 -3
- package/dist/progress/message-edit-scheduler.js.map +1 -1
- package/dist/progress/progress-message-renderer.js.map +1 -1
- package/dist/progress/progress-presenter.js +3 -3
- package/dist/progress/progress-presenter.js.map +1 -1
- package/dist/progress/serialize-telegram.js.map +1 -1
- package/dist/progress/tool-summary.js +3 -1
- package/dist/progress/tool-summary.js.map +1 -1
- package/dist/progress/turn-progress.js +3 -1
- package/dist/progress/turn-progress.js.map +1 -1
- package/dist/recovery.js +11 -3
- package/dist/recovery.js.map +1 -1
- package/dist/replay.js +9 -3
- package/dist/replay.js.map +1 -1
- package/dist/replay_cli.js +5 -3
- package/dist/replay_cli.js.map +1 -1
- package/dist/runtime/executor.js +66 -20
- package/dist/runtime/executor.js.map +1 -1
- package/dist/runtime/health.js.map +1 -1
- package/dist/runtime/host-runner.js +103 -0
- package/dist/runtime/host-runner.js.map +1 -0
- package/dist/runtime/planner.js +3 -1
- package/dist/runtime/planner.js.map +1 -1
- package/dist/runtime/secrets.js +102 -0
- package/dist/runtime/secrets.js.map +1 -0
- package/dist/runtime/store.js +95 -19
- package/dist/runtime/store.js.map +1 -1
- package/dist/safety.js +38 -21
- package/dist/safety.js.map +1 -1
- package/dist/spinner.js +7 -8
- package/dist/spinner.js.map +1 -1
- package/dist/sys/context.js +3 -3
- package/dist/sys/context.js.map +1 -1
- package/dist/term.js +1 -1
- package/dist/term.js.map +1 -1
- package/dist/themes.js +11 -5
- package/dist/themes.js.map +1 -1
- package/dist/tools/tool-error.js +2 -5
- package/dist/tools/tool-error.js.map +1 -1
- package/dist/tools.js +84 -35
- package/dist/tools.js.map +1 -1
- package/dist/tui/branch-picker.js +9 -3
- package/dist/tui/branch-picker.js.map +1 -1
- package/dist/tui/command-handler.js +88 -36
- package/dist/tui/command-handler.js.map +1 -1
- package/dist/tui/confirm.js.map +1 -1
- package/dist/tui/controller.js +234 -117
- package/dist/tui/controller.js.map +1 -1
- package/dist/tui/event-bridge.js.map +1 -1
- package/dist/tui/keymap.js +93 -71
- package/dist/tui/keymap.js.map +1 -1
- package/dist/tui/layout.js +9 -1
- package/dist/tui/layout.js.map +1 -1
- package/dist/tui/render.js +17 -5
- package/dist/tui/render.js.map +1 -1
- package/dist/tui/screen.js.map +1 -1
- package/dist/tui/state.js +129 -63
- package/dist/tui/state.js.map +1 -1
- package/dist/tui/theme.js +12 -3
- package/dist/tui/theme.js.map +1 -1
- package/dist/upgrade.js +28 -15
- package/dist/upgrade.js.map +1 -1
- package/dist/utils.js +8 -5
- package/dist/utils.js.map +1 -1
- package/dist/vault.js +48 -12
- package/dist/vault.js.map +1 -1
- package/dist/vim.js.map +1 -1
- package/package.json +11 -2
package/dist/themes.js
CHANGED
|
@@ -8,9 +8,9 @@
|
|
|
8
8
|
* Built-in: default, dark, light, minimal, hacker
|
|
9
9
|
* Custom: ~/.config/idlehands/themes/<name>.json
|
|
10
10
|
*/
|
|
11
|
-
import pc from 'picocolors';
|
|
12
11
|
import fs from 'node:fs/promises';
|
|
13
12
|
import path from 'node:path';
|
|
13
|
+
import pc from 'picocolors';
|
|
14
14
|
import { configDir } from './utils.js';
|
|
15
15
|
// All supported picocolors primitives.
|
|
16
16
|
const PC = {
|
|
@@ -31,8 +31,8 @@ const PC = {
|
|
|
31
31
|
};
|
|
32
32
|
/** Resolve a palette string like "bold+cyan" to a composed function. */
|
|
33
33
|
function resolveColor(spec) {
|
|
34
|
-
const parts = spec.split('+').map(p => p.trim().toLowerCase());
|
|
35
|
-
const fns = parts.map(p => PC[p]).filter(Boolean);
|
|
34
|
+
const parts = spec.split('+').map((p) => p.trim().toLowerCase());
|
|
35
|
+
const fns = parts.map((p) => PC[p]).filter(Boolean);
|
|
36
36
|
if (fns.length === 0)
|
|
37
37
|
return undefined;
|
|
38
38
|
if (fns.length === 1)
|
|
@@ -41,8 +41,14 @@ function resolveColor(spec) {
|
|
|
41
41
|
}
|
|
42
42
|
/** The default (identity) palette — no remapping. */
|
|
43
43
|
const DEFAULT_PALETTE = {
|
|
44
|
-
dim: 'dim',
|
|
45
|
-
|
|
44
|
+
dim: 'dim',
|
|
45
|
+
bold: 'bold',
|
|
46
|
+
red: 'red',
|
|
47
|
+
yellow: 'yellow',
|
|
48
|
+
green: 'green',
|
|
49
|
+
cyan: 'cyan',
|
|
50
|
+
magenta: 'magenta',
|
|
51
|
+
blue: 'blue',
|
|
46
52
|
};
|
|
47
53
|
const BUILTIN_PALETTES = {
|
|
48
54
|
default: {},
|
package/dist/themes.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"themes.js","sourceRoot":"","sources":["../src/themes.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"themes.js","sourceRoot":"","sources":["../src/themes.ts"],"names":[],"mappings":"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"}
|
package/dist/tools/tool-error.js
CHANGED
|
@@ -19,10 +19,7 @@ export class ToolError extends Error {
|
|
|
19
19
|
* Format as a concise tool result content string
|
|
20
20
|
*/
|
|
21
21
|
toToolResult() {
|
|
22
|
-
const lines = [
|
|
23
|
-
`ERROR: code=${this.code} retryable=${this.retryable}`,
|
|
24
|
-
`msg=${this.message}`
|
|
25
|
-
];
|
|
22
|
+
const lines = [`ERROR: code=${this.code} retryable=${this.retryable}`, `msg=${this.message}`];
|
|
26
23
|
if (this.hint) {
|
|
27
24
|
lines.push(`hint=${this.hint}`);
|
|
28
25
|
}
|
|
@@ -76,7 +73,7 @@ export class ToolError extends Error {
|
|
|
76
73
|
export class ValidationError extends ToolError {
|
|
77
74
|
errors;
|
|
78
75
|
constructor(errors) {
|
|
79
|
-
super('invalid_args', `Validation failed: ${errors.map(e => e.message).join('; ')}`, false, undefined, { fields: errors.map(e => e.field) });
|
|
76
|
+
super('invalid_args', `Validation failed: ${errors.map((e) => e.message).join('; ')}`, false, undefined, { fields: errors.map((e) => e.field) });
|
|
80
77
|
this.errors = errors;
|
|
81
78
|
this.name = 'ValidationError';
|
|
82
79
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tool-error.js","sourceRoot":"","sources":["../../src/tools/tool-error.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAaH,MAAM,OAAO,SAAU,SAAQ,KAAK;IAEhB;IAEA;IACA;IACA;IALlB,YACkB,IAAmB,EACnC,OAAe,EACC,YAAqB,KAAK,EAC1B,IAAa,EACb,OAA6B;QAE7C,KAAK,CAAC,OAAO,CAAC,CAAC;QANC,SAAI,GAAJ,IAAI,CAAe;QAEnB,cAAS,GAAT,SAAS,CAAiB;QAC1B,SAAI,GAAJ,IAAI,CAAS;QACb,YAAO,GAAP,OAAO,CAAsB;QAG7C,IAAI,CAAC,IAAI,GAAG,WAAW,CAAC;IAC1B,CAAC;IAED;;OAEG;IACH,YAAY;QACV,MAAM,KAAK,GAAG
|
|
1
|
+
{"version":3,"file":"tool-error.js","sourceRoot":"","sources":["../../src/tools/tool-error.ts"],"names":[],"mappings":"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"}
|
package/dist/tools.js
CHANGED
|
@@ -279,8 +279,10 @@ export async function undo_path(ctx, args) {
|
|
|
279
279
|
const p = directPath ? resolvePath(ctx, directPath) : ctx.lastEditedPath;
|
|
280
280
|
if (!p)
|
|
281
281
|
throw new Error('undo: missing path');
|
|
282
|
+
const absCwd = path.resolve(ctx.cwd);
|
|
283
|
+
const redactedPath = redactPath(p, absCwd);
|
|
282
284
|
if (!ctx.noConfirm && ctx.confirm) {
|
|
283
|
-
const ok = await ctx.confirm(`Restore latest backup for:\n ${
|
|
285
|
+
const ok = await ctx.confirm(`Restore latest backup for:\n ${redactedPath}\nThis will overwrite the current file. Proceed? (y/N) `);
|
|
284
286
|
if (!ok)
|
|
285
287
|
return 'undo: cancelled';
|
|
286
288
|
}
|
|
@@ -288,11 +290,13 @@ export async function undo_path(ctx, args) {
|
|
|
288
290
|
throw new Error('undo: confirmation required (run with --no-confirm/--yolo or in interactive mode)');
|
|
289
291
|
}
|
|
290
292
|
if (ctx.dryRun)
|
|
291
|
-
return `dry-run: would restore latest backup for ${
|
|
293
|
+
return `dry-run: would restore latest backup for ${redactedPath}`;
|
|
292
294
|
return await restoreLatestBackup(p, ctx);
|
|
293
295
|
}
|
|
294
296
|
export async function read_file(ctx, args) {
|
|
295
297
|
const p = resolvePath(ctx, args?.path);
|
|
298
|
+
const absCwd = path.resolve(ctx.cwd);
|
|
299
|
+
const redactedPath = redactPath(p, absCwd);
|
|
296
300
|
const offset = args?.offset != null ? Number(args.offset) : undefined;
|
|
297
301
|
const rawLimit = args?.limit != null ? Number(args.limit) : undefined;
|
|
298
302
|
const limit = Number.isFinite(rawLimit) && rawLimit > 0
|
|
@@ -317,7 +321,7 @@ export async function read_file(ctx, args) {
|
|
|
317
321
|
try {
|
|
318
322
|
const stat = await fs.stat(p);
|
|
319
323
|
if (stat.isDirectory()) {
|
|
320
|
-
return `read_file: "${
|
|
324
|
+
return `read_file: "${redactedPath}" is a directory, not a file. Use list_dir to see its contents, or search_files to find specific code.`;
|
|
321
325
|
}
|
|
322
326
|
}
|
|
323
327
|
catch (e) {
|
|
@@ -419,6 +423,8 @@ export async function read_files(ctx, args) {
|
|
|
419
423
|
}
|
|
420
424
|
export async function write_file(ctx, args) {
|
|
421
425
|
const p = resolvePath(ctx, args?.path);
|
|
426
|
+
const absCwd = path.resolve(ctx.cwd);
|
|
427
|
+
const redactedPath = redactPath(p, absCwd);
|
|
422
428
|
// Content may arrive as a string (normal) or as a parsed JSON object
|
|
423
429
|
// (when llama-server's XML parser auto-parses JSON content values).
|
|
424
430
|
const raw = args?.content;
|
|
@@ -434,6 +440,7 @@ export async function write_file(ctx, args) {
|
|
|
434
440
|
throw new Error('write_file: missing path');
|
|
435
441
|
if (content == null)
|
|
436
442
|
throw new Error('write_file: missing content (got ' + typeof raw + ')');
|
|
443
|
+
const overwrite = Boolean(args?.overwrite ?? args?.force);
|
|
437
444
|
enforceMutationWithinCwd('write_file', p, ctx);
|
|
438
445
|
const cwdWarning = checkCwdWarning('write_file', p, ctx);
|
|
439
446
|
// Path safety check (Phase 9)
|
|
@@ -443,7 +450,7 @@ export async function write_file(ctx, args) {
|
|
|
443
450
|
}
|
|
444
451
|
if (pathVerdict.tier === 'cautious' && !ctx.noConfirm) {
|
|
445
452
|
if (ctx.confirm) {
|
|
446
|
-
const ok = await ctx.confirm(pathVerdict.prompt || `Write to ${
|
|
453
|
+
const ok = await ctx.confirm(pathVerdict.prompt || `Write to ${redactedPath}?`, { tool: 'write_file', args: { path: p } });
|
|
447
454
|
if (!ok)
|
|
448
455
|
throw new Error(`write_file: cancelled by user (${pathVerdict.reason})`);
|
|
449
456
|
}
|
|
@@ -451,8 +458,15 @@ export async function write_file(ctx, args) {
|
|
|
451
458
|
throw new Error(`write_file: blocked (${pathVerdict.reason}) without --no-confirm/--yolo`);
|
|
452
459
|
}
|
|
453
460
|
}
|
|
454
|
-
|
|
455
|
-
|
|
461
|
+
const existingStat = await fs.stat(p).catch(() => null);
|
|
462
|
+
if (existingStat?.isFile() && existingStat.size > 0 && !overwrite) {
|
|
463
|
+
throw new Error(`write_file: refusing to overwrite existing non-empty file ${redactedPath} without explicit overwrite=true (or force=true). ` +
|
|
464
|
+
`Use edit_range/apply_patch for surgical edits, or set overwrite=true for intentional full-file replacement.`);
|
|
465
|
+
}
|
|
466
|
+
if (ctx.dryRun) {
|
|
467
|
+
const mode = existingStat?.isFile() ? (existingStat.size > 0 ? 'overwrite' : 'update-empty') : 'create';
|
|
468
|
+
return `dry-run: would write ${p} (${Buffer.byteLength(content, 'utf8')} bytes, mode=${mode}${overwrite ? ', explicit-overwrite' : ''})${cwdWarning}`;
|
|
469
|
+
}
|
|
456
470
|
// Phase 9d: snapshot /etc/ files before editing
|
|
457
471
|
if (ctx.mode === 'sys' && ctx.vault) {
|
|
458
472
|
await snapshotBeforeEdit(ctx.vault, p).catch(() => { });
|
|
@@ -463,10 +477,12 @@ export async function write_file(ctx, args) {
|
|
|
463
477
|
ctx.onMutation?.(p);
|
|
464
478
|
const afterBuf = Buffer.from(content, 'utf8');
|
|
465
479
|
const replayNote = await checkpointReplay(ctx, { op: 'write_file', filePath: p, before: beforeBuf, after: afterBuf });
|
|
466
|
-
return `wrote ${
|
|
480
|
+
return `wrote ${redactedPath} (${Buffer.byteLength(content, 'utf8')} bytes)${replayNote}${cwdWarning}`;
|
|
467
481
|
}
|
|
468
482
|
export async function insert_file(ctx, args) {
|
|
469
483
|
const p = resolvePath(ctx, args?.path);
|
|
484
|
+
const absCwd = path.resolve(ctx.cwd);
|
|
485
|
+
const redactedPath = redactPath(p, absCwd);
|
|
470
486
|
const line = Number(args?.line);
|
|
471
487
|
const rawText = args?.text;
|
|
472
488
|
const text = typeof rawText === 'string' ? rawText
|
|
@@ -485,7 +501,7 @@ export async function insert_file(ctx, args) {
|
|
|
485
501
|
}
|
|
486
502
|
if (pathVerdict.tier === 'cautious' && !ctx.noConfirm) {
|
|
487
503
|
if (ctx.confirm) {
|
|
488
|
-
const ok = await ctx.confirm(pathVerdict.prompt || `Insert into ${
|
|
504
|
+
const ok = await ctx.confirm(pathVerdict.prompt || `Insert into ${redactedPath}?`, { tool: 'insert_file', args: { path: p } });
|
|
489
505
|
if (!ok)
|
|
490
506
|
throw new Error(`insert_file: cancelled by user (${pathVerdict.reason})`);
|
|
491
507
|
}
|
|
@@ -494,7 +510,7 @@ export async function insert_file(ctx, args) {
|
|
|
494
510
|
}
|
|
495
511
|
}
|
|
496
512
|
if (ctx.dryRun)
|
|
497
|
-
return `dry-run: would insert into ${
|
|
513
|
+
return `dry-run: would insert into ${redactedPath} at line=${line} (${Buffer.byteLength(text, 'utf8')} bytes)`;
|
|
498
514
|
// Phase 9d: snapshot /etc/ files before editing
|
|
499
515
|
if (ctx.mode === 'sys' && ctx.vault) {
|
|
500
516
|
await snapshotBeforeEdit(ctx.vault, p).catch(() => { });
|
|
@@ -515,7 +531,7 @@ export async function insert_file(ctx, args) {
|
|
|
515
531
|
after: Buffer.from(out, 'utf8')
|
|
516
532
|
});
|
|
517
533
|
const cwdWarning = checkCwdWarning('insert_file', p, ctx);
|
|
518
|
-
return `inserted into ${
|
|
534
|
+
return `inserted into ${redactedPath} at 0${replayNote}${cwdWarning}`;
|
|
519
535
|
}
|
|
520
536
|
const lines = beforeText.split(/\r?\n/);
|
|
521
537
|
let idx;
|
|
@@ -544,10 +560,12 @@ export async function insert_file(ctx, args) {
|
|
|
544
560
|
after: Buffer.from(out, 'utf8')
|
|
545
561
|
});
|
|
546
562
|
const cwdWarning = checkCwdWarning('insert_file', p, ctx);
|
|
547
|
-
return `inserted into ${
|
|
563
|
+
return `inserted into ${redactedPath} at ${idx}${replayNote}${cwdWarning}`;
|
|
548
564
|
}
|
|
549
565
|
export async function edit_file(ctx, args) {
|
|
550
566
|
const p = resolvePath(ctx, args?.path);
|
|
567
|
+
const absCwd = path.resolve(ctx.cwd);
|
|
568
|
+
const redactedPath = redactPath(p, absCwd);
|
|
551
569
|
const rawOld = args?.old_text;
|
|
552
570
|
const oldText = typeof rawOld === 'string' ? rawOld
|
|
553
571
|
: (rawOld != null && typeof rawOld === 'object' ? JSON.stringify(rawOld, null, 2) : undefined);
|
|
@@ -569,7 +587,7 @@ export async function edit_file(ctx, args) {
|
|
|
569
587
|
}
|
|
570
588
|
if (pathVerdict.tier === 'cautious' && !ctx.noConfirm) {
|
|
571
589
|
if (ctx.confirm) {
|
|
572
|
-
const ok = await ctx.confirm(pathVerdict.prompt || `Edit ${
|
|
590
|
+
const ok = await ctx.confirm(pathVerdict.prompt || `Edit ${redactedPath}?`, { tool: 'edit_file', args: { path: p, old_text: oldText, new_text: newText } });
|
|
573
591
|
if (!ok)
|
|
574
592
|
throw new Error(`edit_file: cancelled by user (${pathVerdict.reason})`);
|
|
575
593
|
}
|
|
@@ -582,7 +600,7 @@ export async function edit_file(ctx, args) {
|
|
|
582
600
|
await snapshotBeforeEdit(ctx.vault, p).catch(() => { });
|
|
583
601
|
}
|
|
584
602
|
const cur = await fs.readFile(p, 'utf8').catch((e) => {
|
|
585
|
-
throw new Error(`edit_file: cannot read ${
|
|
603
|
+
throw new Error(`edit_file: cannot read ${redactedPath}: ${e?.message ?? String(e)}`);
|
|
586
604
|
});
|
|
587
605
|
const idx = cur.indexOf(oldText);
|
|
588
606
|
if (idx === -1) {
|
|
@@ -618,11 +636,11 @@ export async function edit_file(ctx, args) {
|
|
|
618
636
|
else {
|
|
619
637
|
hint = `\nFile head (first 400 chars):\n${cur.slice(0, 400)}`;
|
|
620
638
|
}
|
|
621
|
-
throw new Error(`edit_file: old_text not found in ${
|
|
639
|
+
throw new Error(`edit_file: old_text not found in ${redactedPath}. Re-read the file and retry with exact text.${hint}`);
|
|
622
640
|
}
|
|
623
641
|
const next = replaceAll ? cur.split(oldText).join(newText) : cur.slice(0, idx) + newText + cur.slice(idx + oldText.length);
|
|
624
642
|
if (ctx.dryRun)
|
|
625
|
-
return `dry-run: would edit ${
|
|
643
|
+
return `dry-run: would edit ${redactedPath} (replace_all=${replaceAll})`;
|
|
626
644
|
await backupFile(p, ctx);
|
|
627
645
|
await atomicWrite(p, next);
|
|
628
646
|
ctx.onMutation?.(p);
|
|
@@ -633,7 +651,7 @@ export async function edit_file(ctx, args) {
|
|
|
633
651
|
after: Buffer.from(next, 'utf8')
|
|
634
652
|
});
|
|
635
653
|
const cwdWarning = checkCwdWarning('edit_file', p, ctx);
|
|
636
|
-
return `edited ${
|
|
654
|
+
return `edited ${redactedPath} (replace_all=${replaceAll})${replayNote}${cwdWarning}`;
|
|
637
655
|
}
|
|
638
656
|
function normalizePatchPath(p) {
|
|
639
657
|
let s = String(p ?? '').trim();
|
|
@@ -766,6 +784,12 @@ export async function edit_range(ctx, args) {
|
|
|
766
784
|
throw new Error('edit_range: missing/invalid end_line');
|
|
767
785
|
if (replacement == null)
|
|
768
786
|
throw new Error('edit_range: missing replacement (got ' + typeof rawReplacement + ')');
|
|
787
|
+
const hasLiteralEscapedNewlines = replacement.includes('\\n');
|
|
788
|
+
const hasRealNewlines = replacement.includes('\n') || replacement.includes('\r');
|
|
789
|
+
if (hasLiteralEscapedNewlines && !hasRealNewlines) {
|
|
790
|
+
throw new Error('edit_range: replacement appears double-escaped (contains literal "\\n" sequences). ' +
|
|
791
|
+
'Resend replacement with REAL newline characters (multi-line string), not escaped backslash-n text.');
|
|
792
|
+
}
|
|
769
793
|
enforceMutationWithinCwd('edit_range', p, ctx);
|
|
770
794
|
// Path safety check (Phase 9)
|
|
771
795
|
const pathVerdict = checkPathSafety(p);
|
|
@@ -877,7 +901,8 @@ export async function apply_patch(ctx, args) {
|
|
|
877
901
|
if (chk.rc !== 0)
|
|
878
902
|
throw new Error(`apply_patch: patch --dry-run failed:\n${chk.err || chk.out}`);
|
|
879
903
|
}
|
|
880
|
-
|
|
904
|
+
const redactedPaths = touched.paths.map((rel) => redactPath(resolvePath(ctx, rel), path.resolve(ctx.cwd)));
|
|
905
|
+
return `dry-run: patch would apply cleanly (${touched.paths.length} files): ${redactedPaths.join(', ')}`;
|
|
881
906
|
}
|
|
882
907
|
// Snapshot + backup before applying
|
|
883
908
|
const beforeMap = new Map();
|
|
@@ -923,7 +948,8 @@ export async function apply_patch(ctx, args) {
|
|
|
923
948
|
replayNotes += replayNote;
|
|
924
949
|
cwdWarnings += checkCwdWarning('apply_patch', abs, ctx);
|
|
925
950
|
}
|
|
926
|
-
|
|
951
|
+
const redactedPaths = touched.paths.map((rel) => redactPath(resolvePath(ctx, rel), path.resolve(ctx.cwd)));
|
|
952
|
+
return `applied patch (${touched.paths.length} files): ${redactedPaths.join(', ')}${replayNotes}${cwdWarnings}`;
|
|
927
953
|
}
|
|
928
954
|
export async function list_dir(ctx, args) {
|
|
929
955
|
const p = resolvePath(ctx, args?.path ?? '.');
|
|
@@ -931,6 +957,7 @@ export async function list_dir(ctx, args) {
|
|
|
931
957
|
const maxEntries = Math.min(args?.max_entries ? Number(args.max_entries) : 200, 500);
|
|
932
958
|
if (!p)
|
|
933
959
|
throw new Error('list_dir: missing path');
|
|
960
|
+
const absCwd = path.resolve(ctx.cwd);
|
|
934
961
|
const lines = [];
|
|
935
962
|
let count = 0;
|
|
936
963
|
async function walk(dir, depth) {
|
|
@@ -945,7 +972,7 @@ export async function list_dir(ctx, args) {
|
|
|
945
972
|
const full = path.join(dir, ent.name);
|
|
946
973
|
const st = await fs.lstat(full).catch(() => null);
|
|
947
974
|
const kind = ent.isDirectory() ? 'dir' : ent.isSymbolicLink() ? 'link' : 'file';
|
|
948
|
-
lines.push(`${kind}\t${st?.size ?? 0}\t${full}`);
|
|
975
|
+
lines.push(`${kind}\t${st?.size ?? 0}\t${redactPath(full, absCwd)}`);
|
|
949
976
|
count++;
|
|
950
977
|
if (recursive && ent.isDirectory() && depth < 3) {
|
|
951
978
|
await walk(full, depth + 1);
|
|
@@ -956,7 +983,7 @@ export async function list_dir(ctx, args) {
|
|
|
956
983
|
if (count >= maxEntries)
|
|
957
984
|
lines.push(`[truncated after ${maxEntries} entries]`);
|
|
958
985
|
if (!lines.length)
|
|
959
|
-
return `[empty directory: ${p}]`;
|
|
986
|
+
return `[empty directory: ${redactPath(p, absCwd)}]`;
|
|
960
987
|
return lines.join('\n');
|
|
961
988
|
}
|
|
962
989
|
export async function search_files(ctx, args) {
|
|
@@ -968,6 +995,7 @@ export async function search_files(ctx, args) {
|
|
|
968
995
|
throw new Error('search_files: missing path');
|
|
969
996
|
if (!pattern)
|
|
970
997
|
throw new Error('search_files: missing pattern');
|
|
998
|
+
const absCwd = path.resolve(ctx.cwd);
|
|
971
999
|
// Prefer rg if available (fast, bounded output)
|
|
972
1000
|
if (await hasRg()) {
|
|
973
1001
|
const cmd = ['rg', '-n', '--no-heading', '--color', 'never', pattern, root];
|
|
@@ -978,7 +1006,7 @@ export async function search_files(ctx, args) {
|
|
|
978
1006
|
const parsed = JSON.parse(rawJson);
|
|
979
1007
|
// rg exits 1 when no matches found (not an error), 2+ for real errors.
|
|
980
1008
|
if (parsed.rc === 1 && !parsed.out?.trim()) {
|
|
981
|
-
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}`;
|
|
1009
|
+
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}`;
|
|
982
1010
|
}
|
|
983
1011
|
if (parsed.rc >= 2) {
|
|
984
1012
|
// Real rg error — fall through to regex fallback below
|
|
@@ -989,7 +1017,16 @@ export async function search_files(ctx, args) {
|
|
|
989
1017
|
const lines = rgOutput.split(/\r?\n/).filter(Boolean).slice(0, maxResults);
|
|
990
1018
|
if (lines.length >= maxResults)
|
|
991
1019
|
lines.push(`[truncated after ${maxResults} results]`);
|
|
992
|
-
|
|
1020
|
+
// Redact paths in rg output
|
|
1021
|
+
const redactedLines = lines.map(line => {
|
|
1022
|
+
const colonIdx = line.indexOf(':');
|
|
1023
|
+
if (colonIdx === -1)
|
|
1024
|
+
return line;
|
|
1025
|
+
const filePath = line.substring(0, colonIdx);
|
|
1026
|
+
const rest = line.substring(colonIdx + 1);
|
|
1027
|
+
return redactPath(filePath, absCwd) + ':' + rest;
|
|
1028
|
+
});
|
|
1029
|
+
return redactedLines.join('\n');
|
|
993
1030
|
}
|
|
994
1031
|
}
|
|
995
1032
|
}
|
|
@@ -1003,7 +1040,7 @@ export async function search_files(ctx, args) {
|
|
|
1003
1040
|
re = new RegExp(pattern);
|
|
1004
1041
|
}
|
|
1005
1042
|
catch (e) {
|
|
1006
|
-
throw new ToolError('invalid_args', `search_files: invalid regex pattern: ${e?.message ?? String(e)}`, false, 'Escape regex metacharacters (
|
|
1043
|
+
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.');
|
|
1007
1044
|
}
|
|
1008
1045
|
const out = [];
|
|
1009
1046
|
async function walk(dir, depth) {
|
|
@@ -1042,7 +1079,7 @@ export async function search_files(ctx, args) {
|
|
|
1042
1079
|
const lines = buf.split(/\r?\n/);
|
|
1043
1080
|
for (let i = 0; i < lines.length; i++) {
|
|
1044
1081
|
if (re.test(lines[i])) {
|
|
1045
|
-
out.push(`${full}:${i + 1}:${lines[i]}`);
|
|
1082
|
+
out.push(`${redactPath(full, absCwd)}:${i + 1}:${lines[i]}`);
|
|
1046
1083
|
if (out.length >= maxResults)
|
|
1047
1084
|
return;
|
|
1048
1085
|
}
|
|
@@ -1052,10 +1089,9 @@ export async function search_files(ctx, args) {
|
|
|
1052
1089
|
await walk(root, 0);
|
|
1053
1090
|
if (out.length >= maxResults)
|
|
1054
1091
|
out.push(`[truncated after ${maxResults} results]`);
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
return result;
|
|
1092
|
+
if (!out.length)
|
|
1093
|
+
return `No matches for pattern \"${pattern}\" in ${redactPath(root, absCwd)}.`;
|
|
1094
|
+
return out.join('\n');
|
|
1059
1095
|
}
|
|
1060
1096
|
function stripSimpleQuotedSegments(s) {
|
|
1061
1097
|
// Best-effort quote stripping for lightweight shell pattern checks.
|
|
@@ -1390,7 +1426,7 @@ export async function exec(ctx, args) {
|
|
|
1390
1426
|
}
|
|
1391
1427
|
async function execWithPty(args) {
|
|
1392
1428
|
const { pty, command, cwd, timeout, maxBytes, captureLimit, signal, execCwdWarning } = args;
|
|
1393
|
-
const proc = pty.spawn(BASH_PATH, ['-
|
|
1429
|
+
const proc = pty.spawn(BASH_PATH, ['-c', command], {
|
|
1394
1430
|
name: 'xterm-color',
|
|
1395
1431
|
cwd,
|
|
1396
1432
|
cols: 120,
|
|
@@ -1512,11 +1548,6 @@ export async function vault_search(ctx, args) {
|
|
|
1512
1548
|
export async function sys_context(ctx, args) {
|
|
1513
1549
|
return sysContextTool(ctx, args);
|
|
1514
1550
|
}
|
|
1515
|
-
function resolvePath(ctx, p) {
|
|
1516
|
-
if (typeof p !== 'string' || !p.trim())
|
|
1517
|
-
throw new Error('missing path');
|
|
1518
|
-
return path.resolve(ctx.cwd, p);
|
|
1519
|
-
}
|
|
1520
1551
|
/**
|
|
1521
1552
|
* Check if a target path is within a directory.
|
|
1522
1553
|
* Handles the classic root directory edge case: when dir is `/`, every absolute path is valid.
|
|
@@ -1526,6 +1557,24 @@ function isWithinDir(target, dir) {
|
|
|
1526
1557
|
return target.startsWith('/');
|
|
1527
1558
|
return target === dir || target.startsWith(dir + path.sep);
|
|
1528
1559
|
}
|
|
1560
|
+
function resolvePath(ctx, p) {
|
|
1561
|
+
if (typeof p !== 'string' || !p.trim())
|
|
1562
|
+
throw new Error('missing path');
|
|
1563
|
+
return path.resolve(ctx.cwd, p);
|
|
1564
|
+
}
|
|
1565
|
+
/**
|
|
1566
|
+
* Redact a path for safe output.
|
|
1567
|
+
* - Paths within cwd are shown as relative paths
|
|
1568
|
+
* - Paths outside cwd are redacted as [outside-cwd]/basename
|
|
1569
|
+
*/
|
|
1570
|
+
function redactPath(filePath, absCwd) {
|
|
1571
|
+
const resolved = path.resolve(filePath);
|
|
1572
|
+
if (isWithinDir(resolved, absCwd)) {
|
|
1573
|
+
return path.relative(absCwd, resolved);
|
|
1574
|
+
}
|
|
1575
|
+
const basename = path.basename(resolved);
|
|
1576
|
+
return `[outside-cwd]/${basename}`;
|
|
1577
|
+
}
|
|
1529
1578
|
/**
|
|
1530
1579
|
* Check if a resolved path is outside the working directory.
|
|
1531
1580
|
* Returns a model-visible warning string if so, empty string otherwise.
|
|
@@ -1586,7 +1635,7 @@ async function hasRg() {
|
|
|
1586
1635
|
catch {
|
|
1587
1636
|
// try PATH
|
|
1588
1637
|
return await new Promise((resolve) => {
|
|
1589
|
-
const c = spawn(BASH_PATH, ['-
|
|
1638
|
+
const c = spawn(BASH_PATH, ['-c', 'command -v rg >/dev/null 2>&1'], { stdio: 'ignore' });
|
|
1590
1639
|
c.on('error', () => resolve(false));
|
|
1591
1640
|
c.on('close', (code) => resolve(code === 0));
|
|
1592
1641
|
});
|