zerg-ztc 0.1.4 → 0.1.6
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/App.d.ts.map +1 -1
- package/dist/App.js +141 -16
- package/dist/App.js.map +1 -1
- package/dist/agent/agent.d.ts +4 -0
- package/dist/agent/agent.d.ts.map +1 -1
- package/dist/agent/agent.js +21 -3
- package/dist/agent/agent.js.map +1 -1
- package/dist/agent/commands/config.d.ts.map +1 -1
- package/dist/agent/commands/config.js +68 -2
- package/dist/agent/commands/config.js.map +1 -1
- package/dist/agent/commands/index.d.ts.map +1 -1
- package/dist/agent/commands/index.js +2 -1
- package/dist/agent/commands/index.js.map +1 -1
- package/dist/agent/commands/update.d.ts +3 -0
- package/dist/agent/commands/update.d.ts.map +1 -0
- package/dist/agent/commands/update.js +33 -0
- package/dist/agent/commands/update.js.map +1 -0
- package/dist/agent/tools/file.d.ts.map +1 -1
- package/dist/agent/tools/file.js +10 -6
- package/dist/agent/tools/file.js.map +1 -1
- package/dist/agent/tools/index.d.ts +2 -2
- package/dist/agent/tools/index.d.ts.map +1 -1
- package/dist/agent/tools/index.js +2 -2
- package/dist/agent/tools/index.js.map +1 -1
- package/dist/agent/tools/search.d.ts.map +1 -1
- package/dist/agent/tools/search.js +5 -4
- package/dist/agent/tools/search.js.map +1 -1
- package/dist/agent/tools/shell.d.ts.map +1 -1
- package/dist/agent/tools/shell.js +7 -3
- package/dist/agent/tools/shell.js.map +1 -1
- package/dist/agent/tools/types.d.ts +4 -1
- package/dist/agent/tools/types.d.ts.map +1 -1
- package/dist/cli.js +46 -31
- package/dist/cli.js.map +1 -1
- package/dist/components/ActivityLine.d.ts +11 -0
- package/dist/components/ActivityLine.d.ts.map +1 -0
- package/dist/components/ActivityLine.js +9 -0
- package/dist/components/ActivityLine.js.map +1 -0
- package/dist/components/FullScreen.d.ts +1 -0
- package/dist/components/FullScreen.d.ts.map +1 -1
- package/dist/components/FullScreen.js +2 -2
- package/dist/components/FullScreen.js.map +1 -1
- package/dist/components/MessageList.d.ts +2 -1
- package/dist/components/MessageList.d.ts.map +1 -1
- package/dist/components/MessageList.js +41 -2
- package/dist/components/MessageList.js.map +1 -1
- package/dist/components/SingleMessage.d.ts +9 -0
- package/dist/components/SingleMessage.d.ts.map +1 -0
- package/dist/components/SingleMessage.js +27 -0
- package/dist/components/SingleMessage.js.map +1 -0
- package/dist/components/StatusBar.d.ts +1 -0
- package/dist/components/StatusBar.d.ts.map +1 -1
- package/dist/components/StatusBar.js +2 -1
- package/dist/components/StatusBar.js.map +1 -1
- package/dist/components/index.d.ts +2 -0
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/index.js +2 -0
- package/dist/components/index.js.map +1 -1
- package/dist/config/types.d.ts +1 -0
- package/dist/config/types.d.ts.map +1 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +8 -0
- package/dist/config.js.map +1 -1
- package/dist/ui/views/activity_line.d.ts +11 -0
- package/dist/ui/views/activity_line.d.ts.map +1 -0
- package/dist/ui/views/activity_line.js +20 -0
- package/dist/ui/views/activity_line.js.map +1 -0
- package/dist/ui/views/app.d.ts +5 -2
- package/dist/ui/views/app.d.ts.map +1 -1
- package/dist/ui/views/app.js +17 -14
- package/dist/ui/views/app.js.map +1 -1
- package/dist/ui/views/header.d.ts.map +1 -1
- package/dist/ui/views/header.js +3 -4
- package/dist/ui/views/header.js.map +1 -1
- package/dist/ui/views/input_area.d.ts.map +1 -1
- package/dist/ui/views/input_area.js +25 -12
- package/dist/ui/views/input_area.js.map +1 -1
- package/dist/ui/views/message_list.d.ts +3 -2
- package/dist/ui/views/message_list.d.ts.map +1 -1
- package/dist/ui/views/message_list.js +33 -19
- package/dist/ui/views/message_list.js.map +1 -1
- package/dist/ui/views/status_bar.d.ts +2 -1
- package/dist/ui/views/status_bar.d.ts.map +1 -1
- package/dist/ui/views/status_bar.js +4 -2
- package/dist/ui/views/status_bar.js.map +1 -1
- package/dist/utils/shell.d.ts.map +1 -1
- package/dist/utils/shell.js +9 -1
- package/dist/utils/shell.js.map +1 -1
- package/dist/utils/spinner_frames.d.ts +2 -0
- package/dist/utils/spinner_frames.d.ts.map +1 -0
- package/dist/utils/spinner_frames.js +2 -0
- package/dist/utils/spinner_frames.js.map +1 -0
- package/dist/utils/spinner_verbs.d.ts +4 -0
- package/dist/utils/spinner_verbs.d.ts.map +1 -0
- package/dist/utils/spinner_verbs.js +22 -0
- package/dist/utils/spinner_verbs.js.map +1 -0
- package/dist/utils/tool_trace.d.ts.map +1 -1
- package/dist/utils/tool_trace.js +12 -2
- package/dist/utils/tool_trace.js.map +1 -1
- package/dist/utils/update.d.ts +9 -0
- package/dist/utils/update.d.ts.map +1 -0
- package/dist/utils/update.js +37 -0
- package/dist/utils/update.js.map +1 -0
- package/dist/utils/version.d.ts +2 -0
- package/dist/utils/version.d.ts.map +1 -0
- package/dist/utils/version.js +16 -0
- package/dist/utils/version.js.map +1 -0
- package/package.json +1 -1
- package/src/App.tsx +180 -26
- package/src/agent/agent.ts +26 -6
- package/src/agent/commands/config.ts +76 -2
- package/src/agent/commands/index.ts +2 -0
- package/src/agent/commands/update.ts +32 -0
- package/src/agent/tools/file.ts +24 -19
- package/src/agent/tools/index.ts +5 -4
- package/src/agent/tools/search.ts +6 -5
- package/src/agent/tools/shell.ts +13 -9
- package/src/agent/tools/types.ts +5 -1
- package/src/cli.tsx +50 -30
- package/src/components/ActivityLine.tsx +23 -0
- package/src/components/FullScreen.tsx +4 -3
- package/src/components/MessageList.tsx +52 -6
- package/src/components/SingleMessage.tsx +59 -0
- package/src/components/StatusBar.tsx +3 -0
- package/src/components/index.tsx +3 -1
- package/src/config/types.ts +1 -0
- package/src/config.ts +8 -0
- package/src/ui/views/activity_line.ts +33 -0
- package/src/ui/views/app.ts +23 -14
- package/src/ui/views/header.ts +3 -4
- package/src/ui/views/input_area.ts +28 -17
- package/src/ui/views/message_list.ts +36 -20
- package/src/ui/views/status_bar.ts +5 -1
- package/src/utils/shell.ts +10 -1
- package/src/utils/spinner_frames.ts +1 -0
- package/src/utils/spinner_verbs.ts +23 -0
- package/src/utils/tool_trace.ts +12 -2
- package/src/utils/update.ts +44 -0
- package/src/utils/version.ts +15 -0
package/src/ui/views/app.ts
CHANGED
|
@@ -5,6 +5,7 @@ import { buildHeaderView } from './header.js';
|
|
|
5
5
|
import { buildMessageListView } from './message_list.js';
|
|
6
6
|
import { buildInputAreaView, estimateInputLines } from './input_area.js';
|
|
7
7
|
import { buildStatusBarView } from './status_bar.js';
|
|
8
|
+
import { buildActivityLineView } from './activity_line.js';
|
|
8
9
|
|
|
9
10
|
interface AppViewProps {
|
|
10
11
|
messages: Message[];
|
|
@@ -15,13 +16,16 @@ interface AppViewProps {
|
|
|
15
16
|
rows: number;
|
|
16
17
|
commands: Array<{ name: string; description: string; usage?: string }>;
|
|
17
18
|
hasApiKey: boolean;
|
|
19
|
+
version?: string;
|
|
18
20
|
contextLength?: number;
|
|
19
21
|
contextEstimated?: boolean;
|
|
20
22
|
provider?: string;
|
|
21
23
|
model?: string;
|
|
22
24
|
emulationId?: string;
|
|
23
|
-
inputMode?: 'queue' | 'interrupt';
|
|
24
25
|
toast?: string | null;
|
|
26
|
+
spinnerLabel?: string | null;
|
|
27
|
+
spinnerFrame?: string | null;
|
|
28
|
+
inputMode?: 'queue' | 'interrupt';
|
|
25
29
|
debug?: boolean;
|
|
26
30
|
expandToolOutputs?: boolean;
|
|
27
31
|
}
|
|
@@ -48,6 +52,7 @@ export function buildAppView({
|
|
|
48
52
|
rows,
|
|
49
53
|
commands,
|
|
50
54
|
hasApiKey,
|
|
55
|
+
version = '0.1.0',
|
|
51
56
|
contextLength,
|
|
52
57
|
contextEstimated,
|
|
53
58
|
provider,
|
|
@@ -55,17 +60,20 @@ export function buildAppView({
|
|
|
55
60
|
emulationId,
|
|
56
61
|
inputMode,
|
|
57
62
|
toast,
|
|
63
|
+
spinnerLabel,
|
|
64
|
+
spinnerFrame,
|
|
58
65
|
debug = false,
|
|
59
66
|
expandToolOutputs = false
|
|
60
67
|
}: AppViewProps): LayoutNode {
|
|
61
68
|
const suggestionLines = 4;
|
|
62
|
-
const headerHeight =
|
|
69
|
+
const headerHeight = 1;
|
|
63
70
|
const statusHeight = 1;
|
|
71
|
+
const activityHeight = (agentState.status === 'thinking' || agentState.status === 'streaming') && spinnerLabel ? 3 : 0;
|
|
64
72
|
const inputLineCount = estimateInputLines(inputState.segments, cols);
|
|
65
73
|
const previewLines = estimateBadgePreviewLines(inputState);
|
|
66
|
-
const maxInputLines = Math.max(1, rows - (headerHeight + statusHeight + suggestionLines + 5));
|
|
74
|
+
const maxInputLines = Math.max(1, rows - (headerHeight + statusHeight + activityHeight + suggestionLines + 5));
|
|
67
75
|
const inputHeight = Math.min(Math.max(1, inputLineCount + previewLines), maxInputLines) + suggestionLines;
|
|
68
|
-
const contentHeight = Math.max(rows - (headerHeight + inputHeight + statusHeight), 5);
|
|
76
|
+
const contentHeight = Math.max(rows - (headerHeight + inputHeight + statusHeight + activityHeight), 5);
|
|
69
77
|
|
|
70
78
|
const placeholder = !hasApiKey ? 'Set API key with /config key <key>' :
|
|
71
79
|
agentState.status === 'thinking' ? 'Thinking...' :
|
|
@@ -74,12 +82,21 @@ export function buildAppView({
|
|
|
74
82
|
'Type a message or /help for commands...';
|
|
75
83
|
|
|
76
84
|
return box([
|
|
77
|
-
buildHeaderView({ version
|
|
85
|
+
buildHeaderView({ version, debug }),
|
|
78
86
|
buildMessageListView({ messages, height: contentHeight, debug, expandToolOutputs }),
|
|
87
|
+
buildActivityLineView({ state: agentState, spinnerLabel, spinnerFrame, inputMode }),
|
|
88
|
+
buildInputAreaView({
|
|
89
|
+
state: inputState,
|
|
90
|
+
placeholder,
|
|
91
|
+
disabled: agentState.status !== 'idle' && agentState.status !== 'error',
|
|
92
|
+
commands,
|
|
93
|
+
cols,
|
|
94
|
+
debug
|
|
95
|
+
}),
|
|
79
96
|
buildStatusBarView({
|
|
80
97
|
state: agentState,
|
|
81
98
|
sessionId,
|
|
82
|
-
version
|
|
99
|
+
version,
|
|
83
100
|
connectionStatus: hasApiKey ? 'connected' : 'disconnected',
|
|
84
101
|
contextLength,
|
|
85
102
|
contextEstimated,
|
|
@@ -89,14 +106,6 @@ export function buildAppView({
|
|
|
89
106
|
inputMode,
|
|
90
107
|
toast,
|
|
91
108
|
debug
|
|
92
|
-
}),
|
|
93
|
-
buildInputAreaView({
|
|
94
|
-
state: inputState,
|
|
95
|
-
placeholder,
|
|
96
|
-
disabled: agentState.status !== 'idle' && agentState.status !== 'error',
|
|
97
|
-
commands,
|
|
98
|
-
cols,
|
|
99
|
-
debug
|
|
100
109
|
})
|
|
101
110
|
], { flexDirection: 'column' });
|
|
102
111
|
}
|
package/src/ui/views/header.ts
CHANGED
|
@@ -15,7 +15,6 @@ export function buildHeaderView({
|
|
|
15
15
|
showHelp = true,
|
|
16
16
|
debug = false
|
|
17
17
|
}: HeaderProps): LayoutNode {
|
|
18
|
-
// Single-row header to minimize vertical space
|
|
19
18
|
return box([
|
|
20
19
|
box([
|
|
21
20
|
text(title, { bold: true }),
|
|
@@ -37,9 +36,9 @@ export function buildHeaderView({
|
|
|
37
36
|
flexDirection: 'row',
|
|
38
37
|
justifyContent: 'space-between',
|
|
39
38
|
paddingX: 1,
|
|
40
|
-
height: 1,
|
|
41
39
|
flexShrink: 0,
|
|
42
|
-
borderStyle:
|
|
43
|
-
borderColor: debug ? 'cyan' :
|
|
40
|
+
borderStyle: 'single',
|
|
41
|
+
borderColor: debug ? 'cyan' : 'gray',
|
|
42
|
+
marginBottom: 1
|
|
44
43
|
});
|
|
45
44
|
}
|
|
@@ -216,19 +216,20 @@ export function buildInputAreaView({
|
|
|
216
216
|
borderColor: debug ? (disabled ? 'gray' : 'blue') : undefined
|
|
217
217
|
});
|
|
218
218
|
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
219
|
+
// Only render suggestion lines if there are actual command matches
|
|
220
|
+
const suggestions = commandMatches.length > 0
|
|
221
|
+
? box(
|
|
222
|
+
commandMatches.map((cmd) => {
|
|
223
|
+
const usage = cmd.usage ? ` ${truncate(cmd.usage, 36)}` : '';
|
|
224
|
+
return box([
|
|
225
|
+
text(`/${cmd.name}`, { color: 'cyan', bold: true }),
|
|
226
|
+
text(usage, { color: 'white' }),
|
|
227
|
+
text(` — ${truncate(cmd.description, 48)}`, { color: 'gray', dimColor: true })
|
|
228
|
+
], { flexDirection: 'row' });
|
|
229
|
+
}),
|
|
230
|
+
{ flexDirection: 'column', paddingX: 2 }
|
|
231
|
+
)
|
|
232
|
+
: null;
|
|
232
233
|
|
|
233
234
|
const previewLines = showBadgePreview && badgePreview && badgePreview.length > 0
|
|
234
235
|
? box(badgePreview.map(line => text(line, { color: 'gray', dimColor: true })), {
|
|
@@ -237,8 +238,18 @@ export function buildInputAreaView({
|
|
|
237
238
|
})
|
|
238
239
|
: null;
|
|
239
240
|
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
241
|
+
// Horizontal separator lines
|
|
242
|
+
const separatorTop = text('─'.repeat(Math.max(10, cols - 2)), { color: 'gray', dimColor: true });
|
|
243
|
+
const separatorBottom = text('─'.repeat(Math.max(10, cols - 2)), { color: 'gray', dimColor: true });
|
|
244
|
+
|
|
245
|
+
// Build the content array, filtering out nulls
|
|
246
|
+
const content: LayoutNode[] = [separatorTop, inputLine];
|
|
247
|
+
if (previewLines) content.push(previewLines);
|
|
248
|
+
if (suggestions) content.push(suggestions);
|
|
249
|
+
content.push(separatorBottom);
|
|
250
|
+
|
|
251
|
+
return box(content, {
|
|
252
|
+
flexDirection: 'column',
|
|
253
|
+
flexShrink: 0
|
|
254
|
+
});
|
|
244
255
|
}
|
|
@@ -278,7 +278,17 @@ function renderTable(lines: string[]): LayoutNode[] {
|
|
|
278
278
|
|
|
279
279
|
function renderMarkdownLines(lines: string[]): LayoutNode[] {
|
|
280
280
|
const normalized: string[] = [];
|
|
281
|
+
let inCodeBlock = false;
|
|
281
282
|
lines.forEach(line => {
|
|
283
|
+
if (line.trim().startsWith('```')) {
|
|
284
|
+
inCodeBlock = !inCodeBlock;
|
|
285
|
+
normalized.push(line);
|
|
286
|
+
return;
|
|
287
|
+
}
|
|
288
|
+
if (inCodeBlock) {
|
|
289
|
+
normalized.push(line);
|
|
290
|
+
return;
|
|
291
|
+
}
|
|
282
292
|
if (line.includes('||') && line.includes('|')) {
|
|
283
293
|
const segments = line.split('||').map(segment => segment.trim()).filter(Boolean);
|
|
284
294
|
segments.forEach(segment => {
|
|
@@ -288,6 +298,7 @@ function renderMarkdownLines(lines: string[]): LayoutNode[] {
|
|
|
288
298
|
normalized.push(out);
|
|
289
299
|
});
|
|
290
300
|
} else {
|
|
301
|
+
// Preserve the original line - don't collapse whitespace as it breaks formatting
|
|
291
302
|
normalized.push(line);
|
|
292
303
|
}
|
|
293
304
|
});
|
|
@@ -373,10 +384,11 @@ function clipMessage(message: Message, available: number): Message | null {
|
|
|
373
384
|
|
|
374
385
|
interface MessageListProps {
|
|
375
386
|
messages: Message[];
|
|
376
|
-
height
|
|
387
|
+
height?: number;
|
|
377
388
|
maxMessages?: number;
|
|
378
389
|
debug?: boolean;
|
|
379
390
|
expandToolOutputs?: boolean;
|
|
391
|
+
scrollback?: boolean;
|
|
380
392
|
}
|
|
381
393
|
|
|
382
394
|
export function buildMessageListView({
|
|
@@ -384,30 +396,34 @@ export function buildMessageListView({
|
|
|
384
396
|
height,
|
|
385
397
|
maxMessages = 50,
|
|
386
398
|
debug = false,
|
|
387
|
-
expandToolOutputs = false
|
|
399
|
+
expandToolOutputs = false,
|
|
400
|
+
scrollback = false
|
|
388
401
|
}: MessageListProps): LayoutNode {
|
|
389
402
|
const recentMessages = messages.slice(-maxMessages);
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
remaining
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
const clipped = clipMessage(msg, remaining);
|
|
402
|
-
if (clipped) {
|
|
403
|
-
visibleMessages.unshift(clipped);
|
|
403
|
+
let visibleMessages: Message[] = recentMessages;
|
|
404
|
+
if (!scrollback && typeof height === 'number') {
|
|
405
|
+
visibleMessages = [];
|
|
406
|
+
let remaining = height;
|
|
407
|
+
for (let i = recentMessages.length - 1; i >= 0; i -= 1) {
|
|
408
|
+
const msg = recentMessages[i];
|
|
409
|
+
const estimate = estimateMessageLines(msg);
|
|
410
|
+
if (estimate <= remaining) {
|
|
411
|
+
visibleMessages.unshift(msg);
|
|
412
|
+
remaining -= estimate;
|
|
413
|
+
continue;
|
|
404
414
|
}
|
|
415
|
+
if (visibleMessages.length === 0) {
|
|
416
|
+
const clipped = clipMessage(msg, remaining);
|
|
417
|
+
if (clipped) {
|
|
418
|
+
visibleMessages.unshift(clipped);
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
break;
|
|
405
422
|
}
|
|
406
|
-
break;
|
|
407
423
|
}
|
|
408
424
|
|
|
409
425
|
return box([
|
|
410
|
-
box([], { flexGrow: 1 }),
|
|
426
|
+
!scrollback ? box([], { flexGrow: 1 }) : box([], {}),
|
|
411
427
|
box(visibleMessages.map(msg => messageView(msg, expandToolOutputs)), { flexDirection: 'column' }),
|
|
412
428
|
messages.length === 0
|
|
413
429
|
? box([text('No messages yet. Type something to begin.', { color: 'gray', dimColor: true })], {
|
|
@@ -418,9 +434,9 @@ export function buildMessageListView({
|
|
|
418
434
|
: box([], {})
|
|
419
435
|
], {
|
|
420
436
|
flexDirection: 'column',
|
|
421
|
-
height,
|
|
437
|
+
height: scrollback ? undefined : height,
|
|
422
438
|
paddingX: 1,
|
|
423
|
-
flexGrow: 1,
|
|
439
|
+
flexGrow: scrollback ? undefined : 1,
|
|
424
440
|
borderStyle: debug ? 'single' : undefined,
|
|
425
441
|
borderColor: debug ? 'gray' : undefined
|
|
426
442
|
});
|
|
@@ -13,6 +13,7 @@ interface StatusBarProps {
|
|
|
13
13
|
emulationId?: string;
|
|
14
14
|
inputMode?: 'queue' | 'interrupt';
|
|
15
15
|
toast?: string | null;
|
|
16
|
+
spinnerLabel?: string | null;
|
|
16
17
|
debug?: boolean;
|
|
17
18
|
}
|
|
18
19
|
|
|
@@ -39,10 +40,13 @@ export function buildStatusBarView({
|
|
|
39
40
|
emulationId,
|
|
40
41
|
inputMode,
|
|
41
42
|
toast,
|
|
43
|
+
spinnerLabel,
|
|
42
44
|
debug = false
|
|
43
45
|
}: StatusBarProps): LayoutNode {
|
|
44
46
|
const config = getStatusConfig(state);
|
|
45
47
|
const isActive = state.status !== 'idle' && state.status !== 'error';
|
|
48
|
+
const useSpinnerLabel = spinnerLabel && (state.status === 'thinking' || state.status === 'streaming');
|
|
49
|
+
const label = useSpinnerLabel ? spinnerLabel : config.label;
|
|
46
50
|
|
|
47
51
|
const connectionColors = {
|
|
48
52
|
connected: 'green',
|
|
@@ -64,7 +68,7 @@ export function buildStatusBarView({
|
|
|
64
68
|
return box([
|
|
65
69
|
box([
|
|
66
70
|
text(isActive ? '⠋ ' : `${config.icon} `, { color: config.color }),
|
|
67
|
-
text(
|
|
71
|
+
text(label, { color: config.color }),
|
|
68
72
|
state.error ? text(` - ${state.error}`, { color: 'red' }) : text('', {}),
|
|
69
73
|
toastLabel ? text(' • ', { color: 'gray', dimColor: true }) : text('', {}),
|
|
70
74
|
toastLabel ? text(toastLabel, { color: 'yellow' }) : text('', {})
|
package/src/utils/shell.ts
CHANGED
|
@@ -54,7 +54,16 @@ export async function runShellCommand(command: string, cwd: string): Promise<She
|
|
|
54
54
|
|
|
55
55
|
export async function resolveWorkingDir(current: string, target?: string): Promise<string> {
|
|
56
56
|
const base = current || process.cwd();
|
|
57
|
-
|
|
57
|
+
let path = target?.trim() || '';
|
|
58
|
+
|
|
59
|
+
// Expand tilde to home directory
|
|
60
|
+
if (path.startsWith('~/')) {
|
|
61
|
+
path = resolve(process.env.HOME || '', path.slice(2));
|
|
62
|
+
} else if (path === '~') {
|
|
63
|
+
path = process.env.HOME || '';
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const next = path.length > 0 ? resolve(base, path) : base;
|
|
58
67
|
const info = await stat(next);
|
|
59
68
|
if (!info.isDirectory()) {
|
|
60
69
|
throw new Error('Not a directory');
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const SPINNER_FRAMES = ['-', '\\', '|', '/'];
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export const DEFAULT_SPINNER_VERBS = [
|
|
2
|
+
'Thinking',
|
|
3
|
+
'Reticulating splines',
|
|
4
|
+
'Allocating time slices',
|
|
5
|
+
'Negotiating with entropy',
|
|
6
|
+
'Consulting the archives',
|
|
7
|
+
'Organizing thoughts',
|
|
8
|
+
'Compiling context',
|
|
9
|
+
'Assembling response',
|
|
10
|
+
'Tracing dependencies',
|
|
11
|
+
'Resolving intent'
|
|
12
|
+
];
|
|
13
|
+
|
|
14
|
+
export function parseSpinnerVerbs(input: string): string[] {
|
|
15
|
+
return input
|
|
16
|
+
.split(/[,|;\n]+/g)
|
|
17
|
+
.map(part => part.trim())
|
|
18
|
+
.filter(Boolean);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function formatSpinnerVerbs(verbs: string[]): string {
|
|
22
|
+
return verbs.length === 0 ? '(none)' : verbs.join(', ');
|
|
23
|
+
}
|
package/src/utils/tool_trace.ts
CHANGED
|
@@ -138,7 +138,11 @@ export function formatToolStart(tool: string, args: Record<string, unknown>, emu
|
|
|
138
138
|
return `⏺ ${label}${argsText}`;
|
|
139
139
|
}
|
|
140
140
|
if (style === 'codex') {
|
|
141
|
-
|
|
141
|
+
if (tool === 'run_command') {
|
|
142
|
+
const command = args.command ? String(args.command) : '';
|
|
143
|
+
return `! ${command}`;
|
|
144
|
+
}
|
|
145
|
+
return `⏺ ${label}${argsText}`;
|
|
142
146
|
}
|
|
143
147
|
return `> ${label}${argsText}`;
|
|
144
148
|
}
|
|
@@ -150,7 +154,13 @@ export function formatToolEnd(tool: string, result: string, durationMs?: number,
|
|
|
150
154
|
return `⎿ ${summary}`;
|
|
151
155
|
}
|
|
152
156
|
if (style === 'codex') {
|
|
153
|
-
|
|
157
|
+
if (tool === 'run_command') {
|
|
158
|
+
const { lines } = buildOutputLines(tool, result);
|
|
159
|
+
if (lines.length === 0) {
|
|
160
|
+
return ` ⎿ (No content)`;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
return ` ⎿ ${summary}`;
|
|
154
164
|
}
|
|
155
165
|
return `< ${summary}`;
|
|
156
166
|
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
const PACKAGE_NAME = 'zerg-ztc';
|
|
2
|
+
|
|
3
|
+
export interface UpdateCheck {
|
|
4
|
+
current: string;
|
|
5
|
+
latest: string;
|
|
6
|
+
hasUpdate: boolean;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
function parseVersion(value: string): number[] {
|
|
10
|
+
return value.split('.').map(part => Number(part.replace(/[^0-9]/g, '')) || 0);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function compareVersions(a: string, b: string): number {
|
|
14
|
+
const pa = parseVersion(a);
|
|
15
|
+
const pb = parseVersion(b);
|
|
16
|
+
const len = Math.max(pa.length, pb.length);
|
|
17
|
+
for (let i = 0; i < len; i += 1) {
|
|
18
|
+
const av = pa[i] ?? 0;
|
|
19
|
+
const bv = pb[i] ?? 0;
|
|
20
|
+
if (av > bv) return 1;
|
|
21
|
+
if (av < bv) return -1;
|
|
22
|
+
}
|
|
23
|
+
return 0;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export async function fetchLatestVersion(): Promise<string> {
|
|
27
|
+
const res = await fetch(`https://registry.npmjs.org/${PACKAGE_NAME}/latest`, {
|
|
28
|
+
headers: { 'Accept': 'application/json' }
|
|
29
|
+
});
|
|
30
|
+
if (!res.ok) {
|
|
31
|
+
throw new Error(`Version check failed (${res.status})`);
|
|
32
|
+
}
|
|
33
|
+
const data = await res.json() as { version?: string };
|
|
34
|
+
return data.version || '0.0.0';
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export async function checkForUpdate(current: string): Promise<UpdateCheck> {
|
|
38
|
+
const latest = await fetchLatestVersion();
|
|
39
|
+
return {
|
|
40
|
+
current,
|
|
41
|
+
latest,
|
|
42
|
+
hasUpdate: compareVersions(latest, current) > 0
|
|
43
|
+
};
|
|
44
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { readFileSync } from 'fs';
|
|
2
|
+
import { dirname, resolve } from 'path';
|
|
3
|
+
import { fileURLToPath } from 'url';
|
|
4
|
+
|
|
5
|
+
export function getVersion(): string {
|
|
6
|
+
try {
|
|
7
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
8
|
+
const pkgPath = resolve(here, '../../package.json');
|
|
9
|
+
const raw = readFileSync(pkgPath, 'utf-8');
|
|
10
|
+
const parsed = JSON.parse(raw) as { version?: string };
|
|
11
|
+
return parsed.version || '0.0.0';
|
|
12
|
+
} catch {
|
|
13
|
+
return '0.0.0';
|
|
14
|
+
}
|
|
15
|
+
}
|