nexrall-code 0.5.77 → 0.5.79

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 +120 -43
  2. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -55817,7 +55817,18 @@ function prepareSessionScreen() {
55817
55817
  return;
55818
55818
  process.stdout.write("\x1B[2J\x1B[3J\x1B[H");
55819
55819
  }
55820
- var BOTTOM_CHROME_ROWS = 3;
55820
+ function bottomChromeRows(opts = {
55821
+ footerText: ""
55822
+ }) {
55823
+ const columns = opts.columns ?? process.stdout.columns ?? 80;
55824
+ const separatorRows = 1;
55825
+ const inputRows = rowsOccupied(
55826
+ " ".repeat(opts.promptWidth ?? 2) + (opts.inputText ?? ""),
55827
+ columns
55828
+ );
55829
+ const footerRows = rowsOccupied(opts.footerText, columns);
55830
+ return separatorRows + inputRows + footerRows;
55831
+ }
55821
55832
  function rowsOccupied(text, columns = process.stdout.columns || 80) {
55822
55833
  let rows = 0;
55823
55834
  for (const line of text.split("\n")) {
@@ -55826,11 +55837,12 @@ function rowsOccupied(text, columns = process.stdout.columns || 80) {
55826
55837
  }
55827
55838
  return rows;
55828
55839
  }
55829
- function padToBottom(rowsAlreadyPrinted) {
55840
+ function padToBottom(rowsAlreadyPrinted, footerText2 = "") {
55830
55841
  const rows = process.stdout.rows;
55831
55842
  if (!process.stdout.isTTY || !rows)
55832
55843
  return;
55833
- const filler = rows - rowsAlreadyPrinted - BOTTOM_CHROME_ROWS - 2;
55844
+ const chromeRows = bottomChromeRows({ footerText: footerText2, columns: process.stdout.columns ?? 80 });
55845
+ const filler = rows - rowsAlreadyPrinted - chromeRows - 2;
55834
55846
  const MIN_WORTHWHILE_FILLER = 3;
55835
55847
  if (filler < MIN_WORTHWHILE_FILLER)
55836
55848
  return;
@@ -55841,19 +55853,26 @@ function startRowCount() {
55841
55853
  if (rowCounter)
55842
55854
  return;
55843
55855
  const columns = process.stdout.columns || 80;
55844
- const original = console.log;
55845
- const state = { rows: 0, original };
55856
+ const originalLog = console.log;
55857
+ const originalError = console.error;
55858
+ const state = { rows: 0, originalLog, originalError };
55846
55859
  rowCounter = state;
55860
+ const measure = (args) => args.length === 0 ? 1 : rowsOccupied(args.map(String).join(" "), columns);
55847
55861
  console.log = (...args) => {
55848
- state.rows += args.length === 0 ? 1 : rowsOccupied(args.map(String).join(" "), columns);
55849
- original(...args);
55862
+ state.rows += measure(args);
55863
+ originalLog(...args);
55864
+ };
55865
+ console.error = (...args) => {
55866
+ state.rows += measure(args);
55867
+ originalError(...args);
55850
55868
  };
55851
55869
  }
55852
55870
  function stopRowCount() {
55853
55871
  if (!rowCounter)
55854
55872
  return 0;
55855
- const { rows, original } = rowCounter;
55856
- console.log = original;
55873
+ const { rows, originalLog, originalError } = rowCounter;
55874
+ console.log = originalLog;
55875
+ console.error = originalError;
55857
55876
  rowCounter = null;
55858
55877
  return rows;
55859
55878
  }
@@ -64713,8 +64732,6 @@ var use_app_default = useApp;
64713
64732
 
64714
64733
  // ../../node_modules/.pnpm/ink@7.1.1_@types+react@19.2.18_react@19.2.8/node_modules/ink/build/hooks/use-stdout.js
64715
64734
  var import_react25 = __toESM(require_react(), 1);
64716
- var useStdout = () => (0, import_react25.useContext)(StdoutContext_default);
64717
- var use_stdout_default = useStdout;
64718
64735
 
64719
64736
  // ../../node_modules/.pnpm/ink@7.1.1_@types+react@19.2.18_react@19.2.8/node_modules/ink/build/hooks/use-stderr.js
64720
64737
  var import_react26 = __toESM(require_react(), 1);
@@ -64736,21 +64753,6 @@ var import_react31 = __toESM(require_react(), 1);
64736
64753
 
64737
64754
  // ../../node_modules/.pnpm/ink@7.1.1_@types+react@19.2.18_react@19.2.8/node_modules/ink/build/hooks/use-window-size.js
64738
64755
  var import_react32 = __toESM(require_react(), 1);
64739
- var useWindowSize = () => {
64740
- const { stdout } = use_stdout_default();
64741
- const [size, setSize] = (0, import_react32.useState)(() => getWindowSize(stdout));
64742
- (0, import_react32.useEffect)(() => {
64743
- const onResize = () => {
64744
- setSize(getWindowSize(stdout));
64745
- };
64746
- stdout.on("resize", onResize);
64747
- return () => {
64748
- stdout.off("resize", onResize);
64749
- };
64750
- }, [stdout]);
64751
- return size;
64752
- };
64753
- var use_window_size_default = useWindowSize;
64754
64756
 
64755
64757
  // ../../node_modules/.pnpm/ink@7.1.1_@types+react@19.2.18_react@19.2.8/node_modules/ink/build/hooks/use-box-metrics.js
64756
64758
  var import_react33 = __toESM(require_react(), 1);
@@ -64873,6 +64875,26 @@ function nextGraphemeBoundary(text, pos) {
64873
64875
  }
64874
64876
  return text.length;
64875
64877
  }
64878
+ function renderInputSlices(line, cursorPos) {
64879
+ const end = nextGraphemeBoundary(line, cursorPos);
64880
+ return {
64881
+ before: line.slice(0, cursorPos),
64882
+ atCursor: line.slice(cursorPos, end) || " ",
64883
+ after: line.slice(end)
64884
+ };
64885
+ }
64886
+ var CTRL_C_QUIT_WINDOW_MS = 3e3;
64887
+ var CTRL_C_HINT = "Press Ctrl+C again to exit";
64888
+ function decideCtrlC(state) {
64889
+ if (state.hasPendingQuestion)
64890
+ return "answer-prompt";
64891
+ if (state.busy)
64892
+ return "interrupt-turn";
64893
+ if (state.quitArmedAt !== null && state.now - state.quitArmedAt <= CTRL_C_QUIT_WINDOW_MS) {
64894
+ return "quit";
64895
+ }
64896
+ return "arm-quit";
64897
+ }
64876
64898
  var handle = null;
64877
64899
  var inkInstance = null;
64878
64900
  var idCounter = 0;
@@ -64880,7 +64902,6 @@ var DEFAULT_PROMPT = colors.primary.bold("> ");
64880
64902
  var App2 = ({ onReady }) => {
64881
64903
  const [items, setItems] = (0, import_react35.useState)([]);
64882
64904
  const [footer, setFooterState] = (0, import_react35.useState)({ mode: "auto", autoApprove: false });
64883
- const { columns } = use_window_size_default();
64884
64905
  const [busy, setBusyState] = (0, import_react35.useState)(false);
64885
64906
  const [line, setLineState] = (0, import_react35.useState)("");
64886
64907
  const [cursorPos, setCursorPosState] = (0, import_react35.useState)(0);
@@ -64902,6 +64923,12 @@ var App2 = ({ onReady }) => {
64902
64923
  const onLineRef = (0, import_react35.useRef)(null);
64903
64924
  const onQueuedLineRef = (0, import_react35.useRef)(null);
64904
64925
  const busyRef = (0, import_react35.useRef)(false);
64926
+ const [quitArmed, setQuitArmedState] = (0, import_react35.useState)(null);
64927
+ const quitArmedAtRef = (0, import_react35.useRef)(null);
64928
+ const setQuitArmed = (0, import_react35.useCallback)((at) => {
64929
+ quitArmedAtRef.current = at;
64930
+ setQuitArmedState(at);
64931
+ }, []);
64905
64932
  const [live, setLiveState] = (0, import_react35.useState)("");
64906
64933
  const print = (0, import_react35.useCallback)((text) => {
64907
64934
  setItems((prev) => [...prev, { id: idCounter++, content: text }]);
@@ -64933,23 +64960,49 @@ var App2 = ({ onReady }) => {
64933
64960
  const onInterrupt = (0, import_react35.useCallback)((cb) => {
64934
64961
  onInterruptRef.current = cb;
64935
64962
  }, []);
64963
+ const onExitRef = (0, import_react35.useRef)(null);
64964
+ const onExit = (0, import_react35.useCallback)((cb) => {
64965
+ onExitRef.current = cb;
64966
+ }, []);
64967
+ const exitedRef = (0, import_react35.useRef)(false);
64968
+ const requestExit = (0, import_react35.useCallback)((notify) => {
64969
+ if (exitedRef.current)
64970
+ return;
64971
+ exitedRef.current = true;
64972
+ if (notify)
64973
+ onExitRef.current?.();
64974
+ exit();
64975
+ }, []);
64936
64976
  use_input_default((char, key) => {
64937
64977
  if (key.ctrl && char === "c") {
64938
- const askResolve = askResolverRef.current;
64939
- if (askResolve) {
64978
+ const action = decideCtrlC({
64979
+ hasPendingQuestion: askResolverRef.current !== null,
64980
+ busy: busyRef.current && onInterruptRef.current !== null,
64981
+ quitArmedAt: quitArmedAtRef.current,
64982
+ now: Date.now()
64983
+ });
64984
+ if (action === "answer-prompt") {
64985
+ const askResolve = askResolverRef.current;
64940
64986
  askResolverRef.current = null;
64941
64987
  setLine("");
64942
64988
  setCursorPos(0);
64943
64989
  askResolve("n");
64944
64990
  return;
64945
64991
  }
64946
- if (busyRef.current && onInterruptRef.current) {
64947
- onInterruptRef.current();
64992
+ if (action === "interrupt-turn") {
64993
+ setQuitArmed(null);
64994
+ onInterruptRef.current?.();
64995
+ return;
64996
+ }
64997
+ if (action === "arm-quit") {
64998
+ setQuitArmed(Date.now());
64948
64999
  return;
64949
65000
  }
64950
- exit();
65001
+ requestExit(true);
64951
65002
  return;
64952
65003
  }
65004
+ if (quitArmedAtRef.current !== null)
65005
+ setQuitArmed(null);
64953
65006
  if (key.backspace || key.delete) {
64954
65007
  if (key.delete) {
64955
65008
  setLine((s2) => {
@@ -65022,11 +65075,23 @@ var App2 = ({ onReady }) => {
65022
65075
  onLine,
65023
65076
  onQueuedLine,
65024
65077
  onInterrupt,
65078
+ onExit,
65025
65079
  setBusy,
65026
- close: () => exit()
65080
+ close: () => requestExit(false)
65027
65081
  });
65028
65082
  }, []);
65029
- return /* @__PURE__ */ import_react35.default.createElement(Box_default, { flexDirection: "column" }, /* @__PURE__ */ import_react35.default.createElement(Static, { items }, (item) => /* @__PURE__ */ import_react35.default.createElement(Text, { key: item.id }, item.content)), live ? /* @__PURE__ */ import_react35.default.createElement(Text, null, live) : null, /* @__PURE__ */ import_react35.default.createElement(Text, { dimColor: true }, "\u2500".repeat(Math.max(1, columns - 1))), /* @__PURE__ */ import_react35.default.createElement(Box_default, null, /* @__PURE__ */ import_react35.default.createElement(Text, null, prompt2), /* @__PURE__ */ import_react35.default.createElement(Text, null, line.slice(0, cursorPos)), /* @__PURE__ */ import_react35.default.createElement(Text, { inverse: true }, line.slice(cursorPos, cursorPos + 1) || " "), /* @__PURE__ */ import_react35.default.createElement(Text, null, line.slice(cursorPos + 1))), /* @__PURE__ */ import_react35.default.createElement(Text, { dimColor: true }, footerText(footer), busy ? " \xB7 agent working\u2026" : ""));
65083
+ const slices = renderInputSlices(line, cursorPos);
65084
+ return /* @__PURE__ */ import_react35.default.createElement(Box_default, { flexDirection: "column" }, /* @__PURE__ */ import_react35.default.createElement(Static, { items }, (item) => /* @__PURE__ */ import_react35.default.createElement(Text, { key: item.id }, item.content)), live ? /* @__PURE__ */ import_react35.default.createElement(Text, null, live) : null, /* @__PURE__ */ import_react35.default.createElement(
65085
+ Box_default,
65086
+ {
65087
+ borderStyle: "single",
65088
+ borderTop: false,
65089
+ borderBottom: true,
65090
+ borderLeft: false,
65091
+ borderRight: false,
65092
+ borderDimColor: true
65093
+ }
65094
+ ), /* @__PURE__ */ import_react35.default.createElement(Box_default, null, /* @__PURE__ */ import_react35.default.createElement(Text, null, prompt2), /* @__PURE__ */ import_react35.default.createElement(Text, null, slices.before), /* @__PURE__ */ import_react35.default.createElement(Text, { inverse: true }, slices.atCursor), /* @__PURE__ */ import_react35.default.createElement(Text, null, slices.after)), quitArmed !== null ? /* @__PURE__ */ import_react35.default.createElement(Text, { color: "yellow" }, CTRL_C_HINT) : /* @__PURE__ */ import_react35.default.createElement(Text, { dimColor: true }, footerText(footer), busy ? " \xB7 agent working\u2026" : ""));
65030
65095
  };
65031
65096
  function startInkTerminal() {
65032
65097
  if (handle)
@@ -65110,6 +65175,9 @@ var InkReadlineAdapter = class extends EventEmitter3 {
65110
65175
  this.ink.onInterrupt(() => {
65111
65176
  this.emit("interrupt");
65112
65177
  });
65178
+ this.ink.onExit(() => {
65179
+ this.emit("close");
65180
+ });
65113
65181
  }
65114
65182
  prompt(_preserveCursor) {
65115
65183
  }
@@ -65868,15 +65936,20 @@ Set ${TRUST_ENV_VAR}=1 to confirm non-interactively, or run \`nex\` interactivel
65868
65936
  let skills = (0, import_code_core3.loadSkills)(workDir);
65869
65937
  if (skills.length && !headless)
65870
65938
  console.log(source_default.dim(` ${skills.length} skill(s) loaded`));
65871
- try {
65872
- await initMcp(workDir);
65873
- const connected = _mcpManager?.serverNames.length ?? 0;
65874
- const total = _mcpConfig ? Object.keys(_mcpConfig.mcpServers).length : 0;
65875
- if (total && !headless) {
65876
- console.log(source_default.dim(` ${connected}/${total} MCP server(s) connected`) + source_default.dim(" (/mcp for details)"));
65939
+ const mcpStarted = (async () => {
65940
+ try {
65941
+ await initMcp(workDir);
65942
+ const connected = _mcpManager?.serverNames.length ?? 0;
65943
+ const total = _mcpConfig ? Object.keys(_mcpConfig.mcpServers).length : 0;
65944
+ if (total && !headless) {
65945
+ console.log(source_default.dim(` ${connected}/${total} MCP server(s) connected`) + source_default.dim(" (/mcp for details)"));
65946
+ }
65947
+ } catch {
65877
65948
  }
65878
- } catch {
65879
- }
65949
+ })();
65950
+ const mcpReady = () => mcpStarted;
65951
+ if (!interactive)
65952
+ await mcpStarted;
65880
65953
  if (headless && !options.prompt && !options.stdinText) {
65881
65954
  process.stdout.write(JSON.stringify({ type: "error", error: "--output-format json/stream-json requires a one-shot prompt (or piped stdin)." }) + "\n");
65882
65955
  process.exit(1);
@@ -65999,7 +66072,7 @@ ${text}` : "");
65999
66072
  console.log();
66000
66073
  if (interactive) {
66001
66074
  await flushInkFrame();
66002
- padToBottom(stopRowCount());
66075
+ padToBottom(stopRowCount(), footerText({ mode: agentMode, autoApprove: isYoloMode() }));
66003
66076
  }
66004
66077
  let agentRunning = false;
66005
66078
  const abortSignal = { aborted: false };
@@ -66469,6 +66542,7 @@ ${text}
66469
66542
  rl.pause();
66470
66543
  abortSignal.aborted = false;
66471
66544
  agentRunning = true;
66545
+ await mcpReady();
66472
66546
  try {
66473
66547
  const result = await runTurn(messages, modelAlias, workDir, abortSignal, env3, nexrallMd, agentMode, effortLevel, checkpoints, saveProgress, takePendingInput);
66474
66548
  messages = result.messages;
@@ -66510,6 +66584,7 @@ ${text}
66510
66584
  return;
66511
66585
  }
66512
66586
  case "/mcp": {
66587
+ await mcpReady();
66513
66588
  console.log();
66514
66589
  console.log(source_default.bold(" MCP servers:"));
66515
66590
  console.log(formatMcpStatus());
@@ -66589,6 +66664,7 @@ ${text}
66589
66664
  rl.pause();
66590
66665
  abortSignal.aborted = false;
66591
66666
  agentRunning = true;
66667
+ await mcpReady();
66592
66668
  try {
66593
66669
  const result = await runTurn(messages, turnModel, workDir, abortSignal, env3, nexrallMd, turnMode, effortLevel, checkpoints, saveProgress, takePendingInput);
66594
66670
  messages = result.messages;
@@ -66613,6 +66689,7 @@ ${text}
66613
66689
  rl.pause();
66614
66690
  abortSignal.aborted = false;
66615
66691
  agentRunning = true;
66692
+ await mcpReady();
66616
66693
  try {
66617
66694
  const result = await runTurn(messages, modelAlias, workDir, abortSignal, env3, nexrallMd, agentMode, effortLevel, checkpoints, saveProgress, takePendingInput);
66618
66695
  messages = result.messages;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nexrall-code",
3
- "version": "0.5.77",
3
+ "version": "0.5.79",
4
4
  "description": "Nexrall Code — AI coding assistant for your terminal (headless agent for scripts, CI and automation)",
5
5
  "keywords": [
6
6
  "ai",
@@ -41,7 +41,7 @@
41
41
  "release": "node build.js && node scripts/upload-release.cjs"
42
42
  },
43
43
  "dependencies": {
44
- "@nexrall/code-core": "^1.4.45",
44
+ "@nexrall/code-core": "^1.4.46",
45
45
  "chalk": "^5.3.0",
46
46
  "commander": "^12.0.0",
47
47
  "diff": "^5.2.0",