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