nexrall-code 0.5.204 → 0.5.206

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 +186 -19
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -30921,6 +30921,7 @@ var require_askUser = __commonJS({
30921
30921
  properties: {
30922
30922
  question: { type: "string", description: "The full question, ending with a question mark." },
30923
30923
  header: { type: "string", description: "Very short label (max ~12 chars)." },
30924
+ multiSelect: { type: "boolean", description: "true when the user may pick MORE than one option (default: pick one)." },
30924
30925
  options: {
30925
30926
  type: "array",
30926
30927
  minItems: 2,
@@ -30960,7 +30961,12 @@ var require_askUser = __commonJS({
30960
30961
  const d = x2.description;
30961
30962
  options.push({ label, description: typeof d === "string" && d.trim() ? d.trim() : void 0 });
30962
30963
  }
30963
- out.push({ question, header: typeof o.header === "string" ? o.header.trim().slice(0, 14) : void 0, options });
30964
+ out.push({
30965
+ question,
30966
+ header: typeof o.header === "string" ? o.header.trim().slice(0, 14) : void 0,
30967
+ multiSelect: o.multiSelect === true ? true : void 0,
30968
+ options
30969
+ });
30964
30970
  }
30965
30971
  return out;
30966
30972
  }
@@ -30974,7 +30980,19 @@ var require_askUser = __commonJS({
30974
30980
  for (let i2 = 0; i2 < qs.length; i2++) {
30975
30981
  const a = answers[i2];
30976
30982
  const q = qs[i2];
30977
- if ("text" in a) {
30983
+ if ("indices" in a) {
30984
+ const labels = [];
30985
+ for (const ix of Array.isArray(a.indices) ? a.indices : []) {
30986
+ const opt = q.options[ix];
30987
+ if (opt && !labels.includes(opt.label))
30988
+ labels.push(opt.label);
30989
+ }
30990
+ const t2 = String(a.text ?? "").trim();
30991
+ if (!labels.length && !t2)
30992
+ return dismissed;
30993
+ const shown = labels.map((l) => `"${l}"`).join(", ");
30994
+ parts.push(`"${q.question}" = ${shown}${t2 ? `${shown ? " + " : ""}"${t2}" (user's own answer)` : ""}`);
30995
+ } else if ("text" in a) {
30978
30996
  const t2 = String(a.text ?? "").trim();
30979
30997
  if (!t2)
30980
30998
  return dismissed;
@@ -39037,20 +39055,20 @@ var require_ms = __commonJS({
39037
39055
  function fmtLong(ms) {
39038
39056
  var msAbs = Math.abs(ms);
39039
39057
  if (msAbs >= d) {
39040
- return plural(ms, msAbs, d, "day");
39058
+ return plural2(ms, msAbs, d, "day");
39041
39059
  }
39042
39060
  if (msAbs >= h2) {
39043
- return plural(ms, msAbs, h2, "hour");
39061
+ return plural2(ms, msAbs, h2, "hour");
39044
39062
  }
39045
39063
  if (msAbs >= m2) {
39046
- return plural(ms, msAbs, m2, "minute");
39064
+ return plural2(ms, msAbs, m2, "minute");
39047
39065
  }
39048
39066
  if (msAbs >= s2) {
39049
- return plural(ms, msAbs, s2, "second");
39067
+ return plural2(ms, msAbs, s2, "second");
39050
39068
  }
39051
39069
  return ms + " ms";
39052
39070
  }
39053
- function plural(ms, msAbs, n, name) {
39071
+ function plural2(ms, msAbs, n, name) {
39054
39072
  var isPlural = msAbs >= n * 1.5;
39055
39073
  return Math.round(ms / n) + " " + name + (isPlural ? "s" : "");
39056
39074
  }
@@ -82673,6 +82691,7 @@ var ASK_USER_TOOL_SCHEMAS = [
82673
82691
  properties: {
82674
82692
  question: { type: "string", description: "The full question, ending with a question mark." },
82675
82693
  header: { type: "string", description: "Very short label (max ~12 chars)." },
82694
+ multiSelect: { type: "boolean", description: "true when the user may pick MORE than one option (default: pick one)." },
82676
82695
  options: {
82677
82696
  type: "array",
82678
82697
  minItems: 2,
@@ -82713,10 +82732,49 @@ function parseQuestions(input) {
82713
82732
  const d = x2.description;
82714
82733
  options.push({ label, description: typeof d === "string" && d.trim() ? d.trim() : void 0 });
82715
82734
  }
82716
- out.push({ question, header: typeof o.header === "string" ? o.header.trim().slice(0, 14) : void 0, options });
82735
+ out.push({
82736
+ question,
82737
+ header: typeof o.header === "string" ? o.header.trim().slice(0, 14) : void 0,
82738
+ multiSelect: o.multiSelect === true ? true : void 0,
82739
+ options
82740
+ });
82717
82741
  }
82718
82742
  return out;
82719
82743
  }
82744
+ async function askMulti(q, io) {
82745
+ const chosen = /* @__PURE__ */ new Set();
82746
+ let typed = "";
82747
+ let at = 0;
82748
+ for (; ; ) {
82749
+ const n = q.options.length;
82750
+ const options = [
82751
+ ...q.options.map((o, i2) => ({
82752
+ key: String(i2 + 1),
82753
+ label: `${chosen.has(i2) ? "[x]" : "[ ]"} ${o.description ? `${o.label} \u2014 ${o.description}` : o.label}`
82754
+ })),
82755
+ { key: String(n + 1), label: `${typed ? "[x]" : "[ ]"} Other (type your own answer)${typed ? `: ${typed}` : ""}` },
82756
+ { key: String(n + 2), label: chosen.size || typed ? "Done \u2014 submit these" : "Done (pick at least one first)" }
82757
+ ];
82758
+ const title = `${q.header ? `${q.header}: ` : ""}${q.question}`;
82759
+ const pick = await io.choose({ title, detail: "Select all that apply \u2014 toggle with Enter / its number", options, risk: "low", defaultIndex: at, replacesInput: true });
82760
+ if (pick < 0 || pick > n + 1)
82761
+ return null;
82762
+ at = pick;
82763
+ if (pick < n) {
82764
+ if (chosen.has(pick))
82765
+ chosen.delete(pick);
82766
+ else
82767
+ chosen.add(pick);
82768
+ } else if (pick === n) {
82769
+ typed = (await io.line(" Your answer: ")).trim();
82770
+ } else if (chosen.size || typed) {
82771
+ break;
82772
+ }
82773
+ }
82774
+ const labels = [...chosen].sort((a, b) => a - b).map((i2) => `"${q.options[i2].label}"`);
82775
+ const own = typed ? `"${typed}" (user's own answer)` : "";
82776
+ return `"${q.question}" = ${[...labels, own].filter(Boolean).join(", ")}`;
82777
+ }
82720
82778
  async function executeAskUserTool(name, input, io) {
82721
82779
  if (name !== ASK_USER_TOOL_NAME)
82722
82780
  return null;
@@ -82728,7 +82786,17 @@ async function executeAskUserTool(name, input, io) {
82728
82786
  if (typeof qs === "string")
82729
82787
  return { error: qs };
82730
82788
  const answers = [];
82789
+ const dismissedResult = {
82790
+ error: "The user dismissed the question without answering. Do not assume an answer; proceed on your best judgement or stop and say what you need."
82791
+ };
82731
82792
  for (const q of qs) {
82793
+ if (q.multiSelect) {
82794
+ const r2 = await askMulti(q, io);
82795
+ if (r2 === null)
82796
+ return dismissedResult;
82797
+ answers.push(r2);
82798
+ continue;
82799
+ }
82732
82800
  const options = [
82733
82801
  ...q.options.map((o, i2) => ({
82734
82802
  key: String(i2 + 1),
@@ -82736,19 +82804,16 @@ async function executeAskUserTool(name, input, io) {
82736
82804
  })),
82737
82805
  { key: String(q.options.length + 1), label: "Other (type your own answer)" }
82738
82806
  ];
82739
- const pick = await io.choose({ title: q.header ? `${q.header}: ${q.question}` : q.question, options, risk: "low" });
82740
- const dismissed = {
82741
- error: "The user dismissed the question without answering. Do not assume an answer; proceed on your best judgement or stop and say what you need."
82742
- };
82807
+ const pick = await io.choose({ title: q.header ? `${q.header}: ${q.question}` : q.question, options, risk: "low", replacesInput: true });
82743
82808
  if (pick === q.options.length) {
82744
82809
  const typed = (await io.line(" Your answer: ")).trim();
82745
82810
  if (!typed)
82746
- return dismissed;
82811
+ return dismissedResult;
82747
82812
  answers.push(`"${q.question}" = "${typed}" (user's own answer)`);
82748
82813
  continue;
82749
82814
  }
82750
82815
  if (pick < 0 || pick > q.options.length)
82751
- return dismissed;
82816
+ return dismissedResult;
82752
82817
  answers.push(`"${q.question}" = "${q.options[pick].label}"`);
82753
82818
  }
82754
82819
  return { output: "User answered: " + answers.join("; ") };
@@ -100187,6 +100252,7 @@ function choiceView(spec, sel, maxRows) {
100187
100252
  });
100188
100253
  const range = size < n ? `rows ${start + 1}-${start + size} of ${n} \xB7 ` : "";
100189
100254
  return {
100255
+ replacesInput: spec.replacesInput === true,
100190
100256
  title: spec.title,
100191
100257
  detail: spec.detail,
100192
100258
  risk: spec.risk,
@@ -100434,6 +100500,100 @@ function indicatorText(above, below, columns) {
100434
100500
  return truncateCells(" " + parts.join(" \xB7 "), Math.max(1, Math.floor(columns)));
100435
100501
  }
100436
100502
 
100503
+ // src/ui/inputWindow.ts
100504
+ function inputRowBudget(terminalRows) {
100505
+ const rows = Number.isFinite(terminalRows) && terminalRows > 0 ? terminalRows : 24;
100506
+ return Math.max(5, Math.min(12, Math.floor(rows / 3)));
100507
+ }
100508
+ function rowsFor(chars, columns) {
100509
+ return Math.max(1, Math.ceil(chars / columns));
100510
+ }
100511
+ function countNewlines(s2, from, to) {
100512
+ let n = 0;
100513
+ for (let i2 = s2.indexOf("\n", from); i2 !== -1 && i2 < to; i2 = s2.indexOf("\n", i2 + 1))
100514
+ n++;
100515
+ return n;
100516
+ }
100517
+ function plural(n, word) {
100518
+ return `${n.toLocaleString("en-US")} ${word}${n === 1 ? "" : "s"}`;
100519
+ }
100520
+ function windowInput(line, cursor, maxRows, columns) {
100521
+ const cols3 = Math.max(1, Math.floor(columns) || 80);
100522
+ const budget = Math.max(3, Math.floor(maxRows) - 2);
100523
+ const identity = { text: line, cursor, windowed: false, toView: (o) => o };
100524
+ if (line.length === 0)
100525
+ return identity;
100526
+ let newlines = 0;
100527
+ for (let i2 = line.indexOf("\n"); i2 !== -1 && newlines <= maxRows; i2 = line.indexOf("\n", i2 + 1))
100528
+ newlines++;
100529
+ if (newlines + 1 + Math.ceil(2 * line.length / cols3) <= maxRows + 2)
100530
+ return identity;
100531
+ const at = Math.min(Math.max(0, cursor), line.length);
100532
+ const lineStart2 = (pos) => line.lastIndexOf("\n", pos - 1) + 1;
100533
+ const lineEnd2 = (pos) => {
100534
+ const e2 = line.indexOf("\n", pos);
100535
+ return e2 === -1 ? line.length : e2;
100536
+ };
100537
+ const cs = lineStart2(at);
100538
+ const ce = lineEnd2(at);
100539
+ let start;
100540
+ let end;
100541
+ if (rowsFor(ce - cs, cols3) > budget) {
100542
+ const caretRow = Math.floor((at - cs) / cols3);
100543
+ const firstRow = Math.max(0, caretRow - Math.floor((budget - 1) / 2));
100544
+ start = cs + firstRow * cols3;
100545
+ end = Math.min(ce, start + budget * cols3);
100546
+ } else {
100547
+ start = cs;
100548
+ end = ce;
100549
+ let used = rowsFor(ce - cs, cols3);
100550
+ for (; ; ) {
100551
+ let grew = false;
100552
+ if (start > 0) {
100553
+ const ps = lineStart2(start - 1);
100554
+ const r2 = rowsFor(start - 1 - ps, cols3);
100555
+ if (used + r2 <= budget) {
100556
+ start = ps;
100557
+ used += r2;
100558
+ grew = true;
100559
+ }
100560
+ }
100561
+ if (end < line.length) {
100562
+ const ne = lineEnd2(end + 1);
100563
+ const r2 = rowsFor(ne - (end + 1), cols3);
100564
+ if (used + r2 <= budget) {
100565
+ end = ne;
100566
+ used += r2;
100567
+ grew = true;
100568
+ }
100569
+ }
100570
+ if (!grew)
100571
+ break;
100572
+ }
100573
+ }
100574
+ const isLow = (i2) => i2 > 0 && i2 < line.length && /[\udc00-\udfff]/.test(line[i2]);
100575
+ if (isLow(start))
100576
+ start -= 1;
100577
+ if (isLow(end))
100578
+ end += 1;
100579
+ end = Math.min(end, line.length);
100580
+ if (start === 0 && end === line.length)
100581
+ return identity;
100582
+ const hiddenAbove = countNewlines(line, 0, start) + (start > 0 && line[start - 1] !== "\n" ? 1 : 0);
100583
+ const hiddenBelow = countNewlines(line, end, line.length) + (end < line.length && line[end] !== "\n" ? 1 : 0);
100584
+ const above = hiddenAbove > 0 ? `\u22EF ${plural(hiddenAbove, "more line")} above
100585
+ ` : "";
100586
+ const below = hiddenBelow > 0 ? `
100587
+ \u22EF ${plural(hiddenBelow, "more line")} below` : "";
100588
+ const body = line.slice(start, end);
100589
+ return {
100590
+ text: above + body + below,
100591
+ cursor: above.length + Math.min(Math.max(at, start), end) - start,
100592
+ windowed: true,
100593
+ toView: (o) => above.length + Math.min(Math.max(o, start), end) - start
100594
+ };
100595
+ }
100596
+
100437
100597
  // src/ui/caretPosition.ts
100438
100598
  function caretPosition(opts) {
100439
100599
  const columns = Math.max(1, opts.columns);
@@ -101784,9 +101944,16 @@ var App2 = ({ onReady }) => {
101784
101944
  suspend: (fn) => suspendTerminal(fn)
101785
101945
  });
101786
101946
  }, []);
101787
- const slices = renderInputSlices(line, cursorPos);
101947
+ const inputView = windowInput(
101948
+ line,
101949
+ cursorPos,
101950
+ inputRowBudget(process.stdout.rows || 24),
101951
+ process.stdout.columns || 80
101952
+ );
101953
+ const slices = renderInputSlices(inputView.text, inputView.cursor);
101788
101954
  const shownPrompt = prompt2 ?? (isVimEnabled() && vimMode === "normal" ? paint.yellow("[N] ") : null);
101789
- const vsel = isVimEnabled() && vimVisual ? visualRange(line, vimRef.current, cursorPos) : null;
101955
+ const vselFull = isVimEnabled() && vimVisual ? visualRange(line, vimRef.current, cursorPos) : null;
101956
+ const vsel = vselFull ? { ...vselFull, from: inputView.toView(vselFull.from), to: inputView.toView(vselFull.to) } : null;
101790
101957
  const colorsOn = colorEnabled();
101791
101958
  const inputRowRef = (0, import_react36.useRef)(null);
101792
101959
  const inputMetrics = use_box_metrics_default(inputRowRef);
@@ -101798,8 +101965,8 @@ var App2 = ({ onReady }) => {
101798
101965
  viewHeightRef.current = viewHeight;
101799
101966
  const caret = inputMetrics.hasMeasured && !choiceRef.current ? caretPosition({
101800
101967
  promptWidth: visibleLen(promptFor(shownPrompt)),
101801
- line,
101802
- cursorPos,
101968
+ line: inputView.text,
101969
+ cursorPos: inputView.cursor,
101803
101970
  columns: process.stdout.columns || 80,
101804
101971
  inputRowTop: inputMetrics.top
101805
101972
  }) : void 0;
@@ -101911,7 +102078,7 @@ var App2 = ({ onReady }) => {
101911
102078
  borderRight: false,
101912
102079
  borderDimColor: colorsOn
101913
102080
  }
101914
- ), /* @__PURE__ */ import_react36.default.createElement(Box_default, { ref: inputRowRef }, /* @__PURE__ */ import_react36.default.createElement(Text, { wrap: "hard" }, /* @__PURE__ */ import_react36.default.createElement(Text, null, promptFor(shownPrompt)), vsel ? /* @__PURE__ */ import_react36.default.createElement(import_react36.default.Fragment, null, /* @__PURE__ */ import_react36.default.createElement(Text, null, line.slice(0, vsel.from)), /* @__PURE__ */ import_react36.default.createElement(Text, { inverse: true }, line.slice(vsel.from, vsel.to)), /* @__PURE__ */ import_react36.default.createElement(Text, null, line.slice(vsel.to))) : /* @__PURE__ */ import_react36.default.createElement(import_react36.default.Fragment, null, /* @__PURE__ */ import_react36.default.createElement(Text, null, slices.before), /* @__PURE__ */ import_react36.default.createElement(Text, { inverse: true }, slices.atCursor), /* @__PURE__ */ import_react36.default.createElement(Text, null, slices.after), suggestion && line === "" && !isAxMode() ? /* @__PURE__ */ import_react36.default.createElement(Text, { dimColor: colorsOn }, suggestion) : null))), isAxMode() ? /* @__PURE__ */ import_react36.default.createElement(Box_default, { height: 1 }) : /* @__PURE__ */ import_react36.default.createElement(
102081
+ ), choiceVm?.replacesInput ? null : /* @__PURE__ */ import_react36.default.createElement(Box_default, { ref: inputRowRef }, /* @__PURE__ */ import_react36.default.createElement(Text, { wrap: "hard" }, /* @__PURE__ */ import_react36.default.createElement(Text, null, promptFor(shownPrompt)), vsel ? /* @__PURE__ */ import_react36.default.createElement(import_react36.default.Fragment, null, /* @__PURE__ */ import_react36.default.createElement(Text, null, inputView.text.slice(0, vsel.from)), /* @__PURE__ */ import_react36.default.createElement(Text, { inverse: true }, inputView.text.slice(vsel.from, vsel.to)), /* @__PURE__ */ import_react36.default.createElement(Text, null, inputView.text.slice(vsel.to))) : /* @__PURE__ */ import_react36.default.createElement(import_react36.default.Fragment, null, /* @__PURE__ */ import_react36.default.createElement(Text, null, slices.before), /* @__PURE__ */ import_react36.default.createElement(Text, { inverse: true }, slices.atCursor), /* @__PURE__ */ import_react36.default.createElement(Text, null, slices.after), suggestion && line === "" && !isAxMode() ? /* @__PURE__ */ import_react36.default.createElement(Text, { dimColor: colorsOn }, suggestion) : null))), isAxMode() ? /* @__PURE__ */ import_react36.default.createElement(Box_default, { height: 1 }) : /* @__PURE__ */ import_react36.default.createElement(
101915
102082
  Box_default,
101916
102083
  {
101917
102084
  borderStyle: "single",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nexrall-code",
3
- "version": "0.5.204",
3
+ "version": "0.5.206",
4
4
  "description": "Nexrall Code \u2014 AI coding assistant for your terminal (headless agent for scripts, CI and automation)",
5
5
  "keywords": [
6
6
  "ai",