stratum-cli 0.8.0-beta.1 → 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.
- package/CHANGELOG.md +10 -0
- package/dist/index.js +84 -26
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,15 @@ 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
|
+
|
|
9
18
|
## [0.8.0-beta.1] — 2026-10-12
|
|
10
19
|
|
|
11
20
|
Segunda pre-release (`npm install -g stratum-cli@beta`), con lo visto al probar la beta.0.
|
|
@@ -284,6 +293,7 @@ Primera versión etiquetada. Hitos 0 y 1.
|
|
|
284
293
|
- `/init` para generar o actualizar `STRATUM.md`.
|
|
285
294
|
- Render de markdown de las respuestas.
|
|
286
295
|
|
|
296
|
+
[0.8.0-beta.2]: https://github.com/choruzo/CLI/compare/v0.8.0-beta.1...v0.8.0-beta.2
|
|
287
297
|
[0.8.0-beta.1]: https://github.com/choruzo/CLI/compare/v0.8.0-beta.0...v0.8.0-beta.1
|
|
288
298
|
[0.8.0-beta.0]: https://github.com/choruzo/CLI/compare/v0.7.0...v0.8.0-beta.0
|
|
289
299
|
[0.7.0]: https://github.com/choruzo/CLI/compare/v0.6.0...v0.7.0
|
package/dist/index.js
CHANGED
|
@@ -19726,6 +19726,16 @@ var init_store2 = __esm({
|
|
|
19726
19726
|
mkdirSync7(this.sessionsDir, { recursive: true });
|
|
19727
19727
|
}
|
|
19728
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
|
+
}
|
|
19729
19739
|
sessionPath(id) {
|
|
19730
19740
|
if (!isSessionId(id)) {
|
|
19731
19741
|
throw new Error(`Id de sesi\xF3n inv\xE1lido: ${JSON.stringify(id.slice(0, 80))}`);
|
|
@@ -19747,14 +19757,14 @@ var init_store2 = __esm({
|
|
|
19747
19757
|
async save(params) {
|
|
19748
19758
|
this.ensureDir();
|
|
19749
19759
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
19750
|
-
let id = params.existingId ??
|
|
19760
|
+
let id = params.existingId ?? this.freshSessionId();
|
|
19751
19761
|
let createdAt = params.createdAt ?? now;
|
|
19752
19762
|
let forkedFrom = params.forkedFrom;
|
|
19753
19763
|
if (params.existingId) {
|
|
19754
19764
|
const onDisk = this.diskUpdatedAt(params.existingId);
|
|
19755
19765
|
if (onDisk !== null && onDisk !== params.expectedUpdatedAt) {
|
|
19756
19766
|
forkedFrom = params.existingId;
|
|
19757
|
-
id =
|
|
19767
|
+
id = this.freshSessionId();
|
|
19758
19768
|
createdAt = now;
|
|
19759
19769
|
log21.warn("session changed on disk: saving as a fork", { from: forkedFrom, to: id });
|
|
19760
19770
|
}
|
|
@@ -20781,6 +20791,51 @@ var init_spinner = __esm({
|
|
|
20781
20791
|
}
|
|
20782
20792
|
});
|
|
20783
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
|
+
|
|
20784
20839
|
// src/cli/ui/ToolCallBlock.tsx
|
|
20785
20840
|
import { Box, Text } from "ink";
|
|
20786
20841
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
@@ -20811,15 +20866,7 @@ function formatInput(state) {
|
|
|
20811
20866
|
return truncateLabel(`${from} \u2192 ${to}`);
|
|
20812
20867
|
}
|
|
20813
20868
|
}
|
|
20814
|
-
return truncateLabel(
|
|
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;
|
|
20869
|
+
return truncateLabel(primaryArg(src));
|
|
20823
20870
|
}
|
|
20824
20871
|
function formatDuration(ms) {
|
|
20825
20872
|
return `${(ms / 1e3).toFixed(1)}s`;
|
|
@@ -20940,6 +20987,7 @@ var init_ToolCallBlock = __esm({
|
|
|
20940
20987
|
init_esm_shims();
|
|
20941
20988
|
init_theme();
|
|
20942
20989
|
init_spinner();
|
|
20990
|
+
init_tool_arg_label();
|
|
20943
20991
|
MAX_EXPANDED_LINES = 10;
|
|
20944
20992
|
SLOW_MS = 1e3;
|
|
20945
20993
|
}
|
|
@@ -22398,7 +22446,7 @@ var init_SubagentBlock = __esm({
|
|
|
22398
22446
|
});
|
|
22399
22447
|
|
|
22400
22448
|
// src/cli/ui/AgentTree.tsx
|
|
22401
|
-
import { Box as Box12, Text as Text13 } from "ink";
|
|
22449
|
+
import { Box as Box12, Text as Text13, useStdout as useStdout3 } from "ink";
|
|
22402
22450
|
import { jsx as jsx13, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
22403
22451
|
function fmtDur2(ms) {
|
|
22404
22452
|
return `${(ms / 1e3).toFixed(1)}s`;
|
|
@@ -22408,10 +22456,7 @@ function truncate2(text, max) {
|
|
|
22408
22456
|
return oneLine.length > max ? oneLine.slice(0, max - 1) + "\u2026" : oneLine;
|
|
22409
22457
|
}
|
|
22410
22458
|
function firstArg(tc) {
|
|
22411
|
-
|
|
22412
|
-
const keys = Object.keys(src);
|
|
22413
|
-
if (keys.length === 0) return "";
|
|
22414
|
-
return truncate2(String(src[keys[0]] ?? ""), 40);
|
|
22459
|
+
return truncate2(primaryArg(tc.input), 40);
|
|
22415
22460
|
}
|
|
22416
22461
|
function ToolLine({ tc }) {
|
|
22417
22462
|
const icon = tc.status === "completed" ? "\u2713" : tc.status === "error" ? "\u2717" : "\u27F3";
|
|
@@ -22446,7 +22491,7 @@ function nodeIcon(status) {
|
|
|
22446
22491
|
return { icon: "\u22B3", color: theme.accent };
|
|
22447
22492
|
}
|
|
22448
22493
|
}
|
|
22449
|
-
function SubagentNode({ node, speaking, focused, expanded, isLast, now }) {
|
|
22494
|
+
function SubagentNode({ node, speaking, focused, expanded, isLast, now, maxToolLines }) {
|
|
22450
22495
|
const running = node.status === "running";
|
|
22451
22496
|
const elapsedMs = node.startedAt ? Math.max(0, now - node.startedAt) : 0;
|
|
22452
22497
|
const { icon, color } = nodeIcon(node.status);
|
|
@@ -22458,7 +22503,7 @@ function SubagentNode({ node, speaking, focused, expanded, isLast, now }) {
|
|
|
22458
22503
|
node.iterations !== void 0 ? `${node.iterations} it` : ""
|
|
22459
22504
|
].filter(Boolean).join(" \xB7 ");
|
|
22460
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);
|
|
22461
|
-
const tcs = node.toolCalls ?? [];
|
|
22506
|
+
const { hidden, visible: tcs } = tailWindow(node.toolCalls ?? [], maxToolLines);
|
|
22462
22507
|
const files = node.filesChanged ?? [];
|
|
22463
22508
|
return /* @__PURE__ */ jsxs11(Box12, { flexDirection: "column", children: [
|
|
22464
22509
|
/* @__PURE__ */ jsxs11(Text13, { wrap: "truncate-end", children: [
|
|
@@ -22482,6 +22527,15 @@ function SubagentNode({ node, speaking, focused, expanded, isLast, now }) {
|
|
|
22482
22527
|
] }),
|
|
22483
22528
|
focused ? /* @__PURE__ */ jsx13(Text13, { color: theme.accent, children: " \u25C0" }) : null
|
|
22484
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
|
+
] }),
|
|
22485
22539
|
tcs.map((tc) => /* @__PURE__ */ jsx13(ToolLine, { tc }, tc.id)),
|
|
22486
22540
|
expanded && node.summary && /* @__PURE__ */ jsxs11(Text13, { color: theme.textFaint, wrap: "truncate-end", children: [
|
|
22487
22541
|
" ",
|
|
@@ -22507,6 +22561,8 @@ function AgentTree({
|
|
|
22507
22561
|
expandedBlockIds,
|
|
22508
22562
|
now = Date.now()
|
|
22509
22563
|
}) {
|
|
22564
|
+
const { stdout } = useStdout3();
|
|
22565
|
+
const maxToolLines = treeToolBudget(stdout?.rows ?? 24, nodes.length);
|
|
22510
22566
|
const allTerminal = nodes.every((n) => TERMINAL.has(n.status));
|
|
22511
22567
|
if (allTerminal) {
|
|
22512
22568
|
const completed = nodes.filter((n) => n.status === "completed").length;
|
|
@@ -22552,7 +22608,8 @@ function AgentTree({
|
|
|
22552
22608
|
focused: focusedBlockId === n.id,
|
|
22553
22609
|
expanded: expandedBlockIds?.has(n.id) ?? false,
|
|
22554
22610
|
isLast: i === nodes.length - 1,
|
|
22555
|
-
now
|
|
22611
|
+
now,
|
|
22612
|
+
maxToolLines
|
|
22556
22613
|
},
|
|
22557
22614
|
n.id
|
|
22558
22615
|
))
|
|
@@ -22565,6 +22622,7 @@ var init_AgentTree = __esm({
|
|
|
22565
22622
|
init_esm_shims();
|
|
22566
22623
|
init_theme();
|
|
22567
22624
|
init_spinner();
|
|
22625
|
+
init_tool_arg_label();
|
|
22568
22626
|
TERMINAL = /* @__PURE__ */ new Set([
|
|
22569
22627
|
"completed",
|
|
22570
22628
|
"failed",
|
|
@@ -22786,7 +22844,7 @@ var init_AgentMessage = __esm({
|
|
|
22786
22844
|
});
|
|
22787
22845
|
|
|
22788
22846
|
// src/cli/ui/MessageList.tsx
|
|
22789
|
-
import { Box as Box15, Static as Static2, useStdout as
|
|
22847
|
+
import { Box as Box15, Static as Static2, useStdout as useStdout4 } from "ink";
|
|
22790
22848
|
import { jsx as jsx17, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
22791
22849
|
function renderItem(item, key, focusedBlockId, expandedBlockIds, debug, now, liveTextLines, liveColumns, liveActionLimit) {
|
|
22792
22850
|
if (item.kind === "user") {
|
|
@@ -22836,7 +22894,7 @@ function MessageList({
|
|
|
22836
22894
|
now = Date.now(),
|
|
22837
22895
|
availableRows
|
|
22838
22896
|
}) {
|
|
22839
|
-
const { stdout } =
|
|
22897
|
+
const { stdout } = useStdout4();
|
|
22840
22898
|
const { contentWidth } = resolveLayout(stdout.columns ?? 80, stdout.rows ?? 24);
|
|
22841
22899
|
const terminalRows = availableRows ?? stdout.rows ?? 24;
|
|
22842
22900
|
const liveActionLimit = Math.max(1, Math.min(6, Math.floor((terminalRows - 8) / 2)));
|
|
@@ -23563,7 +23621,7 @@ var init_FatalError = __esm({
|
|
|
23563
23621
|
});
|
|
23564
23622
|
|
|
23565
23623
|
// src/cli/ui/ConversationView.tsx
|
|
23566
|
-
import { Box as Box26, Text as Text27, useStdout as
|
|
23624
|
+
import { Box as Box26, Text as Text27, useStdout as useStdout5 } from "ink";
|
|
23567
23625
|
import { jsx as jsx28, jsxs as jsxs26 } from "react/jsx-runtime";
|
|
23568
23626
|
function ConversationView({
|
|
23569
23627
|
completedItems,
|
|
@@ -23613,7 +23671,7 @@ function ConversationView({
|
|
|
23613
23671
|
const liveNow = useLiveClock(thinking);
|
|
23614
23672
|
const gateOpen = !!pendingConfirm || !!pendingQuestions?.length || !!pendingApproval;
|
|
23615
23673
|
const steerable = thinking && !gateOpen && !fatalError;
|
|
23616
|
-
const { stdout } =
|
|
23674
|
+
const { stdout } = useStdout5();
|
|
23617
23675
|
const terminalRows = stdout.rows ?? 24;
|
|
23618
23676
|
const panelMaxSteps = Math.max(2, Math.min(5, Math.floor(terminalRows / 5)));
|
|
23619
23677
|
const jobsClock = jobsNow ?? Date.now();
|
|
@@ -26961,7 +27019,7 @@ async function settle(step, fn) {
|
|
|
26961
27019
|
}
|
|
26962
27020
|
}
|
|
26963
27021
|
function resolveVersion() {
|
|
26964
|
-
if (true) return "0.8.0-beta.
|
|
27022
|
+
if (true) return "0.8.0-beta.2";
|
|
26965
27023
|
try {
|
|
26966
27024
|
const thisDir = dirname14(fileURLToPath2(import.meta.url));
|
|
26967
27025
|
const pkg = JSON.parse(
|
|
@@ -32816,7 +32874,7 @@ function resolveResult(ref, dir) {
|
|
|
32816
32874
|
}
|
|
32817
32875
|
|
|
32818
32876
|
// src/cli/commands/eval.ts
|
|
32819
|
-
var VERSION = true ? "0.8.0-beta.
|
|
32877
|
+
var VERSION = true ? "0.8.0-beta.2" : "dev";
|
|
32820
32878
|
var fail2 = (message) => {
|
|
32821
32879
|
process.stderr.write(`${message}
|
|
32822
32880
|
`);
|
|
@@ -33429,7 +33487,7 @@ var statsCommand = new Command14("stats").description(
|
|
|
33429
33487
|
init_loader();
|
|
33430
33488
|
init_schema_version();
|
|
33431
33489
|
function resolveVersion2() {
|
|
33432
|
-
if (true) return "0.8.0-beta.
|
|
33490
|
+
if (true) return "0.8.0-beta.2";
|
|
33433
33491
|
const thisDir = dirname19(fileURLToPath5(import.meta.url));
|
|
33434
33492
|
const pkg = JSON.parse(readFileSync26(join32(thisDir, "..", "..", "package.json"), "utf-8"));
|
|
33435
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.
|
|
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",
|