clixad 0.0.1-beta.42 → 0.0.1-beta.43

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.
@@ -2319,6 +2319,94 @@ function diffStat(lines) {
2319
2319
  }
2320
2320
  return { added, removed };
2321
2321
  }
2322
+ var MAX_VIEW_ROWS = 2e3;
2323
+ function diffView(path, before, after, opts = {}) {
2324
+ const context = opts.context ?? 3;
2325
+ if (opts.created) {
2326
+ const lines2 = splitLines(after);
2327
+ const rows2 = lines2.slice(0, MAX_VIEW_ROWS).map((text, i) => ({ kind: "add", line: i + 1, text }));
2328
+ if (lines2.length > MAX_VIEW_ROWS) rows2.push({ kind: "gap", count: lines2.length - MAX_VIEW_ROWS });
2329
+ return { kind: "diff", path, created: true, added: after === "" ? 0 : lines2.length, removed: 0, rows: rows2 };
2330
+ }
2331
+ const lines = diffLines(splitLines(before), splitLines(after));
2332
+ const { added, removed } = diffStat(lines);
2333
+ const keep = new Array(lines.length).fill(false);
2334
+ lines.forEach((l, i) => {
2335
+ if (l.kind === "ctx") return;
2336
+ for (let k = Math.max(0, i - context); k <= Math.min(lines.length - 1, i + context); k++) keep[k] = true;
2337
+ });
2338
+ const rows = [];
2339
+ let skipped = 0;
2340
+ for (let i = 0; i < lines.length; i++) {
2341
+ if (!keep[i]) {
2342
+ skipped++;
2343
+ continue;
2344
+ }
2345
+ if (skipped > 0 && rows.length > 0) rows.push({ kind: "gap", count: skipped });
2346
+ skipped = 0;
2347
+ if (rows.length >= MAX_VIEW_ROWS) {
2348
+ rows.push({ kind: "gap", count: lines.length - i });
2349
+ break;
2350
+ }
2351
+ rows.push({ ...lines[i] });
2352
+ }
2353
+ return { kind: "diff", path, created: false, added, removed, rows };
2354
+ }
2355
+ function diffTint(env = process.env) {
2356
+ if (env.NO_COLOR) return "none";
2357
+ if (env.COLORTERM === "truecolor" || env.COLORTERM === "24bit") return "truecolor";
2358
+ if (/256/.test(env.TERM ?? "")) return "256";
2359
+ if (env.TERM === "dumb") return "none";
2360
+ return "basic";
2361
+ }
2362
+ var TINTS = {
2363
+ truecolor: {
2364
+ add: "\x1B[48;2;28;62;38m",
2365
+ del: "\x1B[48;2;78;32;36m",
2366
+ addSign: "\x1B[38;2;126;231;135m",
2367
+ delSign: "\x1B[38;2;255;123;114m",
2368
+ off: R
2369
+ },
2370
+ "256": { add: "\x1B[48;5;22m", del: "\x1B[48;5;52m", addSign: "\x1B[92m", delSign: "\x1B[91m", off: R },
2371
+ basic: { add: GREEN, del: RED, addSign: "", delSign: "", off: R },
2372
+ none: { add: "", del: "", addSign: "", delSign: "", off: "" }
2373
+ };
2374
+ function renderDiffRows(view, width, opts) {
2375
+ const tint = TINTS[opts.tint ?? "basic"];
2376
+ const max = opts.maxRows ?? Number.POSITIVE_INFINITY;
2377
+ const numbers = view.rows.filter((r) => r.kind !== "gap");
2378
+ const gutter = Math.max(3, ...numbers.map((r) => String(r.line).length));
2379
+ const fit = (text) => {
2380
+ const cut2 = opts.measure(text) > width ? opts.cut(text, width) : text;
2381
+ return cut2 + " ".repeat(Math.max(0, width - opts.measure(cut2)));
2382
+ };
2383
+ const rows = [];
2384
+ let more = 0;
2385
+ for (const row of view.rows) {
2386
+ if (rows.length >= max) {
2387
+ if (row.kind !== "gap") more++;
2388
+ continue;
2389
+ }
2390
+ if (row.kind === "gap") {
2391
+ const dim = tint === TINTS.none ? "" : DIM;
2392
+ rows.push(`${dim}${opts.cut(`${" ".repeat(gutter)} \u22EF ${row.count} unchanged line${row.count === 1 ? "" : "s"}`, width)}${tint.off}`);
2393
+ continue;
2394
+ }
2395
+ const text = row.text.replace(/\t/g, " ");
2396
+ const no = String(row.line).padStart(gutter);
2397
+ if (row.kind === "ctx") {
2398
+ rows.push(`${tint === TINTS.none ? "" : DIM}${opts.cut(`${no} ${text}`, width)}${tint.off}`);
2399
+ continue;
2400
+ }
2401
+ const sign = row.kind === "add" ? "+" : "-";
2402
+ const bg2 = row.kind === "add" ? tint.add : tint.del;
2403
+ const signColor = row.kind === "add" ? tint.addSign : tint.delSign;
2404
+ const body = fit(`${no} ${sign} ${text}`);
2405
+ const at = gutter + 1;
2406
+ rows.push(`${bg2}${body.slice(0, at)}${signColor}${sign}${signColor ? "\x1B[39m" : ""}${body.slice(at + 1)}${tint.off}`);
2407
+ }
2408
+ return { rows, more };
2409
+ }
2322
2410
  function renderDiff(oldText, newText, opts = {}) {
2323
2411
  const context = opts.context ?? 3;
2324
2412
  const maxLines = opts.maxLines ?? 40;
@@ -3618,13 +3706,16 @@ ${body2}` + (pdf.text.length > MAX_FILE_BYTES ? "\n\u2026 [truncated]" : "");
3618
3706
  const abs = safeResolve(ctx.root, requireString(args.path, "write_file", "path"));
3619
3707
  const content = requireString(args.content, "write_file", "content");
3620
3708
  const name = rel(ctx.root, abs);
3621
- const before = existsSync2(abs) ? fsCall(`cannot write ${name}`, () => readFileSync3(abs, "utf8")) : "";
3709
+ const existed = existsSync2(abs);
3710
+ const before = existed ? fsCall(`cannot write ${name}`, () => readFileSync3(abs, "utf8")) : "";
3622
3711
  const preview = renderDiff(before, content) || "(no change)";
3712
+ const view = diffView(name, before, content, { created: !existed });
3623
3713
  await gate(ctx, {
3624
3714
  tool: "write_file",
3625
3715
  signature: `write:${name}`,
3626
3716
  summary: `${before ? "overwrite" : "create"} ${name}`,
3627
3717
  preview,
3718
+ view,
3628
3719
  path: name,
3629
3720
  root: ctx.root
3630
3721
  });
@@ -3632,6 +3723,7 @@ ${body2}` + (pdf.text.length > MAX_FILE_BYTES ? "\n\u2026 [truncated]" : "");
3632
3723
  mkdirSync2(dirname2(abs), { recursive: true });
3633
3724
  writeFileSync2(abs, content, "utf8");
3634
3725
  });
3726
+ ctx.present?.(view);
3635
3727
  return `wrote ${content.length} bytes to ${name}`;
3636
3728
  },
3637
3729
  /**
@@ -3661,15 +3753,18 @@ ${body2}` + (pdf.text.length > MAX_FILE_BYTES ? "\n\u2026 [truncated]" : "");
3661
3753
  }
3662
3754
  const after = before.split(oldString).join(newString);
3663
3755
  const preview = renderDiff(before, after) || "(no change)";
3756
+ const view = diffView(name, before, after);
3664
3757
  await gate(ctx, {
3665
3758
  tool: "edit_file",
3666
3759
  signature: `write:${name}`,
3667
3760
  summary: `edit ${name}`,
3668
3761
  preview,
3762
+ view,
3669
3763
  path: name,
3670
3764
  root: ctx.root
3671
3765
  });
3672
3766
  fsCall(`cannot edit ${name}`, () => writeFileSync2(abs, after, "utf8"));
3767
+ ctx.present?.(view);
3673
3768
  return `edited ${name} (${count > 1 ? `${count} occurrences` : "1 occurrence"})`;
3674
3769
  },
3675
3770
  /**
@@ -5316,6 +5411,9 @@ async function runAgent(client, task, opts) {
5316
5411
  mcp: opts.mcp,
5317
5412
  shells: opts.shells,
5318
5413
  attach: (image) => pendingImages.push(image),
5414
+ // A placeholder that says writes are shown at this depth; each call gets
5415
+ // its own (`executeToolCall`), which puts the view on its result event.
5416
+ ...depth === 0 ? { present: () => void 0 } : {},
5319
5417
  // The recursion guard, and the whole of it: a sub-agent runs at depth 1 and
5320
5418
  // is handed no way to start another.
5321
5419
  ...depth === 0 ? { spawn: runSubAgent } : {}
@@ -5511,9 +5609,13 @@ async function executeToolCall(ctx, call, emit) {
5511
5609
  }
5512
5610
  emit({ type: "tool_call", id, name, args });
5513
5611
  const search = ctx.webSearch;
5612
+ let view;
5514
5613
  const scoped = {
5515
5614
  ...ctx,
5516
5615
  onOutput: (chunk) => emit({ type: "tool_output", id, chunk }),
5616
+ // Only where something draws it: a sub-agent's writes are not shown, and
5617
+ // `ctx.present` is how this run says whether anybody is watching.
5618
+ ...ctx.present ? { present: (v) => view = v } : {},
5517
5619
  // Reported against this call the moment it is charged, so the row says
5518
5620
  // what it cost and the balance on screen moves with it.
5519
5621
  ...search ? {
@@ -5533,7 +5635,7 @@ async function executeToolCall(ctx, call, emit) {
5533
5635
  };
5534
5636
  try {
5535
5637
  const result = await dispatch(scoped, name, args);
5536
- emit({ type: "tool_result", id, name, result, ok: true });
5638
+ emit({ type: "tool_result", id, name, result, ok: true, ...view ? { view } : {} });
5537
5639
  return result;
5538
5640
  } catch (err) {
5539
5641
  if (err instanceof ToolError) {
@@ -6457,6 +6559,8 @@ export {
6457
6559
  webSearchCostLine,
6458
6560
  freeHelpLine,
6459
6561
  freeAlternativeLine,
6562
+ diffTint,
6563
+ renderDiffRows,
6460
6564
  shells,
6461
6565
  TodoList,
6462
6566
  loadMcpConfig,
package/dist/clixad.mjs CHANGED
@@ -53,7 +53,7 @@ import {
53
53
  takeConfigNotices,
54
54
  toolSchemaFor,
55
55
  usd
56
- } from "./chunk-ABDMJDYJ.mjs";
56
+ } from "./chunk-T7SUUKJC.mjs";
57
57
 
58
58
  // src/tui/load.ts
59
59
  import * as nodeModule from "node:module";
@@ -132,7 +132,7 @@ async function withProductionReact(load) {
132
132
  }
133
133
  function loadTui() {
134
134
  boundInkCaches();
135
- return withProductionReact(() => import("./tui-377KNURT.mjs"));
135
+ return withProductionReact(() => import("./tui-773NJ4FQ.mjs"));
136
136
  }
137
137
 
138
138
  // src/title.ts