nexrall-code 0.5.135 → 0.5.136

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 +160 -27
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -159144,19 +159144,26 @@ function decideChoiceKey(spec, sel, char, key, now, openedAt) {
159144
159144
  const idx = spec.options.findIndex((o) => o.key.toLowerCase() === char.toLowerCase());
159145
159145
  return idx >= 0 ? { kind: "pick", idx } : { kind: "none" };
159146
159146
  }
159147
- function choiceView(spec, sel) {
159147
+ function choiceView(spec, sel, maxRows) {
159148
159148
  const n = spec.options.length;
159149
+ const size = maxRows && maxRows > 0 ? Math.min(maxRows, n) : n;
159150
+ const start = windowStart(Math.min(Math.max(sel, 0), Math.max(n - 1, 0)), n, size);
159151
+ const rows = spec.options.slice(start, start + size).map((o, k) => {
159152
+ const i2 = start + k;
159153
+ return { selected: i2 === sel, text: `${i2 === sel ? "\u203A" : " "} ${i2 + 1}. ${o.label}` };
159154
+ });
159155
+ const range = size < n ? `rows ${start + 1}-${start + size} of ${n} \xB7 ` : "";
159149
159156
  return {
159150
159157
  title: spec.title,
159151
159158
  detail: spec.detail,
159152
159159
  risk: spec.risk,
159153
- rows: spec.options.map((o, i2) => ({
159154
- selected: i2 === sel,
159155
- text: `${i2 === sel ? "\u203A" : " "} ${i2 + 1}. ${o.label}`
159156
- })),
159157
- hint: ` \u2191\u2193 select \xB7 1-${Math.min(n, 9)} pick \xB7 Enter confirm \xB7 Esc = no`
159160
+ rows,
159161
+ hint: ` \u2191\u2193 select \xB7 ${range}1-${Math.min(n, 9)} pick \xB7 Enter confirm \xB7 Esc = no`
159158
159162
  };
159159
159163
  }
159164
+ function choiceRowBudget(terminalRows, max = 12) {
159165
+ return Math.max(3, Math.min(max, terminalRows - 12));
159166
+ }
159160
159167
  function denyIndex(spec) {
159161
159168
  return spec.options.findIndex((o) => o.key.toLowerCase() === "n");
159162
159169
  }
@@ -159359,6 +159366,14 @@ var App2 = ({ onReady }) => {
159359
159366
  const onRewind = (0, import_react35.useCallback)((cb) => {
159360
159367
  onRewindRef.current = cb;
159361
159368
  }, []);
159369
+ const onModelRef = (0, import_react35.useRef)(null);
159370
+ const onModel = (0, import_react35.useCallback)((cb) => {
159371
+ onModelRef.current = cb;
159372
+ }, []);
159373
+ const onEffortRef = (0, import_react35.useRef)(null);
159374
+ const onEffort = (0, import_react35.useCallback)((cb) => {
159375
+ onEffortRef.current = cb;
159376
+ }, []);
159362
159377
  const onHelpRef = (0, import_react35.useRef)(null);
159363
159378
  const onHelp = (0, import_react35.useCallback)((cb) => {
159364
159379
  onHelpRef.current = cb;
@@ -159759,6 +159774,17 @@ var App2 = ({ onReady }) => {
159759
159774
  });
159760
159775
  return;
159761
159776
  }
159777
+ if (key.meta && (char === "m" || char === "M" || char === "t" || char === "T")) {
159778
+ if (askResolverRef.current)
159779
+ return;
159780
+ const which = char.toLowerCase() === "m" ? "model" : "effort";
159781
+ if (busyRef.current) {
159782
+ setHintText(`The ${which} can be changed when this turn finishes`);
159783
+ return;
159784
+ }
159785
+ (which === "model" ? onModelRef.current : onEffortRef.current)?.();
159786
+ return;
159787
+ }
159762
159788
  if (key.ctrl && char === "l") {
159763
159789
  forceFullRedrawRef?.();
159764
159790
  return;
@@ -159974,7 +160000,9 @@ var App2 = ({ onReady }) => {
159974
160000
  onExpand,
159975
160001
  onBackground,
159976
160002
  onJump,
159977
- onEscape
160003
+ onEscape,
160004
+ onModel,
160005
+ onEffort
159978
160006
  });
159979
160007
  }, []);
159980
160008
  const slices = renderInputSlices(line, cursorPos);
@@ -160042,7 +160070,7 @@ var App2 = ({ onReady }) => {
160042
160070
  menuReserveRef.current,
160043
160071
  slashMenu ? slashMenu.rows.length + 1 : mentionMenu ? mentionMenu.rows.length + 1 : 0
160044
160072
  );
160045
- const choiceVm = choiceRef.current ? choiceView(choiceRef.current.spec, choiceRef.current.sel) : null;
160073
+ const choiceVm = choiceRef.current ? choiceView(choiceRef.current.spec, choiceRef.current.sel, choiceRowBudget(process.stdout.rows ?? 24)) : null;
160046
160074
  const riskColor = choiceVm?.risk === "high" ? "red" : choiceVm?.risk === "medium" ? "yellow" : "green";
160047
160075
  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(
160048
160076
  Box_default,
@@ -160262,6 +160290,52 @@ function rewindChoices(cps) {
160262
160290
  return { options, ids: newest.map((c) => c.id) };
160263
160291
  }
160264
160292
 
160293
+ // src/ui/pickList.ts
160294
+ var PICK_LABEL_MAX = 34;
160295
+ var PICK_NOTE_MAX = 30;
160296
+ var CURRENT_MARK = "current";
160297
+ function buildPickList(o) {
160298
+ if (!o.items.length)
160299
+ return null;
160300
+ const labelMax = o.labelMax ?? PICK_LABEL_MAX;
160301
+ const values = [];
160302
+ const options = o.items.map((it, i2) => {
160303
+ values.push(it.value);
160304
+ const parts = [clip4(it.label, labelMax)];
160305
+ if (it.note)
160306
+ parts.push(clip4(it.note, PICK_NOTE_MAX));
160307
+ if (o.current !== void 0 && it.value === o.current)
160308
+ parts.push(CURRENT_MARK);
160309
+ return { key: String(i2 + 1), label: parts.join(" ") };
160310
+ });
160311
+ options.push({ key: "n", label: o.cancelLabel ?? "Cancel" });
160312
+ const currentIndex = o.current ? values.indexOf(o.current) : -1;
160313
+ return {
160314
+ spec: {
160315
+ title: o.title,
160316
+ detail: o.detail,
160317
+ risk: o.risk ?? "low",
160318
+ options,
160319
+ defaultIndex: currentIndex >= 0 ? currentIndex : 0
160320
+ },
160321
+ values,
160322
+ currentIndex
160323
+ };
160324
+ }
160325
+ function pickOutcome(list, index) {
160326
+ const value = list.values[index];
160327
+ return value === void 0 ? { kind: "cancel" } : { kind: "pick", value };
160328
+ }
160329
+ function currentNotOfferedLine(label) {
160330
+ return `Current: ${label} \u2014 not in the list below, so no row is marked.`;
160331
+ }
160332
+
160333
+ // src/modelSwitch.ts
160334
+ function planModelSwitch(next, effort, clamp) {
160335
+ const clamped = clamp(effort, next);
160336
+ return { model: next, effort: clamped, effortChanged: clamped !== effort };
160337
+ }
160338
+
160265
160339
  // src/ui/backgroundShells.ts
160266
160340
  var MAX_KEPT = 64e3;
160267
160341
  var BackgroundShells = class {
@@ -160491,10 +160565,6 @@ var MODEL_COST_MULTIPLIER = {
160491
160565
  // 130_glm_5_3_pricing.sql's own caveat if Z.ai ships a GLM-5.3-specific rate.
160492
160566
  "glm-5.3": 0.6
160493
160567
  };
160494
- function modelWithCostHint(id) {
160495
- const mult = (0, import_code_core9.liveCostMultiplierFor)(id, MODEL_COST_MULTIPLIER[id]);
160496
- return mult === void 0 ? id : `${id} (${mult}x)`;
160497
- }
160498
160568
  var NO_VISION_MODELS = /* @__PURE__ */ new Set(["qwen3.7-max", "glm-5.3"]);
160499
160569
  var NO_PDF_MODELS = /* @__PURE__ */ new Set([
160500
160570
  "gpt-5.6-sol",
@@ -161539,9 +161609,9 @@ function printHelp() {
161539
161609
  ["/worktree [list]", "Show this session's worktree isolation status, or list every worktree (start one with `nex --worktree [name]`)"],
161540
161610
  ["/peers [send <name> <msg>|broadcast <msg>]", "List other discoverable Nexrall Code sessions, message one directly, or broadcast to all"],
161541
161611
  ["/help", "Show this help"],
161542
- ["/model [name]", "Switch model (e.g. claude-opus-5-5, gpt-5.4)"],
161612
+ ["/model [name]", "Pick a model from a list (or name one: /model claude-opus-5-5)"],
161543
161613
  ["/mode [ask|edit|plan|auto]", "Set agent mode"],
161544
- ["/effort [level]", "Set thinking effort level (options depend on the current model \u2014 run /effort with no argument to see them)"],
161614
+ ["/effort [level]", "Pick a thinking effort level (the scale is the current model's own \u2014 /effort opens a list)"],
161545
161615
  ["/yolo", "Auto-approve all permissions"],
161546
161616
  ["/balance", "Show what is paying (personal wallet or team) and what is left"],
161547
161617
  ["/team [use <id|personal>]", "List your teams, or switch what usage is billed to"],
@@ -161590,6 +161660,7 @@ function printHelp() {
161590
161660
  ["Ctrl+W / Alt+\u232B", "Delete the previous word"],
161591
161661
  ["Ctrl+U / Ctrl+K", "Delete to the start / end of the line (Ctrl+Y pastes it back)"],
161592
161662
  ["Shift+Tab", "Cycle the mode: ask \u2192 edit \u2192 auto \u2192 plan (when no turn is running)"],
161663
+ ["Alt+M / Alt+T", "Pick the model / the thinking effort without typing a command"],
161593
161664
  ["Ctrl+R", "Search your prompt history: type to filter, Ctrl+R older, Enter use it, Esc cancel"],
161594
161665
  ["Ctrl+G", "Edit the draft in $VISUAL / $EDITOR"],
161595
161666
  ["Esc Esc", "At an empty prompt: pick a checkpoint to rewind to (same as /rewind)"],
@@ -162037,10 +162108,81 @@ ${text}` : "");
162037
162108
  }
162038
162109
  }
162039
162110
  };
162111
+ const applyModel = (id) => {
162112
+ const plan = planModelSwitch(id, effortLevel, clampEffortForModel);
162113
+ modelAlias = plan.model;
162114
+ effortLevel = plan.effort;
162115
+ updateFooter();
162116
+ console.log(source_default.green(` Model \u2192 ${source_default.bold(resolveModelLabel(modelAlias))}`));
162117
+ if (plan.effortChanged)
162118
+ console.log(source_default.dim(` Effort reset \u2192 ${effortLevel} (previous level not available on this model)`));
162119
+ };
162120
+ const applyEffort = (level) => {
162121
+ effortLevel = level;
162122
+ console.log(source_default.green(` Effort \u2192 ${source_default.bold(level)}`));
162123
+ };
162124
+ const modelPickerNote = (id) => {
162125
+ const parts = [];
162126
+ const mult = (0, import_code_core9.liveCostMultiplierFor)(id, MODEL_COST_MULTIPLIER[id]);
162127
+ if (mult !== void 0 && mult !== 1)
162128
+ parts.push(`${mult}x`);
162129
+ if (needsVisionSidecar(id))
162130
+ parts.push("images via Sonnet");
162131
+ if (needsPdfSidecar(id))
162132
+ parts.push("no PDFs");
162133
+ return parts.length ? parts.join(" \xB7 ") : void 0;
162134
+ };
162135
+ const openModelPicker = async () => {
162136
+ if (!(rl instanceof InkReadlineAdapter))
162137
+ return;
162138
+ const list = buildPickList({
162139
+ title: "Switch model",
162140
+ detail: "Takes effect on the next turn \u2014 the conversation and your files are untouched",
162141
+ items: selectableModelIds().map((id) => ({ value: id, label: resolveModelLabel(id), note: modelPickerNote(id) })),
162142
+ current: modelAlias
162143
+ });
162144
+ if (!list) {
162145
+ console.log(source_default.red(" No model list available (offline and no cached catalogue)."));
162146
+ return;
162147
+ }
162148
+ if (list.currentIndex < 0)
162149
+ console.log(source_default.dim(" " + currentNotOfferedLine(resolveModelLabel(modelAlias))));
162150
+ const out = pickOutcome(list, await rl.askChoice(list.spec));
162151
+ if (out.kind === "cancel")
162152
+ return;
162153
+ applyModel(out.value);
162154
+ };
162155
+ const openEffortPicker = async () => {
162156
+ if (!(rl instanceof InkReadlineAdapter))
162157
+ return;
162158
+ const config = effortConfigFor(modelAlias);
162159
+ if (config.levels.length < 2) {
162160
+ console.log(source_default.dim(` ${resolveModelLabel(modelAlias)} has one thinking setting (${config.levels[0] ?? "\u2014"} \xB7 ${config.names[0] ?? "not configurable"}); there is nothing to pick.`));
162161
+ return;
162162
+ }
162163
+ const list = buildPickList({
162164
+ title: `Thinking effort \u2014 ${resolveModelLabel(modelAlias)}`,
162165
+ detail: "Higher levels think longer before answering; the scale belongs to this model",
162166
+ items: config.levels.map((v, i2) => ({ value: v, label: v, note: config.names[i2] })),
162167
+ current: effortLevel
162168
+ });
162169
+ if (!list)
162170
+ return;
162171
+ const out = pickOutcome(list, await rl.askChoice(list.spec));
162172
+ if (out.kind === "cancel")
162173
+ return;
162174
+ applyEffort(out.value);
162175
+ };
162040
162176
  if (interactive) {
162041
162177
  getInkTerminal().onHelp(() => {
162042
162178
  printHelp();
162043
162179
  });
162180
+ getInkTerminal().onModel(() => {
162181
+ void openModelPicker();
162182
+ });
162183
+ getInkTerminal().onEffort(() => {
162184
+ void openEffortPicker();
162185
+ });
162044
162186
  getInkTerminal().onJump((delta) => {
162045
162187
  jumpTranscript(messages, delta);
162046
162188
  updateFooter();
@@ -162477,17 +162619,9 @@ ${dirList}`;
162477
162619
  const requested = arg.trim();
162478
162620
  const models = selectableModelIds();
162479
162621
  if (!requested) {
162480
- console.log(source_default.dim(` Current: ${source_default.cyan(resolveModelLabel(modelAlias))}`));
162481
- console.log(source_default.dim(` Options: ${models.map(modelWithCostHint).join(" \xB7 ")}`));
162622
+ await openModelPicker();
162482
162623
  } else if (models.includes(normaliseModelId(requested))) {
162483
- modelAlias = normaliseModelId(requested);
162484
- const clamped = clampEffortForModel(effortLevel, modelAlias);
162485
- const effortChanged = clamped !== effortLevel;
162486
- effortLevel = clamped;
162487
- console.log(source_default.green(` Model \u2192 ${source_default.bold(resolveModelLabel(modelAlias))}`));
162488
- if (effortChanged) {
162489
- console.log(source_default.dim(` Effort reset \u2192 ${effortLevel} (previous level not available on this model)`));
162490
- }
162624
+ applyModel(normaliseModelId(requested));
162491
162625
  } else {
162492
162626
  console.log(source_default.red(` Unknown: ${requested}. Options: ${models.join(", ")}`));
162493
162627
  }
@@ -162521,10 +162655,9 @@ ${dirList}`;
162521
162655
  const config = effortConfigFor(modelAlias);
162522
162656
  const optionsLine = config.levels.map((v, i2) => `${v} (${config.names[i2]})`).join(" \xB7 ");
162523
162657
  if (!effortArg) {
162524
- console.log(source_default.dim(` Current: ${source_default.cyan(effortLevel)} Options: ${optionsLine}`));
162658
+ await openEffortPicker();
162525
162659
  } else if (config.levels.includes(effortArg)) {
162526
- effortLevel = effortArg;
162527
- console.log(source_default.green(` Effort \u2192 ${source_default.bold(effortArg)}`));
162660
+ applyEffort(effortArg);
162528
162661
  } else {
162529
162662
  console.log(source_default.red(` Unknown: ${effortArg}. Options: ${optionsLine}`));
162530
162663
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nexrall-code",
3
- "version": "0.5.135",
3
+ "version": "0.5.136",
4
4
  "description": "Nexrall Code — AI coding assistant for your terminal (headless agent for scripts, CI and automation)",
5
5
  "keywords": [
6
6
  "ai",