nexrall-code 0.5.127 → 0.5.129

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 (2) hide show
  1. package/dist/index.js +193 -30
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -146428,47 +146428,44 @@ function formatToolUse(name, input) {
146428
146428
  const argDisplay = primaryArg ? source_default.dim(" ") + source_default.white(String(primaryArg).replace(/\n/g, "\u21B5").slice(0, 80)) : "";
146429
146429
  return source_default.yellow(icons.tool) + " " + source_default.bold(name) + argDisplay;
146430
146430
  }
146431
- var ToolStreamPrinter = class _ToolStreamPrinter {
146431
+ var ToolStreamPrinter = class {
146432
+ // Only the first PREVIEW lines are printed live; the rest are COUNTED, not printed, and the
146433
+ // full text stays in the ToolOutputStore for Ctrl+O / /expand. A build log used to bury the
146434
+ // conversation under 200 lines of scrollback that nobody re-reads.
146435
+ constructor(preview = 8) {
146436
+ this.preview = preview;
146437
+ }
146432
146438
  carry = "";
146433
146439
  linesShown = 0;
146434
- // Cap visible lines per command so a runaway/very chatty process (e.g. a
146435
- // build tool re-printing a progress bar thousands of times) doesn't flood
146436
- // the terminal — the full output is still available afterward via
146437
- // formatToolResult's summary and, for large output, the spill file.
146438
- static MAX_LINES = 200;
146439
- capped = false;
146440
+ hidden = 0;
146440
146441
  feed(chunk) {
146441
- if (this.capped)
146442
- return;
146443
146442
  this.carry += chunk;
146444
146443
  const lines = this.carry.split("\n");
146445
146444
  this.carry = lines.pop() ?? "";
146446
- for (const line of lines) {
146447
- if (!this.printLine(line))
146448
- return;
146449
- }
146445
+ for (const line of lines)
146446
+ this.printLine(line);
146450
146447
  }
146451
146448
  printLine(line) {
146452
- if (this.linesShown >= _ToolStreamPrinter.MAX_LINES) {
146453
- if (!this.capped) {
146454
- this.capped = true;
146455
- console.log(source_default.dim(" \u2502 \u2026 output continues (full log in the result above once finished)"));
146456
- }
146457
- return false;
146449
+ if (this.linesShown >= this.preview) {
146450
+ this.hidden++;
146451
+ return;
146458
146452
  }
146459
146453
  this.linesShown++;
146460
146454
  console.log(source_default.dim(" \u2502 ") + source_default.gray(line.slice(0, 300)));
146461
- return true;
146455
+ }
146456
+ /** Lines counted but not printed so far (the trailing partial line is not included). */
146457
+ get hiddenLines() {
146458
+ return this.hidden;
146462
146459
  }
146463
146460
  flush() {
146464
- if (!this.capped && this.carry)
146461
+ if (this.carry)
146465
146462
  this.printLine(this.carry);
146466
146463
  this.carry = "";
146467
146464
  }
146468
146465
  reset() {
146469
146466
  this.carry = "";
146470
146467
  this.linesShown = 0;
146471
- this.capped = false;
146468
+ this.hidden = 0;
146472
146469
  }
146473
146470
  };
146474
146471
  function formatToolResult(name, result, durationMs) {
@@ -155005,9 +155002,9 @@ function ErrorOverview({ error }) {
155005
155002
  ))) : null,
155006
155003
  error.stack ? import_react13.default.createElement(Box_default, { marginTop: 1, flexDirection: "column" }, error.stack.split("\n").slice(1).map((line) => {
155007
155004
  const parsedLine = stackUtils.parseLine(line);
155008
- const lineCount = stackLineCounts.get(line) ?? 0;
155009
- stackLineCounts.set(line, lineCount + 1);
155010
- const key = `${line}-${lineCount}`;
155005
+ const lineCount2 = stackLineCounts.get(line) ?? 0;
155006
+ stackLineCounts.set(line, lineCount2 + 1);
155007
+ const key = `${line}-${lineCount2}`;
155011
155008
  if (!parsedLine?.file || !parsedLine.line || !parsedLine.column) {
155012
155009
  return import_react13.default.createElement(
155013
155010
  Box_default,
@@ -157583,6 +157580,7 @@ var BUILTIN_SLASH_COMMANDS = [
157583
157580
  { name: "cost", description: "Tokens and cost of this conversation", kind: "builtin" },
157584
157581
  { name: "status", description: "Version, model, mode, directory and session at a glance", kind: "builtin" },
157585
157582
  { name: "copy", description: "Copy the last reply to the clipboard", kind: "builtin" },
157583
+ { name: "expand", description: "Show the full output of a folded tool call (Ctrl+O)", argHint: "[n]", kind: "builtin" },
157586
157584
  { name: "export", description: "Write this conversation to a Markdown file", argHint: "[file]", kind: "builtin" },
157587
157585
  { name: "rename", description: "Rename this session", argHint: "<title>", requiresArg: true, kind: "builtin" },
157588
157586
  { name: "diff", description: "Show uncommitted changes in the working directory", kind: "builtin" },
@@ -157696,6 +157694,9 @@ function windowStart(selected, total, size) {
157696
157694
  const start = selected - Math.floor(size / 2);
157697
157695
  return Math.max(0, Math.min(start, total - size));
157698
157696
  }
157697
+ function reserveMenuRows(previous, showing) {
157698
+ return Math.max(0, previous, showing);
157699
+ }
157699
157700
 
157700
157701
  // src/commands/bangShell.ts
157701
157702
  import { spawn } from "child_process";
@@ -158394,6 +158395,7 @@ var App2 = ({ onReady }) => {
158394
158395
  const slashEntriesRef = (0, import_react35.useRef)(BUILTIN_SLASH_COMMANDS);
158395
158396
  const menuSelRef = (0, import_react35.useRef)({ query: "", idx: 0 });
158396
158397
  const menuDismissedRef = (0, import_react35.useRef)(null);
158398
+ const menuReserveRef = (0, import_react35.useRef)(0);
158397
158399
  const [, bumpMenu] = (0, import_react35.useState)(0);
158398
158400
  const searchRef = (0, import_react35.useRef)(null);
158399
158401
  const draftBeforeSearchRef = (0, import_react35.useRef)("");
@@ -158405,6 +158407,10 @@ var App2 = ({ onReady }) => {
158405
158407
  const onHelp = (0, import_react35.useCallback)((cb) => {
158406
158408
  onHelpRef.current = cb;
158407
158409
  }, []);
158410
+ const onExpandRef = (0, import_react35.useRef)(null);
158411
+ const onExpand = (0, import_react35.useCallback)((cb) => {
158412
+ onExpandRef.current = cb;
158413
+ }, []);
158408
158414
  const mentionSelRef = (0, import_react35.useRef)({ query: "", idx: 0 });
158409
158415
  const mentionDismissedRef = (0, import_react35.useRef)(null);
158410
158416
  const mentionRootRef = (0, import_react35.useRef)(process.cwd());
@@ -158602,6 +158608,12 @@ var App2 = ({ onReady }) => {
158602
158608
  onCycleModeRef.current?.();
158603
158609
  return;
158604
158610
  }
158611
+ if (key.ctrl && char === "o") {
158612
+ if (askResolverRef.current)
158613
+ return;
158614
+ onExpandRef.current?.();
158615
+ return;
158616
+ }
158605
158617
  const menu = askResolverRef.current || searchRef.current || historyRef.current.browsing ? null : computeMenu(lineRef.current, cursorRef.current, slashEntriesRef.current, menuDismissedRef.current);
158606
158618
  if (menu) {
158607
158619
  const sel = menuSelRef.current.query === menu.query ? menuSelRef.current.idx : 0;
@@ -158846,6 +158858,7 @@ var App2 = ({ onReady }) => {
158846
158858
  const submitted = lineRef.current.slice(0, pos) + before + lineRef.current.slice(pos);
158847
158859
  setLine(after);
158848
158860
  setCursorPos(0);
158861
+ menuReserveRef.current = 0;
158849
158862
  print(prompt2 + submitted);
158850
158863
  const askResolve = askResolverRef.current;
158851
158864
  if (!askResolve && submitted.trim()) {
@@ -158971,7 +158984,8 @@ var App2 = ({ onReady }) => {
158971
158984
  onBusyCommand,
158972
158985
  onCycleMode,
158973
158986
  onRewind,
158974
- onHelp
158987
+ onHelp,
158988
+ onExpand
158975
158989
  });
158976
158990
  }, []);
158977
158991
  const slices = renderInputSlices(line, cursorPos);
@@ -159035,9 +159049,13 @@ var App2 = ({ onReady }) => {
159035
159049
  hint: ` ${sel + 1}/${m2.items.length} \xB7 \u2191\u2193 select \xB7 Tab/Enter insert \xB7 Esc close`
159036
159050
  };
159037
159051
  })();
159052
+ menuReserveRef.current = reserveMenuRows(
159053
+ menuReserveRef.current,
159054
+ slashMenu ? slashMenu.rows.length + 1 : mentionMenu ? mentionMenu.rows.length + 1 : 0
159055
+ );
159038
159056
  const choiceVm = choiceRef.current ? choiceView(choiceRef.current.spec, choiceRef.current.sel) : null;
159039
159057
  const riskColor = choiceVm?.risk === "high" ? "red" : choiceVm?.risk === "medium" ? "yellow" : "green";
159040
- return /* @__PURE__ */ import_react35.default.createElement(Box_default, { flexDirection: "column" }, /* @__PURE__ */ import_react35.default.createElement(Static, { items, key: staticKey }, (item) => /* @__PURE__ */ import_react35.default.createElement(Text, { key: item.id }, item.content)), live ? /* @__PURE__ */ import_react35.default.createElement(Text, null, live) : null, choiceVm ? /* @__PURE__ */ import_react35.default.createElement(Box_default, { flexDirection: "column" }, /* @__PURE__ */ import_react35.default.createElement(Text, { bold: true, wrap: "truncate-end" }, choiceVm.title), choiceVm.detail ? /* @__PURE__ */ import_react35.default.createElement(Text, { wrap: "truncate-end", color: choiceVm.risk ? riskColor : void 0, dimColor: !choiceVm.risk }, choiceVm.risk ? `${choiceVm.risk.toUpperCase()} RISK \xB7 ` : "", choiceVm.detail) : null, choiceVm.rows.map((r2) => /* @__PURE__ */ import_react35.default.createElement(Text, { key: r2.text, wrap: "truncate-end", inverse: r2.selected }, r2.text)), /* @__PURE__ */ import_react35.default.createElement(Text, { dimColor: true, wrap: "truncate-end" }, choiceVm.hint)) : null, searchVm ? /* @__PURE__ */ import_react35.default.createElement(Box_default, { flexDirection: "column" }, /* @__PURE__ */ import_react35.default.createElement(Text, { color: searchVm.failed ? "red" : "cyan", wrap: "truncate-end" }, " " + searchVm.label), /* @__PURE__ */ import_react35.default.createElement(Text, { dimColor: true, wrap: "truncate-end" }, SEARCH_HINT)) : null, mentionMenu ? /* @__PURE__ */ import_react35.default.createElement(Box_default, { flexDirection: "column" }, mentionMenu.rows.map((r2) => /* @__PURE__ */ import_react35.default.createElement(Text, { key: r2.key, wrap: "truncate-end", inverse: r2.selected }, r2.text)), /* @__PURE__ */ import_react35.default.createElement(Text, { dimColor: true, wrap: "truncate-end" }, mentionMenu.hint)) : null, slashMenu ? /* @__PURE__ */ import_react35.default.createElement(Box_default, { flexDirection: "column" }, slashMenu.rows.map((r2) => /* @__PURE__ */ import_react35.default.createElement(Text, { key: r2.key, wrap: "truncate-end", inverse: r2.selected }, r2.text)), /* @__PURE__ */ import_react35.default.createElement(Text, { dimColor: true, wrap: "truncate-end" }, slashMenu.hint)) : null, /* @__PURE__ */ import_react35.default.createElement(
159058
+ return /* @__PURE__ */ import_react35.default.createElement(Box_default, { flexDirection: "column" }, /* @__PURE__ */ import_react35.default.createElement(Static, { items, key: staticKey }, (item) => /* @__PURE__ */ import_react35.default.createElement(Text, { key: item.id }, item.content)), live ? /* @__PURE__ */ import_react35.default.createElement(Text, null, live) : null, choiceVm ? /* @__PURE__ */ import_react35.default.createElement(Box_default, { flexDirection: "column" }, /* @__PURE__ */ import_react35.default.createElement(Text, { bold: true, wrap: "truncate-end" }, choiceVm.title), choiceVm.detail ? /* @__PURE__ */ import_react35.default.createElement(Text, { wrap: "truncate-end", color: choiceVm.risk ? riskColor : void 0, dimColor: !choiceVm.risk }, choiceVm.risk ? `${choiceVm.risk.toUpperCase()} RISK \xB7 ` : "", choiceVm.detail) : null, choiceVm.rows.map((r2) => /* @__PURE__ */ import_react35.default.createElement(Text, { key: r2.text, wrap: "truncate-end", inverse: r2.selected }, r2.text)), /* @__PURE__ */ import_react35.default.createElement(Text, { dimColor: true, wrap: "truncate-end" }, choiceVm.hint)) : null, searchVm ? /* @__PURE__ */ import_react35.default.createElement(Box_default, { flexDirection: "column" }, /* @__PURE__ */ import_react35.default.createElement(Text, { color: searchVm.failed ? "red" : "cyan", wrap: "truncate-end" }, " " + searchVm.label), /* @__PURE__ */ import_react35.default.createElement(Text, { dimColor: true, wrap: "truncate-end" }, SEARCH_HINT)) : null, menuReserveRef.current > 0 ? /* @__PURE__ */ import_react35.default.createElement(Box_default, { flexDirection: "column", justifyContent: "flex-end", minHeight: menuReserveRef.current }, mentionMenu ? /* @__PURE__ */ import_react35.default.createElement(Box_default, { flexDirection: "column" }, mentionMenu.rows.map((r2) => /* @__PURE__ */ import_react35.default.createElement(Text, { key: r2.key, wrap: "truncate-end", inverse: r2.selected }, r2.text)), /* @__PURE__ */ import_react35.default.createElement(Text, { dimColor: true, wrap: "truncate-end" }, mentionMenu.hint)) : null, slashMenu ? /* @__PURE__ */ import_react35.default.createElement(Box_default, { flexDirection: "column" }, slashMenu.rows.map((r2) => /* @__PURE__ */ import_react35.default.createElement(Text, { key: r2.key, wrap: "truncate-end", inverse: r2.selected }, r2.text)), /* @__PURE__ */ import_react35.default.createElement(Text, { dimColor: true, wrap: "truncate-end" }, slashMenu.hint)) : null) : null, /* @__PURE__ */ import_react35.default.createElement(
159041
159059
  Box_default,
159042
159060
  {
159043
159061
  borderStyle: "single",
@@ -159255,6 +159273,99 @@ function rewindChoices(cps) {
159255
159273
  return { options, ids: newest.map((c) => c.id) };
159256
159274
  }
159257
159275
 
159276
+ // src/ui/toolOutputStore.ts
159277
+ var MAX_ENTRIES = 20;
159278
+ var MAX_ENTRY_CHARS = 4e5;
159279
+ var MAX_EXPAND_LINES = 1500;
159280
+ var PREVIEW_LINES = 8;
159281
+ function toolLabel(name, input) {
159282
+ const rec = input && typeof input === "object" ? input : {};
159283
+ const pick = ["command", "file_path", "path", "pattern", "url", "query", "description"].map((k) => rec[k]).find((v) => typeof v === "string" && v.length > 0);
159284
+ const flat = (pick ?? "").replace(/\s+/g, " ").trim();
159285
+ const clipped = flat.length > 100 ? flat.slice(0, 99) + "\u2026" : flat;
159286
+ return clipped ? `${name}: ${clipped}` : name;
159287
+ }
159288
+ function capOutput(text, max = MAX_ENTRY_CHARS) {
159289
+ if (text.length <= max)
159290
+ return text;
159291
+ const head = Math.floor(max * 0.6);
159292
+ const tail = max - head;
159293
+ const dropped = text.length - max;
159294
+ return text.slice(0, head) + `
159295
+ \u2026 ${dropped.toLocaleString()} characters omitted \u2026
159296
+ ` + text.slice(text.length - tail);
159297
+ }
159298
+ var ToolOutputStore = class {
159299
+ entries = [];
159300
+ nextId = 1;
159301
+ add(e2) {
159302
+ const entry = { ...e2, id: this.nextId++, output: capOutput(e2.output) };
159303
+ this.entries.push(entry);
159304
+ if (this.entries.length > MAX_ENTRIES)
159305
+ this.entries.shift();
159306
+ return entry;
159307
+ }
159308
+ /** Newest first; `n` = 1 is the latest. */
159309
+ fromEnd(n) {
159310
+ if (!Number.isInteger(n) || n < 1)
159311
+ return void 0;
159312
+ return this.entries[this.entries.length - n];
159313
+ }
159314
+ byId(id) {
159315
+ return this.entries.find((e2) => e2.id === id);
159316
+ }
159317
+ /** The newest entry that actually has something worth expanding. */
159318
+ latestFoldable() {
159319
+ for (let i2 = this.entries.length - 1; i2 >= 0; i2--) {
159320
+ const e2 = this.entries[i2];
159321
+ if (lineCount(e2.output) > PREVIEW_LINES)
159322
+ return e2;
159323
+ }
159324
+ return void 0;
159325
+ }
159326
+ list() {
159327
+ return this.entries.slice();
159328
+ }
159329
+ clear() {
159330
+ this.entries = [];
159331
+ }
159332
+ };
159333
+ function lineCount(text) {
159334
+ if (!text)
159335
+ return 0;
159336
+ const t2 = text.endsWith("\n") ? text.slice(0, -1) : text;
159337
+ return t2.split("\n").length;
159338
+ }
159339
+ function expansionOf(e2, maxLines = MAX_EXPAND_LINES) {
159340
+ const all = (e2.output.endsWith("\n") ? e2.output.slice(0, -1) : e2.output).split("\n");
159341
+ const total = all.length;
159342
+ let lines = all;
159343
+ let omitted = 0;
159344
+ if (total > maxLines) {
159345
+ const head = Math.floor(maxLines * 0.4);
159346
+ const tail = maxLines - head;
159347
+ lines = [...all.slice(0, head), ...all.slice(total - tail)];
159348
+ omitted = total - maxLines;
159349
+ }
159350
+ return {
159351
+ header: `#${e2.id} ${e2.label}${e2.isError ? " (failed)" : ""} \u2014 ${total} line${total === 1 ? "" : "s"}`,
159352
+ lines,
159353
+ omitted
159354
+ };
159355
+ }
159356
+ function resolveExpand(store, arg) {
159357
+ const a = arg.trim();
159358
+ if (a === "") {
159359
+ const e3 = store.latestFoldable() ?? store.fromEnd(1);
159360
+ return e3 ? { entry: e3 } : { error: "No tool output yet." };
159361
+ }
159362
+ const n = Number(a.replace(/^#/, ""));
159363
+ if (!Number.isInteger(n) || n < 1)
159364
+ return { error: `Usage: /expand [n] (n = 1 is the latest tool call, 2 the one before \u2026)` };
159365
+ const e2 = store.fromEnd(n);
159366
+ return e2 ? { entry: e2 } : { error: `Only the last ${Math.min(MAX_ENTRIES, store.list().length)} tool call(s) are kept.` };
159367
+ }
159368
+
159258
159369
  // src/commands/chat.ts
159259
159370
  var require2 = createRequire(import.meta.url);
159260
159371
  var CLI_VERSION = (() => {
@@ -159277,6 +159388,23 @@ var CLI_VERSION = (() => {
159277
159388
  return "unknown";
159278
159389
  })();
159279
159390
  var sessionUsage = new UsageTotals();
159391
+ var toolOutputs = new ToolOutputStore();
159392
+ function printExpansion(arg) {
159393
+ const r2 = resolveExpand(toolOutputs, arg);
159394
+ if (!r2.entry) {
159395
+ console.log(source_default.dim(" " + (r2.error ?? "Nothing to expand.")));
159396
+ return;
159397
+ }
159398
+ const x2 = expansionOf(r2.entry);
159399
+ console.log();
159400
+ console.log(source_default.cyan(" \u25BE " + x2.header));
159401
+ for (const l2 of x2.lines)
159402
+ console.log(source_default.dim(" \u2502 ") + source_default.gray(l2.slice(0, 300)));
159403
+ if (x2.omitted > 0)
159404
+ console.log(source_default.dim(` \u2514 \u2026 ${x2.omitted} line(s) omitted \u2014 /export writes the whole conversation to a file`));
159405
+ else
159406
+ console.log(source_default.dim(" \u2514 end"));
159407
+ }
159280
159408
  var MODEL_LABELS = {
159281
159409
  "claude-sonnet-5-5": "Claude Sonnet 5.5",
159282
159410
  "claude-opus-5-5": "Claude Opus 5.5",
@@ -159836,9 +159964,10 @@ async function runTurn(messages, modelAlias, workDir, abortSignal, env4, nexrall
159836
159964
  let lastUsage;
159837
159965
  const spinner = new Spinner();
159838
159966
  const mdRender = new MarkdownStreamRenderer();
159839
- const toolStream = new ToolStreamPrinter();
159967
+ const toolStream = new ToolStreamPrinter(PREVIEW_LINES);
159840
159968
  let toolStartTime = 0;
159841
159969
  let lastToolName = "";
159970
+ let lastToolLabel = "";
159842
159971
  const toolCalls = /* @__PURE__ */ new Map();
159843
159972
  const subPrefix = (meta) => source_default.dim(` \u21B3 ${meta?.agentName ?? "sub-agent"} `);
159844
159973
  let thinkingTokens = 0;
@@ -159944,7 +160073,7 @@ async function runTurn(messages, modelAlias, workDir, abortSignal, env4, nexrall
159944
160073
  mdRender.flush();
159945
160074
  spinner.stop();
159946
160075
  if (meta?.id)
159947
- toolCalls.set(meta.id, { name, start: Date.now() });
160076
+ toolCalls.set(meta.id, { name, start: Date.now(), label: toolLabel(name, input) });
159948
160077
  if (isSubTask) {
159949
160078
  console.log(subPrefix(meta) + formatToolUse(name, input).trim());
159950
160079
  spinner.start(`${meta?.agentName ?? "sub-agent"}: ${name}\u2026`);
@@ -159954,6 +160083,7 @@ async function runTurn(messages, modelAlias, workDir, abortSignal, env4, nexrall
159954
160083
  console.log();
159955
160084
  console.log(formatToolUse(name, input));
159956
160085
  lastToolName = name;
160086
+ lastToolLabel = toolLabel(name, input);
159957
160087
  toolStartTime = Date.now();
159958
160088
  spinner.start(`running ${name}\u2026`);
159959
160089
  },
@@ -159984,6 +160114,26 @@ async function runTurn(messages, modelAlias, workDir, abortSignal, env4, nexrall
159984
160114
  toolStream.flush();
159985
160115
  const durationMs = Date.now() - (tracked?.start ?? toolStartTime);
159986
160116
  console.log(formatToolResult(tracked?.name ?? lastToolName, res, durationMs));
160117
+ const text = res.error ? `${res.output ? res.output + "\n" : ""}${res.error}` : res.output ?? "";
160118
+ if (text) {
160119
+ const entry = toolOutputs.add({
160120
+ name: tracked?.name ?? lastToolName,
160121
+ label: tracked?.label ?? lastToolLabel,
160122
+ output: text,
160123
+ isError: !!res.error,
160124
+ durationMs
160125
+ });
160126
+ const total = lineCount(text);
160127
+ if (total > PREVIEW_LINES) {
160128
+ if (res.error) {
160129
+ const tail = text.replace(/\n$/, "").split("\n").slice(-3);
160130
+ for (const l2 of tail)
160131
+ console.log(source_default.dim(" \u2502 ") + source_default.red(l2.slice(0, 300)));
160132
+ }
160133
+ const more = Math.max(0, total - PREVIEW_LINES - (res.error ? 3 : 0));
160134
+ console.log(source_default.dim(` \u2514 \u2026 +${more} more line${more === 1 ? "" : "s"} \xB7 Ctrl+O or /expand to see all (#${entry.id})`));
160135
+ }
160136
+ }
159987
160137
  toolStartTime = 0;
159988
160138
  resumeWorking();
159989
160139
  },
@@ -160215,6 +160365,7 @@ function printHelp() {
160215
160365
  ["/team [use <id|personal>]", "List your teams, or switch what usage is billed to"],
160216
160366
  ["/cost", "Tokens and cost of this conversation so far"],
160217
160367
  ["/status", "Version, model, mode, directory, session and cost at a glance"],
160368
+ ["/expand [n]", "Show the full output of a folded tool call (newest by default; Ctrl+O does the same)"],
160218
160369
  ["/copy", "Copy the last reply to the clipboard"],
160219
160370
  ["/export [file]", "Write this conversation to a Markdown file"],
160220
160371
  ["/rename <title>", "Rename this session"],
@@ -160257,6 +160408,7 @@ function printHelp() {
160257
160408
  ["Ctrl+G", "Edit the draft in $VISUAL / $EDITOR"],
160258
160409
  ["Esc Esc", "At an empty prompt: pick a checkpoint to rewind to (same as /rewind)"],
160259
160410
  ["?", "At an empty prompt: show this list"],
160411
+ ["Ctrl+O", "Expand the last folded tool output (also works while it is running; /expand [n] picks an older one)"],
160260
160412
  ["Ctrl+L", "Redraw the screen"],
160261
160413
  ["Ctrl+D", "Delete the character under the cursor; press twice on an empty line to exit"],
160262
160414
  ["(paste)", "Multi-line paste goes into the input box \u2014 it does not send until you press Enter"]
@@ -160674,6 +160826,9 @@ ${text}` : "");
160674
160826
  getInkTerminal().onHelp(() => {
160675
160827
  printHelp();
160676
160828
  });
160829
+ getInkTerminal().onExpand(() => {
160830
+ printExpansion("");
160831
+ });
160677
160832
  getInkTerminal().onRewind(() => {
160678
160833
  void (async () => {
160679
160834
  const opts = rewindChoices(checkpoints.list());
@@ -160742,6 +160897,7 @@ ${text}` : "");
160742
160897
  sessionTitle = "";
160743
160898
  (0, import_code_core8.resetSessionSubAgentBudget)();
160744
160899
  sessionUsage.reset();
160900
+ toolOutputs.clear();
160745
160901
  cliBackgroundAgents.stopAll({ dropPending: true });
160746
160902
  inputBuffer = "";
160747
160903
  if (interactive)
@@ -161198,6 +161354,10 @@ ${dirList}`;
161198
161354
  rl.prompt();
161199
161355
  return;
161200
161356
  }
161357
+ case "/expand":
161358
+ printExpansion(arg);
161359
+ rl.prompt();
161360
+ return;
161201
161361
  case "/copy": {
161202
161362
  const text = lastAssistantText(messages);
161203
161363
  if (!text) {
@@ -161633,7 +161793,7 @@ ${text}
161633
161793
  if (shown < MAX_SHOWN_LINES)
161634
161794
  console.log(" " + clean);
161635
161795
  else if (shown === MAX_SHOWN_LINES)
161636
- console.log(source_default.dim(" \u2026 output continues (the model still gets the start and the end)"));
161796
+ console.log(source_default.dim(" \u2026 output continues (the model still gets the start and the end; /expand shows it all)"));
161637
161797
  shown++;
161638
161798
  };
161639
161799
  let result;
@@ -161655,6 +161815,9 @@ ${text}
161655
161815
  }
161656
161816
  const tag = result.aborted ? source_default.yellow(" interrupted") : result.timedOut ? source_default.yellow(" timed out") : result.code === 0 ? source_default.dim(" exit 0") : source_default.red(` exit ${result.code ?? result.signal}`);
161657
161817
  console.log(tag);
161818
+ if (result.output) {
161819
+ toolOutputs.add({ name: "bash", label: `!${bang}`, output: result.output, isError: result.code !== 0, durationMs: 0 });
161820
+ }
161658
161821
  messages.push(
161659
161822
  { role: "user", content: [{ type: "text", text: bangTranscript(bang, result) }] },
161660
161823
  { role: "assistant", content: [{ type: "text", text: "Noted the command output." }] }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nexrall-code",
3
- "version": "0.5.127",
3
+ "version": "0.5.129",
4
4
  "description": "Nexrall Code — AI coding assistant for your terminal (headless agent for scripts, CI and automation)",
5
5
  "keywords": [
6
6
  "ai",