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/shell-app.js CHANGED
@@ -4635,6 +4635,7 @@ function runErrorThrowable(kind, message) {
4635
4635
  }
4636
4636
 
4637
4637
  // src/capabilities/kernel/spec.ts
4638
+ var READ_STATE_HANDLE_KEY = "readState";
4638
4639
  function coerceInput(raw) {
4639
4640
  if (typeof raw === "string") {
4640
4641
  const trimmed = raw.trim();
@@ -5088,7 +5089,7 @@ var standardBudget = {
5088
5089
  [${omitted} bytes elided to stay within the output ceiling]
5089
5090
  `
5090
5091
  };
5091
- function makeNodeContext(cwd, signal, budget) {
5092
+ function makeNodeContext(cwd, signal, budget, framework) {
5092
5093
  if (typeof cwd !== "string" || cwd.length === 0) {
5093
5094
  throw new Error("makeNodeContext requires a non-empty working directory.");
5094
5095
  }
@@ -5097,10 +5098,60 @@ function makeNodeContext(cwd, signal, budget) {
5097
5098
  fs: nodeFs,
5098
5099
  shell: nodeShell,
5099
5100
  signal: signal ?? neverAborts(),
5100
- budget: budget ?? standardBudget
5101
+ budget: budget ?? standardBudget,
5102
+ ...framework ? { framework } : {}
5101
5103
  };
5102
5104
  }
5103
5105
 
5106
+ // src/capabilities/files/read-state-gate.ts
5107
+ var READ_BEFORE_EDIT_MESSAGE = "File has not been read yet. Read it first before writing to it.";
5108
+ 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.";
5109
+ function getReadStateHandle(ctx) {
5110
+ const bag = ctx.framework;
5111
+ if (!bag || typeof bag !== "object") return void 0;
5112
+ const candidate = bag[READ_STATE_HANDLE_KEY];
5113
+ if (!candidate || typeof candidate !== "object") return void 0;
5114
+ const handle = candidate;
5115
+ if (typeof handle.get !== "function" || typeof handle.set !== "function" || typeof handle.has !== "function") {
5116
+ return void 0;
5117
+ }
5118
+ return handle;
5119
+ }
5120
+ async function recordReadState(ctx, absPath, handle) {
5121
+ if (!handle) return;
5122
+ try {
5123
+ const info = await ctx.fs.stat(absPath);
5124
+ const record = {
5125
+ mtimeMs: Math.floor(info.modifiedMs),
5126
+ size: info.size,
5127
+ readAt: Date.now()
5128
+ };
5129
+ handle.set(absPath, record);
5130
+ } catch {
5131
+ }
5132
+ }
5133
+ async function enforceReadGate(ctx, absPath, handle) {
5134
+ if (!handle) return { ok: true };
5135
+ if (!handle.has(absPath)) {
5136
+ return { ok: false, message: READ_BEFORE_EDIT_MESSAGE };
5137
+ }
5138
+ const recorded = handle.get(absPath);
5139
+ if (!recorded) {
5140
+ return { ok: false, message: READ_BEFORE_EDIT_MESSAGE };
5141
+ }
5142
+ let info;
5143
+ try {
5144
+ info = await ctx.fs.stat(absPath);
5145
+ } catch {
5146
+ return { ok: true };
5147
+ }
5148
+ const currentMtime = Math.floor(info.modifiedMs);
5149
+ if (currentMtime > recorded.mtimeMs || info.size !== recorded.size) {
5150
+ return { ok: false, message: MODIFIED_SINCE_READ_MESSAGE };
5151
+ }
5152
+ return { ok: true };
5153
+ }
5154
+
5104
5155
  // src/capabilities/files/read.ts
5105
5156
  var GUTTER_WIDTH = 6;
5106
5157
  var DESCRIPTION = [
@@ -5203,6 +5254,7 @@ var readTool = defineTool({
5203
5254
  const detail = err instanceof Error ? err.message : String(err);
5204
5255
  return failure(`Could not read ${path2}: ${detail}`);
5205
5256
  }
5257
+ await recordReadState(ctx, path2, getReadStateHandle(ctx));
5206
5258
  const allLines = toLines(text);
5207
5259
  const totalLines = allLines.length;
5208
5260
  if (totalLines === 0) {
@@ -5310,11 +5362,19 @@ var writeTool = defineTool({
5310
5362
  async run(input, ctx) {
5311
5363
  const path2 = readPath(input?.path);
5312
5364
  const content = readContent(input?.content);
5365
+ const handle = getReadStateHandle(ctx);
5366
+ if (handle && await ctx.fs.exists(path2)) {
5367
+ const gate = await enforceReadGate(ctx, path2, handle);
5368
+ if (!gate.ok) {
5369
+ return asText(gate.message, true);
5370
+ }
5371
+ }
5313
5372
  const folder = parentDir(path2);
5314
5373
  if (folder.length > 0) {
5315
5374
  await ctx.fs.mkdir(folder, { recursive: true });
5316
5375
  }
5317
5376
  await ctx.fs.writeFile(path2, content, "utf8");
5377
+ await recordReadState(ctx, path2, handle);
5318
5378
  const bytes = Buffer.byteLength(content, "utf8");
5319
5379
  const unit = bytes === 1 ? "byte" : "bytes";
5320
5380
  return asText(`Saved ${bytes} ${unit} to ${path2}.`);
@@ -5604,6 +5664,11 @@ async function runEdit(input, ctx) {
5604
5664
  if (!info.isFile) {
5605
5665
  return failure2(`${path2} is not a regular file, so it cannot be edited.`);
5606
5666
  }
5667
+ const handle = getReadStateHandle(ctx);
5668
+ const gate = await enforceReadGate(ctx, path2, handle);
5669
+ if (!gate.ok) {
5670
+ return failure2(gate.message);
5671
+ }
5607
5672
  const before = await ctx.fs.readFile(path2, "utf8");
5608
5673
  const literalHits = countLiteral(before, oldText);
5609
5674
  if (literalHits > 0) {
@@ -5617,6 +5682,7 @@ async function runEdit(input, ctx) {
5617
5682
  return failure2(`The replacement left ${path2} unchanged.`);
5618
5683
  }
5619
5684
  await ctx.fs.writeFile(path2, after2, "utf8");
5685
+ await recordReadState(ctx, path2, handle);
5620
5686
  return success(path2, before, after2, replaceAll ? literalHits : 1);
5621
5687
  }
5622
5688
  const spans = findFuzzySpans(before, oldText);
@@ -5640,6 +5706,7 @@ async function runEdit(input, ctx) {
5640
5706
  return failure2(`The fuzzy replacement left ${path2} unchanged.`);
5641
5707
  }
5642
5708
  await ctx.fs.writeFile(path2, after, "utf8");
5709
+ await recordReadState(ctx, path2, handle);
5643
5710
  return success(path2, before, after, targets.length);
5644
5711
  }
5645
5712
  var editTool = defineTool({
@@ -8469,6 +8536,34 @@ var useInput = ink.useInput;
8469
8536
  // src/react-ink/theme-adapter.ts
8470
8537
  import chalk from "chalk";
8471
8538
  import stripAnsi from "strip-ansi";
8539
+ var DEFAULT_ROLE_KEYS = {
8540
+ codeInline: "codeInline",
8541
+ heading: "heading",
8542
+ blockquoteBar: "blockquoteBar",
8543
+ diffAddedBg: "diffAddedBg",
8544
+ diffRemovedBg: "diffRemovedBg",
8545
+ diffAddedText: "diffAddedText",
8546
+ diffRemovedText: "diffRemovedText",
8547
+ synKeyword: "synKeyword",
8548
+ synString: "synString",
8549
+ synNumber: "synNumber",
8550
+ synComment: "synComment",
8551
+ synType: "synType"
8552
+ };
8553
+ var ROLE_FALLBACK_KEYS = {
8554
+ codeInline: "accent",
8555
+ heading: "accent",
8556
+ blockquoteBar: "muted",
8557
+ diffAddedBg: "success",
8558
+ diffRemovedBg: "error",
8559
+ diffAddedText: "success",
8560
+ diffRemovedText: "error",
8561
+ synKeyword: "accent",
8562
+ synString: "success",
8563
+ synNumber: "warning",
8564
+ synComment: "muted",
8565
+ synType: "info"
8566
+ };
8472
8567
  function applyForeground(color, text) {
8473
8568
  if (!color) return text;
8474
8569
  try {
@@ -8495,15 +8590,27 @@ function applyBackground(background, foreground, text) {
8495
8590
  return applyForeground(foreground, text);
8496
8591
  }
8497
8592
  }
8498
- function createThemeAdapter(themeName, colors) {
8593
+ function createThemeAdapter(themeName, colors, roleOverrides) {
8499
8594
  const fallback = colors.text ?? "#e5e5e7";
8595
+ const roles = { ...DEFAULT_ROLE_KEYS, ...roleOverrides };
8596
+ const resolveRoleColor = (role) => resolveColorToken(
8597
+ colors[roles[role]],
8598
+ resolveColorToken(colors[ROLE_FALLBACK_KEYS[role]], fallback)
8599
+ );
8500
8600
  return {
8501
8601
  name: themeName,
8502
8602
  colors,
8603
+ roles,
8503
8604
  color: (key, text) => applyForeground(resolveColorToken(colors[key], fallback), text),
8504
8605
  background: (key, text, textKey = "text") => applyBackground(resolveColorToken(colors[key]), resolveColorToken(colors[textKey], fallback), ` ${stripAnsi(text)} `),
8505
8606
  dim: (text) => applyForeground(resolveColorToken(colors.dim, "#666666"), text),
8506
- muted: (text) => applyForeground(resolveColorToken(colors.muted, "#808080"), text)
8607
+ muted: (text) => applyForeground(resolveColorToken(colors.muted, "#808080"), text),
8608
+ role: (role, text) => applyForeground(resolveRoleColor(role), text),
8609
+ roleBackground: (role, text, foregroundRole) => applyBackground(
8610
+ resolveRoleColor(role),
8611
+ foregroundRole ? resolveRoleColor(foregroundRole) : void 0,
8612
+ text
8613
+ )
8507
8614
  };
8508
8615
  }
8509
8616
 
@@ -8888,8 +8995,1120 @@ function parseSkillInvocation(content) {
8888
8995
  };
8889
8996
  }
8890
8997
 
8998
+ // src/react-ink/markdown/format-token.ts
8999
+ import chalk2 from "chalk";
9000
+ import { marked } from "marked";
9001
+ import stripAnsi3 from "strip-ansi";
9002
+
9003
+ // src/ui/utils.ts
9004
+ import { eastAsianWidth } from "get-east-asian-width";
9005
+ var segmenter = new Intl.Segmenter(void 0, { granularity: "grapheme" });
9006
+ var TextWidthCalculator = class {
9007
+ getWidth(text) {
9008
+ return visibleWidth(text);
9009
+ }
9010
+ clearCache() {
9011
+ widthCache.clear();
9012
+ }
9013
+ };
9014
+ var textWidthCalculator = new TextWidthCalculator();
9015
+ function looksLikeEmojiCandidate(segment) {
9016
+ const cp = segment.codePointAt(0);
9017
+ return cp >= 126976 && cp <= 130047 || // pictographs and emoji proper
9018
+ cp >= 8960 && cp <= 9215 || // miscellaneous technical glyphs
9019
+ cp >= 9728 && cp <= 10175 || // assorted symbols and dingbats
9020
+ cp >= 11088 && cp <= 11093 || // a few star/circle characters
9021
+ segment.includes("\uFE0F") || // carries VS16, the emoji-presentation selector
9022
+ segment.length > 2;
9023
+ }
9024
+ function createUnicodeRegex(vPattern, uFallback) {
9025
+ try {
9026
+ return new RegExp(vPattern, "v");
9027
+ } catch {
9028
+ return new RegExp(uFallback, "u");
9029
+ }
9030
+ }
9031
+ var zeroWidthRegex = createUnicodeRegex(
9032
+ "^(?:\\p{Default_Ignorable_Code_Point}|\\p{Control}|\\p{Mark}|\\p{Surrogate})+$",
9033
+ "^(?:\\p{Default_Ignorable_Code_Point}|\\p{Control}|\\p{Mark}|\\p{Surrogate})+$"
9034
+ );
9035
+ var leadingNonPrintingRegex = createUnicodeRegex(
9036
+ "^[\\p{Default_Ignorable_Code_Point}\\p{Control}\\p{Format}\\p{Mark}\\p{Surrogate}]+",
9037
+ "^[\\p{Default_Ignorable_Code_Point}\\p{Control}\\p{Format}\\p{Mark}\\p{Surrogate}]+"
9038
+ );
9039
+ var rgiEmojiRegex = createUnicodeRegex("^\\p{RGI_Emoji}$", "^\\p{Extended_Pictographic}$");
9040
+ var AnsiStripper = {
9041
+ strip(text) {
9042
+ if (!text.includes("\x1B")) {
9043
+ return text;
9044
+ }
9045
+ let cleaned = text;
9046
+ cleaned = cleaned.replace(/\x1b\[[0-9;]*[mGKHJ]/g, "");
9047
+ cleaned = cleaned.replace(/\x1b\]8;;[^\x07]*\x07/g, "");
9048
+ cleaned = cleaned.replace(/\x1b_[^\x07\x1b]*(?:\x07|\x1b\\)/g, "");
9049
+ return cleaned;
9050
+ }
9051
+ };
9052
+ var WIDTH_CACHE_SIZE = 512;
9053
+ var widthCache = /* @__PURE__ */ new Map();
9054
+ function measureClusterWidth(segment) {
9055
+ if (zeroWidthRegex.test(segment)) {
9056
+ return 0;
9057
+ }
9058
+ if (looksLikeEmojiCandidate(segment) && rgiEmojiRegex.test(segment)) {
9059
+ return 2;
9060
+ }
9061
+ const base = segment.replace(leadingNonPrintingRegex, "");
9062
+ const cp = base.codePointAt(0);
9063
+ if (cp === void 0) {
9064
+ return 0;
9065
+ }
9066
+ let width = eastAsianWidth(cp);
9067
+ if (segment.length > 1) {
9068
+ for (const char of segment.slice(1)) {
9069
+ const c = char.codePointAt(0);
9070
+ if (c >= 65280 && c <= 65519) {
9071
+ width += eastAsianWidth(c);
9072
+ }
9073
+ }
9074
+ }
9075
+ return width;
9076
+ }
9077
+ function visibleWidth(str2) {
9078
+ if (str2.length === 0) {
9079
+ return 0;
9080
+ }
9081
+ let isPureAscii = true;
9082
+ for (let i = 0; i < str2.length; i++) {
9083
+ const code = str2.charCodeAt(i);
9084
+ if (code < 32 || code > 126) {
9085
+ isPureAscii = false;
9086
+ break;
9087
+ }
9088
+ }
9089
+ if (isPureAscii) {
9090
+ return str2.length;
9091
+ }
9092
+ const cached = widthCache.get(str2);
9093
+ if (cached !== void 0) {
9094
+ return cached;
9095
+ }
9096
+ let clean = str2;
9097
+ if (str2.includes(" ")) {
9098
+ clean = clean.replace(/\t/g, " ");
9099
+ }
9100
+ clean = AnsiStripper.strip(clean);
9101
+ let width = 0;
9102
+ for (const { segment } of segmenter.segment(clean)) {
9103
+ width += measureClusterWidth(segment);
9104
+ }
9105
+ if (widthCache.size >= WIDTH_CACHE_SIZE) {
9106
+ const firstKey = widthCache.keys().next().value;
9107
+ if (firstKey !== void 0) {
9108
+ widthCache.delete(firstKey);
9109
+ }
9110
+ }
9111
+ widthCache.set(str2, width);
9112
+ return width;
9113
+ }
9114
+ function extractAnsiCode(str2, pos) {
9115
+ if (pos >= str2.length || str2[pos] !== "\x1B") return null;
9116
+ const next = str2[pos + 1];
9117
+ if (next === "[") {
9118
+ let j = pos + 2;
9119
+ while (j < str2.length && !/[mGKHJ]/.test(str2[j])) j++;
9120
+ if (j < str2.length) return { code: str2.substring(pos, j + 1), length: j + 1 - pos };
9121
+ return null;
9122
+ }
9123
+ if (next === "]") {
9124
+ let j = pos + 2;
9125
+ while (j < str2.length) {
9126
+ if (str2[j] === "\x07") return { code: str2.substring(pos, j + 1), length: j + 1 - pos };
9127
+ if (str2[j] === "\x1B" && str2[j + 1] === "\\") return { code: str2.substring(pos, j + 2), length: j + 2 - pos };
9128
+ j++;
9129
+ }
9130
+ return null;
9131
+ }
9132
+ if (next === "_") {
9133
+ let j = pos + 2;
9134
+ while (j < str2.length) {
9135
+ if (str2[j] === "\x07") return { code: str2.substring(pos, j + 1), length: j + 1 - pos };
9136
+ if (str2[j] === "\x1B" && str2[j + 1] === "\\") return { code: str2.substring(pos, j + 2), length: j + 2 - pos };
9137
+ j++;
9138
+ }
9139
+ return null;
9140
+ }
9141
+ return null;
9142
+ }
9143
+ var AnsiStateTracker = class {
9144
+ // Each attribute is kept on its own flag, which lets us clear them one at a time.
9145
+ _bold = false;
9146
+ _dim = false;
9147
+ italic = false;
9148
+ underline = false;
9149
+ blink = false;
9150
+ inverse = false;
9151
+ hidden = false;
9152
+ strikethrough = false;
9153
+ _colors = { fg: null, bg: null };
9154
+ process(ansiCode) {
9155
+ if (!ansiCode.endsWith("m")) {
9156
+ return;
9157
+ }
9158
+ const match = ansiCode.match(/\x1b\[([\d;]*)m/);
9159
+ if (!match) return;
9160
+ const params = match[1];
9161
+ if (params === "" || params === "0") {
9162
+ this.reset();
9163
+ return;
9164
+ }
9165
+ const parts = params.split(";");
9166
+ let i = 0;
9167
+ while (i < parts.length) {
9168
+ const code = Number.parseInt(parts[i] ?? "", 10);
9169
+ if (Number.isNaN(code)) {
9170
+ i++;
9171
+ continue;
9172
+ }
9173
+ const consumed = this.tryConsumeColorCode(parts, i, code);
9174
+ if (consumed > 0) {
9175
+ i += consumed;
9176
+ continue;
9177
+ }
9178
+ this.applyStandardCode(code);
9179
+ i++;
9180
+ }
9181
+ }
9182
+ tryConsumeColorCode(parts, index, code) {
9183
+ if (code !== 38 && code !== 48) {
9184
+ return 0;
9185
+ }
9186
+ if (parts[index + 1] === "5" && parts[index + 2] !== void 0) {
9187
+ const colorCode = `${parts[index]};${parts[index + 1]};${parts[index + 2]}`;
9188
+ if (code === 38) {
9189
+ this._colors.fg = colorCode;
9190
+ } else {
9191
+ this._colors.bg = colorCode;
9192
+ }
9193
+ return 3;
9194
+ }
9195
+ if (parts[index + 1] === "2" && parts[index + 4] !== void 0) {
9196
+ const colorCode = `${parts[index]};${parts[index + 1]};${parts[index + 2]};${parts[index + 3]};${parts[index + 4]}`;
9197
+ if (code === 38) {
9198
+ this._colors.fg = colorCode;
9199
+ } else {
9200
+ this._colors.bg = colorCode;
9201
+ }
9202
+ return 5;
9203
+ }
9204
+ return 0;
9205
+ }
9206
+ applyStandardCode(code) {
9207
+ switch (code) {
9208
+ case 0:
9209
+ this.reset();
9210
+ return;
9211
+ case 1:
9212
+ this._bold = true;
9213
+ return;
9214
+ case 2:
9215
+ this._dim = true;
9216
+ return;
9217
+ case 3:
9218
+ this.italic = true;
9219
+ return;
9220
+ case 4:
9221
+ this.underline = true;
9222
+ return;
9223
+ case 5:
9224
+ this.blink = true;
9225
+ return;
9226
+ case 7:
9227
+ this.inverse = true;
9228
+ return;
9229
+ case 8:
9230
+ this.hidden = true;
9231
+ return;
9232
+ case 9:
9233
+ this.strikethrough = true;
9234
+ return;
9235
+ case 21:
9236
+ this._bold = false;
9237
+ return;
9238
+ case 22:
9239
+ this._bold = false;
9240
+ this._dim = false;
9241
+ return;
9242
+ case 23:
9243
+ this.italic = false;
9244
+ return;
9245
+ case 24:
9246
+ this.underline = false;
9247
+ return;
9248
+ case 25:
9249
+ this.blink = false;
9250
+ return;
9251
+ case 27:
9252
+ this.inverse = false;
9253
+ return;
9254
+ case 28:
9255
+ this.hidden = false;
9256
+ return;
9257
+ case 29:
9258
+ this.strikethrough = false;
9259
+ return;
9260
+ case 39:
9261
+ this._colors.fg = null;
9262
+ return;
9263
+ case 49:
9264
+ this._colors.bg = null;
9265
+ return;
9266
+ default:
9267
+ if (code >= 30 && code <= 37 || code >= 90 && code <= 97) {
9268
+ this._colors.fg = String(code);
9269
+ return;
9270
+ }
9271
+ if (code >= 40 && code <= 47 || code >= 100 && code <= 107) {
9272
+ this._colors.bg = String(code);
9273
+ }
9274
+ }
9275
+ }
9276
+ reset() {
9277
+ this._bold = false;
9278
+ this._dim = false;
9279
+ this.italic = false;
9280
+ this.underline = false;
9281
+ this.blink = false;
9282
+ this.inverse = false;
9283
+ this.hidden = false;
9284
+ this.strikethrough = false;
9285
+ this._colors.fg = null;
9286
+ this._colors.bg = null;
9287
+ }
9288
+ /** Wipe all tracked state so the instance can be reused. */
9289
+ clear() {
9290
+ this.reset();
9291
+ }
9292
+ getActiveCodes() {
9293
+ const codes = [];
9294
+ if (this._bold) codes.push("1");
9295
+ if (this._dim) codes.push("2");
9296
+ if (this.italic) codes.push("3");
9297
+ if (this.underline) codes.push("4");
9298
+ if (this.blink) codes.push("5");
9299
+ if (this.inverse) codes.push("7");
9300
+ if (this.hidden) codes.push("8");
9301
+ if (this.strikethrough) codes.push("9");
9302
+ if (this._colors.fg) codes.push(this._colors.fg);
9303
+ if (this._colors.bg) codes.push(this._colors.bg);
9304
+ if (codes.length === 0) return "";
9305
+ return `\x1B[${codes.join(";")}m`;
9306
+ }
9307
+ hasActiveCodes() {
9308
+ 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;
9309
+ }
9310
+ /**
9311
+ * Produce the escape code needed to switch off any attribute that would
9312
+ * otherwise smear into the padding at the end of a line. In practice that
9313
+ * is just underline. Yields an empty string when nothing needs disabling.
9314
+ */
9315
+ getLineEndReset() {
9316
+ if (this.underline) {
9317
+ return "\x1B[24m";
9318
+ }
9319
+ return "";
9320
+ }
9321
+ };
9322
+ function mergeTextIntoTracker(text, tracker) {
9323
+ let i = 0;
9324
+ while (i < text.length) {
9325
+ const ansiResult = extractAnsiCode(text, i);
9326
+ if (ansiResult) {
9327
+ tracker.process(ansiResult.code);
9328
+ i += ansiResult.length;
9329
+ } else {
9330
+ i++;
9331
+ }
9332
+ }
9333
+ }
9334
+ function tokenizeTextWithAnsi(text) {
9335
+ const tokens = [];
9336
+ let current = "";
9337
+ let pendingAnsi = "";
9338
+ let inWhitespace = false;
9339
+ let i = 0;
9340
+ while (i < text.length) {
9341
+ const ansiResult = extractAnsiCode(text, i);
9342
+ if (ansiResult) {
9343
+ pendingAnsi += ansiResult.code;
9344
+ i += ansiResult.length;
9345
+ continue;
9346
+ }
9347
+ const char = text[i];
9348
+ const charIsSpace = char === " ";
9349
+ if (charIsSpace !== inWhitespace && current) {
9350
+ tokens.push(current);
9351
+ current = "";
9352
+ }
9353
+ if (pendingAnsi) {
9354
+ current += pendingAnsi;
9355
+ pendingAnsi = "";
9356
+ }
9357
+ inWhitespace = charIsSpace;
9358
+ current += char;
9359
+ i++;
9360
+ }
9361
+ if (pendingAnsi) {
9362
+ current += pendingAnsi;
9363
+ }
9364
+ if (current) {
9365
+ tokens.push(current);
9366
+ }
9367
+ return tokens;
9368
+ }
9369
+ var TextWrapper = class {
9370
+ wrap(text, width) {
9371
+ return wrapTextWithAnsi(text, width);
9372
+ }
9373
+ };
9374
+ var textWrapper = new TextWrapper();
9375
+ var TokenWrapEngine = {
9376
+ wrap(tokens, width, tracker) {
9377
+ const wrapped = [];
9378
+ let currentLine = "";
9379
+ let currentVisibleLength = 0;
9380
+ for (const token of tokens) {
9381
+ const tokenVisibleLength = visibleWidth(token);
9382
+ const isWhitespace = token.trim() === "";
9383
+ if (tokenVisibleLength > width && !isWhitespace) {
9384
+ if (currentLine) {
9385
+ const lineEndReset = tracker.getLineEndReset();
9386
+ if (lineEndReset) {
9387
+ currentLine += lineEndReset;
9388
+ }
9389
+ wrapped.push(currentLine);
9390
+ currentLine = "";
9391
+ currentVisibleLength = 0;
9392
+ }
9393
+ const broken = splitLongToken(token, width, tracker);
9394
+ wrapped.push(...broken.slice(0, -1));
9395
+ currentLine = broken[broken.length - 1];
9396
+ currentVisibleLength = visibleWidth(currentLine);
9397
+ continue;
9398
+ }
9399
+ const totalNeeded = currentVisibleLength + tokenVisibleLength;
9400
+ if (totalNeeded > width && currentVisibleLength > 0) {
9401
+ let lineToWrap = currentLine.trimEnd();
9402
+ const lineEndReset = tracker.getLineEndReset();
9403
+ if (lineEndReset) {
9404
+ lineToWrap += lineEndReset;
9405
+ }
9406
+ wrapped.push(lineToWrap);
9407
+ if (isWhitespace) {
9408
+ currentLine = tracker.getActiveCodes();
9409
+ currentVisibleLength = 0;
9410
+ } else {
9411
+ currentLine = tracker.getActiveCodes() + token;
9412
+ currentVisibleLength = tokenVisibleLength;
9413
+ }
9414
+ } else {
9415
+ currentLine += token;
9416
+ currentVisibleLength += tokenVisibleLength;
9417
+ }
9418
+ mergeTextIntoTracker(token, tracker);
9419
+ }
9420
+ return { wrapped, currentLine, currentVisibleLength };
9421
+ }
9422
+ };
9423
+ function wrapTextWithAnsi(text, width) {
9424
+ if (!text) {
9425
+ return [""];
9426
+ }
9427
+ const inputLines = text.split("\n");
9428
+ const result = [];
9429
+ const tracker = new AnsiStateTracker();
9430
+ for (const inputLine of inputLines) {
9431
+ const prefix = result.length > 0 ? tracker.getActiveCodes() : "";
9432
+ result.push(...wrapLinePreservingAnsi(prefix + inputLine, width));
9433
+ mergeTextIntoTracker(inputLine, tracker);
9434
+ }
9435
+ return result.length > 0 ? result : [""];
9436
+ }
9437
+ function wrapLinePreservingAnsi(line, width) {
9438
+ if (!line) {
9439
+ return [""];
9440
+ }
9441
+ const visibleLength = visibleWidth(line);
9442
+ if (visibleLength <= width) {
9443
+ return [line];
9444
+ }
9445
+ const tracker = new AnsiStateTracker();
9446
+ const tokens = tokenizeTextWithAnsi(line);
9447
+ const { wrapped, currentLine } = TokenWrapEngine.wrap(tokens, width, tracker);
9448
+ if (currentLine) {
9449
+ wrapped.push(currentLine);
9450
+ }
9451
+ return wrapped.length > 0 ? wrapped.map((segmentLine) => segmentLine.trimEnd()) : [""];
9452
+ }
9453
+ function splitLongToken(word, width, tracker) {
9454
+ const lines = [];
9455
+ let currentLine = tracker.getActiveCodes();
9456
+ let currentWidth = 0;
9457
+ let i = 0;
9458
+ const segments = [];
9459
+ while (i < word.length) {
9460
+ const ansiResult = extractAnsiCode(word, i);
9461
+ if (ansiResult) {
9462
+ segments.push({ type: "ansi", value: ansiResult.code });
9463
+ i += ansiResult.length;
9464
+ } else {
9465
+ let end = i;
9466
+ while (end < word.length) {
9467
+ const nextAnsi = extractAnsiCode(word, end);
9468
+ if (nextAnsi) break;
9469
+ end++;
9470
+ }
9471
+ const textPortion = word.slice(i, end);
9472
+ for (const seg of segmenter.segment(textPortion)) {
9473
+ segments.push({ type: "grapheme", value: seg.segment });
9474
+ }
9475
+ i = end;
9476
+ }
9477
+ }
9478
+ for (const seg of segments) {
9479
+ if (seg.type === "ansi") {
9480
+ currentLine += seg.value;
9481
+ tracker.process(seg.value);
9482
+ continue;
9483
+ }
9484
+ const grapheme = seg.value;
9485
+ if (!grapheme) continue;
9486
+ const clusterWidth = visibleWidth(grapheme);
9487
+ if (currentWidth + clusterWidth > width) {
9488
+ const lineEndReset = tracker.getLineEndReset();
9489
+ if (lineEndReset) {
9490
+ currentLine += lineEndReset;
9491
+ }
9492
+ lines.push(currentLine);
9493
+ currentLine = tracker.getActiveCodes();
9494
+ currentWidth = 0;
9495
+ }
9496
+ currentLine += grapheme;
9497
+ currentWidth += clusterWidth;
9498
+ }
9499
+ if (currentLine) {
9500
+ lines.push(currentLine);
9501
+ }
9502
+ return lines.length > 0 ? lines : [""];
9503
+ }
9504
+ var pooledStyleTracker = new AnsiStateTracker();
9505
+
9506
+ // src/react-ink/markdown/format-token.ts
9507
+ var EOL = "\n";
9508
+ var BLOCKQUOTE_BAR = "\u2502";
9509
+ var markedConfigured = false;
9510
+ function configureMarked() {
9511
+ if (markedConfigured) {
9512
+ return;
9513
+ }
9514
+ markedConfigured = true;
9515
+ marked.use({
9516
+ tokenizer: {
9517
+ del() {
9518
+ return void 0;
9519
+ }
9520
+ }
9521
+ });
9522
+ }
9523
+ var TOKEN_CACHE_MAX = 500;
9524
+ var tokenCache = /* @__PURE__ */ new Map();
9525
+ var MD_SYNTAX_RE = /[#*`|[>\-_~]|\n\n|^\d+\. |\n\d+\. /;
9526
+ function hasMarkdownSyntax(text) {
9527
+ return MD_SYNTAX_RE.test(text.length > 500 ? text.slice(0, 500) : text);
9528
+ }
9529
+ function hashContent(content) {
9530
+ let hash = 2166136261;
9531
+ for (let i = 0; i < content.length; i++) {
9532
+ hash ^= content.charCodeAt(i);
9533
+ hash = Math.imul(hash, 16777619);
9534
+ }
9535
+ return (hash >>> 0).toString(36) + ":" + content.length.toString(36);
9536
+ }
9537
+ function cachedLexer(content) {
9538
+ configureMarked();
9539
+ if (!hasMarkdownSyntax(content)) {
9540
+ return [
9541
+ {
9542
+ type: "paragraph",
9543
+ raw: content,
9544
+ text: content,
9545
+ tokens: [{ type: "text", raw: content, text: content }]
9546
+ }
9547
+ ];
9548
+ }
9549
+ const key = hashContent(content);
9550
+ const hit = tokenCache.get(key);
9551
+ if (hit) {
9552
+ tokenCache.delete(key);
9553
+ tokenCache.set(key, hit);
9554
+ return hit;
9555
+ }
9556
+ const tokens = marked.lexer(content);
9557
+ if (tokenCache.size >= TOKEN_CACHE_MAX) {
9558
+ const first = tokenCache.keys().next().value;
9559
+ if (first !== void 0) {
9560
+ tokenCache.delete(first);
9561
+ }
9562
+ }
9563
+ tokenCache.set(key, tokens);
9564
+ return tokens;
9565
+ }
9566
+ function formatToken(token, theme, highlight = null, listDepth = 0, orderedListNumber = null, parent = null) {
9567
+ switch (token.type) {
9568
+ case "blockquote": {
9569
+ const inner = (token.tokens ?? []).map((child) => formatToken(child, theme, highlight)).join("");
9570
+ const bar = theme.dim(BLOCKQUOTE_BAR);
9571
+ return inner.split(EOL).map((line) => stripAnsi3(line).trim() ? `${bar} ${chalk2.italic(line)}` : line).join(EOL);
9572
+ }
9573
+ case "code": {
9574
+ const codeToken = token;
9575
+ if (!highlight) {
9576
+ return codeToken.text + EOL;
9577
+ }
9578
+ let language = "plaintext";
9579
+ if (codeToken.lang && highlight.supportsLanguage(codeToken.lang)) {
9580
+ language = codeToken.lang;
9581
+ }
9582
+ return highlight.highlight(codeToken.text, { language }) + EOL;
9583
+ }
9584
+ case "codespan":
9585
+ return theme.role("codeInline", token.text);
9586
+ case "em":
9587
+ return chalk2.italic(
9588
+ (token.tokens ?? []).map((child) => formatToken(child, theme, highlight, 0, null, parent)).join("")
9589
+ );
9590
+ case "strong":
9591
+ return chalk2.bold(
9592
+ (token.tokens ?? []).map((child) => formatToken(child, theme, highlight, 0, null, parent)).join("")
9593
+ );
9594
+ case "heading": {
9595
+ const headingToken = token;
9596
+ const inner = (headingToken.tokens ?? []).map((child) => formatToken(child, theme, highlight)).join("");
9597
+ const colored = theme.role("heading", inner);
9598
+ if (headingToken.depth === 1) {
9599
+ return chalk2.bold.italic.underline(colored) + EOL + EOL;
9600
+ }
9601
+ return chalk2.bold(colored) + EOL + EOL;
9602
+ }
9603
+ case "hr":
9604
+ return "---";
9605
+ case "image":
9606
+ return token.href;
9607
+ case "link": {
9608
+ const linkToken = token;
9609
+ if (linkToken.href.startsWith("mailto:")) {
9610
+ return linkToken.href.replace(/^mailto:/, "");
9611
+ }
9612
+ const linkText = (linkToken.tokens ?? []).map((child) => formatToken(child, theme, highlight, 0, null, linkToken)).join("");
9613
+ const plainLinkText = stripAnsi3(linkText);
9614
+ const display = plainLinkText && plainLinkText !== linkToken.href ? linkText : linkToken.href;
9615
+ return `\x1B]8;;${linkToken.href}\x07${display}\x1B]8;;\x07`;
9616
+ }
9617
+ case "list": {
9618
+ const listToken = token;
9619
+ const start = typeof listToken.start === "number" ? listToken.start : Number(listToken.start) || 1;
9620
+ return listToken.items.map(
9621
+ (item, index) => formatToken(
9622
+ item,
9623
+ theme,
9624
+ highlight,
9625
+ listDepth,
9626
+ listToken.ordered ? start + index : null,
9627
+ listToken
9628
+ )
9629
+ ).join("");
9630
+ }
9631
+ case "list_item":
9632
+ return (token.tokens ?? []).map(
9633
+ (child) => `${" ".repeat(listDepth)}${formatToken(child, theme, highlight, listDepth + 1, orderedListNumber, token)}`
9634
+ ).join("");
9635
+ case "paragraph":
9636
+ return (token.tokens ?? []).map((child) => formatToken(child, theme, highlight)).join("") + EOL;
9637
+ case "space":
9638
+ case "br":
9639
+ return EOL;
9640
+ case "text": {
9641
+ const textToken = token;
9642
+ if (parent?.type === "link") {
9643
+ return textToken.text;
9644
+ }
9645
+ if (parent?.type === "list_item") {
9646
+ const marker = orderedListNumber === null ? "-" : `${getListNumber(listDepth, orderedListNumber)}.`;
9647
+ const body = textToken.tokens ? textToken.tokens.map((child) => formatToken(child, theme, highlight, listDepth, orderedListNumber, token)).join("") : textToken.text;
9648
+ return `${marker} ${body}${EOL}`;
9649
+ }
9650
+ return textToken.text;
9651
+ }
9652
+ case "escape":
9653
+ return token.text;
9654
+ case "table":
9655
+ case "def":
9656
+ case "del":
9657
+ case "html":
9658
+ return "";
9659
+ default:
9660
+ return "";
9661
+ }
9662
+ }
9663
+ function numberToLetter(n) {
9664
+ let result = "";
9665
+ while (n > 0) {
9666
+ n--;
9667
+ result = String.fromCharCode(97 + n % 26) + result;
9668
+ n = Math.floor(n / 26);
9669
+ }
9670
+ return result;
9671
+ }
9672
+ var ROMAN_VALUES = [
9673
+ [1e3, "m"],
9674
+ [900, "cm"],
9675
+ [500, "d"],
9676
+ [400, "cd"],
9677
+ [100, "c"],
9678
+ [90, "xc"],
9679
+ [50, "l"],
9680
+ [40, "xl"],
9681
+ [10, "x"],
9682
+ [9, "ix"],
9683
+ [5, "v"],
9684
+ [4, "iv"],
9685
+ [1, "i"]
9686
+ ];
9687
+ function numberToRoman(n) {
9688
+ let result = "";
9689
+ for (const [value, numeral] of ROMAN_VALUES) {
9690
+ while (n >= value) {
9691
+ result += numeral;
9692
+ n -= value;
9693
+ }
9694
+ }
9695
+ return result;
9696
+ }
9697
+ function getListNumber(listDepth, orderedListNumber) {
9698
+ switch (listDepth) {
9699
+ case 0:
9700
+ case 1:
9701
+ return orderedListNumber.toString();
9702
+ case 2:
9703
+ return numberToLetter(orderedListNumber);
9704
+ case 3:
9705
+ return numberToRoman(orderedListNumber);
9706
+ default:
9707
+ return orderedListNumber.toString();
9708
+ }
9709
+ }
9710
+ function padAligned(content, displayWidth, targetWidth, align) {
9711
+ const padding = Math.max(0, targetWidth - displayWidth);
9712
+ if (align === "center") {
9713
+ const leftPad = Math.floor(padding / 2);
9714
+ return " ".repeat(leftPad) + content + " ".repeat(padding - leftPad);
9715
+ }
9716
+ if (align === "right") {
9717
+ return " ".repeat(padding) + content;
9718
+ }
9719
+ return content + " ".repeat(padding);
9720
+ }
9721
+ function stringWidth(text) {
9722
+ return visibleWidth(text);
9723
+ }
9724
+
9725
+ // src/react-ink/markdown/highlight.ts
9726
+ import { extname } from "node:path";
9727
+ import hljs from "highlight.js";
9728
+ function scopeToRole(scope) {
9729
+ const head = scope.split(".")[0] ?? scope;
9730
+ switch (head) {
9731
+ case "keyword":
9732
+ case "built_in":
9733
+ case "literal":
9734
+ case "operator":
9735
+ return "synKeyword";
9736
+ case "string":
9737
+ case "regexp":
9738
+ case "symbol":
9739
+ case "char":
9740
+ case "meta":
9741
+ return "synString";
9742
+ case "number":
9743
+ return "synNumber";
9744
+ case "comment":
9745
+ case "quote":
9746
+ return "synComment";
9747
+ case "type":
9748
+ case "class":
9749
+ case "title":
9750
+ case "tag":
9751
+ case "name":
9752
+ case "attr":
9753
+ case "attribute":
9754
+ case "selector":
9755
+ return "synType";
9756
+ default:
9757
+ return null;
9758
+ }
9759
+ }
9760
+ var HTML_ENTITIES = {
9761
+ "&amp;": "&",
9762
+ "&lt;": "<",
9763
+ "&gt;": ">",
9764
+ "&quot;": '"',
9765
+ "&#x27;": "'",
9766
+ "&#39;": "'"
9767
+ };
9768
+ function decodeEntities2(text) {
9769
+ return text.replace(/&(?:amp|lt|gt|quot|#x27|#39);/g, (match) => HTML_ENTITIES[match] ?? match);
9770
+ }
9771
+ function parseHljsHtml(html) {
9772
+ const nodes = [];
9773
+ const scopeStack = [];
9774
+ const tagRe = /<span class="hljs-([^"]+)">|<\/span>/g;
9775
+ let lastIndex = 0;
9776
+ let match;
9777
+ const pushText = (raw) => {
9778
+ if (!raw) return;
9779
+ const currentScope = scopeStack.length > 0 ? scopeStack[scopeStack.length - 1] : null;
9780
+ const scope = currentScope ? currentScope.split(/\s+/)[0].replace(/_$/, "") : null;
9781
+ nodes.push({ text: decodeEntities2(raw), scope });
9782
+ };
9783
+ while ((match = tagRe.exec(html)) !== null) {
9784
+ pushText(html.slice(lastIndex, match.index));
9785
+ lastIndex = tagRe.lastIndex;
9786
+ if (match[0] === "</span>") {
9787
+ scopeStack.pop();
9788
+ } else if (match[1]) {
9789
+ scopeStack.push(match[1]);
9790
+ }
9791
+ }
9792
+ pushText(html.slice(lastIndex));
9793
+ return nodes;
9794
+ }
9795
+ function createHighlighter(theme) {
9796
+ return {
9797
+ supportsLanguage: (language) => {
9798
+ if (!language || language === "plaintext" || language === "text") {
9799
+ return false;
9800
+ }
9801
+ try {
9802
+ return hljs.getLanguage(language) !== void 0;
9803
+ } catch {
9804
+ return false;
9805
+ }
9806
+ },
9807
+ highlight: (code, options) => {
9808
+ const language = options.language;
9809
+ if (!language || language === "plaintext" || language === "text") {
9810
+ return code;
9811
+ }
9812
+ try {
9813
+ if (hljs.getLanguage(language) === void 0) {
9814
+ return code;
9815
+ }
9816
+ const { value } = hljs.highlight(code, { language, ignoreIllegals: true });
9817
+ return parseHljsHtml(value).map((node) => {
9818
+ const role = node.scope ? scopeToRole(node.scope) : null;
9819
+ return role ? theme.role(role, node.text) : node.text;
9820
+ }).join("");
9821
+ } catch {
9822
+ return code;
9823
+ }
9824
+ }
9825
+ };
9826
+ }
9827
+ function highlightByPath(code, filePath, theme) {
9828
+ const language = languageFromPath(filePath);
9829
+ if (!language) {
9830
+ return code;
9831
+ }
9832
+ const highlighter = createHighlighter(theme);
9833
+ if (!highlighter.supportsLanguage(language)) {
9834
+ return code;
9835
+ }
9836
+ return highlighter.highlight(code, { language });
9837
+ }
9838
+ var EXTENSION_LANGUAGES = {
9839
+ ts: "typescript",
9840
+ tsx: "typescript",
9841
+ mts: "typescript",
9842
+ cts: "typescript",
9843
+ js: "javascript",
9844
+ jsx: "javascript",
9845
+ mjs: "javascript",
9846
+ cjs: "javascript",
9847
+ py: "python",
9848
+ rb: "ruby",
9849
+ rs: "rust",
9850
+ go: "go",
9851
+ java: "java",
9852
+ kt: "kotlin",
9853
+ c: "c",
9854
+ h: "c",
9855
+ cc: "cpp",
9856
+ cpp: "cpp",
9857
+ hpp: "cpp",
9858
+ cs: "csharp",
9859
+ sh: "bash",
9860
+ bash: "bash",
9861
+ zsh: "bash",
9862
+ yml: "yaml",
9863
+ yaml: "yaml",
9864
+ json: "json",
9865
+ md: "markdown",
9866
+ html: "xml",
9867
+ xml: "xml",
9868
+ css: "css",
9869
+ scss: "scss",
9870
+ sql: "sql",
9871
+ toml: "ini",
9872
+ ini: "ini",
9873
+ php: "php",
9874
+ swift: "swift"
9875
+ };
9876
+ function languageFromPath(filePath) {
9877
+ const ext = extname(filePath).slice(1).toLowerCase();
9878
+ if (!ext) {
9879
+ return null;
9880
+ }
9881
+ const mapped = EXTENSION_LANGUAGES[ext];
9882
+ if (mapped) {
9883
+ return mapped;
9884
+ }
9885
+ try {
9886
+ return hljs.getLanguage(ext) !== void 0 ? ext : null;
9887
+ } catch {
9888
+ return null;
9889
+ }
9890
+ }
9891
+
9892
+ // src/react-ink/markdown/MarkdownTable.tsx
9893
+ import chalk3 from "chalk";
9894
+ import stripAnsi4 from "strip-ansi";
9895
+ var MIN_COLUMN_WIDTH = 3;
9896
+ var COLUMN_GAP = 2;
9897
+ function MarkdownTable({ token, theme, highlight = null }) {
9898
+ const formatCell = (tokens) => (tokens ?? []).map((child) => formatToken(child, theme, highlight)).join("");
9899
+ const displayWidth = (tokens) => stringWidth(stripAnsi4(formatCell(tokens)));
9900
+ const columnCount = token.header.length;
9901
+ const columnWidths = token.header.map((header, index) => {
9902
+ let max = displayWidth(header.tokens);
9903
+ for (const row of token.rows) {
9904
+ max = Math.max(max, displayWidth(row[index]?.tokens));
9905
+ }
9906
+ return Math.max(max, MIN_COLUMN_WIDTH);
9907
+ });
9908
+ const renderRow2 = (cells, key, bold) => /* @__PURE__ */ jsx(Box, { flexDirection: "row", children: columnWidths.map((width, index) => {
9909
+ const cell = cells[index];
9910
+ const content = formatCell(cell?.tokens);
9911
+ const visible = stringWidth(stripAnsi4(content));
9912
+ const align = token.align?.[index];
9913
+ const padded = padAligned(content, visible, width, align ?? "left");
9914
+ const styled = bold ? chalk3.bold(padded) : padded;
9915
+ const gap = index < columnCount - 1 ? " ".repeat(COLUMN_GAP) : "";
9916
+ return /* @__PURE__ */ jsxs(Text, { children: [
9917
+ styled,
9918
+ gap
9919
+ ] }, index);
9920
+ }) }, key);
9921
+ const separator = /* @__PURE__ */ jsx(Box, { flexDirection: "row", children: columnWidths.map((width, index) => {
9922
+ const gap = index < columnCount - 1 ? " ".repeat(COLUMN_GAP) : "";
9923
+ return /* @__PURE__ */ jsxs(Text, { children: [
9924
+ theme.dim("-".repeat(width)),
9925
+ gap
9926
+ ] }, index);
9927
+ }) });
9928
+ return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
9929
+ renderRow2(token.header, "header", true),
9930
+ separator,
9931
+ token.rows.map((row, rowIndex) => renderRow2(row, `row-${rowIndex}`, false))
9932
+ ] });
9933
+ }
9934
+
9935
+ // src/react-ink/markdown/Markdown.tsx
9936
+ function stripPromptXMLTags(text) {
9937
+ return text.replace(/<\/?(?:system-reminder|prompt|context)[^>]*>/g, "");
9938
+ }
9939
+ function Markdown({ children, theme, highlightCode = true, dim = false }) {
9940
+ const highlight = useMemo(
9941
+ () => highlightCode ? createHighlighter(theme) : null,
9942
+ [highlightCode, theme]
9943
+ );
9944
+ const elements = useMemo(() => {
9945
+ configureMarked();
9946
+ const tokens = cachedLexer(stripPromptXMLTags(children));
9947
+ const out = [];
9948
+ let buffer = "";
9949
+ const flush = () => {
9950
+ if (buffer) {
9951
+ const text = buffer.replace(/\n+$/, "");
9952
+ if (text) {
9953
+ out.push(
9954
+ /* @__PURE__ */ jsx(Text, { children: dim ? theme.dim(text) : text }, out.length)
9955
+ );
9956
+ }
9957
+ buffer = "";
9958
+ }
9959
+ };
9960
+ for (const token of tokens) {
9961
+ if (token.type === "table") {
9962
+ flush();
9963
+ out.push(
9964
+ /* @__PURE__ */ jsx(
9965
+ MarkdownTable,
9966
+ {
9967
+ token,
9968
+ theme,
9969
+ highlight
9970
+ },
9971
+ out.length
9972
+ )
9973
+ );
9974
+ } else {
9975
+ buffer += formatToken(token, theme, highlight);
9976
+ }
9977
+ }
9978
+ flush();
9979
+ return out;
9980
+ }, [children, dim, highlight, theme]);
9981
+ return /* @__PURE__ */ jsx(Box, { flexDirection: "column", children: elements });
9982
+ }
9983
+
8891
9984
  // src/react-ink/utils/tool-display.ts
8892
9985
  import { homedir as homedir2 } from "node:os";
9986
+
9987
+ // src/react-ink/diff/structured.ts
9988
+ import { structuredPatch } from "diff";
9989
+
9990
+ // src/react-ink/diff/word-diff.ts
9991
+ import { diffWordsWithSpace } from "diff";
9992
+ var CHANGE_THRESHOLD = 0.4;
9993
+ function wordDiffLine(oldLine, newLine, side) {
9994
+ const lineText = side === "removed" ? oldLine : newLine;
9995
+ if (oldLine.length === 0 || newLine.length === 0) {
9996
+ return [{ text: lineText, changed: true }];
9997
+ }
9998
+ let changes;
9999
+ try {
10000
+ changes = diffWordsWithSpace(oldLine, newLine);
10001
+ } catch {
10002
+ return [{ text: lineText, changed: true }];
10003
+ }
10004
+ let changedChars = 0;
10005
+ let totalChars = 0;
10006
+ for (const change of changes) {
10007
+ totalChars += change.value.length;
10008
+ if (change.added || change.removed) {
10009
+ changedChars += change.value.length;
10010
+ }
10011
+ }
10012
+ const fraction = totalChars === 0 ? 0 : changedChars / totalChars;
10013
+ if (fraction > CHANGE_THRESHOLD) {
10014
+ return [{ text: lineText, changed: true }];
10015
+ }
10016
+ const spans = [];
10017
+ for (const change of changes) {
10018
+ const belongs = side === "removed" ? !change.added : !change.removed;
10019
+ if (!belongs) {
10020
+ continue;
10021
+ }
10022
+ spans.push({ text: change.value, changed: Boolean(change.added || change.removed) });
10023
+ }
10024
+ return spans.length > 0 ? spans : [{ text: lineText, changed: false }];
10025
+ }
10026
+
10027
+ // src/react-ink/diff/structured.ts
10028
+ var CONTEXT_LINES = 3;
10029
+ function buildStructuredDiff(oldStr, newStr, filePath = "") {
10030
+ if (oldStr === newStr) {
10031
+ return null;
10032
+ }
10033
+ let patch;
10034
+ try {
10035
+ patch = structuredPatch(filePath, filePath, oldStr, newStr, "", "", { context: CONTEXT_LINES });
10036
+ } catch {
10037
+ return null;
10038
+ }
10039
+ const hunks = [];
10040
+ let addedCount = 0;
10041
+ let removedCount = 0;
10042
+ for (const hunk of patch.hunks) {
10043
+ const lines = classifyHunkLines(hunk);
10044
+ for (const line of lines) {
10045
+ if (line.kind === "added") addedCount += 1;
10046
+ else if (line.kind === "removed") removedCount += 1;
10047
+ }
10048
+ if (lines.length > 0) {
10049
+ hunks.push({ oldStart: hunk.oldStart, newStart: hunk.newStart, lines });
10050
+ }
10051
+ }
10052
+ if (hunks.length === 0) {
10053
+ return null;
10054
+ }
10055
+ return { hunks, addedCount, removedCount };
10056
+ }
10057
+ function classifyHunkLines(hunk) {
10058
+ const out = [];
10059
+ let oldNum = hunk.oldStart;
10060
+ let newNum = hunk.newStart;
10061
+ const rawLines = hunk.lines.filter((line) => !line.startsWith("\\"));
10062
+ let removedRun = [];
10063
+ let removedRunStart = -1;
10064
+ const flushPairing = (addedRun2) => {
10065
+ const pairs = Math.min(removedRun.length, addedRun2.length);
10066
+ for (let i = 0; i < pairs; i += 1) {
10067
+ const removed = removedRun[i];
10068
+ const added = addedRun2[i];
10069
+ removed.spans = wordDiffLine(removed.text, added.text, "removed");
10070
+ added.spans = wordDiffLine(removed.text, added.text, "added");
10071
+ }
10072
+ removedRun = [];
10073
+ removedRunStart = -1;
10074
+ };
10075
+ let addedRun = [];
10076
+ for (const raw of rawLines) {
10077
+ const marker = raw[0];
10078
+ const text = raw.slice(1);
10079
+ if (marker === "-") {
10080
+ if (addedRun.length > 0) {
10081
+ addedRun = [];
10082
+ }
10083
+ const line = { kind: "removed", oldLine: oldNum, text };
10084
+ out.push(line);
10085
+ if (removedRunStart === -1) removedRunStart = out.length - 1;
10086
+ removedRun.push(line);
10087
+ oldNum += 1;
10088
+ } else if (marker === "+") {
10089
+ const line = { kind: "added", newLine: newNum, text };
10090
+ out.push(line);
10091
+ addedRun.push(line);
10092
+ newNum += 1;
10093
+ } else {
10094
+ if (removedRun.length > 0 && addedRun.length > 0) {
10095
+ flushPairing(addedRun);
10096
+ }
10097
+ removedRun = [];
10098
+ removedRunStart = -1;
10099
+ addedRun = [];
10100
+ out.push({ kind: "context", oldLine: oldNum, newLine: newNum, text });
10101
+ oldNum += 1;
10102
+ newNum += 1;
10103
+ }
10104
+ }
10105
+ if (removedRun.length > 0 && addedRun.length > 0) {
10106
+ flushPairing(addedRun);
10107
+ }
10108
+ return out;
10109
+ }
10110
+
10111
+ // src/react-ink/utils/tool-display.ts
8893
10112
  function asRecord2(value) {
8894
10113
  return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
8895
10114
  }
@@ -8972,6 +10191,12 @@ function clipBody(value, maxLines = 8, maxChars = 1400) {
8972
10191
  }
8973
10192
  return previewMultiline(value, { maxLines, maxChars });
8974
10193
  }
10194
+ function highlightedBody(body, filePath, theme) {
10195
+ if (!body || !theme || !filePath || languageFromPath(filePath) === null) {
10196
+ return { body };
10197
+ }
10198
+ return { body: highlightByPath(body, filePath, theme), preformatted: true };
10199
+ }
8975
10200
  function extractDetails(value) {
8976
10201
  return asRecord2(asRecord2(value)?.details);
8977
10202
  }
@@ -9038,7 +10263,7 @@ function fallbackBody(output, fallbackOutputText, showImages = false) {
9038
10263
  return clipBody(fallbackOutputText);
9039
10264
  }
9040
10265
  function describeToolSource(source) {
9041
- const { args, fallbackArgsText, fallbackOutputText, output, showImages = false, toolName } = source;
10266
+ const { args, fallbackArgsText, fallbackOutputText, output, showImages = false, theme, toolName } = source;
9042
10267
  const argsRecord = asRecord2(args);
9043
10268
  const details = extractDetails(output);
9044
10269
  const outputText = toolText(output, showImages);
@@ -9056,10 +10281,12 @@ function describeToolSource(source) {
9056
10281
  summary += `:${start}${end ? `-${end}` : ""}`;
9057
10282
  }
9058
10283
  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";
10284
+ const readBody = hasResult && !containsImage(output) ? highlightedBody(clipBody(outputText, 10, 1800), rawPath, theme) : { body: hasResult ? clipBody(outputText, 10, 1800) : void 0 };
9059
10285
  return {
9060
10286
  title,
9061
10287
  summary: hasResult ? responseSummary : summary,
9062
- body: hasResult ? clipBody(outputText, 10, 1800) : void 0,
10288
+ body: readBody.body,
10289
+ preformatted: readBody.preformatted,
9063
10290
  emptyText: "Reading file..."
9064
10291
  };
9065
10292
  }
@@ -9067,10 +10294,14 @@ function describeToolSource(source) {
9067
10294
  const rawPath = asString(argsRecord?.file_path) ?? asString(argsRecord?.path) ?? "";
9068
10295
  const content = asString(argsRecord?.content);
9069
10296
  const responseSummary = firstMeaningfulLine(outputText) ?? "Wrote file";
10297
+ const writeDiff = !hasResult && content !== void 0 && content.length > 0 ? buildStructuredDiff("", content, rawPath) ?? void 0 : void 0;
10298
+ const writeBody = hasResult ? { body: clipBody(outputText, 8, 1500) } : highlightedBody(clipBody(content, 8, 1500), rawPath, theme);
9070
10299
  return {
9071
10300
  title,
9072
10301
  summary: hasResult ? responseSummary : rawPath ? shortenPath2(rawPath) : void 0,
9073
- body: hasResult ? clipBody(outputText, 8, 1500) : clipBody(content, 8, 1500),
10302
+ body: writeDiff ? void 0 : writeBody.body,
10303
+ preformatted: writeDiff ? void 0 : writeBody.preformatted,
10304
+ diff: writeDiff,
9074
10305
  emptyText: "Preparing file write..."
9075
10306
  };
9076
10307
  }
@@ -9087,10 +10318,15 @@ function describeToolSource(source) {
9087
10318
  400
9088
10319
  ) : void 0;
9089
10320
  const responseSummary = firstMeaningfulLine(outputText) ?? "Applied edit";
10321
+ const editDiff = oldText !== void 0 && newText !== void 0 ? buildStructuredDiff(oldText, newText, rawPath) ?? void 0 : void 0;
10322
+ const editBody = hasResult ? highlightedBody(clipBody(outputText, 8, 1500), rawPath, theme) : { body: replacementPreview };
10323
+ const editChangeSummary = editDiff ? `+${editDiff.addedCount} -${editDiff.removedCount}` : void 0;
9090
10324
  return {
9091
10325
  title,
9092
- summary: hasResult ? responseSummary : rawPath ? shortenPath2(rawPath) : void 0,
9093
- body: hasResult ? clipBody(outputText, 8, 1500) : replacementPreview,
10326
+ summary: hasResult ? editChangeSummary ?? responseSummary : editChangeSummary ?? (rawPath ? shortenPath2(rawPath) : void 0),
10327
+ body: editDiff ? void 0 : editBody.body,
10328
+ preformatted: editDiff ? void 0 : editBody.preformatted,
10329
+ diff: editDiff,
9094
10330
  emptyText: "Applying edit..."
9095
10331
  };
9096
10332
  }
@@ -9187,7 +10423,7 @@ function describeToolCall(toolCall) {
9187
10423
  args: toolCall.arguments
9188
10424
  });
9189
10425
  }
9190
- function describeToolResult(message, showImages, args) {
10426
+ function describeToolResult(message, showImages, args, theme) {
9191
10427
  return describeToolSource({
9192
10428
  args,
9193
10429
  toolName: message.toolName,
@@ -9195,12 +10431,13 @@ function describeToolResult(message, showImages, args) {
9195
10431
  content: message.content,
9196
10432
  details: message.details
9197
10433
  },
9198
- showImages
10434
+ showImages,
10435
+ theme
9199
10436
  });
9200
10437
  }
9201
10438
 
9202
10439
  // src/react-ink/components/ToolEventBlock.tsx
9203
- import stripAnsi3 from "strip-ansi";
10440
+ import stripAnsi5 from "strip-ansi";
9204
10441
  function statusMarker(status) {
9205
10442
  switch (status) {
9206
10443
  case "error":
@@ -9211,12 +10448,15 @@ function statusMarker(status) {
9211
10448
  return ">";
9212
10449
  }
9213
10450
  }
10451
+ function statusColorKey(status) {
10452
+ return status === "error" ? "error" : "text";
10453
+ }
9214
10454
  function plainToolText(text) {
9215
- return stripAnsi3(text);
10455
+ return stripAnsi5(text);
9216
10456
  }
9217
10457
  function splitVisibleLines(text) {
9218
10458
  return (text ?? "").split(/\r?\n/).map((line) => line.trimEnd()).filter((line, index, lines) => {
9219
- if (line.length > 0) {
10459
+ if (stripAnsi5(line).length > 0) {
9220
10460
  return true;
9221
10461
  }
9222
10462
  return index !== 0 && index !== lines.length - 1;
@@ -9247,11 +10487,12 @@ function ToolEventBlock({
9247
10487
  indent = 0,
9248
10488
  marginBottom = 1,
9249
10489
  maxContentLines = 10,
10490
+ preformatted = false,
9250
10491
  showSummaryInline = false,
9251
10492
  showTitle = true,
9252
10493
  status,
9253
10494
  summary,
9254
- theme: _theme,
10495
+ theme,
9255
10496
  title
9256
10497
  }) {
9257
10498
  const normalizedSummary = summary?.trim();
@@ -9267,19 +10508,19 @@ function ToolEventBlock({
9267
10508
  const { visibleLines, hiddenLineCount } = clampContentLines(combinedLines, maxContentLines);
9268
10509
  return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginBottom, marginLeft: indent, children: [
9269
10510
  showTitle ? /* @__PURE__ */ jsxs(Box, { children: [
9270
- /* @__PURE__ */ jsx(Text, { color: "white", children: plainToolText(`${statusMarker(status)} `) }),
9271
- /* @__PURE__ */ jsx(Text, { color: "white", children: plainToolText(title) }),
9272
- showSummaryInline && normalizedSummary ? /* @__PURE__ */ jsx(Text, { color: "white", children: plainToolText(` (${normalizedSummary})`) }) : null
10511
+ /* @__PURE__ */ jsx(Text, { children: theme.color(statusColorKey(status), plainToolText(`${statusMarker(status)} `)) }),
10512
+ /* @__PURE__ */ jsx(Text, { children: theme.color(statusColorKey(status), plainToolText(title)) }),
10513
+ showSummaryInline && normalizedSummary ? /* @__PURE__ */ jsx(Text, { children: theme.muted(plainToolText(` (${normalizedSummary})`)) }) : null
9273
10514
  ] }) : null,
9274
10515
  visibleLines.map((line, index) => /* @__PURE__ */ jsx(
9275
10516
  Box,
9276
10517
  {
9277
10518
  marginLeft: line.kind === "response" ? showTitle ? 2 : 0 : showTitle ? 4 : 2,
9278
- children: /* @__PURE__ */ jsx(Text, { color: "white", children: plainToolText(line.text) })
10519
+ children: preformatted ? /* @__PURE__ */ jsx(Text, { children: line.text }) : /* @__PURE__ */ jsx(Text, { children: theme.color("text", plainToolText(line.text)) })
9279
10520
  },
9280
- `${line.kind}:${index}:${line.text}`
10521
+ `${line.kind}:${index}:${stripAnsi5(line.text)}`
9281
10522
  )),
9282
- hiddenLineCount > 0 ? /* @__PURE__ */ jsx(Box, { marginLeft: showTitle ? 4 : 2, children: /* @__PURE__ */ jsx(Text, { color: "white", children: plainToolText(`... ${hiddenLineCount} more line(s)`) }) }) : null
10523
+ hiddenLineCount > 0 ? /* @__PURE__ */ jsx(Box, { marginLeft: showTitle ? 4 : 2, children: /* @__PURE__ */ jsx(Text, { children: theme.muted(plainToolText(`... ${hiddenLineCount} more line(s)`)) }) }) : null
9283
10524
  ] });
9284
10525
  }
9285
10526
 
@@ -9302,17 +10543,100 @@ function ToolCallMessage({ theme, toolCall }) {
9302
10543
  );
9303
10544
  }
9304
10545
 
10546
+ // src/react-ink/diff/Diff.tsx
10547
+ import chalk4 from "chalk";
10548
+ function lineMarker(kind) {
10549
+ switch (kind) {
10550
+ case "added":
10551
+ return "+";
10552
+ case "removed":
10553
+ return "-";
10554
+ default:
10555
+ return " ";
10556
+ }
10557
+ }
10558
+ function gutterWidth(diff) {
10559
+ let max = 1;
10560
+ for (const hunk of diff.hunks) {
10561
+ for (const line of hunk.lines) {
10562
+ const num2 = line.kind === "removed" ? line.oldLine : line.newLine;
10563
+ if (num2 !== void 0) {
10564
+ max = Math.max(max, stringWidth(String(num2)));
10565
+ }
10566
+ }
10567
+ }
10568
+ return max;
10569
+ }
10570
+ function renderLineText(line, theme, bgRole, fgRole) {
10571
+ const paintSpan = (text, changed) => {
10572
+ const fg = theme.role(fgRole, text);
10573
+ return changed ? chalk4.bold(fg) : fg;
10574
+ };
10575
+ let body;
10576
+ if (line.spans && line.spans.length > 0) {
10577
+ body = line.spans.map((span) => paintSpan(span.text, span.changed)).join("");
10578
+ } else {
10579
+ body = theme.role(fgRole, line.text);
10580
+ }
10581
+ return bgRole ? theme.roleBackground(bgRole, body) : body;
10582
+ }
10583
+ function Diff({ diff, theme, indent = 0, marginBottom = 0 }) {
10584
+ const gutter = gutterWidth(diff);
10585
+ const renderLine = (line, key) => {
10586
+ const num2 = line.kind === "removed" ? line.oldLine : line.newLine;
10587
+ const gutterText = (num2 !== void 0 ? String(num2) : "").padStart(gutter);
10588
+ const marker = lineMarker(line.kind);
10589
+ const bgRole = line.kind === "added" ? "diffAddedBg" : line.kind === "removed" ? "diffRemovedBg" : null;
10590
+ const fgRole = line.kind === "added" ? "diffAddedText" : line.kind === "removed" ? "diffRemovedText" : "blockquoteBar";
10591
+ const gutterStyled = theme.dim(`${gutterText} `);
10592
+ const markerStyled = line.kind === "context" ? theme.dim(`${marker} `) : theme.role(fgRole, `${marker} `);
10593
+ const text = renderLineText(line, theme, bgRole, fgRole);
10594
+ return /* @__PURE__ */ jsx(Box, { children: /* @__PURE__ */ jsxs(Text, { children: [
10595
+ gutterStyled,
10596
+ markerStyled,
10597
+ text
10598
+ ] }) }, key);
10599
+ };
10600
+ return /* @__PURE__ */ jsx(Box, { flexDirection: "column", marginLeft: indent, marginBottom, children: diff.hunks.map((hunk, hunkIndex) => /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
10601
+ hunkIndex > 0 ? /* @__PURE__ */ jsx(Box, { children: /* @__PURE__ */ jsx(Text, { children: theme.dim("...") }) }) : null,
10602
+ hunk.lines.map((line, lineIndex) => renderLine(line, `${hunkIndex}-${lineIndex}`))
10603
+ ] }, `hunk-${hunkIndex}`)) });
10604
+ }
10605
+
9305
10606
  // src/react-ink/components/messages/ToolResultBlock.tsx
9306
10607
  function ToolResultBlock({ expanded = false, message, nested = false, showImages, theme, toolCall }) {
9307
- const descriptor = describeToolResult(message, showImages, toolCall?.arguments);
10608
+ const descriptor = describeToolResult(message, showImages, toolCall?.arguments, theme);
10609
+ const indent = nested ? 4 : 2;
10610
+ if (descriptor.diff) {
10611
+ return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginBottom: 0, children: [
10612
+ /* @__PURE__ */ jsx(
10613
+ ToolEventBlock,
10614
+ {
10615
+ detail: void 0,
10616
+ emptyText: descriptor.emptyText,
10617
+ indent,
10618
+ marginBottom: 0,
10619
+ maxContentLines: 0,
10620
+ showSummaryInline: false,
10621
+ showTitle: !nested,
10622
+ status: message.isError ? "error" : "success",
10623
+ summary: descriptor.summary,
10624
+ theme,
10625
+ title: descriptor.title
10626
+ }
10627
+ ),
10628
+ /* @__PURE__ */ jsx(Diff, { diff: descriptor.diff, theme, indent: indent + 2 })
10629
+ ] });
10630
+ }
9308
10631
  return /* @__PURE__ */ jsx(
9309
10632
  ToolEventBlock,
9310
10633
  {
9311
10634
  detail: descriptor.body,
9312
10635
  emptyText: descriptor.emptyText,
9313
- indent: nested ? 4 : 2,
10636
+ indent,
9314
10637
  marginBottom: 0,
9315
10638
  maxContentLines: expanded ? Number.MAX_SAFE_INTEGER : 10,
10639
+ preformatted: descriptor.preformatted,
9316
10640
  showSummaryInline: false,
9317
10641
  showTitle: !nested,
9318
10642
  status: message.isError ? "error" : "success",
@@ -9337,7 +10661,7 @@ function AssistantMessageView({
9337
10661
  const matchedToolResultIds = /* @__PURE__ */ new Set();
9338
10662
  return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [
9339
10663
  /* @__PURE__ */ jsx(Text, { children: theme.color("accent", `Assistant ${message.provider}/${message.model} ${formatMessageTimestamp(message.timestamp)}`) }),
9340
- parts.text ? /* @__PURE__ */ jsx(Text, { children: parts.text }) : null,
10664
+ parts.text ? /* @__PURE__ */ jsx(Markdown, { theme, children: parts.text }) : null,
9341
10665
  parts.thinking.map((thinking, index) => /* @__PURE__ */ jsx(Text, { children: theme.muted(`[thinking] ${thinking}`) }, index)),
9342
10666
  parts.toolCalls.map((toolCall) => {
9343
10667
  const toolResult = toolResultsByCallId.get(toolCall.id);
@@ -9539,20 +10863,20 @@ function MessageList({
9539
10863
  }
9540
10864
 
9541
10865
  // src/react-ink/components/StatusLine.tsx
9542
- function StatusLine({ snapshot, status, theme }) {
10866
+ function StatusLine({ snapshot, status, theme, showBusyText = true }) {
9543
10867
  let text = status?.text;
9544
10868
  let tone = status?.kind ?? "info";
9545
10869
  if (!text) {
9546
- if (snapshot.isCompacting) {
10870
+ if (showBusyText && snapshot.isCompacting) {
9547
10871
  text = "Compacting conversation context...";
9548
10872
  tone = "busy";
9549
- } else if (snapshot.isBashRunning) {
10873
+ } else if (showBusyText && snapshot.isBashRunning) {
9550
10874
  text = "Running bash command...";
9551
10875
  tone = "busy";
9552
- } else if (snapshot.pendingToolCallCount > 0 || snapshot.pendingMessageCount > 0) {
10876
+ } else if (showBusyText && (snapshot.pendingToolCallCount > 0 || snapshot.pendingMessageCount > 0)) {
9553
10877
  text = "Agent working...";
9554
10878
  tone = "busy";
9555
- } else if (snapshot.isStreaming) {
10879
+ } else if (showBusyText && snapshot.isStreaming) {
9556
10880
  text = "Agent working...";
9557
10881
  tone = "busy";
9558
10882
  } else if (snapshot.error) {