nexrall-code 0.5.74 → 0.5.75

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 +181 -39
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -64732,23 +64732,50 @@ function footerText(info) {
64732
64732
  }
64733
64733
  var IntlWithSegmenter = Intl;
64734
64734
  var graphemeSegmenter = typeof IntlWithSegmenter.Segmenter === "function" ? new IntlWithSegmenter.Segmenter(void 0, { granularity: "grapheme" }) : null;
64735
- function dropLastGrapheme(text) {
64736
- if (!text)
64737
- return "";
64735
+ function graphemeBoundaries(text) {
64738
64736
  if (graphemeSegmenter) {
64739
- let lastStart = 0;
64740
- for (const { index } of graphemeSegmenter.segment(text))
64741
- lastStart = index;
64742
- return text.slice(0, lastStart);
64743
- }
64744
- let end = text.length - 1;
64745
- while (end > 0 && /[\u0300-\u036F\u1AB0-\u1AFF\u20D0-\u20F0]/.test(text[end]))
64746
- end--;
64747
- const prev = text.charCodeAt(end - 1);
64748
- const curr = text.charCodeAt(end);
64749
- if (end > 0 && prev >= 55296 && prev <= 56319 && curr >= 56320 && curr <= 57343)
64750
- end--;
64751
- return text.slice(0, end);
64737
+ const bounds2 = [0];
64738
+ for (const { index } of graphemeSegmenter.segment(text)) {
64739
+ if (index > 0)
64740
+ bounds2.push(index);
64741
+ }
64742
+ bounds2.push(text.length);
64743
+ return bounds2;
64744
+ }
64745
+ const bounds = [0];
64746
+ let i2 = 0;
64747
+ while (i2 < text.length) {
64748
+ let next = i2 + 1;
64749
+ const code = text.charCodeAt(i2);
64750
+ if (code >= 55296 && code <= 56319 && next < text.length) {
64751
+ const low = text.charCodeAt(next);
64752
+ if (low >= 56320 && low <= 57343)
64753
+ next++;
64754
+ }
64755
+ while (next < text.length && /[\u0300-\u036F\u1AB0-\u1AFF\u20D0-\u20F0]/.test(text[next]))
64756
+ next++;
64757
+ bounds.push(next);
64758
+ i2 = next;
64759
+ }
64760
+ return bounds;
64761
+ }
64762
+ function prevGraphemeBoundary(text, pos) {
64763
+ const bounds = graphemeBoundaries(text);
64764
+ let prev = 0;
64765
+ for (const b of bounds) {
64766
+ if (b >= pos)
64767
+ break;
64768
+ prev = b;
64769
+ }
64770
+ return prev;
64771
+ }
64772
+ function nextGraphemeBoundary(text, pos) {
64773
+ const bounds = graphemeBoundaries(text);
64774
+ for (const b of bounds) {
64775
+ if (b > pos)
64776
+ return b;
64777
+ }
64778
+ return text.length;
64752
64779
  }
64753
64780
  var handle = null;
64754
64781
  var inkInstance = null;
@@ -64758,8 +64785,9 @@ var App2 = ({ onReady }) => {
64758
64785
  const [items, setItems] = (0, import_react35.useState)([]);
64759
64786
  const [footer, setFooterState] = (0, import_react35.useState)({ mode: "auto", autoApprove: false });
64760
64787
  const { columns } = use_window_size_default();
64761
- const [inputEnabled, setInputEnabledState] = (0, import_react35.useState)(true);
64788
+ const [busy, setBusyState] = (0, import_react35.useState)(false);
64762
64789
  const [line, setLineState] = (0, import_react35.useState)("");
64790
+ const [cursorPos, setCursorPosState] = (0, import_react35.useState)(0);
64763
64791
  const [prompt2, setPrompt] = (0, import_react35.useState)(DEFAULT_PROMPT);
64764
64792
  const { exit } = use_app_default();
64765
64793
  const lineRef = (0, import_react35.useRef)("");
@@ -64768,18 +64796,30 @@ var App2 = ({ onReady }) => {
64768
64796
  lineRef.current = value;
64769
64797
  setLineState(value);
64770
64798
  }, []);
64799
+ const cursorRef = (0, import_react35.useRef)(0);
64800
+ const setCursorPos = (0, import_react35.useCallback)((next) => {
64801
+ const value = typeof next === "function" ? next(cursorRef.current) : next;
64802
+ cursorRef.current = value;
64803
+ setCursorPosState(value);
64804
+ }, []);
64771
64805
  const askResolverRef = (0, import_react35.useRef)(null);
64772
64806
  const onLineRef = (0, import_react35.useRef)(null);
64807
+ const onQueuedLineRef = (0, import_react35.useRef)(null);
64808
+ const busyRef = (0, import_react35.useRef)(false);
64773
64809
  const [live, setLiveState] = (0, import_react35.useState)("");
64774
64810
  const print = (0, import_react35.useCallback)((text) => {
64775
64811
  setItems((prev) => [...prev, { id: idCounter++, content: text }]);
64776
64812
  }, []);
64777
64813
  const setFooter = (0, import_react35.useCallback)((info) => setFooterState(info), []);
64778
64814
  const setLive = (0, import_react35.useCallback)((text) => setLiveState(text), []);
64779
- const setInputEnabled = (0, import_react35.useCallback)((enabled) => setInputEnabledState(enabled), []);
64815
+ const setBusy = (0, import_react35.useCallback)((value) => {
64816
+ busyRef.current = value;
64817
+ setBusyState(value);
64818
+ }, []);
64780
64819
  const askLine = (0, import_react35.useCallback)((promptText) => {
64781
64820
  setPrompt(promptText || DEFAULT_PROMPT);
64782
64821
  setLine("");
64822
+ setCursorPos(0);
64783
64823
  return new Promise((resolve3) => {
64784
64824
  askResolverRef.current = (answer) => {
64785
64825
  setPrompt(DEFAULT_PROMPT);
@@ -64790,18 +64830,58 @@ var App2 = ({ onReady }) => {
64790
64830
  const onLine = (0, import_react35.useCallback)((cb) => {
64791
64831
  onLineRef.current = cb;
64792
64832
  }, []);
64833
+ const onQueuedLine = (0, import_react35.useCallback)((cb) => {
64834
+ onQueuedLineRef.current = cb;
64835
+ }, []);
64836
+ const onInterruptRef = (0, import_react35.useRef)(null);
64837
+ const onInterrupt = (0, import_react35.useCallback)((cb) => {
64838
+ onInterruptRef.current = cb;
64839
+ }, []);
64793
64840
  use_input_default((char, key) => {
64794
- if (!inputEnabled)
64795
- return;
64796
64841
  if (key.ctrl && char === "c") {
64842
+ const askResolve = askResolverRef.current;
64843
+ if (askResolve) {
64844
+ askResolverRef.current = null;
64845
+ setLine("");
64846
+ setCursorPos(0);
64847
+ askResolve("n");
64848
+ return;
64849
+ }
64850
+ if (busyRef.current && onInterruptRef.current) {
64851
+ onInterruptRef.current();
64852
+ return;
64853
+ }
64797
64854
  exit();
64798
64855
  return;
64799
64856
  }
64800
64857
  if (key.backspace || key.delete) {
64801
- setLine((s2) => dropLastGrapheme(s2));
64858
+ if (key.delete) {
64859
+ setLine((s2) => {
64860
+ const pos = cursorRef.current;
64861
+ const end = nextGraphemeBoundary(s2, pos);
64862
+ return s2.slice(0, pos) + s2.slice(end);
64863
+ });
64864
+ return;
64865
+ }
64866
+ setLine((s2) => {
64867
+ const pos = cursorRef.current;
64868
+ if (pos === 0)
64869
+ return s2;
64870
+ const start = prevGraphemeBoundary(s2, pos);
64871
+ setCursorPos(start);
64872
+ return s2.slice(0, start) + s2.slice(pos);
64873
+ });
64874
+ return;
64875
+ }
64876
+ if (key.leftArrow) {
64877
+ setCursorPos((pos) => prevGraphemeBoundary(lineRef.current, pos));
64878
+ return;
64879
+ }
64880
+ if (key.rightArrow) {
64881
+ setCursorPos((pos) => nextGraphemeBoundary(lineRef.current, pos));
64802
64882
  return;
64803
64883
  }
64804
- if (key.upArrow || key.downArrow || key.leftArrow || key.rightArrow || key.tab) {
64884
+ if (key.upArrow || key.downArrow || key.tab) {
64805
64885
  return;
64806
64886
  }
64807
64887
  const newlineIdx = char.search(/[\r\n]/);
@@ -64809,24 +64889,42 @@ var App2 = ({ onReady }) => {
64809
64889
  if (hasNewline) {
64810
64890
  const before = newlineIdx === -1 ? char : char.slice(0, newlineIdx);
64811
64891
  const after = newlineIdx === -1 ? "" : char.slice(newlineIdx + 1).replace(/^[\r\n]/, "");
64812
- const submitted = lineRef.current + before;
64892
+ const pos = cursorRef.current;
64893
+ const submitted = lineRef.current.slice(0, pos) + before + lineRef.current.slice(pos);
64813
64894
  setLine(after);
64895
+ setCursorPos(0);
64814
64896
  print(prompt2 + submitted);
64815
64897
  const askResolve = askResolverRef.current;
64816
64898
  if (askResolve) {
64817
64899
  askResolverRef.current = null;
64818
64900
  askResolve(submitted);
64901
+ } else if (busyRef.current) {
64902
+ onQueuedLineRef.current?.(submitted);
64819
64903
  } else {
64820
64904
  onLineRef.current?.(submitted);
64821
64905
  }
64822
64906
  return;
64823
64907
  }
64824
- setLine((s2) => s2 + char);
64908
+ setLine((s2) => {
64909
+ const pos = cursorRef.current;
64910
+ setCursorPos(pos + char.length);
64911
+ return s2.slice(0, pos) + char + s2.slice(pos);
64912
+ });
64825
64913
  });
64826
64914
  import_react35.default.useEffect(() => {
64827
- onReady({ print, setFooter, setLive, askLine, onLine, setInputEnabled, close: () => exit() });
64915
+ onReady({
64916
+ print,
64917
+ setFooter,
64918
+ setLive,
64919
+ askLine,
64920
+ onLine,
64921
+ onQueuedLine,
64922
+ onInterrupt,
64923
+ setBusy,
64924
+ close: () => exit()
64925
+ });
64828
64926
  }, []);
64829
- 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))), inputEnabled ? /* @__PURE__ */ import_react35.default.createElement(Box_default, null, /* @__PURE__ */ import_react35.default.createElement(Text, null, prompt2), /* @__PURE__ */ import_react35.default.createElement(Text, null, line), /* @__PURE__ */ import_react35.default.createElement(Text, { inverse: true }, " ")) : /* @__PURE__ */ import_react35.default.createElement(Text, { dimColor: true }, " (working\u2026)"), /* @__PURE__ */ import_react35.default.createElement(Text, { dimColor: true }, footerText(footer)));
64927
+ 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" : ""));
64830
64928
  };
64831
64929
  function startInkTerminal() {
64832
64930
  if (handle)
@@ -64867,26 +64965,31 @@ var InkReadlineAdapter = class extends EventEmitter3 {
64867
64965
  line = "";
64868
64966
  cursor = 0;
64869
64967
  ink;
64870
- paused = false;
64968
+ busy = false;
64969
+ pendingInput = [];
64871
64970
  constructor(ink) {
64872
64971
  super();
64873
64972
  this.ink = ink;
64874
64973
  this.ink.onLine((text) => {
64875
- if (this.paused)
64876
- return;
64877
64974
  this.emit("line", text);
64878
64975
  });
64976
+ this.ink.onQueuedLine((text) => {
64977
+ this.pendingInput.push(text);
64978
+ });
64979
+ this.ink.onInterrupt(() => {
64980
+ this.emit("interrupt");
64981
+ });
64879
64982
  }
64880
64983
  prompt(_preserveCursor) {
64881
64984
  }
64882
64985
  pause() {
64883
- this.paused = true;
64884
- this.ink.setInputEnabled(false);
64986
+ this.busy = true;
64987
+ this.ink.setBusy(true);
64885
64988
  return this;
64886
64989
  }
64887
64990
  resume() {
64888
- this.paused = false;
64889
- this.ink.setInputEnabled(true);
64991
+ this.busy = false;
64992
+ this.ink.setBusy(false);
64890
64993
  return this;
64891
64994
  }
64892
64995
  close() {
@@ -64896,6 +64999,21 @@ var InkReadlineAdapter = class extends EventEmitter3 {
64896
64999
  question(query, cb) {
64897
65000
  this.ink.askLine(query).then(cb);
64898
65001
  }
65002
+ /**
65003
+ * Drains and returns every follow-up message the user typed and sent while
65004
+ * `pause()` was active (i.e. during a running agent turn). Intended to be
65005
+ * passed straight through as `AgentLoopOptions.takePendingInput` — see
65006
+ * loop.ts, and the VS Code panel's `_pendingInjections.splice(0)` for the
65007
+ * pattern this mirrors. Returns `[]` when nothing is queued, and empties
65008
+ * the queue on every call so the same follow-up is never folded in twice.
65009
+ */
65010
+ takePendingInput() {
65011
+ return this.pendingInput.splice(0);
65012
+ }
65013
+ /** True while a `pause()`/`resume()` pair is in effect (an agent turn is running). */
65014
+ isBusy() {
65015
+ return this.busy;
65016
+ }
64899
65017
  };
64900
65018
 
64901
65019
  // src/commands/chat.ts
@@ -65211,7 +65329,7 @@ function formatAgentsList(workDir) {
65211
65329
  }
65212
65330
  return lines.join("\n");
65213
65331
  }
65214
- async function runTurn(messages, modelAlias, workDir, abortSignal, env3, nexrallMd, mode, effort, checkpointManager, onProgress) {
65332
+ async function runTurn(messages, modelAlias, workDir, abortSignal, env3, nexrallMd, mode, effort, checkpointManager, onProgress, takePendingInput) {
65215
65333
  let lastUsage;
65216
65334
  const spinner = new Spinner();
65217
65335
  const mdRender = new MarkdownStreamRenderer();
@@ -65252,6 +65370,14 @@ async function runTurn(messages, modelAlias, workDir, abortSignal, env3, nexrall
65252
65370
  // prompt, and sub-agents inherit the lock.
65253
65371
  planMode: mode === "plan",
65254
65372
  effort,
65373
+ // Claude-Code-style follow-ups: text the user typed and sent WHILE this
65374
+ // turn was already running. inkTerminal.tsx echoes each one into the
65375
+ // transcript itself the moment it's submitted (chronological, matching
65376
+ // the VS Code panel's queueMessage) — so onInjectedInput is intentionally
65377
+ // a no-op here rather than printing it again.
65378
+ takePendingInput,
65379
+ onInjectedInput: () => {
65380
+ },
65255
65381
  // The backend streams a cumulative output-token count (routes/code.js's
65256
65382
  // sendProgress, throttled to ≤5/s) covering thinking, visible text AND
65257
65383
  // tool-argument JSON. This used to be stored in a variable and rendered only
@@ -65752,12 +65878,22 @@ ${text}` : "");
65752
65878
  }
65753
65879
  });
65754
65880
  const rl = interactive ? new InkReadlineAdapter(getInkTerminal()) : readline3.createInterface({ input: process.stdin, output: process.stdout, terminal: true });
65881
+ if (rl instanceof InkReadlineAdapter) {
65882
+ rl.on("interrupt", () => {
65883
+ if (agentRunning) {
65884
+ console.log("\n" + source_default.yellow(" Interrupted."));
65885
+ abortSignal.aborted = true;
65886
+ agentRunning = false;
65887
+ }
65888
+ });
65889
+ }
65755
65890
  setReadlineInterface(rl);
65756
65891
  const updateFooter = () => {
65757
65892
  if (interactive)
65758
65893
  getInkTerminal().setFooter({ mode: agentMode, autoApprove: isYoloMode() });
65759
65894
  };
65760
65895
  updateFooter();
65896
+ const takePendingInput = rl instanceof InkReadlineAdapter ? () => rl.takePendingInput() : void 0;
65761
65897
  let inputBuffer = "";
65762
65898
  rl.on("line", async (rawLine) => {
65763
65899
  if (rawLine.endsWith("\\")) {
@@ -65911,7 +66047,10 @@ ${convText}`;
65911
66047
  env3,
65912
66048
  nexrallMd,
65913
66049
  agentMode,
65914
- effortLevel
66050
+ effortLevel,
66051
+ void 0,
66052
+ void 0,
66053
+ takePendingInput
65915
66054
  );
65916
66055
  agentRunning = false;
65917
66056
  const summaryText = [...r2.messages].reverse().find((m2) => m2.role === "assistant")?.content[0]?.text ?? "";
@@ -65970,7 +66109,10 @@ ${dirList}`;
65970
66109
  env3,
65971
66110
  nexrallMd,
65972
66111
  "auto",
65973
- effortLevel
66112
+ effortLevel,
66113
+ void 0,
66114
+ void 0,
66115
+ takePendingInput
65974
66116
  );
65975
66117
  agentRunning = false;
65976
66118
  const content = [...r2.messages].reverse().find((m2) => m2.role === "assistant")?.content[0]?.text ?? "";
@@ -66189,7 +66331,7 @@ ${text}
66189
66331
  abortSignal.aborted = false;
66190
66332
  agentRunning = true;
66191
66333
  try {
66192
- const result = await runTurn(messages, modelAlias, workDir, abortSignal, env3, nexrallMd, agentMode, effortLevel, checkpoints, saveProgress);
66334
+ const result = await runTurn(messages, modelAlias, workDir, abortSignal, env3, nexrallMd, agentMode, effortLevel, checkpoints, saveProgress, takePendingInput);
66193
66335
  messages = result.messages;
66194
66336
  updateTitle();
66195
66337
  saveSession(sessionId, sessionTitle, workDir, messages);
@@ -66309,7 +66451,7 @@ ${text}
66309
66451
  abortSignal.aborted = false;
66310
66452
  agentRunning = true;
66311
66453
  try {
66312
- const result = await runTurn(messages, turnModel, workDir, abortSignal, env3, nexrallMd, turnMode, effortLevel, checkpoints, saveProgress);
66454
+ const result = await runTurn(messages, turnModel, workDir, abortSignal, env3, nexrallMd, turnMode, effortLevel, checkpoints, saveProgress, takePendingInput);
66313
66455
  messages = result.messages;
66314
66456
  updateTitle();
66315
66457
  saveSession(sessionId, sessionTitle, workDir, messages);
@@ -66333,7 +66475,7 @@ ${text}
66333
66475
  abortSignal.aborted = false;
66334
66476
  agentRunning = true;
66335
66477
  try {
66336
- const result = await runTurn(messages, modelAlias, workDir, abortSignal, env3, nexrallMd, agentMode, effortLevel, checkpoints, saveProgress);
66478
+ const result = await runTurn(messages, modelAlias, workDir, abortSignal, env3, nexrallMd, agentMode, effortLevel, checkpoints, saveProgress, takePendingInput);
66337
66479
  messages = result.messages;
66338
66480
  updateTitle();
66339
66481
  saveSession(sessionId, sessionTitle, workDir, messages);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nexrall-code",
3
- "version": "0.5.74",
3
+ "version": "0.5.75",
4
4
  "description": "Nexrall Code — AI coding assistant for your terminal (headless agent for scripts, CI and automation)",
5
5
  "keywords": [
6
6
  "ai",