indusagi 0.12.33 → 0.13.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.
Files changed (70) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/dist/agent.js +1247 -184
  3. package/dist/ai.js +72 -4
  4. package/dist/capabilities.js +69 -2
  5. package/dist/cli.js +1353 -29
  6. package/dist/connectors-saas.js +66 -0
  7. package/dist/index.js +1353 -29
  8. package/dist/interop.js +66 -0
  9. package/dist/mcp.js +270 -363
  10. package/dist/react-ink.js +1391 -41
  11. package/dist/shell-app.js +1353 -29
  12. package/dist/smithy.js +69 -2
  13. package/dist/swarm.js +69 -2
  14. package/dist/types/capabilities/backends/node-backends.d.ts +3 -1
  15. package/dist/types/capabilities/files/read-state-gate.d.ts +69 -0
  16. package/dist/types/capabilities/files/read-state-gate.test.d.ts +14 -0
  17. package/dist/types/capabilities/kernel/context.d.ts +4 -0
  18. package/dist/types/capabilities/kernel/index.d.ts +2 -2
  19. package/dist/types/capabilities/kernel/spec.d.ts +55 -0
  20. package/dist/types/facade/bot/actions/bash.d.ts +15 -0
  21. package/dist/types/facade/bot/actions/bash.test.d.ts +1 -0
  22. package/dist/types/facade/bot/actions/checkpoint.d.ts +49 -0
  23. package/dist/types/facade/bot/actions/checkpoint.test.d.ts +1 -0
  24. package/dist/types/facade/bot/actions/edit-utils.d.ts +86 -0
  25. package/dist/types/facade/bot/actions/edit.d.ts +18 -0
  26. package/dist/types/facade/bot/actions/edit.test.d.ts +1 -0
  27. package/dist/types/facade/bot/actions/find.d.ts +2 -0
  28. package/dist/types/facade/bot/actions/find.test.d.ts +1 -0
  29. package/dist/types/facade/bot/actions/grep.d.ts +10 -0
  30. package/dist/types/facade/bot/actions/grep.test.d.ts +1 -0
  31. package/dist/types/facade/bot/actions/index.d.ts +16 -0
  32. package/dist/types/facade/bot/actions/read-state.d.ts +83 -0
  33. package/dist/types/facade/bot/actions/read-state.test.d.ts +1 -0
  34. package/dist/types/facade/bot/actions/read.d.ts +7 -0
  35. package/dist/types/facade/bot/actions/read.test.d.ts +1 -0
  36. package/dist/types/facade/bot/actions/sandbox-backend.d.ts +99 -0
  37. package/dist/types/facade/bot/actions/sandbox-backend.test.d.ts +1 -0
  38. package/dist/types/facade/bot/actions/websearch.d.ts +5 -2
  39. package/dist/types/facade/bot/actions/websearch.test.d.ts +1 -0
  40. package/dist/types/facade/bot/actions/write.d.ts +15 -0
  41. package/dist/types/facade/bot/agent-loop.d.ts +10 -0
  42. package/dist/types/facade/bot/agent-loop.test.d.ts +1 -0
  43. package/dist/types/facade/bot/agent.d.ts +9 -1
  44. package/dist/types/facade/bot/permission-gate.test.d.ts +1 -0
  45. package/dist/types/facade/bot/types.d.ts +60 -0
  46. package/dist/types/facade/mcp-core/client.d.ts +71 -15
  47. package/dist/types/facade/mcp-core/client.test.d.ts +18 -0
  48. package/dist/types/facade/mcp-core/types.d.ts +10 -0
  49. package/dist/types/facade/ml/adapters/anthropic-retry.test.d.ts +1 -0
  50. package/dist/types/facade/ml/adapters/anthropic.d.ts +17 -0
  51. package/dist/types/facade/ml/adapters/simple-options.d.ts +13 -0
  52. package/dist/types/facade/ml/adapters/simple-options.test.d.ts +1 -0
  53. package/dist/types/react-ink/components/StatusLine.d.ts +10 -1
  54. package/dist/types/react-ink/components/ToolEventBlock.d.ts +9 -1
  55. package/dist/types/react-ink/components/ToolEventBlock.test.d.ts +1 -0
  56. package/dist/types/react-ink/components/dialogs/SelectableDialog.d.ts +7 -1
  57. package/dist/types/react-ink/components/dialogs/ThemeDialog.d.ts +21 -2
  58. package/dist/types/react-ink/diff/Diff.d.ts +22 -0
  59. package/dist/types/react-ink/diff/diff.test.d.ts +1 -0
  60. package/dist/types/react-ink/diff/structured.d.ts +41 -0
  61. package/dist/types/react-ink/diff/word-diff.d.ts +27 -0
  62. package/dist/types/react-ink/index.d.ts +8 -0
  63. package/dist/types/react-ink/markdown/Markdown.d.ts +23 -0
  64. package/dist/types/react-ink/markdown/MarkdownTable.d.ts +19 -0
  65. package/dist/types/react-ink/markdown/StreamingMarkdown.d.ts +34 -0
  66. package/dist/types/react-ink/markdown/format-token.d.ts +39 -0
  67. package/dist/types/react-ink/markdown/highlight.d.ts +31 -0
  68. package/dist/types/react-ink/theme-adapter.d.ts +58 -1
  69. package/dist/types/react-ink/utils/tool-display.d.ts +17 -1
  70. package/package.json +5 -1
package/dist/index.js CHANGED
@@ -4512,6 +4512,7 @@ __export(capabilities_exports, {
4512
4512
  });
4513
4513
 
4514
4514
  // src/capabilities/kernel/spec.ts
4515
+ var READ_STATE_HANDLE_KEY = "readState";
4515
4516
  function coerceInput(raw) {
4516
4517
  if (typeof raw === "string") {
4517
4518
  const trimmed = raw.trim();
@@ -4965,7 +4966,7 @@ var standardBudget = {
4965
4966
  [${omitted} bytes elided to stay within the output ceiling]
4966
4967
  `
4967
4968
  };
4968
- function makeNodeContext(cwd, signal, budget) {
4969
+ function makeNodeContext(cwd, signal, budget, framework) {
4969
4970
  if (typeof cwd !== "string" || cwd.length === 0) {
4970
4971
  throw new Error("makeNodeContext requires a non-empty working directory.");
4971
4972
  }
@@ -4974,10 +4975,60 @@ function makeNodeContext(cwd, signal, budget) {
4974
4975
  fs: nodeFs,
4975
4976
  shell: nodeShell,
4976
4977
  signal: signal ?? neverAborts(),
4977
- budget: budget ?? standardBudget
4978
+ budget: budget ?? standardBudget,
4979
+ ...framework ? { framework } : {}
4978
4980
  };
4979
4981
  }
4980
4982
 
4983
+ // src/capabilities/files/read-state-gate.ts
4984
+ var READ_BEFORE_EDIT_MESSAGE = "File has not been read yet. Read it first before writing to it.";
4985
+ var MODIFIED_SINCE_READ_MESSAGE = "File has been modified since read, either by the user or by a linter. Read it again before attempting to write it.";
4986
+ function getReadStateHandle(ctx) {
4987
+ const bag = ctx.framework;
4988
+ if (!bag || typeof bag !== "object") return void 0;
4989
+ const candidate = bag[READ_STATE_HANDLE_KEY];
4990
+ if (!candidate || typeof candidate !== "object") return void 0;
4991
+ const handle = candidate;
4992
+ if (typeof handle.get !== "function" || typeof handle.set !== "function" || typeof handle.has !== "function") {
4993
+ return void 0;
4994
+ }
4995
+ return handle;
4996
+ }
4997
+ async function recordReadState(ctx, absPath, handle) {
4998
+ if (!handle) return;
4999
+ try {
5000
+ const info = await ctx.fs.stat(absPath);
5001
+ const record = {
5002
+ mtimeMs: Math.floor(info.modifiedMs),
5003
+ size: info.size,
5004
+ readAt: Date.now()
5005
+ };
5006
+ handle.set(absPath, record);
5007
+ } catch {
5008
+ }
5009
+ }
5010
+ async function enforceReadGate(ctx, absPath, handle) {
5011
+ if (!handle) return { ok: true };
5012
+ if (!handle.has(absPath)) {
5013
+ return { ok: false, message: READ_BEFORE_EDIT_MESSAGE };
5014
+ }
5015
+ const recorded = handle.get(absPath);
5016
+ if (!recorded) {
5017
+ return { ok: false, message: READ_BEFORE_EDIT_MESSAGE };
5018
+ }
5019
+ let info;
5020
+ try {
5021
+ info = await ctx.fs.stat(absPath);
5022
+ } catch {
5023
+ return { ok: true };
5024
+ }
5025
+ const currentMtime = Math.floor(info.modifiedMs);
5026
+ if (currentMtime > recorded.mtimeMs || info.size !== recorded.size) {
5027
+ return { ok: false, message: MODIFIED_SINCE_READ_MESSAGE };
5028
+ }
5029
+ return { ok: true };
5030
+ }
5031
+
4981
5032
  // src/capabilities/files/read.ts
4982
5033
  var GUTTER_WIDTH = 6;
4983
5034
  var DESCRIPTION = [
@@ -5080,6 +5131,7 @@ var readTool = defineTool({
5080
5131
  const detail = err instanceof Error ? err.message : String(err);
5081
5132
  return failure(`Could not read ${path2}: ${detail}`);
5082
5133
  }
5134
+ await recordReadState(ctx, path2, getReadStateHandle(ctx));
5083
5135
  const allLines = toLines(text);
5084
5136
  const totalLines = allLines.length;
5085
5137
  if (totalLines === 0) {
@@ -5187,11 +5239,19 @@ var writeTool = defineTool({
5187
5239
  async run(input, ctx) {
5188
5240
  const path2 = readPath(input?.path);
5189
5241
  const content = readContent(input?.content);
5242
+ const handle = getReadStateHandle(ctx);
5243
+ if (handle && await ctx.fs.exists(path2)) {
5244
+ const gate = await enforceReadGate(ctx, path2, handle);
5245
+ if (!gate.ok) {
5246
+ return asText(gate.message, true);
5247
+ }
5248
+ }
5190
5249
  const folder = parentDir(path2);
5191
5250
  if (folder.length > 0) {
5192
5251
  await ctx.fs.mkdir(folder, { recursive: true });
5193
5252
  }
5194
5253
  await ctx.fs.writeFile(path2, content, "utf8");
5254
+ await recordReadState(ctx, path2, handle);
5195
5255
  const bytes = Buffer.byteLength(content, "utf8");
5196
5256
  const unit = bytes === 1 ? "byte" : "bytes";
5197
5257
  return asText(`Saved ${bytes} ${unit} to ${path2}.`);
@@ -5481,6 +5541,11 @@ async function runEdit(input, ctx) {
5481
5541
  if (!info.isFile) {
5482
5542
  return failure2(`${path2} is not a regular file, so it cannot be edited.`);
5483
5543
  }
5544
+ const handle = getReadStateHandle(ctx);
5545
+ const gate = await enforceReadGate(ctx, path2, handle);
5546
+ if (!gate.ok) {
5547
+ return failure2(gate.message);
5548
+ }
5484
5549
  const before = await ctx.fs.readFile(path2, "utf8");
5485
5550
  const literalHits = countLiteral(before, oldText);
5486
5551
  if (literalHits > 0) {
@@ -5494,6 +5559,7 @@ async function runEdit(input, ctx) {
5494
5559
  return failure2(`The replacement left ${path2} unchanged.`);
5495
5560
  }
5496
5561
  await ctx.fs.writeFile(path2, after2, "utf8");
5562
+ await recordReadState(ctx, path2, handle);
5497
5563
  return success(path2, before, after2, replaceAll ? literalHits : 1);
5498
5564
  }
5499
5565
  const spans = findFuzzySpans(before, oldText);
@@ -5517,6 +5583,7 @@ async function runEdit(input, ctx) {
5517
5583
  return failure2(`The fuzzy replacement left ${path2} unchanged.`);
5518
5584
  }
5519
5585
  await ctx.fs.writeFile(path2, after, "utf8");
5586
+ await recordReadState(ctx, path2, handle);
5520
5587
  return success(path2, before, after, targets.length);
5521
5588
  }
5522
5589
  var editTool = defineTool({
@@ -12790,6 +12857,34 @@ var useInput = ink.useInput;
12790
12857
  // src/react-ink/theme-adapter.ts
12791
12858
  import chalk from "chalk";
12792
12859
  import stripAnsi from "strip-ansi";
12860
+ var DEFAULT_ROLE_KEYS = {
12861
+ codeInline: "codeInline",
12862
+ heading: "heading",
12863
+ blockquoteBar: "blockquoteBar",
12864
+ diffAddedBg: "diffAddedBg",
12865
+ diffRemovedBg: "diffRemovedBg",
12866
+ diffAddedText: "diffAddedText",
12867
+ diffRemovedText: "diffRemovedText",
12868
+ synKeyword: "synKeyword",
12869
+ synString: "synString",
12870
+ synNumber: "synNumber",
12871
+ synComment: "synComment",
12872
+ synType: "synType"
12873
+ };
12874
+ var ROLE_FALLBACK_KEYS = {
12875
+ codeInline: "accent",
12876
+ heading: "accent",
12877
+ blockquoteBar: "muted",
12878
+ diffAddedBg: "success",
12879
+ diffRemovedBg: "error",
12880
+ diffAddedText: "success",
12881
+ diffRemovedText: "error",
12882
+ synKeyword: "accent",
12883
+ synString: "success",
12884
+ synNumber: "warning",
12885
+ synComment: "muted",
12886
+ synType: "info"
12887
+ };
12793
12888
  function applyForeground(color, text) {
12794
12889
  if (!color) return text;
12795
12890
  try {
@@ -12816,15 +12911,27 @@ function applyBackground(background, foreground, text) {
12816
12911
  return applyForeground(foreground, text);
12817
12912
  }
12818
12913
  }
12819
- function createThemeAdapter(themeName, colors) {
12914
+ function createThemeAdapter(themeName, colors, roleOverrides) {
12820
12915
  const fallback = colors.text ?? "#e5e5e7";
12916
+ const roles = { ...DEFAULT_ROLE_KEYS, ...roleOverrides };
12917
+ const resolveRoleColor = (role) => resolveColorToken(
12918
+ colors[roles[role]],
12919
+ resolveColorToken(colors[ROLE_FALLBACK_KEYS[role]], fallback)
12920
+ );
12821
12921
  return {
12822
12922
  name: themeName,
12823
12923
  colors,
12924
+ roles,
12824
12925
  color: (key, text) => applyForeground(resolveColorToken(colors[key], fallback), text),
12825
12926
  background: (key, text, textKey = "text") => applyBackground(resolveColorToken(colors[key]), resolveColorToken(colors[textKey], fallback), ` ${stripAnsi(text)} `),
12826
12927
  dim: (text) => applyForeground(resolveColorToken(colors.dim, "#666666"), text),
12827
- muted: (text) => applyForeground(resolveColorToken(colors.muted, "#808080"), text)
12928
+ muted: (text) => applyForeground(resolveColorToken(colors.muted, "#808080"), text),
12929
+ role: (role, text) => applyForeground(resolveRoleColor(role), text),
12930
+ roleBackground: (role, text, foregroundRole) => applyBackground(
12931
+ resolveRoleColor(role),
12932
+ foregroundRole ? resolveRoleColor(foregroundRole) : void 0,
12933
+ text
12934
+ )
12828
12935
  };
12829
12936
  }
12830
12937
 
@@ -13209,8 +13316,1120 @@ function parseSkillInvocation(content) {
13209
13316
  };
13210
13317
  }
13211
13318
 
13319
+ // src/react-ink/markdown/format-token.ts
13320
+ import chalk2 from "chalk";
13321
+ import { marked } from "marked";
13322
+ import stripAnsi3 from "strip-ansi";
13323
+
13324
+ // src/ui/utils.ts
13325
+ import { eastAsianWidth } from "get-east-asian-width";
13326
+ var segmenter = new Intl.Segmenter(void 0, { granularity: "grapheme" });
13327
+ var TextWidthCalculator = class {
13328
+ getWidth(text) {
13329
+ return visibleWidth(text);
13330
+ }
13331
+ clearCache() {
13332
+ widthCache.clear();
13333
+ }
13334
+ };
13335
+ var textWidthCalculator = new TextWidthCalculator();
13336
+ function looksLikeEmojiCandidate(segment) {
13337
+ const cp = segment.codePointAt(0);
13338
+ return cp >= 126976 && cp <= 130047 || // pictographs and emoji proper
13339
+ cp >= 8960 && cp <= 9215 || // miscellaneous technical glyphs
13340
+ cp >= 9728 && cp <= 10175 || // assorted symbols and dingbats
13341
+ cp >= 11088 && cp <= 11093 || // a few star/circle characters
13342
+ segment.includes("\uFE0F") || // carries VS16, the emoji-presentation selector
13343
+ segment.length > 2;
13344
+ }
13345
+ function createUnicodeRegex(vPattern, uFallback) {
13346
+ try {
13347
+ return new RegExp(vPattern, "v");
13348
+ } catch {
13349
+ return new RegExp(uFallback, "u");
13350
+ }
13351
+ }
13352
+ var zeroWidthRegex = createUnicodeRegex(
13353
+ "^(?:\\p{Default_Ignorable_Code_Point}|\\p{Control}|\\p{Mark}|\\p{Surrogate})+$",
13354
+ "^(?:\\p{Default_Ignorable_Code_Point}|\\p{Control}|\\p{Mark}|\\p{Surrogate})+$"
13355
+ );
13356
+ var leadingNonPrintingRegex = createUnicodeRegex(
13357
+ "^[\\p{Default_Ignorable_Code_Point}\\p{Control}\\p{Format}\\p{Mark}\\p{Surrogate}]+",
13358
+ "^[\\p{Default_Ignorable_Code_Point}\\p{Control}\\p{Format}\\p{Mark}\\p{Surrogate}]+"
13359
+ );
13360
+ var rgiEmojiRegex = createUnicodeRegex("^\\p{RGI_Emoji}$", "^\\p{Extended_Pictographic}$");
13361
+ var AnsiStripper = {
13362
+ strip(text) {
13363
+ if (!text.includes("\x1B")) {
13364
+ return text;
13365
+ }
13366
+ let cleaned = text;
13367
+ cleaned = cleaned.replace(/\x1b\[[0-9;]*[mGKHJ]/g, "");
13368
+ cleaned = cleaned.replace(/\x1b\]8;;[^\x07]*\x07/g, "");
13369
+ cleaned = cleaned.replace(/\x1b_[^\x07\x1b]*(?:\x07|\x1b\\)/g, "");
13370
+ return cleaned;
13371
+ }
13372
+ };
13373
+ var WIDTH_CACHE_SIZE = 512;
13374
+ var widthCache = /* @__PURE__ */ new Map();
13375
+ function measureClusterWidth(segment) {
13376
+ if (zeroWidthRegex.test(segment)) {
13377
+ return 0;
13378
+ }
13379
+ if (looksLikeEmojiCandidate(segment) && rgiEmojiRegex.test(segment)) {
13380
+ return 2;
13381
+ }
13382
+ const base = segment.replace(leadingNonPrintingRegex, "");
13383
+ const cp = base.codePointAt(0);
13384
+ if (cp === void 0) {
13385
+ return 0;
13386
+ }
13387
+ let width = eastAsianWidth(cp);
13388
+ if (segment.length > 1) {
13389
+ for (const char of segment.slice(1)) {
13390
+ const c = char.codePointAt(0);
13391
+ if (c >= 65280 && c <= 65519) {
13392
+ width += eastAsianWidth(c);
13393
+ }
13394
+ }
13395
+ }
13396
+ return width;
13397
+ }
13398
+ function visibleWidth(str2) {
13399
+ if (str2.length === 0) {
13400
+ return 0;
13401
+ }
13402
+ let isPureAscii = true;
13403
+ for (let i = 0; i < str2.length; i++) {
13404
+ const code = str2.charCodeAt(i);
13405
+ if (code < 32 || code > 126) {
13406
+ isPureAscii = false;
13407
+ break;
13408
+ }
13409
+ }
13410
+ if (isPureAscii) {
13411
+ return str2.length;
13412
+ }
13413
+ const cached = widthCache.get(str2);
13414
+ if (cached !== void 0) {
13415
+ return cached;
13416
+ }
13417
+ let clean = str2;
13418
+ if (str2.includes(" ")) {
13419
+ clean = clean.replace(/\t/g, " ");
13420
+ }
13421
+ clean = AnsiStripper.strip(clean);
13422
+ let width = 0;
13423
+ for (const { segment } of segmenter.segment(clean)) {
13424
+ width += measureClusterWidth(segment);
13425
+ }
13426
+ if (widthCache.size >= WIDTH_CACHE_SIZE) {
13427
+ const firstKey = widthCache.keys().next().value;
13428
+ if (firstKey !== void 0) {
13429
+ widthCache.delete(firstKey);
13430
+ }
13431
+ }
13432
+ widthCache.set(str2, width);
13433
+ return width;
13434
+ }
13435
+ function extractAnsiCode(str2, pos) {
13436
+ if (pos >= str2.length || str2[pos] !== "\x1B") return null;
13437
+ const next = str2[pos + 1];
13438
+ if (next === "[") {
13439
+ let j = pos + 2;
13440
+ while (j < str2.length && !/[mGKHJ]/.test(str2[j])) j++;
13441
+ if (j < str2.length) return { code: str2.substring(pos, j + 1), length: j + 1 - pos };
13442
+ return null;
13443
+ }
13444
+ if (next === "]") {
13445
+ let j = pos + 2;
13446
+ while (j < str2.length) {
13447
+ if (str2[j] === "\x07") return { code: str2.substring(pos, j + 1), length: j + 1 - pos };
13448
+ if (str2[j] === "\x1B" && str2[j + 1] === "\\") return { code: str2.substring(pos, j + 2), length: j + 2 - pos };
13449
+ j++;
13450
+ }
13451
+ return null;
13452
+ }
13453
+ if (next === "_") {
13454
+ let j = pos + 2;
13455
+ while (j < str2.length) {
13456
+ if (str2[j] === "\x07") return { code: str2.substring(pos, j + 1), length: j + 1 - pos };
13457
+ if (str2[j] === "\x1B" && str2[j + 1] === "\\") return { code: str2.substring(pos, j + 2), length: j + 2 - pos };
13458
+ j++;
13459
+ }
13460
+ return null;
13461
+ }
13462
+ return null;
13463
+ }
13464
+ var AnsiStateTracker = class {
13465
+ // Each attribute is kept on its own flag, which lets us clear them one at a time.
13466
+ _bold = false;
13467
+ _dim = false;
13468
+ italic = false;
13469
+ underline = false;
13470
+ blink = false;
13471
+ inverse = false;
13472
+ hidden = false;
13473
+ strikethrough = false;
13474
+ _colors = { fg: null, bg: null };
13475
+ process(ansiCode) {
13476
+ if (!ansiCode.endsWith("m")) {
13477
+ return;
13478
+ }
13479
+ const match = ansiCode.match(/\x1b\[([\d;]*)m/);
13480
+ if (!match) return;
13481
+ const params = match[1];
13482
+ if (params === "" || params === "0") {
13483
+ this.reset();
13484
+ return;
13485
+ }
13486
+ const parts = params.split(";");
13487
+ let i = 0;
13488
+ while (i < parts.length) {
13489
+ const code = Number.parseInt(parts[i] ?? "", 10);
13490
+ if (Number.isNaN(code)) {
13491
+ i++;
13492
+ continue;
13493
+ }
13494
+ const consumed = this.tryConsumeColorCode(parts, i, code);
13495
+ if (consumed > 0) {
13496
+ i += consumed;
13497
+ continue;
13498
+ }
13499
+ this.applyStandardCode(code);
13500
+ i++;
13501
+ }
13502
+ }
13503
+ tryConsumeColorCode(parts, index, code) {
13504
+ if (code !== 38 && code !== 48) {
13505
+ return 0;
13506
+ }
13507
+ if (parts[index + 1] === "5" && parts[index + 2] !== void 0) {
13508
+ const colorCode = `${parts[index]};${parts[index + 1]};${parts[index + 2]}`;
13509
+ if (code === 38) {
13510
+ this._colors.fg = colorCode;
13511
+ } else {
13512
+ this._colors.bg = colorCode;
13513
+ }
13514
+ return 3;
13515
+ }
13516
+ if (parts[index + 1] === "2" && parts[index + 4] !== void 0) {
13517
+ const colorCode = `${parts[index]};${parts[index + 1]};${parts[index + 2]};${parts[index + 3]};${parts[index + 4]}`;
13518
+ if (code === 38) {
13519
+ this._colors.fg = colorCode;
13520
+ } else {
13521
+ this._colors.bg = colorCode;
13522
+ }
13523
+ return 5;
13524
+ }
13525
+ return 0;
13526
+ }
13527
+ applyStandardCode(code) {
13528
+ switch (code) {
13529
+ case 0:
13530
+ this.reset();
13531
+ return;
13532
+ case 1:
13533
+ this._bold = true;
13534
+ return;
13535
+ case 2:
13536
+ this._dim = true;
13537
+ return;
13538
+ case 3:
13539
+ this.italic = true;
13540
+ return;
13541
+ case 4:
13542
+ this.underline = true;
13543
+ return;
13544
+ case 5:
13545
+ this.blink = true;
13546
+ return;
13547
+ case 7:
13548
+ this.inverse = true;
13549
+ return;
13550
+ case 8:
13551
+ this.hidden = true;
13552
+ return;
13553
+ case 9:
13554
+ this.strikethrough = true;
13555
+ return;
13556
+ case 21:
13557
+ this._bold = false;
13558
+ return;
13559
+ case 22:
13560
+ this._bold = false;
13561
+ this._dim = false;
13562
+ return;
13563
+ case 23:
13564
+ this.italic = false;
13565
+ return;
13566
+ case 24:
13567
+ this.underline = false;
13568
+ return;
13569
+ case 25:
13570
+ this.blink = false;
13571
+ return;
13572
+ case 27:
13573
+ this.inverse = false;
13574
+ return;
13575
+ case 28:
13576
+ this.hidden = false;
13577
+ return;
13578
+ case 29:
13579
+ this.strikethrough = false;
13580
+ return;
13581
+ case 39:
13582
+ this._colors.fg = null;
13583
+ return;
13584
+ case 49:
13585
+ this._colors.bg = null;
13586
+ return;
13587
+ default:
13588
+ if (code >= 30 && code <= 37 || code >= 90 && code <= 97) {
13589
+ this._colors.fg = String(code);
13590
+ return;
13591
+ }
13592
+ if (code >= 40 && code <= 47 || code >= 100 && code <= 107) {
13593
+ this._colors.bg = String(code);
13594
+ }
13595
+ }
13596
+ }
13597
+ reset() {
13598
+ this._bold = false;
13599
+ this._dim = false;
13600
+ this.italic = false;
13601
+ this.underline = false;
13602
+ this.blink = false;
13603
+ this.inverse = false;
13604
+ this.hidden = false;
13605
+ this.strikethrough = false;
13606
+ this._colors.fg = null;
13607
+ this._colors.bg = null;
13608
+ }
13609
+ /** Wipe all tracked state so the instance can be reused. */
13610
+ clear() {
13611
+ this.reset();
13612
+ }
13613
+ getActiveCodes() {
13614
+ const codes = [];
13615
+ if (this._bold) codes.push("1");
13616
+ if (this._dim) codes.push("2");
13617
+ if (this.italic) codes.push("3");
13618
+ if (this.underline) codes.push("4");
13619
+ if (this.blink) codes.push("5");
13620
+ if (this.inverse) codes.push("7");
13621
+ if (this.hidden) codes.push("8");
13622
+ if (this.strikethrough) codes.push("9");
13623
+ if (this._colors.fg) codes.push(this._colors.fg);
13624
+ if (this._colors.bg) codes.push(this._colors.bg);
13625
+ if (codes.length === 0) return "";
13626
+ return `\x1B[${codes.join(";")}m`;
13627
+ }
13628
+ hasActiveCodes() {
13629
+ return this._bold || this._dim || this.italic || this.underline || this.blink || this.inverse || this.hidden || this.strikethrough || this._colors.fg !== null || this._colors.bg !== null;
13630
+ }
13631
+ /**
13632
+ * Produce the escape code needed to switch off any attribute that would
13633
+ * otherwise smear into the padding at the end of a line. In practice that
13634
+ * is just underline. Yields an empty string when nothing needs disabling.
13635
+ */
13636
+ getLineEndReset() {
13637
+ if (this.underline) {
13638
+ return "\x1B[24m";
13639
+ }
13640
+ return "";
13641
+ }
13642
+ };
13643
+ function mergeTextIntoTracker(text, tracker) {
13644
+ let i = 0;
13645
+ while (i < text.length) {
13646
+ const ansiResult = extractAnsiCode(text, i);
13647
+ if (ansiResult) {
13648
+ tracker.process(ansiResult.code);
13649
+ i += ansiResult.length;
13650
+ } else {
13651
+ i++;
13652
+ }
13653
+ }
13654
+ }
13655
+ function tokenizeTextWithAnsi(text) {
13656
+ const tokens = [];
13657
+ let current = "";
13658
+ let pendingAnsi = "";
13659
+ let inWhitespace = false;
13660
+ let i = 0;
13661
+ while (i < text.length) {
13662
+ const ansiResult = extractAnsiCode(text, i);
13663
+ if (ansiResult) {
13664
+ pendingAnsi += ansiResult.code;
13665
+ i += ansiResult.length;
13666
+ continue;
13667
+ }
13668
+ const char = text[i];
13669
+ const charIsSpace = char === " ";
13670
+ if (charIsSpace !== inWhitespace && current) {
13671
+ tokens.push(current);
13672
+ current = "";
13673
+ }
13674
+ if (pendingAnsi) {
13675
+ current += pendingAnsi;
13676
+ pendingAnsi = "";
13677
+ }
13678
+ inWhitespace = charIsSpace;
13679
+ current += char;
13680
+ i++;
13681
+ }
13682
+ if (pendingAnsi) {
13683
+ current += pendingAnsi;
13684
+ }
13685
+ if (current) {
13686
+ tokens.push(current);
13687
+ }
13688
+ return tokens;
13689
+ }
13690
+ var TextWrapper = class {
13691
+ wrap(text, width) {
13692
+ return wrapTextWithAnsi(text, width);
13693
+ }
13694
+ };
13695
+ var textWrapper = new TextWrapper();
13696
+ var TokenWrapEngine = {
13697
+ wrap(tokens, width, tracker) {
13698
+ const wrapped = [];
13699
+ let currentLine = "";
13700
+ let currentVisibleLength = 0;
13701
+ for (const token of tokens) {
13702
+ const tokenVisibleLength = visibleWidth(token);
13703
+ const isWhitespace = token.trim() === "";
13704
+ if (tokenVisibleLength > width && !isWhitespace) {
13705
+ if (currentLine) {
13706
+ const lineEndReset = tracker.getLineEndReset();
13707
+ if (lineEndReset) {
13708
+ currentLine += lineEndReset;
13709
+ }
13710
+ wrapped.push(currentLine);
13711
+ currentLine = "";
13712
+ currentVisibleLength = 0;
13713
+ }
13714
+ const broken = splitLongToken(token, width, tracker);
13715
+ wrapped.push(...broken.slice(0, -1));
13716
+ currentLine = broken[broken.length - 1];
13717
+ currentVisibleLength = visibleWidth(currentLine);
13718
+ continue;
13719
+ }
13720
+ const totalNeeded = currentVisibleLength + tokenVisibleLength;
13721
+ if (totalNeeded > width && currentVisibleLength > 0) {
13722
+ let lineToWrap = currentLine.trimEnd();
13723
+ const lineEndReset = tracker.getLineEndReset();
13724
+ if (lineEndReset) {
13725
+ lineToWrap += lineEndReset;
13726
+ }
13727
+ wrapped.push(lineToWrap);
13728
+ if (isWhitespace) {
13729
+ currentLine = tracker.getActiveCodes();
13730
+ currentVisibleLength = 0;
13731
+ } else {
13732
+ currentLine = tracker.getActiveCodes() + token;
13733
+ currentVisibleLength = tokenVisibleLength;
13734
+ }
13735
+ } else {
13736
+ currentLine += token;
13737
+ currentVisibleLength += tokenVisibleLength;
13738
+ }
13739
+ mergeTextIntoTracker(token, tracker);
13740
+ }
13741
+ return { wrapped, currentLine, currentVisibleLength };
13742
+ }
13743
+ };
13744
+ function wrapTextWithAnsi(text, width) {
13745
+ if (!text) {
13746
+ return [""];
13747
+ }
13748
+ const inputLines = text.split("\n");
13749
+ const result = [];
13750
+ const tracker = new AnsiStateTracker();
13751
+ for (const inputLine of inputLines) {
13752
+ const prefix = result.length > 0 ? tracker.getActiveCodes() : "";
13753
+ result.push(...wrapLinePreservingAnsi(prefix + inputLine, width));
13754
+ mergeTextIntoTracker(inputLine, tracker);
13755
+ }
13756
+ return result.length > 0 ? result : [""];
13757
+ }
13758
+ function wrapLinePreservingAnsi(line4, width) {
13759
+ if (!line4) {
13760
+ return [""];
13761
+ }
13762
+ const visibleLength = visibleWidth(line4);
13763
+ if (visibleLength <= width) {
13764
+ return [line4];
13765
+ }
13766
+ const tracker = new AnsiStateTracker();
13767
+ const tokens = tokenizeTextWithAnsi(line4);
13768
+ const { wrapped, currentLine } = TokenWrapEngine.wrap(tokens, width, tracker);
13769
+ if (currentLine) {
13770
+ wrapped.push(currentLine);
13771
+ }
13772
+ return wrapped.length > 0 ? wrapped.map((segmentLine) => segmentLine.trimEnd()) : [""];
13773
+ }
13774
+ function splitLongToken(word, width, tracker) {
13775
+ const lines = [];
13776
+ let currentLine = tracker.getActiveCodes();
13777
+ let currentWidth = 0;
13778
+ let i = 0;
13779
+ const segments = [];
13780
+ while (i < word.length) {
13781
+ const ansiResult = extractAnsiCode(word, i);
13782
+ if (ansiResult) {
13783
+ segments.push({ type: "ansi", value: ansiResult.code });
13784
+ i += ansiResult.length;
13785
+ } else {
13786
+ let end = i;
13787
+ while (end < word.length) {
13788
+ const nextAnsi = extractAnsiCode(word, end);
13789
+ if (nextAnsi) break;
13790
+ end++;
13791
+ }
13792
+ const textPortion = word.slice(i, end);
13793
+ for (const seg of segmenter.segment(textPortion)) {
13794
+ segments.push({ type: "grapheme", value: seg.segment });
13795
+ }
13796
+ i = end;
13797
+ }
13798
+ }
13799
+ for (const seg of segments) {
13800
+ if (seg.type === "ansi") {
13801
+ currentLine += seg.value;
13802
+ tracker.process(seg.value);
13803
+ continue;
13804
+ }
13805
+ const grapheme = seg.value;
13806
+ if (!grapheme) continue;
13807
+ const clusterWidth = visibleWidth(grapheme);
13808
+ if (currentWidth + clusterWidth > width) {
13809
+ const lineEndReset = tracker.getLineEndReset();
13810
+ if (lineEndReset) {
13811
+ currentLine += lineEndReset;
13812
+ }
13813
+ lines.push(currentLine);
13814
+ currentLine = tracker.getActiveCodes();
13815
+ currentWidth = 0;
13816
+ }
13817
+ currentLine += grapheme;
13818
+ currentWidth += clusterWidth;
13819
+ }
13820
+ if (currentLine) {
13821
+ lines.push(currentLine);
13822
+ }
13823
+ return lines.length > 0 ? lines : [""];
13824
+ }
13825
+ var pooledStyleTracker = new AnsiStateTracker();
13826
+
13827
+ // src/react-ink/markdown/format-token.ts
13828
+ var EOL = "\n";
13829
+ var BLOCKQUOTE_BAR = "\u2502";
13830
+ var markedConfigured = false;
13831
+ function configureMarked() {
13832
+ if (markedConfigured) {
13833
+ return;
13834
+ }
13835
+ markedConfigured = true;
13836
+ marked.use({
13837
+ tokenizer: {
13838
+ del() {
13839
+ return void 0;
13840
+ }
13841
+ }
13842
+ });
13843
+ }
13844
+ var TOKEN_CACHE_MAX = 500;
13845
+ var tokenCache = /* @__PURE__ */ new Map();
13846
+ var MD_SYNTAX_RE = /[#*`|[>\-_~]|\n\n|^\d+\. |\n\d+\. /;
13847
+ function hasMarkdownSyntax(text) {
13848
+ return MD_SYNTAX_RE.test(text.length > 500 ? text.slice(0, 500) : text);
13849
+ }
13850
+ function hashContent(content) {
13851
+ let hash = 2166136261;
13852
+ for (let i = 0; i < content.length; i++) {
13853
+ hash ^= content.charCodeAt(i);
13854
+ hash = Math.imul(hash, 16777619);
13855
+ }
13856
+ return (hash >>> 0).toString(36) + ":" + content.length.toString(36);
13857
+ }
13858
+ function cachedLexer(content) {
13859
+ configureMarked();
13860
+ if (!hasMarkdownSyntax(content)) {
13861
+ return [
13862
+ {
13863
+ type: "paragraph",
13864
+ raw: content,
13865
+ text: content,
13866
+ tokens: [{ type: "text", raw: content, text: content }]
13867
+ }
13868
+ ];
13869
+ }
13870
+ const key = hashContent(content);
13871
+ const hit = tokenCache.get(key);
13872
+ if (hit) {
13873
+ tokenCache.delete(key);
13874
+ tokenCache.set(key, hit);
13875
+ return hit;
13876
+ }
13877
+ const tokens = marked.lexer(content);
13878
+ if (tokenCache.size >= TOKEN_CACHE_MAX) {
13879
+ const first = tokenCache.keys().next().value;
13880
+ if (first !== void 0) {
13881
+ tokenCache.delete(first);
13882
+ }
13883
+ }
13884
+ tokenCache.set(key, tokens);
13885
+ return tokens;
13886
+ }
13887
+ function formatToken(token, theme, highlight = null, listDepth = 0, orderedListNumber = null, parent = null) {
13888
+ switch (token.type) {
13889
+ case "blockquote": {
13890
+ const inner = (token.tokens ?? []).map((child) => formatToken(child, theme, highlight)).join("");
13891
+ const bar = theme.dim(BLOCKQUOTE_BAR);
13892
+ return inner.split(EOL).map((line4) => stripAnsi3(line4).trim() ? `${bar} ${chalk2.italic(line4)}` : line4).join(EOL);
13893
+ }
13894
+ case "code": {
13895
+ const codeToken = token;
13896
+ if (!highlight) {
13897
+ return codeToken.text + EOL;
13898
+ }
13899
+ let language = "plaintext";
13900
+ if (codeToken.lang && highlight.supportsLanguage(codeToken.lang)) {
13901
+ language = codeToken.lang;
13902
+ }
13903
+ return highlight.highlight(codeToken.text, { language }) + EOL;
13904
+ }
13905
+ case "codespan":
13906
+ return theme.role("codeInline", token.text);
13907
+ case "em":
13908
+ return chalk2.italic(
13909
+ (token.tokens ?? []).map((child) => formatToken(child, theme, highlight, 0, null, parent)).join("")
13910
+ );
13911
+ case "strong":
13912
+ return chalk2.bold(
13913
+ (token.tokens ?? []).map((child) => formatToken(child, theme, highlight, 0, null, parent)).join("")
13914
+ );
13915
+ case "heading": {
13916
+ const headingToken = token;
13917
+ const inner = (headingToken.tokens ?? []).map((child) => formatToken(child, theme, highlight)).join("");
13918
+ const colored = theme.role("heading", inner);
13919
+ if (headingToken.depth === 1) {
13920
+ return chalk2.bold.italic.underline(colored) + EOL + EOL;
13921
+ }
13922
+ return chalk2.bold(colored) + EOL + EOL;
13923
+ }
13924
+ case "hr":
13925
+ return "---";
13926
+ case "image":
13927
+ return token.href;
13928
+ case "link": {
13929
+ const linkToken = token;
13930
+ if (linkToken.href.startsWith("mailto:")) {
13931
+ return linkToken.href.replace(/^mailto:/, "");
13932
+ }
13933
+ const linkText = (linkToken.tokens ?? []).map((child) => formatToken(child, theme, highlight, 0, null, linkToken)).join("");
13934
+ const plainLinkText = stripAnsi3(linkText);
13935
+ const display = plainLinkText && plainLinkText !== linkToken.href ? linkText : linkToken.href;
13936
+ return `\x1B]8;;${linkToken.href}\x07${display}\x1B]8;;\x07`;
13937
+ }
13938
+ case "list": {
13939
+ const listToken = token;
13940
+ const start = typeof listToken.start === "number" ? listToken.start : Number(listToken.start) || 1;
13941
+ return listToken.items.map(
13942
+ (item, index) => formatToken(
13943
+ item,
13944
+ theme,
13945
+ highlight,
13946
+ listDepth,
13947
+ listToken.ordered ? start + index : null,
13948
+ listToken
13949
+ )
13950
+ ).join("");
13951
+ }
13952
+ case "list_item":
13953
+ return (token.tokens ?? []).map(
13954
+ (child) => `${" ".repeat(listDepth)}${formatToken(child, theme, highlight, listDepth + 1, orderedListNumber, token)}`
13955
+ ).join("");
13956
+ case "paragraph":
13957
+ return (token.tokens ?? []).map((child) => formatToken(child, theme, highlight)).join("") + EOL;
13958
+ case "space":
13959
+ case "br":
13960
+ return EOL;
13961
+ case "text": {
13962
+ const textToken = token;
13963
+ if (parent?.type === "link") {
13964
+ return textToken.text;
13965
+ }
13966
+ if (parent?.type === "list_item") {
13967
+ const marker = orderedListNumber === null ? "-" : `${getListNumber(listDepth, orderedListNumber)}.`;
13968
+ const body = textToken.tokens ? textToken.tokens.map((child) => formatToken(child, theme, highlight, listDepth, orderedListNumber, token)).join("") : textToken.text;
13969
+ return `${marker} ${body}${EOL}`;
13970
+ }
13971
+ return textToken.text;
13972
+ }
13973
+ case "escape":
13974
+ return token.text;
13975
+ case "table":
13976
+ case "def":
13977
+ case "del":
13978
+ case "html":
13979
+ return "";
13980
+ default:
13981
+ return "";
13982
+ }
13983
+ }
13984
+ function numberToLetter(n) {
13985
+ let result = "";
13986
+ while (n > 0) {
13987
+ n--;
13988
+ result = String.fromCharCode(97 + n % 26) + result;
13989
+ n = Math.floor(n / 26);
13990
+ }
13991
+ return result;
13992
+ }
13993
+ var ROMAN_VALUES = [
13994
+ [1e3, "m"],
13995
+ [900, "cm"],
13996
+ [500, "d"],
13997
+ [400, "cd"],
13998
+ [100, "c"],
13999
+ [90, "xc"],
14000
+ [50, "l"],
14001
+ [40, "xl"],
14002
+ [10, "x"],
14003
+ [9, "ix"],
14004
+ [5, "v"],
14005
+ [4, "iv"],
14006
+ [1, "i"]
14007
+ ];
14008
+ function numberToRoman(n) {
14009
+ let result = "";
14010
+ for (const [value, numeral] of ROMAN_VALUES) {
14011
+ while (n >= value) {
14012
+ result += numeral;
14013
+ n -= value;
14014
+ }
14015
+ }
14016
+ return result;
14017
+ }
14018
+ function getListNumber(listDepth, orderedListNumber) {
14019
+ switch (listDepth) {
14020
+ case 0:
14021
+ case 1:
14022
+ return orderedListNumber.toString();
14023
+ case 2:
14024
+ return numberToLetter(orderedListNumber);
14025
+ case 3:
14026
+ return numberToRoman(orderedListNumber);
14027
+ default:
14028
+ return orderedListNumber.toString();
14029
+ }
14030
+ }
14031
+ function padAligned(content, displayWidth, targetWidth, align) {
14032
+ const padding = Math.max(0, targetWidth - displayWidth);
14033
+ if (align === "center") {
14034
+ const leftPad = Math.floor(padding / 2);
14035
+ return " ".repeat(leftPad) + content + " ".repeat(padding - leftPad);
14036
+ }
14037
+ if (align === "right") {
14038
+ return " ".repeat(padding) + content;
14039
+ }
14040
+ return content + " ".repeat(padding);
14041
+ }
14042
+ function stringWidth(text) {
14043
+ return visibleWidth(text);
14044
+ }
14045
+
14046
+ // src/react-ink/markdown/highlight.ts
14047
+ import { extname } from "node:path";
14048
+ import hljs from "highlight.js";
14049
+ function scopeToRole(scope) {
14050
+ const head = scope.split(".")[0] ?? scope;
14051
+ switch (head) {
14052
+ case "keyword":
14053
+ case "built_in":
14054
+ case "literal":
14055
+ case "operator":
14056
+ return "synKeyword";
14057
+ case "string":
14058
+ case "regexp":
14059
+ case "symbol":
14060
+ case "char":
14061
+ case "meta":
14062
+ return "synString";
14063
+ case "number":
14064
+ return "synNumber";
14065
+ case "comment":
14066
+ case "quote":
14067
+ return "synComment";
14068
+ case "type":
14069
+ case "class":
14070
+ case "title":
14071
+ case "tag":
14072
+ case "name":
14073
+ case "attr":
14074
+ case "attribute":
14075
+ case "selector":
14076
+ return "synType";
14077
+ default:
14078
+ return null;
14079
+ }
14080
+ }
14081
+ var HTML_ENTITIES = {
14082
+ "&amp;": "&",
14083
+ "&lt;": "<",
14084
+ "&gt;": ">",
14085
+ "&quot;": '"',
14086
+ "&#x27;": "'",
14087
+ "&#39;": "'"
14088
+ };
14089
+ function decodeEntities2(text) {
14090
+ return text.replace(/&(?:amp|lt|gt|quot|#x27|#39);/g, (match) => HTML_ENTITIES[match] ?? match);
14091
+ }
14092
+ function parseHljsHtml(html) {
14093
+ const nodes = [];
14094
+ const scopeStack = [];
14095
+ const tagRe = /<span class="hljs-([^"]+)">|<\/span>/g;
14096
+ let lastIndex = 0;
14097
+ let match;
14098
+ const pushText = (raw) => {
14099
+ if (!raw) return;
14100
+ const currentScope = scopeStack.length > 0 ? scopeStack[scopeStack.length - 1] : null;
14101
+ const scope = currentScope ? currentScope.split(/\s+/)[0].replace(/_$/, "") : null;
14102
+ nodes.push({ text: decodeEntities2(raw), scope });
14103
+ };
14104
+ while ((match = tagRe.exec(html)) !== null) {
14105
+ pushText(html.slice(lastIndex, match.index));
14106
+ lastIndex = tagRe.lastIndex;
14107
+ if (match[0] === "</span>") {
14108
+ scopeStack.pop();
14109
+ } else if (match[1]) {
14110
+ scopeStack.push(match[1]);
14111
+ }
14112
+ }
14113
+ pushText(html.slice(lastIndex));
14114
+ return nodes;
14115
+ }
14116
+ function createHighlighter(theme) {
14117
+ return {
14118
+ supportsLanguage: (language) => {
14119
+ if (!language || language === "plaintext" || language === "text") {
14120
+ return false;
14121
+ }
14122
+ try {
14123
+ return hljs.getLanguage(language) !== void 0;
14124
+ } catch {
14125
+ return false;
14126
+ }
14127
+ },
14128
+ highlight: (code, options) => {
14129
+ const language = options.language;
14130
+ if (!language || language === "plaintext" || language === "text") {
14131
+ return code;
14132
+ }
14133
+ try {
14134
+ if (hljs.getLanguage(language) === void 0) {
14135
+ return code;
14136
+ }
14137
+ const { value } = hljs.highlight(code, { language, ignoreIllegals: true });
14138
+ return parseHljsHtml(value).map((node) => {
14139
+ const role = node.scope ? scopeToRole(node.scope) : null;
14140
+ return role ? theme.role(role, node.text) : node.text;
14141
+ }).join("");
14142
+ } catch {
14143
+ return code;
14144
+ }
14145
+ }
14146
+ };
14147
+ }
14148
+ function highlightByPath(code, filePath, theme) {
14149
+ const language = languageFromPath(filePath);
14150
+ if (!language) {
14151
+ return code;
14152
+ }
14153
+ const highlighter = createHighlighter(theme);
14154
+ if (!highlighter.supportsLanguage(language)) {
14155
+ return code;
14156
+ }
14157
+ return highlighter.highlight(code, { language });
14158
+ }
14159
+ var EXTENSION_LANGUAGES = {
14160
+ ts: "typescript",
14161
+ tsx: "typescript",
14162
+ mts: "typescript",
14163
+ cts: "typescript",
14164
+ js: "javascript",
14165
+ jsx: "javascript",
14166
+ mjs: "javascript",
14167
+ cjs: "javascript",
14168
+ py: "python",
14169
+ rb: "ruby",
14170
+ rs: "rust",
14171
+ go: "go",
14172
+ java: "java",
14173
+ kt: "kotlin",
14174
+ c: "c",
14175
+ h: "c",
14176
+ cc: "cpp",
14177
+ cpp: "cpp",
14178
+ hpp: "cpp",
14179
+ cs: "csharp",
14180
+ sh: "bash",
14181
+ bash: "bash",
14182
+ zsh: "bash",
14183
+ yml: "yaml",
14184
+ yaml: "yaml",
14185
+ json: "json",
14186
+ md: "markdown",
14187
+ html: "xml",
14188
+ xml: "xml",
14189
+ css: "css",
14190
+ scss: "scss",
14191
+ sql: "sql",
14192
+ toml: "ini",
14193
+ ini: "ini",
14194
+ php: "php",
14195
+ swift: "swift"
14196
+ };
14197
+ function languageFromPath(filePath) {
14198
+ const ext = extname(filePath).slice(1).toLowerCase();
14199
+ if (!ext) {
14200
+ return null;
14201
+ }
14202
+ const mapped = EXTENSION_LANGUAGES[ext];
14203
+ if (mapped) {
14204
+ return mapped;
14205
+ }
14206
+ try {
14207
+ return hljs.getLanguage(ext) !== void 0 ? ext : null;
14208
+ } catch {
14209
+ return null;
14210
+ }
14211
+ }
14212
+
14213
+ // src/react-ink/markdown/MarkdownTable.tsx
14214
+ import chalk3 from "chalk";
14215
+ import stripAnsi4 from "strip-ansi";
14216
+ var MIN_COLUMN_WIDTH = 3;
14217
+ var COLUMN_GAP = 2;
14218
+ function MarkdownTable({ token, theme, highlight = null }) {
14219
+ const formatCell = (tokens) => (tokens ?? []).map((child) => formatToken(child, theme, highlight)).join("");
14220
+ const displayWidth = (tokens) => stringWidth(stripAnsi4(formatCell(tokens)));
14221
+ const columnCount = token.header.length;
14222
+ const columnWidths = token.header.map((header, index) => {
14223
+ let max = displayWidth(header.tokens);
14224
+ for (const row of token.rows) {
14225
+ max = Math.max(max, displayWidth(row[index]?.tokens));
14226
+ }
14227
+ return Math.max(max, MIN_COLUMN_WIDTH);
14228
+ });
14229
+ const renderRow2 = (cells, key, bold) => /* @__PURE__ */ jsx(Box, { flexDirection: "row", children: columnWidths.map((width, index) => {
14230
+ const cell = cells[index];
14231
+ const content = formatCell(cell?.tokens);
14232
+ const visible = stringWidth(stripAnsi4(content));
14233
+ const align = token.align?.[index];
14234
+ const padded = padAligned(content, visible, width, align ?? "left");
14235
+ const styled = bold ? chalk3.bold(padded) : padded;
14236
+ const gap = index < columnCount - 1 ? " ".repeat(COLUMN_GAP) : "";
14237
+ return /* @__PURE__ */ jsxs(Text, { children: [
14238
+ styled,
14239
+ gap
14240
+ ] }, index);
14241
+ }) }, key);
14242
+ const separator = /* @__PURE__ */ jsx(Box, { flexDirection: "row", children: columnWidths.map((width, index) => {
14243
+ const gap = index < columnCount - 1 ? " ".repeat(COLUMN_GAP) : "";
14244
+ return /* @__PURE__ */ jsxs(Text, { children: [
14245
+ theme.dim("-".repeat(width)),
14246
+ gap
14247
+ ] }, index);
14248
+ }) });
14249
+ return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
14250
+ renderRow2(token.header, "header", true),
14251
+ separator,
14252
+ token.rows.map((row, rowIndex) => renderRow2(row, `row-${rowIndex}`, false))
14253
+ ] });
14254
+ }
14255
+
14256
+ // src/react-ink/markdown/Markdown.tsx
14257
+ function stripPromptXMLTags(text) {
14258
+ return text.replace(/<\/?(?:system-reminder|prompt|context)[^>]*>/g, "");
14259
+ }
14260
+ function Markdown({ children, theme, highlightCode = true, dim = false }) {
14261
+ const highlight = useMemo(
14262
+ () => highlightCode ? createHighlighter(theme) : null,
14263
+ [highlightCode, theme]
14264
+ );
14265
+ const elements = useMemo(() => {
14266
+ configureMarked();
14267
+ const tokens = cachedLexer(stripPromptXMLTags(children));
14268
+ const out = [];
14269
+ let buffer = "";
14270
+ const flush = () => {
14271
+ if (buffer) {
14272
+ const text = buffer.replace(/\n+$/, "");
14273
+ if (text) {
14274
+ out.push(
14275
+ /* @__PURE__ */ jsx(Text, { children: dim ? theme.dim(text) : text }, out.length)
14276
+ );
14277
+ }
14278
+ buffer = "";
14279
+ }
14280
+ };
14281
+ for (const token of tokens) {
14282
+ if (token.type === "table") {
14283
+ flush();
14284
+ out.push(
14285
+ /* @__PURE__ */ jsx(
14286
+ MarkdownTable,
14287
+ {
14288
+ token,
14289
+ theme,
14290
+ highlight
14291
+ },
14292
+ out.length
14293
+ )
14294
+ );
14295
+ } else {
14296
+ buffer += formatToken(token, theme, highlight);
14297
+ }
14298
+ }
14299
+ flush();
14300
+ return out;
14301
+ }, [children, dim, highlight, theme]);
14302
+ return /* @__PURE__ */ jsx(Box, { flexDirection: "column", children: elements });
14303
+ }
14304
+
13212
14305
  // src/react-ink/utils/tool-display.ts
13213
14306
  import { homedir as homedir2 } from "node:os";
14307
+
14308
+ // src/react-ink/diff/structured.ts
14309
+ import { structuredPatch } from "diff";
14310
+
14311
+ // src/react-ink/diff/word-diff.ts
14312
+ import { diffWordsWithSpace } from "diff";
14313
+ var CHANGE_THRESHOLD = 0.4;
14314
+ function wordDiffLine(oldLine, newLine, side) {
14315
+ const lineText = side === "removed" ? oldLine : newLine;
14316
+ if (oldLine.length === 0 || newLine.length === 0) {
14317
+ return [{ text: lineText, changed: true }];
14318
+ }
14319
+ let changes;
14320
+ try {
14321
+ changes = diffWordsWithSpace(oldLine, newLine);
14322
+ } catch {
14323
+ return [{ text: lineText, changed: true }];
14324
+ }
14325
+ let changedChars = 0;
14326
+ let totalChars = 0;
14327
+ for (const change of changes) {
14328
+ totalChars += change.value.length;
14329
+ if (change.added || change.removed) {
14330
+ changedChars += change.value.length;
14331
+ }
14332
+ }
14333
+ const fraction = totalChars === 0 ? 0 : changedChars / totalChars;
14334
+ if (fraction > CHANGE_THRESHOLD) {
14335
+ return [{ text: lineText, changed: true }];
14336
+ }
14337
+ const spans = [];
14338
+ for (const change of changes) {
14339
+ const belongs = side === "removed" ? !change.added : !change.removed;
14340
+ if (!belongs) {
14341
+ continue;
14342
+ }
14343
+ spans.push({ text: change.value, changed: Boolean(change.added || change.removed) });
14344
+ }
14345
+ return spans.length > 0 ? spans : [{ text: lineText, changed: false }];
14346
+ }
14347
+
14348
+ // src/react-ink/diff/structured.ts
14349
+ var CONTEXT_LINES = 3;
14350
+ function buildStructuredDiff(oldStr, newStr, filePath = "") {
14351
+ if (oldStr === newStr) {
14352
+ return null;
14353
+ }
14354
+ let patch;
14355
+ try {
14356
+ patch = structuredPatch(filePath, filePath, oldStr, newStr, "", "", { context: CONTEXT_LINES });
14357
+ } catch {
14358
+ return null;
14359
+ }
14360
+ const hunks = [];
14361
+ let addedCount = 0;
14362
+ let removedCount = 0;
14363
+ for (const hunk of patch.hunks) {
14364
+ const lines = classifyHunkLines(hunk);
14365
+ for (const line4 of lines) {
14366
+ if (line4.kind === "added") addedCount += 1;
14367
+ else if (line4.kind === "removed") removedCount += 1;
14368
+ }
14369
+ if (lines.length > 0) {
14370
+ hunks.push({ oldStart: hunk.oldStart, newStart: hunk.newStart, lines });
14371
+ }
14372
+ }
14373
+ if (hunks.length === 0) {
14374
+ return null;
14375
+ }
14376
+ return { hunks, addedCount, removedCount };
14377
+ }
14378
+ function classifyHunkLines(hunk) {
14379
+ const out = [];
14380
+ let oldNum = hunk.oldStart;
14381
+ let newNum = hunk.newStart;
14382
+ const rawLines = hunk.lines.filter((line4) => !line4.startsWith("\\"));
14383
+ let removedRun = [];
14384
+ let removedRunStart = -1;
14385
+ const flushPairing = (addedRun2) => {
14386
+ const pairs = Math.min(removedRun.length, addedRun2.length);
14387
+ for (let i = 0; i < pairs; i += 1) {
14388
+ const removed = removedRun[i];
14389
+ const added = addedRun2[i];
14390
+ removed.spans = wordDiffLine(removed.text, added.text, "removed");
14391
+ added.spans = wordDiffLine(removed.text, added.text, "added");
14392
+ }
14393
+ removedRun = [];
14394
+ removedRunStart = -1;
14395
+ };
14396
+ let addedRun = [];
14397
+ for (const raw of rawLines) {
14398
+ const marker = raw[0];
14399
+ const text = raw.slice(1);
14400
+ if (marker === "-") {
14401
+ if (addedRun.length > 0) {
14402
+ addedRun = [];
14403
+ }
14404
+ const line4 = { kind: "removed", oldLine: oldNum, text };
14405
+ out.push(line4);
14406
+ if (removedRunStart === -1) removedRunStart = out.length - 1;
14407
+ removedRun.push(line4);
14408
+ oldNum += 1;
14409
+ } else if (marker === "+") {
14410
+ const line4 = { kind: "added", newLine: newNum, text };
14411
+ out.push(line4);
14412
+ addedRun.push(line4);
14413
+ newNum += 1;
14414
+ } else {
14415
+ if (removedRun.length > 0 && addedRun.length > 0) {
14416
+ flushPairing(addedRun);
14417
+ }
14418
+ removedRun = [];
14419
+ removedRunStart = -1;
14420
+ addedRun = [];
14421
+ out.push({ kind: "context", oldLine: oldNum, newLine: newNum, text });
14422
+ oldNum += 1;
14423
+ newNum += 1;
14424
+ }
14425
+ }
14426
+ if (removedRun.length > 0 && addedRun.length > 0) {
14427
+ flushPairing(addedRun);
14428
+ }
14429
+ return out;
14430
+ }
14431
+
14432
+ // src/react-ink/utils/tool-display.ts
13214
14433
  function asRecord2(value) {
13215
14434
  return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
13216
14435
  }
@@ -13293,6 +14512,12 @@ function clipBody(value, maxLines = 8, maxChars = 1400) {
13293
14512
  }
13294
14513
  return previewMultiline(value, { maxLines, maxChars });
13295
14514
  }
14515
+ function highlightedBody(body, filePath, theme) {
14516
+ if (!body || !theme || !filePath || languageFromPath(filePath) === null) {
14517
+ return { body };
14518
+ }
14519
+ return { body: highlightByPath(body, filePath, theme), preformatted: true };
14520
+ }
13296
14521
  function extractDetails(value) {
13297
14522
  return asRecord2(asRecord2(value)?.details);
13298
14523
  }
@@ -13359,7 +14584,7 @@ function fallbackBody(output, fallbackOutputText, showImages = false) {
13359
14584
  return clipBody(fallbackOutputText);
13360
14585
  }
13361
14586
  function describeToolSource(source) {
13362
- const { args, fallbackArgsText, fallbackOutputText, output, showImages = false, toolName } = source;
14587
+ const { args, fallbackArgsText, fallbackOutputText, output, showImages = false, theme, toolName } = source;
13363
14588
  const argsRecord = asRecord2(args);
13364
14589
  const details = extractDetails(output);
13365
14590
  const outputText = toolText(output, showImages);
@@ -13377,10 +14602,12 @@ function describeToolSource(source) {
13377
14602
  summary += `:${start}${end ? `-${end}` : ""}`;
13378
14603
  }
13379
14604
  const responseSummary = containsImage(output) ? firstMeaningfulLine(outputText) ?? "Read image" : /unchanged/i.test(outputText) ? "Unchanged since last read" : countMeaningfulLines(outputText) > 0 ? `Read ${countMeaningfulLines(outputText)} ${countMeaningfulLines(outputText) === 1 ? "line" : "lines"}` : "Read file";
14605
+ const readBody = hasResult && !containsImage(output) ? highlightedBody(clipBody(outputText, 10, 1800), rawPath, theme) : { body: hasResult ? clipBody(outputText, 10, 1800) : void 0 };
13380
14606
  return {
13381
14607
  title,
13382
14608
  summary: hasResult ? responseSummary : summary,
13383
- body: hasResult ? clipBody(outputText, 10, 1800) : void 0,
14609
+ body: readBody.body,
14610
+ preformatted: readBody.preformatted,
13384
14611
  emptyText: "Reading file..."
13385
14612
  };
13386
14613
  }
@@ -13388,10 +14615,14 @@ function describeToolSource(source) {
13388
14615
  const rawPath = asString(argsRecord?.file_path) ?? asString(argsRecord?.path) ?? "";
13389
14616
  const content = asString(argsRecord?.content);
13390
14617
  const responseSummary = firstMeaningfulLine(outputText) ?? "Wrote file";
14618
+ const writeDiff = !hasResult && content !== void 0 && content.length > 0 ? buildStructuredDiff("", content, rawPath) ?? void 0 : void 0;
14619
+ const writeBody = hasResult ? { body: clipBody(outputText, 8, 1500) } : highlightedBody(clipBody(content, 8, 1500), rawPath, theme);
13391
14620
  return {
13392
14621
  title,
13393
14622
  summary: hasResult ? responseSummary : rawPath ? shortenPath2(rawPath) : void 0,
13394
- body: hasResult ? clipBody(outputText, 8, 1500) : clipBody(content, 8, 1500),
14623
+ body: writeDiff ? void 0 : writeBody.body,
14624
+ preformatted: writeDiff ? void 0 : writeBody.preformatted,
14625
+ diff: writeDiff,
13395
14626
  emptyText: "Preparing file write..."
13396
14627
  };
13397
14628
  }
@@ -13408,10 +14639,15 @@ function describeToolSource(source) {
13408
14639
  400
13409
14640
  ) : void 0;
13410
14641
  const responseSummary = firstMeaningfulLine(outputText) ?? "Applied edit";
14642
+ const editDiff = oldText !== void 0 && newText !== void 0 ? buildStructuredDiff(oldText, newText, rawPath) ?? void 0 : void 0;
14643
+ const editBody = hasResult ? highlightedBody(clipBody(outputText, 8, 1500), rawPath, theme) : { body: replacementPreview };
14644
+ const editChangeSummary = editDiff ? `+${editDiff.addedCount} -${editDiff.removedCount}` : void 0;
13411
14645
  return {
13412
14646
  title,
13413
- summary: hasResult ? responseSummary : rawPath ? shortenPath2(rawPath) : void 0,
13414
- body: hasResult ? clipBody(outputText, 8, 1500) : replacementPreview,
14647
+ summary: hasResult ? editChangeSummary ?? responseSummary : editChangeSummary ?? (rawPath ? shortenPath2(rawPath) : void 0),
14648
+ body: editDiff ? void 0 : editBody.body,
14649
+ preformatted: editDiff ? void 0 : editBody.preformatted,
14650
+ diff: editDiff,
13415
14651
  emptyText: "Applying edit..."
13416
14652
  };
13417
14653
  }
@@ -13508,7 +14744,7 @@ function describeToolCall(toolCall) {
13508
14744
  args: toolCall.arguments
13509
14745
  });
13510
14746
  }
13511
- function describeToolResult(message, showImages, args) {
14747
+ function describeToolResult(message, showImages, args, theme) {
13512
14748
  return describeToolSource({
13513
14749
  args,
13514
14750
  toolName: message.toolName,
@@ -13516,12 +14752,13 @@ function describeToolResult(message, showImages, args) {
13516
14752
  content: message.content,
13517
14753
  details: message.details
13518
14754
  },
13519
- showImages
14755
+ showImages,
14756
+ theme
13520
14757
  });
13521
14758
  }
13522
14759
 
13523
14760
  // src/react-ink/components/ToolEventBlock.tsx
13524
- import stripAnsi3 from "strip-ansi";
14761
+ import stripAnsi5 from "strip-ansi";
13525
14762
  function statusMarker(status) {
13526
14763
  switch (status) {
13527
14764
  case "error":
@@ -13532,12 +14769,15 @@ function statusMarker(status) {
13532
14769
  return ">";
13533
14770
  }
13534
14771
  }
14772
+ function statusColorKey(status) {
14773
+ return status === "error" ? "error" : "text";
14774
+ }
13535
14775
  function plainToolText(text) {
13536
- return stripAnsi3(text);
14776
+ return stripAnsi5(text);
13537
14777
  }
13538
14778
  function splitVisibleLines(text) {
13539
14779
  return (text ?? "").split(/\r?\n/).map((line4) => line4.trimEnd()).filter((line4, index, lines) => {
13540
- if (line4.length > 0) {
14780
+ if (stripAnsi5(line4).length > 0) {
13541
14781
  return true;
13542
14782
  }
13543
14783
  return index !== 0 && index !== lines.length - 1;
@@ -13568,11 +14808,12 @@ function ToolEventBlock({
13568
14808
  indent = 0,
13569
14809
  marginBottom = 1,
13570
14810
  maxContentLines = 10,
14811
+ preformatted = false,
13571
14812
  showSummaryInline = false,
13572
14813
  showTitle = true,
13573
14814
  status,
13574
14815
  summary,
13575
- theme: _theme,
14816
+ theme,
13576
14817
  title
13577
14818
  }) {
13578
14819
  const normalizedSummary = summary?.trim();
@@ -13588,19 +14829,19 @@ function ToolEventBlock({
13588
14829
  const { visibleLines, hiddenLineCount } = clampContentLines(combinedLines, maxContentLines);
13589
14830
  return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginBottom, marginLeft: indent, children: [
13590
14831
  showTitle ? /* @__PURE__ */ jsxs(Box, { children: [
13591
- /* @__PURE__ */ jsx(Text, { color: "white", children: plainToolText(`${statusMarker(status)} `) }),
13592
- /* @__PURE__ */ jsx(Text, { color: "white", children: plainToolText(title) }),
13593
- showSummaryInline && normalizedSummary ? /* @__PURE__ */ jsx(Text, { color: "white", children: plainToolText(` (${normalizedSummary})`) }) : null
14832
+ /* @__PURE__ */ jsx(Text, { children: theme.color(statusColorKey(status), plainToolText(`${statusMarker(status)} `)) }),
14833
+ /* @__PURE__ */ jsx(Text, { children: theme.color(statusColorKey(status), plainToolText(title)) }),
14834
+ showSummaryInline && normalizedSummary ? /* @__PURE__ */ jsx(Text, { children: theme.muted(plainToolText(` (${normalizedSummary})`)) }) : null
13594
14835
  ] }) : null,
13595
14836
  visibleLines.map((line4, index) => /* @__PURE__ */ jsx(
13596
14837
  Box,
13597
14838
  {
13598
14839
  marginLeft: line4.kind === "response" ? showTitle ? 2 : 0 : showTitle ? 4 : 2,
13599
- children: /* @__PURE__ */ jsx(Text, { color: "white", children: plainToolText(line4.text) })
14840
+ children: preformatted ? /* @__PURE__ */ jsx(Text, { children: line4.text }) : /* @__PURE__ */ jsx(Text, { children: theme.color("text", plainToolText(line4.text)) })
13600
14841
  },
13601
- `${line4.kind}:${index}:${line4.text}`
14842
+ `${line4.kind}:${index}:${stripAnsi5(line4.text)}`
13602
14843
  )),
13603
- hiddenLineCount > 0 ? /* @__PURE__ */ jsx(Box, { marginLeft: showTitle ? 4 : 2, children: /* @__PURE__ */ jsx(Text, { color: "white", children: plainToolText(`... ${hiddenLineCount} more line(s)`) }) }) : null
14844
+ hiddenLineCount > 0 ? /* @__PURE__ */ jsx(Box, { marginLeft: showTitle ? 4 : 2, children: /* @__PURE__ */ jsx(Text, { children: theme.muted(plainToolText(`... ${hiddenLineCount} more line(s)`)) }) }) : null
13604
14845
  ] });
13605
14846
  }
13606
14847
 
@@ -13623,17 +14864,100 @@ function ToolCallMessage({ theme, toolCall }) {
13623
14864
  );
13624
14865
  }
13625
14866
 
14867
+ // src/react-ink/diff/Diff.tsx
14868
+ import chalk4 from "chalk";
14869
+ function lineMarker(kind) {
14870
+ switch (kind) {
14871
+ case "added":
14872
+ return "+";
14873
+ case "removed":
14874
+ return "-";
14875
+ default:
14876
+ return " ";
14877
+ }
14878
+ }
14879
+ function gutterWidth(diff) {
14880
+ let max = 1;
14881
+ for (const hunk of diff.hunks) {
14882
+ for (const line4 of hunk.lines) {
14883
+ const num2 = line4.kind === "removed" ? line4.oldLine : line4.newLine;
14884
+ if (num2 !== void 0) {
14885
+ max = Math.max(max, stringWidth(String(num2)));
14886
+ }
14887
+ }
14888
+ }
14889
+ return max;
14890
+ }
14891
+ function renderLineText(line4, theme, bgRole, fgRole) {
14892
+ const paintSpan = (text, changed) => {
14893
+ const fg = theme.role(fgRole, text);
14894
+ return changed ? chalk4.bold(fg) : fg;
14895
+ };
14896
+ let body;
14897
+ if (line4.spans && line4.spans.length > 0) {
14898
+ body = line4.spans.map((span) => paintSpan(span.text, span.changed)).join("");
14899
+ } else {
14900
+ body = theme.role(fgRole, line4.text);
14901
+ }
14902
+ return bgRole ? theme.roleBackground(bgRole, body) : body;
14903
+ }
14904
+ function Diff({ diff, theme, indent = 0, marginBottom = 0 }) {
14905
+ const gutter = gutterWidth(diff);
14906
+ const renderLine = (line4, key) => {
14907
+ const num2 = line4.kind === "removed" ? line4.oldLine : line4.newLine;
14908
+ const gutterText = (num2 !== void 0 ? String(num2) : "").padStart(gutter);
14909
+ const marker = lineMarker(line4.kind);
14910
+ const bgRole = line4.kind === "added" ? "diffAddedBg" : line4.kind === "removed" ? "diffRemovedBg" : null;
14911
+ const fgRole = line4.kind === "added" ? "diffAddedText" : line4.kind === "removed" ? "diffRemovedText" : "blockquoteBar";
14912
+ const gutterStyled = theme.dim(`${gutterText} `);
14913
+ const markerStyled = line4.kind === "context" ? theme.dim(`${marker} `) : theme.role(fgRole, `${marker} `);
14914
+ const text = renderLineText(line4, theme, bgRole, fgRole);
14915
+ return /* @__PURE__ */ jsx(Box, { children: /* @__PURE__ */ jsxs(Text, { children: [
14916
+ gutterStyled,
14917
+ markerStyled,
14918
+ text
14919
+ ] }) }, key);
14920
+ };
14921
+ return /* @__PURE__ */ jsx(Box, { flexDirection: "column", marginLeft: indent, marginBottom, children: diff.hunks.map((hunk, hunkIndex) => /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
14922
+ hunkIndex > 0 ? /* @__PURE__ */ jsx(Box, { children: /* @__PURE__ */ jsx(Text, { children: theme.dim("...") }) }) : null,
14923
+ hunk.lines.map((line4, lineIndex) => renderLine(line4, `${hunkIndex}-${lineIndex}`))
14924
+ ] }, `hunk-${hunkIndex}`)) });
14925
+ }
14926
+
13626
14927
  // src/react-ink/components/messages/ToolResultBlock.tsx
13627
14928
  function ToolResultBlock({ expanded = false, message, nested = false, showImages, theme, toolCall }) {
13628
- const descriptor = describeToolResult(message, showImages, toolCall?.arguments);
14929
+ const descriptor = describeToolResult(message, showImages, toolCall?.arguments, theme);
14930
+ const indent = nested ? 4 : 2;
14931
+ if (descriptor.diff) {
14932
+ return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginBottom: 0, children: [
14933
+ /* @__PURE__ */ jsx(
14934
+ ToolEventBlock,
14935
+ {
14936
+ detail: void 0,
14937
+ emptyText: descriptor.emptyText,
14938
+ indent,
14939
+ marginBottom: 0,
14940
+ maxContentLines: 0,
14941
+ showSummaryInline: false,
14942
+ showTitle: !nested,
14943
+ status: message.isError ? "error" : "success",
14944
+ summary: descriptor.summary,
14945
+ theme,
14946
+ title: descriptor.title
14947
+ }
14948
+ ),
14949
+ /* @__PURE__ */ jsx(Diff, { diff: descriptor.diff, theme, indent: indent + 2 })
14950
+ ] });
14951
+ }
13629
14952
  return /* @__PURE__ */ jsx(
13630
14953
  ToolEventBlock,
13631
14954
  {
13632
14955
  detail: descriptor.body,
13633
14956
  emptyText: descriptor.emptyText,
13634
- indent: nested ? 4 : 2,
14957
+ indent,
13635
14958
  marginBottom: 0,
13636
14959
  maxContentLines: expanded ? Number.MAX_SAFE_INTEGER : 10,
14960
+ preformatted: descriptor.preformatted,
13637
14961
  showSummaryInline: false,
13638
14962
  showTitle: !nested,
13639
14963
  status: message.isError ? "error" : "success",
@@ -13658,7 +14982,7 @@ function AssistantMessageView({
13658
14982
  const matchedToolResultIds = /* @__PURE__ */ new Set();
13659
14983
  return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [
13660
14984
  /* @__PURE__ */ jsx(Text, { children: theme.color("accent", `Assistant ${message.provider}/${message.model} ${formatMessageTimestamp(message.timestamp)}`) }),
13661
- parts.text ? /* @__PURE__ */ jsx(Text, { children: parts.text }) : null,
14985
+ parts.text ? /* @__PURE__ */ jsx(Markdown, { theme, children: parts.text }) : null,
13662
14986
  parts.thinking.map((thinking, index) => /* @__PURE__ */ jsx(Text, { children: theme.muted(`[thinking] ${thinking}`) }, index)),
13663
14987
  parts.toolCalls.map((toolCall) => {
13664
14988
  const toolResult = toolResultsByCallId.get(toolCall.id);
@@ -13860,20 +15184,20 @@ function MessageList({
13860
15184
  }
13861
15185
 
13862
15186
  // src/react-ink/components/StatusLine.tsx
13863
- function StatusLine({ snapshot, status, theme }) {
15187
+ function StatusLine({ snapshot, status, theme, showBusyText = true }) {
13864
15188
  let text = status?.text;
13865
15189
  let tone = status?.kind ?? "info";
13866
15190
  if (!text) {
13867
- if (snapshot.isCompacting) {
15191
+ if (showBusyText && snapshot.isCompacting) {
13868
15192
  text = "Compacting conversation context...";
13869
15193
  tone = "busy";
13870
- } else if (snapshot.isBashRunning) {
15194
+ } else if (showBusyText && snapshot.isBashRunning) {
13871
15195
  text = "Running bash command...";
13872
15196
  tone = "busy";
13873
- } else if (snapshot.pendingToolCallCount > 0 || snapshot.pendingMessageCount > 0) {
15197
+ } else if (showBusyText && (snapshot.pendingToolCallCount > 0 || snapshot.pendingMessageCount > 0)) {
13874
15198
  text = "Agent working...";
13875
15199
  tone = "busy";
13876
- } else if (snapshot.isStreaming) {
15200
+ } else if (showBusyText && snapshot.isStreaming) {
13877
15201
  text = "Agent working...";
13878
15202
  tone = "busy";
13879
15203
  } else if (snapshot.error) {