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

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.
package/CHANGELOG.md CHANGED
@@ -6,6 +6,19 @@ 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.1] — 2026-10-12
10
+
11
+ Segunda pre-release (`npm install -g stratum-cli@beta`), con lo visto al probar la beta.0.
12
+
13
+ ### Añadido
14
+ - **`/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.
15
+
16
+ ### Corregido
17
+ - **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.
18
+ - 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.
19
+ - El bloque de la tool `question` mostraba `[object Object],[object Object]`; ahora enseña la primera pregunta y cuántas más hay.
20
+ - `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.
21
+
9
22
  ## [0.8.0-beta.0] — 2026-10-12
10
23
 
11
24
  Pre-release para pruebas (`npm install -g stratum-cli@beta`); `latest` sigue en la 0.7.0.
@@ -271,6 +284,7 @@ Primera versión etiquetada. Hitos 0 y 1.
271
284
  - `/init` para generar o actualizar `STRATUM.md`.
272
285
  - Render de markdown de las respuestas.
273
286
 
287
+ [0.8.0-beta.1]: https://github.com/choruzo/CLI/compare/v0.8.0-beta.0...v0.8.0-beta.1
274
288
  [0.8.0-beta.0]: https://github.com/choruzo/CLI/compare/v0.7.0...v0.8.0-beta.0
275
289
  [0.7.0]: https://github.com/choruzo/CLI/compare/v0.6.0...v0.7.0
276
290
  [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,
@@ -20804,7 +20811,15 @@ function formatInput(state) {
20804
20811
  return truncateLabel(`${from} \u2192 ${to}`);
20805
20812
  }
20806
20813
  }
20807
- return truncateLabel(String(src[keys[0]] ?? ""));
20814
+ return truncateLabel(labelOf(src[keys[0]]));
20815
+ }
20816
+ function labelOf(value2) {
20817
+ if (value2 === null || value2 === void 0) return "";
20818
+ if (typeof value2 !== "object") return String(value2);
20819
+ if (!Array.isArray(value2)) return JSON.stringify(value2);
20820
+ const first = value2[0];
20821
+ const head = typeof first === "object" && first !== null ? Object.values(first).find((v) => typeof v === "string") ?? JSON.stringify(first) : String(first ?? "");
20822
+ return value2.length > 1 ? `${head} (+${value2.length - 1})` : head;
20808
20823
  }
20809
20824
  function formatDuration(ms) {
20810
20825
  return `${(ms / 1e3).toFixed(1)}s`;
@@ -21663,6 +21678,19 @@ var init_CommandPalette = __esm({
21663
21678
  });
21664
21679
 
21665
21680
  // src/cli/ui/session-commands.ts
21681
+ function matchCommand(input, commands = SESSION_COMMANDS) {
21682
+ const line = input.trim();
21683
+ let best = null;
21684
+ for (const c of commands) {
21685
+ if (line !== c.name && !line.startsWith(c.name + " ")) continue;
21686
+ if (!best || c.name.length > best.name.length) best = c;
21687
+ }
21688
+ return best;
21689
+ }
21690
+ function isBusyCommand(input) {
21691
+ const match = matchCommand(input);
21692
+ return match !== null && BUSY_COMMAND_NAMES.has(match.name);
21693
+ }
21666
21694
  function filterCommands(input, commands = SESSION_COMMANDS) {
21667
21695
  const trimmed = input.trimStart();
21668
21696
  if (!trimmed.startsWith("/")) return [];
@@ -21679,7 +21707,7 @@ function filterProfiles(input, profiles) {
21679
21707
  function slashCommandName(input) {
21680
21708
  return /^(\/[A-Za-z][\w-]*)(\s|$)/.exec(input.trimStart())?.[1] ?? null;
21681
21709
  }
21682
- var SESSION_COMMANDS;
21710
+ var SESSION_COMMANDS, BUSY_COMMAND_NAMES, BUSY_COMMANDS;
21683
21711
  var init_session_commands = __esm({
21684
21712
  "src/cli/ui/session-commands.ts"() {
21685
21713
  "use strict";
@@ -21849,6 +21877,28 @@ var init_session_commands = __esm({
21849
21877
  { name: "/quit", description: "Termina la sesi\xF3n y guarda el historial", hasArgs: false },
21850
21878
  { name: "/exit", description: "Termina la sesi\xF3n y guarda el historial", hasArgs: false }
21851
21879
  ];
21880
+ BUSY_COMMAND_NAMES = /* @__PURE__ */ new Set([
21881
+ "/help",
21882
+ "/context",
21883
+ "/todo",
21884
+ "/changes",
21885
+ "/jobs",
21886
+ "/jobs cancel",
21887
+ "/auditor",
21888
+ "/auditor stop",
21889
+ "/debug",
21890
+ "/tools",
21891
+ "/agents",
21892
+ "/env",
21893
+ "/memory show",
21894
+ "/memory list",
21895
+ "/memory search",
21896
+ "/sessions list",
21897
+ "/config get"
21898
+ ]);
21899
+ BUSY_COMMANDS = SESSION_COMMANDS.filter(
21900
+ (c) => BUSY_COMMAND_NAMES.has(c.name)
21901
+ );
21852
21902
  }
21853
21903
  });
21854
21904
 
@@ -24677,12 +24727,21 @@ function reducer(state, action) {
24677
24727
  }
24678
24728
  case "AGENT_EVENT": {
24679
24729
  const ev = action.event;
24730
+ if (/^(tool_|subagent_|questions_|plan_)/.test(ev.type) && state.currentItem?.kind === "agent" && state.currentItem.text && !state.currentItem.textBreak) {
24731
+ return reducer(
24732
+ { ...state, currentItem: { ...state.currentItem, textBreak: true } },
24733
+ action
24734
+ );
24735
+ }
24680
24736
  if (ev.type === "text_delta") {
24681
24737
  return {
24682
24738
  ...state,
24683
24739
  currentItem: updateCurrentAgent(state.currentItem, (item) => ({
24684
24740
  ...item,
24685
- text: item.text + ev.delta,
24741
+ text: item.textBreak ? `${item.text.trimEnd()}
24742
+
24743
+ ${ev.delta}` : item.text + ev.delta,
24744
+ textBreak: false,
24686
24745
  thinkingOpen: false
24687
24746
  }))
24688
24747
  };
@@ -25294,8 +25353,8 @@ function App({
25294
25353
  const [subagentViewHint, setSubagentViewHint] = useState9(null);
25295
25354
  const [paletteIndex, setPaletteIndex] = useState9(0);
25296
25355
  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);
25356
+ const paletteEligible = !state.pendingConfirm && !state.pendingQuestions && !overlay && state.focusState === "input" && !paletteDismissed && /^[/@]/.test(state.inputValue.trimStart());
25357
+ const paletteItems = !paletteEligible ? [] : state.inputValue.trimStart().startsWith("@") ? state.thinking ? [] : filterProfiles(state.inputValue, profileRows) : filterCommands(state.inputValue, state.thinking ? BUSY_COMMANDS : SESSION_COMMANDS);
25299
25358
  const effPaletteIndex = Math.min(paletteIndex, Math.max(paletteItems.length - 1, 0));
25300
25359
  const handleInputChange = useCallback4((value2) => {
25301
25360
  setPaletteDismissed(false);
@@ -25390,18 +25449,28 @@ function App({
25390
25449
  return;
25391
25450
  }
25392
25451
  }
25452
+ const settleInput = (value2) => {
25453
+ setTimeout(() => dispatch({ type: "INPUT_CHANGE", value: value2 }), 0);
25454
+ };
25393
25455
  if (key.ctrl && input === "l") {
25456
+ if (state.thinking) {
25457
+ settleInput(state.inputValue);
25458
+ return;
25459
+ }
25394
25460
  executeCommandRef.current?.("/clear");
25461
+ settleInput("");
25395
25462
  return;
25396
25463
  }
25397
25464
  if (key.ctrl && input === "t") {
25398
25465
  dispatch({ type: "TOGGLE_TODO" });
25466
+ settleInput(state.inputValue);
25399
25467
  return;
25400
25468
  }
25401
25469
  if (key.ctrl && input === "u") {
25402
25470
  draftRef.current = "";
25403
25471
  setHistoryIndex(null);
25404
25472
  dispatch({ type: "INPUT_CHANGE", value: "" });
25473
+ settleInput("");
25405
25474
  return;
25406
25475
  }
25407
25476
  if (state.focusState === "input" && (key.upArrow || key.downArrow)) {
@@ -26401,7 +26470,20 @@ Uso: /provider <alias> para cambiar en esta sesi\xF3n \xB7 /provider remove <ali
26401
26470
  }
26402
26471
  if (state.thinking) {
26403
26472
  if (/^[/@]/.test(cmd)) {
26404
- setInputHint("Commands are not available while the agent is working (Ctrl+C cancels).");
26473
+ const sel = paletteItems[effPaletteIndex];
26474
+ if (sel && sel.name !== cmd) {
26475
+ if (sel.hasArgs) completePaletteSelection(sel.name, true);
26476
+ else executeCommand(sel.name);
26477
+ return;
26478
+ }
26479
+ if (isBusyCommand(cmd)) {
26480
+ setInputHint(null);
26481
+ executeCommand(cmd);
26482
+ return;
26483
+ }
26484
+ setInputHint(
26485
+ "Not available while the agent is working (Ctrl+C cancels). Type / to see what is."
26486
+ );
26405
26487
  return;
26406
26488
  }
26407
26489
  setInputHint(null);
@@ -26879,7 +26961,7 @@ async function settle(step, fn) {
26879
26961
  }
26880
26962
  }
26881
26963
  function resolveVersion() {
26882
- if (true) return "0.8.0-beta.0";
26964
+ if (true) return "0.8.0-beta.1";
26883
26965
  try {
26884
26966
  const thisDir = dirname14(fileURLToPath2(import.meta.url));
26885
26967
  const pkg = JSON.parse(
@@ -32734,7 +32816,7 @@ function resolveResult(ref, dir) {
32734
32816
  }
32735
32817
 
32736
32818
  // src/cli/commands/eval.ts
32737
- var VERSION = true ? "0.8.0-beta.0" : "dev";
32819
+ var VERSION = true ? "0.8.0-beta.1" : "dev";
32738
32820
  var fail2 = (message) => {
32739
32821
  process.stderr.write(`${message}
32740
32822
  `);
@@ -33347,7 +33429,7 @@ var statsCommand = new Command14("stats").description(
33347
33429
  init_loader();
33348
33430
  init_schema_version();
33349
33431
  function resolveVersion2() {
33350
- if (true) return "0.8.0-beta.0";
33432
+ if (true) return "0.8.0-beta.1";
33351
33433
  const thisDir = dirname19(fileURLToPath5(import.meta.url));
33352
33434
  const pkg = JSON.parse(readFileSync26(join32(thisDir, "..", "..", "package.json"), "utf-8"));
33353
33435
  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.1",
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",