@pugi/cli 0.1.0-beta.15 → 0.1.0-beta.17
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/dist/commands/jobs-watch.js +201 -0
- package/dist/commands/jobs.js +15 -0
- package/dist/core/agent-progress/cleanup.js +134 -0
- package/dist/core/agent-progress/schema.js +144 -0
- package/dist/core/agent-progress/writer.js +101 -0
- package/dist/core/engine/tool-bridge.js +14 -8
- package/dist/core/repl/session.js +30 -5
- package/dist/runtime/cli.js +129 -6
- package/dist/runtime/commands/report.js +299 -0
- package/dist/runtime/version.js +1 -1
- package/dist/tools/file-tools.js +28 -0
- package/dist/tui/agent-progress-card.js +111 -0
- package/dist/tui/repl-render.js +5 -5
- package/dist/tui/tool-stream-pane.js +7 -0
- package/package.json +2 -2
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Box, Text } from 'ink';
|
|
3
|
+
/** Width of the progress bar in display cells. Tuned to fit comfortably
|
|
4
|
+
* inside an 80-col terminal alongside the percent label. */
|
|
5
|
+
export const PROGRESS_BAR_WIDTH = 24;
|
|
6
|
+
/** Max milestone rows the card renders before collapsing to the footer
|
|
7
|
+
* summary. Matches the CC `/compact` cutoff. */
|
|
8
|
+
export const MAX_VISIBLE_MILESTONES = 5;
|
|
9
|
+
const STATUS_GLYPH = {
|
|
10
|
+
done: '◼',
|
|
11
|
+
active: '▸',
|
|
12
|
+
pending: '◻',
|
|
13
|
+
};
|
|
14
|
+
const STATUS_COLOR = {
|
|
15
|
+
done: 'green',
|
|
16
|
+
active: 'yellow',
|
|
17
|
+
pending: 'gray',
|
|
18
|
+
};
|
|
19
|
+
const HEADER_DOT_COLOR = {
|
|
20
|
+
running: 'cyan',
|
|
21
|
+
completed: 'green',
|
|
22
|
+
failed: 'red',
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* Build the unicode progress bar. Exported для тесты — guarantees the
|
|
26
|
+
* filled/empty counts match the percent under all rounding edges.
|
|
27
|
+
*/
|
|
28
|
+
export function renderProgressBarCells(percent, width = PROGRESS_BAR_WIDTH) {
|
|
29
|
+
const safePercent = Math.max(0, Math.min(100, percent));
|
|
30
|
+
const cells = Math.round((safePercent / 100) * width);
|
|
31
|
+
const clamped = Math.max(0, Math.min(width, cells));
|
|
32
|
+
return {
|
|
33
|
+
filled: '▰'.repeat(clamped),
|
|
34
|
+
empty: '▱'.repeat(width - clamped),
|
|
35
|
+
cells: clamped,
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Format milliseconds as the CC-style `Hh Mm Ss` / `Mm Ss` / `Ss` label.
|
|
40
|
+
* Mirrors the rule used by status-bar elapsed slot.
|
|
41
|
+
*/
|
|
42
|
+
export function formatElapsed(ms) {
|
|
43
|
+
const total = Math.max(0, Math.floor(ms / 1000));
|
|
44
|
+
const h = Math.floor(total / 3600);
|
|
45
|
+
const m = Math.floor((total % 3600) / 60);
|
|
46
|
+
const s = total % 60;
|
|
47
|
+
if (h > 0)
|
|
48
|
+
return `${h}h ${m}m ${s}s`;
|
|
49
|
+
if (m > 0)
|
|
50
|
+
return `${m}m ${s}s`;
|
|
51
|
+
return `${s}s`;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Format a raw token count as `21.7k` / `3.4M` / `812`. Mirrors the
|
|
55
|
+
* formatter in `core/repl/model-pricing.ts` so both surfaces stay
|
|
56
|
+
* visually consistent without coupling.
|
|
57
|
+
*/
|
|
58
|
+
export function formatTokenCount(n) {
|
|
59
|
+
if (n === undefined)
|
|
60
|
+
return undefined;
|
|
61
|
+
if (n < 1_000)
|
|
62
|
+
return `${n}`;
|
|
63
|
+
if (n < 1_000_000) {
|
|
64
|
+
const k = n / 1_000;
|
|
65
|
+
return `${k >= 10 ? k.toFixed(1).replace(/\.0$/, '') : k.toFixed(1)}k`;
|
|
66
|
+
}
|
|
67
|
+
const m = n / 1_000_000;
|
|
68
|
+
return `${m >= 10 ? m.toFixed(1).replace(/\.0$/, '') : m.toFixed(1)}M`;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Compute the "… +N pending, M completed" footer counts. When the
|
|
72
|
+
* agent supplied rollups they win; otherwise we derive from the
|
|
73
|
+
* milestone array.
|
|
74
|
+
*/
|
|
75
|
+
export function computeFooterCounts(milestones, visibleCount, rollup) {
|
|
76
|
+
const pending = rollup.pendingCount
|
|
77
|
+
?? milestones.filter((m) => m.status === 'pending').length;
|
|
78
|
+
const completed = rollup.completedCount
|
|
79
|
+
?? milestones.filter((m) => m.status === 'done').length;
|
|
80
|
+
const hidden = Math.max(0, milestones.length - visibleCount);
|
|
81
|
+
return { pending, completed, hidden };
|
|
82
|
+
}
|
|
83
|
+
function MilestoneRow({ milestone }) {
|
|
84
|
+
const glyph = STATUS_GLYPH[milestone.status];
|
|
85
|
+
const color = STATUS_COLOR[milestone.status];
|
|
86
|
+
// Truncate to 64 chars so a verbose label can't wrap and break the
|
|
87
|
+
// grid layout in the watcher.
|
|
88
|
+
const label = milestone.label.length > 64
|
|
89
|
+
? `${milestone.label.slice(0, 63)}…`
|
|
90
|
+
: milestone.label;
|
|
91
|
+
return (_jsxs(Box, { children: [_jsx(Text, { children: ' ' }), _jsx(Text, { color: color, children: glyph }), _jsx(Text, { children: " " }), _jsx(Text, { color: color === 'gray' ? 'gray' : undefined, dimColor: milestone.status === 'pending', children: label })] }));
|
|
92
|
+
}
|
|
93
|
+
export function AgentProgressCard({ progress, nowEpochMs, }) {
|
|
94
|
+
// Re-derive elapsed from the wall clock when the parent supplied it;
|
|
95
|
+
// this is what makes the card tick once a second without the writer
|
|
96
|
+
// re-emitting JSON every tick.
|
|
97
|
+
const elapsed = nowEpochMs !== undefined
|
|
98
|
+
? Math.max(progress.elapsedMs, nowEpochMs - Date.parse(progress.startedAt))
|
|
99
|
+
: progress.elapsedMs;
|
|
100
|
+
const bar = renderProgressBarCells(progress.percentComplete);
|
|
101
|
+
const percentLabel = `${Math.round(Math.max(0, Math.min(100, progress.percentComplete)))}%`;
|
|
102
|
+
const tokensLabel = formatTokenCount(progress.tokensUsed);
|
|
103
|
+
const dotColor = HEADER_DOT_COLOR[progress.status];
|
|
104
|
+
const visibleMilestones = progress.milestones.slice(0, MAX_VISIBLE_MILESTONES);
|
|
105
|
+
const footer = computeFooterCounts(progress.milestones, visibleMilestones.length, { pendingCount: progress.pendingCount, completedCount: progress.completedCount });
|
|
106
|
+
// CC compact pattern: header has a leading `· ` glyph + the task label.
|
|
107
|
+
// We append `…` only while running (matches CC's "Compacting…" verb form).
|
|
108
|
+
const headerVerb = progress.status === 'running' ? '…' : '';
|
|
109
|
+
return (_jsxs(Box, { flexDirection: "column", paddingX: 1, paddingY: 0, marginBottom: 1, children: [_jsxs(Box, { children: [_jsx(Text, { color: dotColor, children: '· ' }), _jsx(Text, { bold: true, children: progress.agentType }), _jsx(Text, { children: " " }), _jsxs(Text, { children: [progress.task, headerVerb] }), _jsxs(Text, { dimColor: true, children: [' (', formatElapsed(elapsed), tokensLabel ? ` · ↑ ${tokensLabel} tokens` : '', ')'] })] }), _jsxs(Box, { children: [_jsx(Text, { children: ' ' }), _jsx(Text, { color: "cyan", children: bar.filled }), _jsx(Text, { dimColor: true, children: bar.empty }), _jsxs(Text, { children: [' ', percentLabel] })] }), progress.stepDescription ? (_jsxs(Box, { children: [_jsx(Text, { children: ' ' }), _jsxs(Text, { dimColor: true, children: ["step ", progress.currentStep, "/", progress.totalSteps, ": ", progress.stepDescription] })] })) : null, visibleMilestones.length > 0 ? (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Box, { children: [_jsx(Text, { children: ' ' }), _jsx(Text, { dimColor: true, children: "\u23BF" })] }), visibleMilestones.map((m, i) => (_jsx(MilestoneRow, { milestone: m }, `${m.label}-${i}`))), footer.hidden > 0 ? (_jsxs(Box, { children: [_jsx(Text, { children: ' ' }), _jsxs(Text, { dimColor: true, children: ["\u2026 +", footer.pending, " pending, ", footer.completed, " completed"] })] })) : null] })) : null] }));
|
|
110
|
+
}
|
|
111
|
+
//# sourceMappingURL=agent-progress-card.js.map
|
package/dist/tui/repl-render.js
CHANGED
|
@@ -16,6 +16,8 @@
|
|
|
16
16
|
* a tiny `event:`/`data:`/`id:` parser. This keeps the dependency
|
|
17
17
|
* graph at zero new packages.
|
|
18
18
|
*/
|
|
19
|
+
import { existsSync } from 'node:fs';
|
|
20
|
+
import { resolve } from 'node:path';
|
|
19
21
|
import React from 'react';
|
|
20
22
|
import { render } from 'ink';
|
|
21
23
|
import { Repl } from './repl.js';
|
|
@@ -314,11 +316,9 @@ export function drainBufferedStdin(stdin = process.stdin) {
|
|
|
314
316
|
* future unit spec can lock the contract.
|
|
315
317
|
*/
|
|
316
318
|
export function isProjectRoot(cwd) {
|
|
317
|
-
//
|
|
318
|
-
//
|
|
319
|
-
//
|
|
320
|
-
const { existsSync } = require('node:fs');
|
|
321
|
-
const { resolve } = require('node:path');
|
|
319
|
+
// ESM static imports — `require()` is not defined in a `"type": "module"`
|
|
320
|
+
// bundle and would throw `ReferenceError: require is not defined` the
|
|
321
|
+
// moment the REPL bootstrap calls this gate. Beta.16 P0 fix 2026-05-27.
|
|
322
322
|
return (existsSync(resolve(cwd, 'package.json')) ||
|
|
323
323
|
existsSync(resolve(cwd, '.git')) ||
|
|
324
324
|
existsSync(resolve(cwd, '.pugi')) ||
|
|
@@ -64,6 +64,13 @@ function toolDisplayName(tool) {
|
|
|
64
64
|
switch (tool) {
|
|
65
65
|
case 'read':
|
|
66
66
|
return 'Read';
|
|
67
|
+
case 'write':
|
|
68
|
+
// 2026-05-27 — Write is the most operator-visible tool for the
|
|
69
|
+
// codegen-dispatch surface (Hiroshi writing index.html / style.css
|
|
70
|
+
// / script.js for a tic-tac-toe brief). Add the display name so
|
|
71
|
+
// the tool stream pane renders ✓ Write(index.html) instead of an
|
|
72
|
+
// unlabeled placeholder. Mirrors the Claude Code Write rendering.
|
|
73
|
+
return 'Write';
|
|
67
74
|
case 'edit':
|
|
68
75
|
return 'Edit';
|
|
69
76
|
case 'bash':
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pugi/cli",
|
|
3
|
-
"version": "0.1.0-beta.
|
|
3
|
+
"version": "0.1.0-beta.17",
|
|
4
4
|
"description": "Pugi CLI - terminal-native software execution system",
|
|
5
5
|
"homepage": "https://pugi.io",
|
|
6
6
|
"repository": {
|
|
@@ -54,7 +54,7 @@
|
|
|
54
54
|
"undici": "^8.3.0",
|
|
55
55
|
"zod": "^3.23.0",
|
|
56
56
|
"@pugi/personas": "0.1.2",
|
|
57
|
-
"@pugi/sdk": "0.1.0-beta.
|
|
57
|
+
"@pugi/sdk": "0.1.0-beta.17"
|
|
58
58
|
},
|
|
59
59
|
"devDependencies": {
|
|
60
60
|
"@types/node": "^22.0.0",
|