toolcraft 0.0.234 → 0.0.236

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/composition.json CHANGED
@@ -113,7 +113,7 @@
113
113
  },
114
114
  {
115
115
  "name": "toolcraft",
116
- "version": "0.0.234",
116
+ "version": "0.0.236",
117
117
  "license": "MIT"
118
118
  },
119
119
  {
@@ -123,7 +123,7 @@
123
123
  },
124
124
  {
125
125
  "name": "toolcraft-schema",
126
- "version": "0.0.234",
126
+ "version": "0.0.236",
127
127
  "license": "MIT"
128
128
  },
129
129
  {
@@ -113,7 +113,7 @@
113
113
  },
114
114
  {
115
115
  "name": "toolcraft",
116
- "version": "0.0.234",
116
+ "version": "0.0.236",
117
117
  "license": "MIT"
118
118
  },
119
119
  {
@@ -123,7 +123,7 @@
123
123
  },
124
124
  {
125
125
  "name": "toolcraft-schema",
126
- "version": "0.0.234",
126
+ "version": "0.0.236",
127
127
  "license": "MIT"
128
128
  },
129
129
  {
@@ -8,7 +8,7 @@
8
8
  },
9
9
  {
10
10
  "name": "toolcraft-schema",
11
- "version": "0.0.234",
11
+ "version": "0.0.236",
12
12
  "license": "MIT"
13
13
  }
14
14
  ]
@@ -0,0 +1,4 @@
1
+ import { ScreenBuffer } from "../buffer.js";
2
+ import type { Rect } from "../types.js";
3
+ /** Reserve fixed plan context while keeping room for the live output. */
4
+ export declare function renderContextPane(buffer: ScreenBuffer, rect: Rect, context: string[]): Rect;
@@ -0,0 +1,22 @@
1
+ import { plainTerminalText } from "../ansi.js";
2
+ import { computeVisualLines } from "./output-pane.js";
3
+ import { truncateToWidth } from "../terminal-width.js";
4
+ /** Reserve fixed plan context while keeping room for the live output. */
5
+ export function renderContextPane(buffer, rect, context) {
6
+ if (context.length === 0 || rect.width <= 0 || rect.height <= 1)
7
+ return rect;
8
+ const available = Math.max(1, Math.floor(rect.height / 2));
9
+ const entries = context.map(text => computeVisualLines([{ kind: "info", text, ts: 0 }], rect.width + 3));
10
+ const lines = entries.flat();
11
+ const visible = lines.slice(0, available);
12
+ if (lines.length > available) {
13
+ const last = visible[available - 1];
14
+ const text = entries[0].length >= available
15
+ ? truncateToWidth(`${available === 1 ? plainTerminalText(context[0]) : last.text}…`, rect.width)
16
+ : truncateToWidth(`… more plans (${context.length - 1} queued)`, rect.width);
17
+ visible[available - 1] = { ...last, text };
18
+ }
19
+ buffer.clearRect({ ...rect, height: visible.length });
20
+ visible.forEach((line, row) => buffer.putInRect(rect, row, line.text, { dim: row > 0 }));
21
+ return { ...rect, y: rect.y + visible.length, height: rect.height - visible.length };
22
+ }
@@ -7,8 +7,10 @@ export function renderCompactStatsPane(buffer, rect, stats) {
7
7
  if (rect.width <= 0 || rect.height <= 0)
8
8
  return;
9
9
  const status = formatStatus(stats.status);
10
- const metrics = `${status} · ${stats.iterationsLabel ?? "Iteration"} ${formatNumber(stats.iterations)} · ${formatElapsed(stats.elapsedMs)} · ${formatNumber(stats.tokensIn + stats.tokensOut)} tokens`;
11
- const firstLine = rect.height === 1 && stats.currentAction ? `${status} · ${stats.currentAction}` : metrics;
10
+ const progress = `${stats.iterationsLabel ?? "Iteration"} ${formatNumber(stats.iterations)}${stats.iterationsTotal === undefined ? "" : `/${formatNumber(stats.iterationsTotal)}`}`;
11
+ const primary = stats.iterationsTotal === undefined ? `${status} · ${progress}` : `${progress} · ${status}`;
12
+ const metrics = `${primary} · ${formatElapsed(stats.elapsedMs)} · ${formatNumber(stats.tokensIn + stats.tokensOut)} tokens`;
13
+ const firstLine = rect.height === 1 && stats.iterationsTotal === undefined && stats.currentAction ? `${status} · ${stats.currentAction}` : metrics;
12
14
  const messages = [firstLine, stats.currentAction ?? ""].map(plainTerminalText);
13
15
  buffer.putInRect(rect, 0, truncateToWidth(messages[0], rect.width), getStatusStyle(stats.status));
14
16
  if (rect.height > 1 && stats.currentAction !== undefined) {
@@ -23,7 +25,8 @@ export function renderStatsPane(buffer, rect, stats) {
23
25
  let lines = statsToLines(stats, rect.width);
24
26
  if (lines.length > rect.height) {
25
27
  const actions = stats.currentAction === undefined ? [] : lines.slice(9);
26
- const visibleActions = actions.slice(0, Math.max(0, rect.height - 1));
28
+ const progressLines = stats.iterationsTotal === undefined ? [] : [lines[1]];
29
+ const visibleActions = actions.slice(0, Math.max(0, rect.height - 1 - progressLines.length));
27
30
  if (visibleActions.length > 0 && visibleActions.length < actions.length) {
28
31
  const last = visibleActions[visibleActions.length - 1];
29
32
  visibleActions[visibleActions.length - 1] = {
@@ -32,9 +35,12 @@ export function renderStatsPane(buffer, rect, stats) {
32
35
  };
33
36
  }
34
37
  lines = [
35
- lines[0],
38
+ stats.iterationsTotal !== undefined && rect.height === 1
39
+ ? createKeyValueLine(formatStatus(stats.status), `${stats.iterations}/${stats.iterationsTotal}`, rect.width, getStatusStyle(stats.status))
40
+ : lines[0],
41
+ ...progressLines,
36
42
  ...visibleActions,
37
- ...lines.slice(1, 7).filter((line) => line.prefix.length > 0 || line.text.length > 0)
43
+ ...lines.slice(stats.iterationsTotal === undefined ? 1 : 2, 7).filter((line) => line.prefix.length > 0 || line.text.length > 0)
38
44
  ];
39
45
  }
40
46
  for (let row = 0; row < rect.height; row += 1) {
@@ -72,7 +78,7 @@ export function statsToLines(stats, width) {
72
78
  const iterationsLabel = plainTerminalText(stats.iterationsLabel ?? "Iteration");
73
79
  const lines = [
74
80
  createKeyValueLine("Status", formatStatus(stats.status), width, getStatusStyle(stats.status)),
75
- createKeyValueLine(iterationsLabel, formatNumber(stats.iterations), width),
81
+ createKeyValueLine(iterationsLabel, formatNumber(stats.iterations) + (stats.iterationsTotal === undefined ? "" : `/${formatNumber(stats.iterationsTotal)}`), width),
76
82
  createKeyValueLine("Elapsed", formatElapsed(stats.elapsedMs), width),
77
83
  createBlankLine(),
78
84
  createKeyValueLine("Tokens In", formatNumber(stats.tokensIn), width),
@@ -2,6 +2,7 @@ import { createRenderPerformanceMonitor, formatRenderPerformance } from "../rend
2
2
  import { createLogger } from "../components/logger.js";
3
3
  import { resolveOutputFormat } from "../internal/output-format.js";
4
4
  import { ScreenBuffer, diff } from "./buffer.js";
5
+ import { renderContextPane } from "./components/context-pane.js";
5
6
  import { renderBorder } from "./components/border.js";
6
7
  import { defaultHints, renderFooter } from "./components/footer.js";
7
8
  import { renderOutputPane } from "./components/output-pane.js";
@@ -36,6 +37,7 @@ export function createDashboard(opts = {}) {
36
37
  const performanceMonitor = createRenderPerformanceMonitor();
37
38
  let showPerformance = false;
38
39
  let scrollOffset = 0;
40
+ let outputViewportHeight = 0;
39
41
  let heldOutput;
40
42
  let renderTimer;
41
43
  function appendOutput(item) {
@@ -108,11 +110,7 @@ export function createDashboard(opts = {}) {
108
110
  command === "scroll-down" ||
109
111
  command === "page-up" ||
110
112
  command === "page-down") {
111
- const page = Math.max(1, computeDashboardLayout({
112
- totalWidth: driver.getSize().cols,
113
- totalHeight: driver.getSize().rows,
114
- rightPaneWidth
115
- }).leftPane.height);
113
+ const page = Math.max(1, outputViewportHeight);
116
114
  const amount = command === "page-up" || command === "page-down" ? page : 1;
117
115
  const direction = command === "scroll-up" || command === "page-up" ? 1 : -1;
118
116
  if (direction > 0)
@@ -189,7 +187,29 @@ export function createDashboard(opts = {}) {
189
187
  rightTitle: statsTitle,
190
188
  style: { dim: true }
191
189
  });
192
- scrollOffset = renderOutputPane(nextBuffer, { ...layout.leftPane, height: Math.max(0, layout.leftPane.height - (showPerformance ? 1 : 0)) }, heldOutput ?? state.output, scrollOffset);
190
+ if (state.stats.context?.length) {
191
+ const contextRect = layout.summary ?? layout.leftPane;
192
+ // Context spans both panes on wide terminals.
193
+ const remaining = renderContextPane(nextBuffer, {
194
+ x: contextRect.x, y: contextRect.y, width: Math.max(0, cols - 2),
195
+ height: layout.summary ? layout.summary.height + layout.leftPane.height : layout.leftPane.height
196
+ }, state.stats.context);
197
+ const height = remaining.y - contextRect.y;
198
+ if (layout.summary) {
199
+ layout.summary.y = remaining.y;
200
+ layout.summary.height = Math.min(layout.summary.height, remaining.height);
201
+ layout.leftPane.y = layout.summary.y + layout.summary.height;
202
+ layout.leftPane.height = Math.max(0, remaining.height - layout.summary.height);
203
+ }
204
+ else {
205
+ layout.leftPane.y += height;
206
+ layout.leftPane.height -= height;
207
+ layout.rightPane.y += height;
208
+ layout.rightPane.height -= height;
209
+ }
210
+ }
211
+ outputViewportHeight = Math.max(0, layout.leftPane.height - (showPerformance ? 1 : 0));
212
+ scrollOffset = renderOutputPane(nextBuffer, { ...layout.leftPane, height: outputViewportHeight }, heldOutput ?? state.output, scrollOffset);
193
213
  if (scrollOffset === 0)
194
214
  heldOutput = undefined;
195
215
  renderStatsPane(nextBuffer, layout.rightPane, state.stats);
@@ -6,7 +6,7 @@ const labelControl = scenario === "label-controls" ? "\u001b]52;c;" + "HIDDEN_".
6
6
  const dashboard = createDashboard({
7
7
  title: scenario === "unicode-title" ? "Pipeline · 界界 · 👩‍💻 · é · ".repeat(3) : `Pipeline · ${scenario}${labelControl}`,
8
8
  statsTitle: scenario === "unicode-title" ? "Run · 界界 · 👩‍💻 · é" : `Run${labelControl}`,
9
- rightPaneWidth: 32,
9
+ rightPaneWidth: 44,
10
10
  hints: [
11
11
  { key: "q", label: `Quit${labelControl}` },
12
12
  { key: "↑↓", label: "Scroll" },
@@ -33,15 +33,17 @@ if (scenario !== "empty")
33
33
  dashboard.updateStats({
34
34
  status: "running",
35
35
  iterationsLabel: `Tasks${labelControl}`,
36
- iterations: 1,
36
+ iterations: scenario === "resumed" || scenario === "queue" ? 68 : 1,
37
+ iterationsTotal: scenario === "resumed" || scenario === "queue" ? 90 : 8,
38
+ context: scenario === "queue" ? ["Plan 1/3: docs/plans/improve-pipeline-task-counts-and-restart-progress.md", "Next 2/3: docs/plans/agent-conversation-recovery.md", "Next 3/3: docs/plans/界界-unicode-long-plan-title.md"] : ["Plan 1/1: docs/plans/improve-pipeline-task-counts-and-restart-progress.md"],
37
39
  tokensIn: 24000,
38
40
  tokensOut: 7000,
39
41
  elapsedMs: 65000,
40
- currentAction: "Improve streaming output (implement)"
42
+ currentAction: scenario === "resumed" || scenario === "queue" ? "Task 69/90 · Improve persisted pipeline progress · implement · step 1/2" : "Improve streaming output (implement)"
41
43
  });
42
44
  append("info", "Config · codex · model-example · docs/plans/fake-pipeline.md");
43
45
  if (scenario !== "empty")
44
- append("status", "Task 2/8 · Improve streaming output (implement)");
46
+ append("status", scenario === "resumed" || scenario === "queue" ? "Task 69/90 · Improve persisted pipeline progress (implement)" : "Task 2/8 · Improve streaming output (implement)");
45
47
  if (scenario === "empty") {
46
48
  dashboard.updateStats({
47
49
  status: "done",
@@ -118,7 +120,7 @@ else {
118
120
  }, 100);
119
121
  }
120
122
  else {
121
- timer = setInterval(() => append("info", `Streaming response ${count} · working on task 2/8`), 500);
123
+ timer = setInterval(() => append("info", `Streaming response ${count} · working on the current task`), 500);
122
124
  }
123
125
  }
124
126
  // Keep terminal state visible until QA sends q, including completed/error scenarios.
@@ -10,6 +10,8 @@ export type DashboardStats = {
10
10
  status: "idle" | "running" | "paused" | "done" | "error";
11
11
  iterations: number;
12
12
  iterationsLabel?: string;
13
+ iterationsTotal?: number;
14
+ context?: string[];
13
15
  tokensIn: number;
14
16
  tokensOut: number;
15
17
  elapsedMs: number;
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "toolcraft-schema",
3
- "version": "0.0.234",
3
+ "version": "0.0.236",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "toolcraft",
3
- "version": "0.0.234",
3
+ "version": "0.0.236",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -158,7 +158,7 @@
158
158
  "yaml"
159
159
  ],
160
160
  "optionalDependencies": {
161
- "toolcraft-schema": "0.0.234",
161
+ "toolcraft-schema": "0.0.236",
162
162
  "toolcraft-design": "*",
163
163
  "@poe-code/frontmatter": "*",
164
164
  "@poe-code/agent-mcp-config": "*",