stratum-cli 0.8.0-beta.0 → 0.8.0-beta.2

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 (3) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/dist/index.js +167 -27
  3. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -6,6 +6,28 @@ El formato sigue [Keep a Changelog](https://keepachangelog.com/es-ES/1.1.0/) y l
6
6
 
7
7
  > Las versiones anteriores a la 0.2.1-beta.0 solo existen como tags de git: la primera publicación en npm fue la 0.2.1-beta.0.
8
8
 
9
+ ## [0.8.0-beta.2] — 2026-10-12
10
+
11
+ Tercera pre-release (`npm install -g stratum-cli@beta`), con más arreglos de lo visto al probar la beta.
12
+
13
+ ### Corregido
14
+ - **El terminal parpadeaba con varios subagentes trabajando.** El árbol de subagentes listaba todas sus tool calls; al superar el alto del terminal, la pantalla entera se borraba y repintaba en cada actualización. Ahora cada subagente enseña solo las últimas (entre 1 y 6, según el alto del terminal) y resume las anteriores en una línea; el detalle completo sigue en `/subagents`.
15
+ - La etiqueta de una tool call enseña el argumento que la identifica (la ruta, el comando, el patrón) aunque el modelo lo envíe detrás de otros: antes salía `read_file: 700` cuando `limit` llegaba antes que `path`.
16
+ - Dos sesiones nuevas guardadas en el mismo segundo podían recibir el mismo id y la segunda pisaba a la primera. Ahora el id generado se comprueba contra el disco.
17
+
18
+ ## [0.8.0-beta.1] — 2026-10-12
19
+
20
+ Segunda pre-release (`npm install -g stratum-cli@beta`), con lo visto al probar la beta.0.
21
+
22
+ ### Añadido
23
+ - **`/comandos` con el agente trabajando**: los de consulta (`/context`, `/jobs`, `/jobs cancel`, `/todo`, `/changes`, `/auditor`, `/debug`, `/tools`, `/agents`, `/env`, `/memory show|list|search`, `/sessions list`, `/config get`, `/help`) se ejecutan sin esperar al final del turno, y la paleta se reduce a ellos. Los que tocan el historial, el modelo o el toolset siguen esperando.
24
+
25
+ ### Corregido
26
+ - **La salida de un comando ya no se vuelca en la pantalla del chat.** Cuando un `exec` fallaba, el log escribía su salida completa en el terminal, encima de la interfaz. Ahora un comando que se ejecutó y salió mal no se registra como aviso, y cualquier campo del log que vaya al terminal sale en una línea y acotado a 300 caracteres; el fichero de logs lo guarda entero.
27
+ - El texto de dos respuestas del mismo turno, con una tool o una pregunta por medio, ya no sale pegado («…rápidas:Perfecto…»): la segunda empieza párrafo.
28
+ - El bloque de la tool `question` mostraba `[object Object],[object Object]`; ahora enseña la primera pregunta y cuántas más hay.
29
+ - `Ctrl+U` y `Ctrl+T` podían dejar su letra escrita en el input en vez de actuar, y `Ctrl+L` limpiaba la conversación con un turno en curso.
30
+
9
31
  ## [0.8.0-beta.0] — 2026-10-12
10
32
 
11
33
  Pre-release para pruebas (`npm install -g stratum-cli@beta`); `latest` sigue en la 0.7.0.
@@ -271,6 +293,8 @@ Primera versión etiquetada. Hitos 0 y 1.
271
293
  - `/init` para generar o actualizar `STRATUM.md`.
272
294
  - Render de markdown de las respuestas.
273
295
 
296
+ [0.8.0-beta.2]: https://github.com/choruzo/CLI/compare/v0.8.0-beta.1...v0.8.0-beta.2
297
+ [0.8.0-beta.1]: https://github.com/choruzo/CLI/compare/v0.8.0-beta.0...v0.8.0-beta.1
274
298
  [0.8.0-beta.0]: https://github.com/choruzo/CLI/compare/v0.7.0...v0.8.0-beta.0
275
299
  [0.7.0]: https://github.com/choruzo/CLI/compare/v0.6.0...v0.7.0
276
300
  [0.7.0-beta.0]: https://github.com/choruzo/CLI/compare/v0.6.0...v0.7.0-beta.0
package/dist/index.js CHANGED
@@ -1305,17 +1305,22 @@ import { dirname as dirname2 } from "path";
1305
1305
  function ansi(code) {
1306
1306
  return (s) => `\x1B[${code}m${s}\x1B[0m`;
1307
1307
  }
1308
+ function formatFieldValue(v) {
1309
+ const raw = typeof v === "string" ? v : typeof v === "object" ? JSON.stringify(v) : String(v);
1310
+ const flat = raw.replace(/\s*[\r\n]+\s*/g, " \u23CE ");
1311
+ if (flat.length <= STDERR_FIELD_MAX_CHARS) return flat;
1312
+ return `${flat.slice(0, STDERR_FIELD_MAX_CHARS)}\u2026 (+${flat.length - STDERR_FIELD_MAX_CHARS} chars)`;
1313
+ }
1308
1314
  function formatFields(fields) {
1309
1315
  if (!fields) return "";
1310
1316
  const parts = [];
1311
1317
  for (const [k, v] of Object.entries(fields)) {
1312
1318
  if (v === void 0) continue;
1313
- const val = typeof v === "string" ? v : typeof v === "object" ? JSON.stringify(v) : String(v);
1314
- parts.push(`${k}=${val}`);
1319
+ parts.push(`${k}=${formatFieldValue(v)}`);
1315
1320
  }
1316
1321
  return parts.length > 0 ? " " + parts.join(" ") : "";
1317
1322
  }
1318
- var NO_COLOR, LEVEL_COLOR, LEVEL_LABEL, StderrSink, FileSink;
1323
+ var NO_COLOR, LEVEL_COLOR, LEVEL_LABEL, STDERR_FIELD_MAX_CHARS, StderrSink, FileSink;
1319
1324
  var init_sinks = __esm({
1320
1325
  "src/logging/sinks.ts"() {
1321
1326
  "use strict";
@@ -1341,6 +1346,7 @@ var init_sinks = __esm({
1341
1346
  warn: "WARN ",
1342
1347
  error: "ERROR"
1343
1348
  };
1349
+ STDERR_FIELD_MAX_CHARS = 300;
1344
1350
  StderrSink = class {
1345
1351
  level;
1346
1352
  stream;
@@ -5589,7 +5595,8 @@ Tool "${name}" has been disabled for this session after ${failures} consecutive
5589
5595
  if (result.ok) {
5590
5596
  log3.debug("tool ok", { tool: call.name, durationMs, outputChars: result.output.length });
5591
5597
  } else {
5592
- log3.warn("tool error", {
5598
+ const level = result.countsAsFailure === false ? "info" : "warn";
5599
+ log3[level]("tool error", {
5593
5600
  tool: call.name,
5594
5601
  durationMs,
5595
5602
  recoverable: result.recoverable,
@@ -19719,6 +19726,16 @@ var init_store2 = __esm({
19719
19726
  mkdirSync7(this.sessionsDir, { recursive: true });
19720
19727
  }
19721
19728
  }
19729
+ /**
19730
+ * Un id que no está en disco. El id lleva la hora al segundo y tres
19731
+ * caracteres al azar: dos sesiones guardadas en el mismo segundo pueden
19732
+ * coincidir, y la segunda pisaría a la primera.
19733
+ */
19734
+ freshSessionId() {
19735
+ let id = generateSessionId();
19736
+ while (existsSync16(this.sessionPath(id))) id = generateSessionId();
19737
+ return id;
19738
+ }
19722
19739
  sessionPath(id) {
19723
19740
  if (!isSessionId(id)) {
19724
19741
  throw new Error(`Id de sesi\xF3n inv\xE1lido: ${JSON.stringify(id.slice(0, 80))}`);
@@ -19740,14 +19757,14 @@ var init_store2 = __esm({
19740
19757
  async save(params) {
19741
19758
  this.ensureDir();
19742
19759
  const now = (/* @__PURE__ */ new Date()).toISOString();
19743
- let id = params.existingId ?? generateSessionId();
19760
+ let id = params.existingId ?? this.freshSessionId();
19744
19761
  let createdAt = params.createdAt ?? now;
19745
19762
  let forkedFrom = params.forkedFrom;
19746
19763
  if (params.existingId) {
19747
19764
  const onDisk = this.diskUpdatedAt(params.existingId);
19748
19765
  if (onDisk !== null && onDisk !== params.expectedUpdatedAt) {
19749
19766
  forkedFrom = params.existingId;
19750
- id = generateSessionId();
19767
+ id = this.freshSessionId();
19751
19768
  createdAt = now;
19752
19769
  log21.warn("session changed on disk: saving as a fork", { from: forkedFrom, to: id });
19753
19770
  }
@@ -20774,6 +20791,51 @@ var init_spinner = __esm({
20774
20791
  }
20775
20792
  });
20776
20793
 
20794
+ // src/cli/ui/tool-arg-label.ts
20795
+ function labelOf(value2) {
20796
+ if (value2 === null || value2 === void 0) return "";
20797
+ if (typeof value2 !== "object") return String(value2);
20798
+ if (!Array.isArray(value2)) return JSON.stringify(value2);
20799
+ const first = value2[0];
20800
+ const head = typeof first === "object" && first !== null ? Object.values(first).find((v) => typeof v === "string") ?? JSON.stringify(first) : String(first ?? "");
20801
+ return value2.length > 1 ? `${head} (+${value2.length - 1})` : head;
20802
+ }
20803
+ function primaryArg(input) {
20804
+ if (!input) return "";
20805
+ const keys = Object.keys(input);
20806
+ if (keys.length === 0) return "";
20807
+ const key = IDENTIFYING_KEYS.find((k) => typeof input[k] === "string" && input[k] !== "") ?? keys[0];
20808
+ return labelOf(input[key]);
20809
+ }
20810
+ function tailWindow(items, max) {
20811
+ const keep = Math.max(1, Math.floor(max));
20812
+ if (items.length <= keep) return { hidden: 0, visible: [...items] };
20813
+ return { hidden: items.length - keep, visible: items.slice(-keep) };
20814
+ }
20815
+ function treeToolBudget(rows, nodes, reservedRows = 22) {
20816
+ const perNode = Math.floor((rows - reservedRows) / Math.max(1, nodes)) - 2;
20817
+ return Math.min(6, Math.max(1, perNode));
20818
+ }
20819
+ var IDENTIFYING_KEYS;
20820
+ var init_tool_arg_label = __esm({
20821
+ "src/cli/ui/tool-arg-label.ts"() {
20822
+ "use strict";
20823
+ init_esm_shims();
20824
+ IDENTIFYING_KEYS = [
20825
+ "command",
20826
+ "path",
20827
+ "file_path",
20828
+ "filePath",
20829
+ "pattern",
20830
+ "query",
20831
+ "url",
20832
+ "task",
20833
+ "jobId",
20834
+ "name"
20835
+ ];
20836
+ }
20837
+ });
20838
+
20777
20839
  // src/cli/ui/ToolCallBlock.tsx
20778
20840
  import { Box, Text } from "ink";
20779
20841
  import { jsx, jsxs } from "react/jsx-runtime";
@@ -20804,7 +20866,7 @@ function formatInput(state) {
20804
20866
  return truncateLabel(`${from} \u2192 ${to}`);
20805
20867
  }
20806
20868
  }
20807
- return truncateLabel(String(src[keys[0]] ?? ""));
20869
+ return truncateLabel(primaryArg(src));
20808
20870
  }
20809
20871
  function formatDuration(ms) {
20810
20872
  return `${(ms / 1e3).toFixed(1)}s`;
@@ -20925,6 +20987,7 @@ var init_ToolCallBlock = __esm({
20925
20987
  init_esm_shims();
20926
20988
  init_theme();
20927
20989
  init_spinner();
20990
+ init_tool_arg_label();
20928
20991
  MAX_EXPANDED_LINES = 10;
20929
20992
  SLOW_MS = 1e3;
20930
20993
  }
@@ -21663,6 +21726,19 @@ var init_CommandPalette = __esm({
21663
21726
  });
21664
21727
 
21665
21728
  // src/cli/ui/session-commands.ts
21729
+ function matchCommand(input, commands = SESSION_COMMANDS) {
21730
+ const line = input.trim();
21731
+ let best = null;
21732
+ for (const c of commands) {
21733
+ if (line !== c.name && !line.startsWith(c.name + " ")) continue;
21734
+ if (!best || c.name.length > best.name.length) best = c;
21735
+ }
21736
+ return best;
21737
+ }
21738
+ function isBusyCommand(input) {
21739
+ const match = matchCommand(input);
21740
+ return match !== null && BUSY_COMMAND_NAMES.has(match.name);
21741
+ }
21666
21742
  function filterCommands(input, commands = SESSION_COMMANDS) {
21667
21743
  const trimmed = input.trimStart();
21668
21744
  if (!trimmed.startsWith("/")) return [];
@@ -21679,7 +21755,7 @@ function filterProfiles(input, profiles) {
21679
21755
  function slashCommandName(input) {
21680
21756
  return /^(\/[A-Za-z][\w-]*)(\s|$)/.exec(input.trimStart())?.[1] ?? null;
21681
21757
  }
21682
- var SESSION_COMMANDS;
21758
+ var SESSION_COMMANDS, BUSY_COMMAND_NAMES, BUSY_COMMANDS;
21683
21759
  var init_session_commands = __esm({
21684
21760
  "src/cli/ui/session-commands.ts"() {
21685
21761
  "use strict";
@@ -21849,6 +21925,28 @@ var init_session_commands = __esm({
21849
21925
  { name: "/quit", description: "Termina la sesi\xF3n y guarda el historial", hasArgs: false },
21850
21926
  { name: "/exit", description: "Termina la sesi\xF3n y guarda el historial", hasArgs: false }
21851
21927
  ];
21928
+ BUSY_COMMAND_NAMES = /* @__PURE__ */ new Set([
21929
+ "/help",
21930
+ "/context",
21931
+ "/todo",
21932
+ "/changes",
21933
+ "/jobs",
21934
+ "/jobs cancel",
21935
+ "/auditor",
21936
+ "/auditor stop",
21937
+ "/debug",
21938
+ "/tools",
21939
+ "/agents",
21940
+ "/env",
21941
+ "/memory show",
21942
+ "/memory list",
21943
+ "/memory search",
21944
+ "/sessions list",
21945
+ "/config get"
21946
+ ]);
21947
+ BUSY_COMMANDS = SESSION_COMMANDS.filter(
21948
+ (c) => BUSY_COMMAND_NAMES.has(c.name)
21949
+ );
21852
21950
  }
21853
21951
  });
21854
21952
 
@@ -22348,7 +22446,7 @@ var init_SubagentBlock = __esm({
22348
22446
  });
22349
22447
 
22350
22448
  // src/cli/ui/AgentTree.tsx
22351
- import { Box as Box12, Text as Text13 } from "ink";
22449
+ import { Box as Box12, Text as Text13, useStdout as useStdout3 } from "ink";
22352
22450
  import { jsx as jsx13, jsxs as jsxs11 } from "react/jsx-runtime";
22353
22451
  function fmtDur2(ms) {
22354
22452
  return `${(ms / 1e3).toFixed(1)}s`;
@@ -22358,10 +22456,7 @@ function truncate2(text, max) {
22358
22456
  return oneLine.length > max ? oneLine.slice(0, max - 1) + "\u2026" : oneLine;
22359
22457
  }
22360
22458
  function firstArg(tc) {
22361
- const src = tc.input ?? {};
22362
- const keys = Object.keys(src);
22363
- if (keys.length === 0) return "";
22364
- return truncate2(String(src[keys[0]] ?? ""), 40);
22459
+ return truncate2(primaryArg(tc.input), 40);
22365
22460
  }
22366
22461
  function ToolLine({ tc }) {
22367
22462
  const icon = tc.status === "completed" ? "\u2713" : tc.status === "error" ? "\u2717" : "\u27F3";
@@ -22396,7 +22491,7 @@ function nodeIcon(status) {
22396
22491
  return { icon: "\u22B3", color: theme.accent };
22397
22492
  }
22398
22493
  }
22399
- function SubagentNode({ node, speaking, focused, expanded, isLast, now }) {
22494
+ function SubagentNode({ node, speaking, focused, expanded, isLast, now, maxToolLines }) {
22400
22495
  const running = node.status === "running";
22401
22496
  const elapsedMs = node.startedAt ? Math.max(0, now - node.startedAt) : 0;
22402
22497
  const { icon, color } = nodeIcon(node.status);
@@ -22408,7 +22503,7 @@ function SubagentNode({ node, speaking, focused, expanded, isLast, now }) {
22408
22503
  node.iterations !== void 0 ? `${node.iterations} it` : ""
22409
22504
  ].filter(Boolean).join(" \xB7 ");
22410
22505
  const tail2 = node.status === "failed" && node.error ? truncate2(node.error, 44) : node.status === "completed" && node.summary ? truncate2(node.summary, 44) : truncate2(node.task, 44);
22411
- const tcs = node.toolCalls ?? [];
22506
+ const { hidden, visible: tcs } = tailWindow(node.toolCalls ?? [], maxToolLines);
22412
22507
  const files = node.filesChanged ?? [];
22413
22508
  return /* @__PURE__ */ jsxs11(Box12, { flexDirection: "column", children: [
22414
22509
  /* @__PURE__ */ jsxs11(Text13, { wrap: "truncate-end", children: [
@@ -22432,6 +22527,15 @@ function SubagentNode({ node, speaking, focused, expanded, isLast, now }) {
22432
22527
  ] }),
22433
22528
  focused ? /* @__PURE__ */ jsx13(Text13, { color: theme.accent, children: " \u25C0" }) : null
22434
22529
  ] }),
22530
+ hidden > 0 && /* @__PURE__ */ jsxs11(Text13, { color: theme.textDisabled, wrap: "truncate-end", children: [
22531
+ " ",
22532
+ "\u2026 ",
22533
+ hidden,
22534
+ " ",
22535
+ hidden === 1 ? "acci\xF3n anterior" : "acciones anteriores",
22536
+ " ",
22537
+ "(/subagents para verlas)"
22538
+ ] }),
22435
22539
  tcs.map((tc) => /* @__PURE__ */ jsx13(ToolLine, { tc }, tc.id)),
22436
22540
  expanded && node.summary && /* @__PURE__ */ jsxs11(Text13, { color: theme.textFaint, wrap: "truncate-end", children: [
22437
22541
  " ",
@@ -22457,6 +22561,8 @@ function AgentTree({
22457
22561
  expandedBlockIds,
22458
22562
  now = Date.now()
22459
22563
  }) {
22564
+ const { stdout } = useStdout3();
22565
+ const maxToolLines = treeToolBudget(stdout?.rows ?? 24, nodes.length);
22460
22566
  const allTerminal = nodes.every((n) => TERMINAL.has(n.status));
22461
22567
  if (allTerminal) {
22462
22568
  const completed = nodes.filter((n) => n.status === "completed").length;
@@ -22502,7 +22608,8 @@ function AgentTree({
22502
22608
  focused: focusedBlockId === n.id,
22503
22609
  expanded: expandedBlockIds?.has(n.id) ?? false,
22504
22610
  isLast: i === nodes.length - 1,
22505
- now
22611
+ now,
22612
+ maxToolLines
22506
22613
  },
22507
22614
  n.id
22508
22615
  ))
@@ -22515,6 +22622,7 @@ var init_AgentTree = __esm({
22515
22622
  init_esm_shims();
22516
22623
  init_theme();
22517
22624
  init_spinner();
22625
+ init_tool_arg_label();
22518
22626
  TERMINAL = /* @__PURE__ */ new Set([
22519
22627
  "completed",
22520
22628
  "failed",
@@ -22736,7 +22844,7 @@ var init_AgentMessage = __esm({
22736
22844
  });
22737
22845
 
22738
22846
  // src/cli/ui/MessageList.tsx
22739
- import { Box as Box15, Static as Static2, useStdout as useStdout3 } from "ink";
22847
+ import { Box as Box15, Static as Static2, useStdout as useStdout4 } from "ink";
22740
22848
  import { jsx as jsx17, jsxs as jsxs15 } from "react/jsx-runtime";
22741
22849
  function renderItem(item, key, focusedBlockId, expandedBlockIds, debug, now, liveTextLines, liveColumns, liveActionLimit) {
22742
22850
  if (item.kind === "user") {
@@ -22786,7 +22894,7 @@ function MessageList({
22786
22894
  now = Date.now(),
22787
22895
  availableRows
22788
22896
  }) {
22789
- const { stdout } = useStdout3();
22897
+ const { stdout } = useStdout4();
22790
22898
  const { contentWidth } = resolveLayout(stdout.columns ?? 80, stdout.rows ?? 24);
22791
22899
  const terminalRows = availableRows ?? stdout.rows ?? 24;
22792
22900
  const liveActionLimit = Math.max(1, Math.min(6, Math.floor((terminalRows - 8) / 2)));
@@ -23513,7 +23621,7 @@ var init_FatalError = __esm({
23513
23621
  });
23514
23622
 
23515
23623
  // src/cli/ui/ConversationView.tsx
23516
- import { Box as Box26, Text as Text27, useStdout as useStdout4 } from "ink";
23624
+ import { Box as Box26, Text as Text27, useStdout as useStdout5 } from "ink";
23517
23625
  import { jsx as jsx28, jsxs as jsxs26 } from "react/jsx-runtime";
23518
23626
  function ConversationView({
23519
23627
  completedItems,
@@ -23563,7 +23671,7 @@ function ConversationView({
23563
23671
  const liveNow = useLiveClock(thinking);
23564
23672
  const gateOpen = !!pendingConfirm || !!pendingQuestions?.length || !!pendingApproval;
23565
23673
  const steerable = thinking && !gateOpen && !fatalError;
23566
- const { stdout } = useStdout4();
23674
+ const { stdout } = useStdout5();
23567
23675
  const terminalRows = stdout.rows ?? 24;
23568
23676
  const panelMaxSteps = Math.max(2, Math.min(5, Math.floor(terminalRows / 5)));
23569
23677
  const jobsClock = jobsNow ?? Date.now();
@@ -24677,12 +24785,21 @@ function reducer(state, action) {
24677
24785
  }
24678
24786
  case "AGENT_EVENT": {
24679
24787
  const ev = action.event;
24788
+ if (/^(tool_|subagent_|questions_|plan_)/.test(ev.type) && state.currentItem?.kind === "agent" && state.currentItem.text && !state.currentItem.textBreak) {
24789
+ return reducer(
24790
+ { ...state, currentItem: { ...state.currentItem, textBreak: true } },
24791
+ action
24792
+ );
24793
+ }
24680
24794
  if (ev.type === "text_delta") {
24681
24795
  return {
24682
24796
  ...state,
24683
24797
  currentItem: updateCurrentAgent(state.currentItem, (item) => ({
24684
24798
  ...item,
24685
- text: item.text + ev.delta,
24799
+ text: item.textBreak ? `${item.text.trimEnd()}
24800
+
24801
+ ${ev.delta}` : item.text + ev.delta,
24802
+ textBreak: false,
24686
24803
  thinkingOpen: false
24687
24804
  }))
24688
24805
  };
@@ -25294,8 +25411,8 @@ function App({
25294
25411
  const [subagentViewHint, setSubagentViewHint] = useState9(null);
25295
25412
  const [paletteIndex, setPaletteIndex] = useState9(0);
25296
25413
  const [paletteDismissed, setPaletteDismissed] = useState9(false);
25297
- const paletteEligible = !state.thinking && !state.pendingConfirm && !state.pendingQuestions && !overlay && state.focusState === "input" && !paletteDismissed && /^[/@]/.test(state.inputValue.trimStart());
25298
- const paletteItems = !paletteEligible ? [] : state.inputValue.trimStart().startsWith("@") ? filterProfiles(state.inputValue, profileRows) : filterCommands(state.inputValue, SESSION_COMMANDS);
25414
+ const paletteEligible = !state.pendingConfirm && !state.pendingQuestions && !overlay && state.focusState === "input" && !paletteDismissed && /^[/@]/.test(state.inputValue.trimStart());
25415
+ const paletteItems = !paletteEligible ? [] : state.inputValue.trimStart().startsWith("@") ? state.thinking ? [] : filterProfiles(state.inputValue, profileRows) : filterCommands(state.inputValue, state.thinking ? BUSY_COMMANDS : SESSION_COMMANDS);
25299
25416
  const effPaletteIndex = Math.min(paletteIndex, Math.max(paletteItems.length - 1, 0));
25300
25417
  const handleInputChange = useCallback4((value2) => {
25301
25418
  setPaletteDismissed(false);
@@ -25390,18 +25507,28 @@ function App({
25390
25507
  return;
25391
25508
  }
25392
25509
  }
25510
+ const settleInput = (value2) => {
25511
+ setTimeout(() => dispatch({ type: "INPUT_CHANGE", value: value2 }), 0);
25512
+ };
25393
25513
  if (key.ctrl && input === "l") {
25514
+ if (state.thinking) {
25515
+ settleInput(state.inputValue);
25516
+ return;
25517
+ }
25394
25518
  executeCommandRef.current?.("/clear");
25519
+ settleInput("");
25395
25520
  return;
25396
25521
  }
25397
25522
  if (key.ctrl && input === "t") {
25398
25523
  dispatch({ type: "TOGGLE_TODO" });
25524
+ settleInput(state.inputValue);
25399
25525
  return;
25400
25526
  }
25401
25527
  if (key.ctrl && input === "u") {
25402
25528
  draftRef.current = "";
25403
25529
  setHistoryIndex(null);
25404
25530
  dispatch({ type: "INPUT_CHANGE", value: "" });
25531
+ settleInput("");
25405
25532
  return;
25406
25533
  }
25407
25534
  if (state.focusState === "input" && (key.upArrow || key.downArrow)) {
@@ -26401,7 +26528,20 @@ Uso: /provider <alias> para cambiar en esta sesi\xF3n \xB7 /provider remove <ali
26401
26528
  }
26402
26529
  if (state.thinking) {
26403
26530
  if (/^[/@]/.test(cmd)) {
26404
- setInputHint("Commands are not available while the agent is working (Ctrl+C cancels).");
26531
+ const sel = paletteItems[effPaletteIndex];
26532
+ if (sel && sel.name !== cmd) {
26533
+ if (sel.hasArgs) completePaletteSelection(sel.name, true);
26534
+ else executeCommand(sel.name);
26535
+ return;
26536
+ }
26537
+ if (isBusyCommand(cmd)) {
26538
+ setInputHint(null);
26539
+ executeCommand(cmd);
26540
+ return;
26541
+ }
26542
+ setInputHint(
26543
+ "Not available while the agent is working (Ctrl+C cancels). Type / to see what is."
26544
+ );
26405
26545
  return;
26406
26546
  }
26407
26547
  setInputHint(null);
@@ -26879,7 +27019,7 @@ async function settle(step, fn) {
26879
27019
  }
26880
27020
  }
26881
27021
  function resolveVersion() {
26882
- if (true) return "0.8.0-beta.0";
27022
+ if (true) return "0.8.0-beta.2";
26883
27023
  try {
26884
27024
  const thisDir = dirname14(fileURLToPath2(import.meta.url));
26885
27025
  const pkg = JSON.parse(
@@ -32734,7 +32874,7 @@ function resolveResult(ref, dir) {
32734
32874
  }
32735
32875
 
32736
32876
  // src/cli/commands/eval.ts
32737
- var VERSION = true ? "0.8.0-beta.0" : "dev";
32877
+ var VERSION = true ? "0.8.0-beta.2" : "dev";
32738
32878
  var fail2 = (message) => {
32739
32879
  process.stderr.write(`${message}
32740
32880
  `);
@@ -33347,7 +33487,7 @@ var statsCommand = new Command14("stats").description(
33347
33487
  init_loader();
33348
33488
  init_schema_version();
33349
33489
  function resolveVersion2() {
33350
- if (true) return "0.8.0-beta.0";
33490
+ if (true) return "0.8.0-beta.2";
33351
33491
  const thisDir = dirname19(fileURLToPath5(import.meta.url));
33352
33492
  const pkg = JSON.parse(readFileSync26(join32(thisDir, "..", "..", "package.json"), "utf-8"));
33353
33493
  return pkg.version;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "stratum-cli",
3
- "version": "0.8.0-beta.0",
3
+ "version": "0.8.0-beta.2",
4
4
  "description": "Agente CLI extensible sobre un loop ReAct — provider-agnostic (Ollama, llama.cpp, vLLM, LiteLLM, OpenAI), con plan-and-execute, subagentes, MCP y SSH nativo",
5
5
  "keywords": [
6
6
  "cli",