zerg-ztc 0.1.11 → 0.1.13
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/bin/ztc-audio-darwin-arm64 +0 -0
- package/dist/utils/dictation_native.d.ts.map +1 -1
- package/dist/utils/dictation_native.js +43 -23
- package/dist/utils/dictation_native.js.map +1 -1
- package/package.json +5 -4
- package/packages/ztc-dictation/Cargo.toml +0 -43
- package/packages/ztc-dictation/README.md +0 -65
- package/packages/ztc-dictation/index.d.ts +0 -16
- package/packages/ztc-dictation/index.js +0 -74
- package/packages/ztc-dictation/package.json +0 -41
- package/packages/ztc-dictation/src/main.rs +0 -430
- package/src/App.tsx +0 -910
- package/src/agent/agent.ts +0 -534
- package/src/agent/backends/anthropic.ts +0 -86
- package/src/agent/backends/gemini.ts +0 -119
- package/src/agent/backends/inception.ts +0 -23
- package/src/agent/backends/index.ts +0 -17
- package/src/agent/backends/openai.ts +0 -23
- package/src/agent/backends/openai_compatible.ts +0 -143
- package/src/agent/backends/types.ts +0 -83
- package/src/agent/commands/clipboard.ts +0 -77
- package/src/agent/commands/config.ts +0 -204
- package/src/agent/commands/debug.ts +0 -23
- package/src/agent/commands/dictation.ts +0 -11
- package/src/agent/commands/emulation.ts +0 -80
- package/src/agent/commands/execution.ts +0 -9
- package/src/agent/commands/help.ts +0 -20
- package/src/agent/commands/history.ts +0 -13
- package/src/agent/commands/index.ts +0 -48
- package/src/agent/commands/input_mode.ts +0 -22
- package/src/agent/commands/keybindings.ts +0 -40
- package/src/agent/commands/model.ts +0 -11
- package/src/agent/commands/models.ts +0 -116
- package/src/agent/commands/permissions.ts +0 -64
- package/src/agent/commands/retry.ts +0 -9
- package/src/agent/commands/shell.ts +0 -68
- package/src/agent/commands/skills.ts +0 -54
- package/src/agent/commands/status.ts +0 -19
- package/src/agent/commands/types.ts +0 -88
- package/src/agent/commands/update.ts +0 -32
- package/src/agent/factory.ts +0 -60
- package/src/agent/index.ts +0 -20
- package/src/agent/runtime/capabilities.ts +0 -7
- package/src/agent/runtime/memory.ts +0 -23
- package/src/agent/runtime/policy.ts +0 -48
- package/src/agent/runtime/session.ts +0 -18
- package/src/agent/runtime/tracing.ts +0 -23
- package/src/agent/tools/file.ts +0 -178
- package/src/agent/tools/index.ts +0 -52
- package/src/agent/tools/screenshot.ts +0 -821
- package/src/agent/tools/search.ts +0 -138
- package/src/agent/tools/shell.ts +0 -69
- package/src/agent/tools/skills.ts +0 -28
- package/src/agent/tools/types.ts +0 -14
- package/src/agent/tools/zerg.ts +0 -50
- package/src/cli.tsx +0 -163
- package/src/components/ActivityLine.tsx +0 -23
- package/src/components/FullScreen.tsx +0 -79
- package/src/components/Header.tsx +0 -27
- package/src/components/InputArea.tsx +0 -1660
- package/src/components/MessageList.tsx +0 -71
- package/src/components/SingleMessage.tsx +0 -298
- package/src/components/StatusBar.tsx +0 -55
- package/src/components/index.tsx +0 -8
- package/src/config/types.ts +0 -19
- package/src/config.ts +0 -186
- package/src/debug/logger.ts +0 -14
- package/src/emulation/README.md +0 -24
- package/src/emulation/catalog.ts +0 -82
- package/src/emulation/trace_style.ts +0 -8
- package/src/emulation/types.ts +0 -7
- package/src/skills/index.ts +0 -36
- package/src/skills/loader.ts +0 -135
- package/src/skills/registry.ts +0 -6
- package/src/skills/types.ts +0 -10
- package/src/types.ts +0 -84
- package/src/ui/README.md +0 -44
- package/src/ui/core/factory.ts +0 -9
- package/src/ui/core/index.ts +0 -4
- package/src/ui/core/input.ts +0 -38
- package/src/ui/core/input_segments.ts +0 -410
- package/src/ui/core/input_state.ts +0 -17
- package/src/ui/core/layout_yoga.ts +0 -122
- package/src/ui/core/style.ts +0 -38
- package/src/ui/core/types.ts +0 -54
- package/src/ui/ink/index.tsx +0 -1
- package/src/ui/ink/render.tsx +0 -60
- package/src/ui/views/activity_line.ts +0 -33
- package/src/ui/views/app.ts +0 -111
- package/src/ui/views/header.ts +0 -44
- package/src/ui/views/input_area.ts +0 -255
- package/src/ui/views/message_list.ts +0 -443
- package/src/ui/views/status_bar.ts +0 -114
- package/src/ui/vue/index.ts +0 -53
- package/src/ui/web/frame_render.tsx +0 -148
- package/src/ui/web/index.tsx +0 -1
- package/src/ui/web/render.tsx +0 -41
- package/src/utils/clipboard.ts +0 -39
- package/src/utils/clipboard_image.ts +0 -40
- package/src/utils/dictation.ts +0 -467
- package/src/utils/dictation_native.ts +0 -258
- package/src/utils/diff.ts +0 -52
- package/src/utils/image_preview.ts +0 -36
- package/src/utils/models.ts +0 -98
- package/src/utils/path_complete.ts +0 -173
- package/src/utils/path_format.ts +0 -99
- package/src/utils/shell.ts +0 -72
- package/src/utils/spinner_frames.ts +0 -1
- package/src/utils/spinner_verbs.ts +0 -23
- package/src/utils/table.ts +0 -171
- package/src/utils/tool_summary.ts +0 -56
- package/src/utils/tool_trace.ts +0 -346
- package/src/utils/update.ts +0 -44
- package/src/utils/version.ts +0 -15
- package/src/web/index.html +0 -352
- package/src/web/mirror-favicon.svg +0 -4
- package/src/web/mirror.html +0 -641
- package/src/web/mirror_hook.ts +0 -25
- package/src/web/mirror_server.ts +0 -204
- package/tsconfig.json +0 -22
- package/vite.config.ts +0 -363
- /package/{packages/ztc-dictation/bin → bin}/.gitkeep +0 -0
package/src/utils/table.ts
DELETED
|
@@ -1,171 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Box-drawing table utilities
|
|
3
|
-
* Uses Unicode box-drawing characters for clean terminal tables
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
// Box-drawing characters
|
|
7
|
-
const BOX = {
|
|
8
|
-
topLeft: '┌',
|
|
9
|
-
topRight: '┐',
|
|
10
|
-
bottomLeft: '└',
|
|
11
|
-
bottomRight: '┘',
|
|
12
|
-
horizontal: '─',
|
|
13
|
-
vertical: '│',
|
|
14
|
-
leftT: '├',
|
|
15
|
-
rightT: '┤',
|
|
16
|
-
topT: '┬',
|
|
17
|
-
bottomT: '┴',
|
|
18
|
-
cross: '┼'
|
|
19
|
-
};
|
|
20
|
-
|
|
21
|
-
// Light box (for subtler tables)
|
|
22
|
-
const BOX_LIGHT = {
|
|
23
|
-
topLeft: '╭',
|
|
24
|
-
topRight: '╮',
|
|
25
|
-
bottomLeft: '╰',
|
|
26
|
-
bottomRight: '╯',
|
|
27
|
-
horizontal: '─',
|
|
28
|
-
vertical: '│',
|
|
29
|
-
leftT: '├',
|
|
30
|
-
rightT: '┤',
|
|
31
|
-
topT: '┬',
|
|
32
|
-
bottomT: '┴',
|
|
33
|
-
cross: '┼'
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
export interface TableColumn {
|
|
37
|
-
header: string;
|
|
38
|
-
key: string;
|
|
39
|
-
width?: number;
|
|
40
|
-
align?: 'left' | 'right' | 'center';
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
export interface TableOptions {
|
|
44
|
-
rounded?: boolean;
|
|
45
|
-
headerSeparator?: boolean;
|
|
46
|
-
compact?: boolean;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
function padString(str: string, width: number, align: 'left' | 'right' | 'center' = 'left'): string {
|
|
50
|
-
const visibleLength = str.replace(/\x1b\[[0-9;]*m/g, '').length;
|
|
51
|
-
const padding = Math.max(0, width - visibleLength);
|
|
52
|
-
|
|
53
|
-
if (align === 'right') {
|
|
54
|
-
return ' '.repeat(padding) + str;
|
|
55
|
-
}
|
|
56
|
-
if (align === 'center') {
|
|
57
|
-
const left = Math.floor(padding / 2);
|
|
58
|
-
const right = padding - left;
|
|
59
|
-
return ' '.repeat(left) + str + ' '.repeat(right);
|
|
60
|
-
}
|
|
61
|
-
return str + ' '.repeat(padding);
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
function getColumnWidths(columns: TableColumn[], rows: Record<string, unknown>[]): number[] {
|
|
65
|
-
return columns.map(col => {
|
|
66
|
-
const headerWidth = col.header.length;
|
|
67
|
-
const dataWidth = rows.reduce((max, row) => {
|
|
68
|
-
const value = String(row[col.key] || '');
|
|
69
|
-
const visibleLength = value.replace(/\x1b\[[0-9;]*m/g, '').length;
|
|
70
|
-
return Math.max(max, visibleLength);
|
|
71
|
-
}, 0);
|
|
72
|
-
return col.width || Math.max(headerWidth, dataWidth);
|
|
73
|
-
});
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
/**
|
|
77
|
-
* Format data as a box-drawing table
|
|
78
|
-
*/
|
|
79
|
-
export function formatTable(
|
|
80
|
-
columns: TableColumn[],
|
|
81
|
-
rows: Record<string, unknown>[],
|
|
82
|
-
options: TableOptions = {}
|
|
83
|
-
): string {
|
|
84
|
-
const { rounded = false, headerSeparator = true, compact = false } = options;
|
|
85
|
-
const box = rounded ? BOX_LIGHT : BOX;
|
|
86
|
-
const widths = getColumnWidths(columns, rows);
|
|
87
|
-
const lines: string[] = [];
|
|
88
|
-
|
|
89
|
-
// Top border
|
|
90
|
-
const topBorder = box.topLeft +
|
|
91
|
-
widths.map(w => box.horizontal.repeat(w + 2)).join(box.topT) +
|
|
92
|
-
box.topRight;
|
|
93
|
-
lines.push(topBorder);
|
|
94
|
-
|
|
95
|
-
// Header row
|
|
96
|
-
const headerRow = box.vertical +
|
|
97
|
-
columns.map((col, i) => ' ' + padString(col.header, widths[i], 'center') + ' ').join(box.vertical) +
|
|
98
|
-
box.vertical;
|
|
99
|
-
lines.push(headerRow);
|
|
100
|
-
|
|
101
|
-
// Header separator
|
|
102
|
-
if (headerSeparator) {
|
|
103
|
-
const separator = box.leftT +
|
|
104
|
-
widths.map(w => box.horizontal.repeat(w + 2)).join(box.cross) +
|
|
105
|
-
box.rightT;
|
|
106
|
-
lines.push(separator);
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
// Data rows
|
|
110
|
-
for (const row of rows) {
|
|
111
|
-
const dataRow = box.vertical +
|
|
112
|
-
columns.map((col, i) => {
|
|
113
|
-
const value = String(row[col.key] || '');
|
|
114
|
-
return ' ' + padString(value, widths[i], col.align) + ' ';
|
|
115
|
-
}).join(box.vertical) +
|
|
116
|
-
box.vertical;
|
|
117
|
-
lines.push(dataRow);
|
|
118
|
-
|
|
119
|
-
// Row separator (if not compact)
|
|
120
|
-
if (!compact && rows.indexOf(row) < rows.length - 1) {
|
|
121
|
-
const rowSep = box.leftT +
|
|
122
|
-
widths.map(w => box.horizontal.repeat(w + 2)).join(box.cross) +
|
|
123
|
-
box.rightT;
|
|
124
|
-
lines.push(rowSep);
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
// Bottom border
|
|
129
|
-
const bottomBorder = box.bottomLeft +
|
|
130
|
-
widths.map(w => box.horizontal.repeat(w + 2)).join(box.bottomT) +
|
|
131
|
-
box.bottomRight;
|
|
132
|
-
lines.push(bottomBorder);
|
|
133
|
-
|
|
134
|
-
return lines.join('\n');
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
/**
|
|
138
|
-
* Simple two-column table (key-value style)
|
|
139
|
-
*/
|
|
140
|
-
export function formatKeyValueTable(
|
|
141
|
-
data: Array<{ label: string; value: string }>,
|
|
142
|
-
options: TableOptions = {}
|
|
143
|
-
): string {
|
|
144
|
-
const columns: TableColumn[] = [
|
|
145
|
-
{ header: 'Key', key: 'label', align: 'left' },
|
|
146
|
-
{ header: 'Value', key: 'value', align: 'left' }
|
|
147
|
-
];
|
|
148
|
-
const rows = data.map(d => ({ label: d.label, value: d.value }));
|
|
149
|
-
return formatTable(columns, rows, { ...options, headerSeparator: false });
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
/**
|
|
153
|
-
* Format a schedule/calendar table
|
|
154
|
-
*/
|
|
155
|
-
export function formatScheduleTable(
|
|
156
|
-
events: Array<{ time: string; event: string }>,
|
|
157
|
-
options: TableOptions = {}
|
|
158
|
-
): string {
|
|
159
|
-
const columns: TableColumn[] = [
|
|
160
|
-
{ header: 'Time', key: 'time', align: 'left', width: 7 },
|
|
161
|
-
{ header: 'Event', key: 'event', align: 'left' }
|
|
162
|
-
];
|
|
163
|
-
return formatTable(columns, events, { rounded: true, compact: true, ...options });
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
/**
|
|
167
|
-
* Simple horizontal line/divider
|
|
168
|
-
*/
|
|
169
|
-
export function formatDivider(width = 80, char = '─'): string {
|
|
170
|
-
return char.repeat(width);
|
|
171
|
-
}
|
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
type ParsedResult = Record<string, unknown>;
|
|
2
|
-
|
|
3
|
-
function parseResult(raw: string): ParsedResult | null {
|
|
4
|
-
try {
|
|
5
|
-
const data = JSON.parse(raw) as ParsedResult;
|
|
6
|
-
if (data && typeof data === 'object') return data;
|
|
7
|
-
} catch {
|
|
8
|
-
return null;
|
|
9
|
-
}
|
|
10
|
-
return null;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
export function summarizeToolResult(tool: string, raw: string): string {
|
|
14
|
-
const parsed = parseResult(raw);
|
|
15
|
-
if (!parsed) {
|
|
16
|
-
const trimmed = raw.trim();
|
|
17
|
-
return trimmed.length > 140 ? `${trimmed.slice(0, 140)}...` : trimmed || 'Done';
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
if (tool === 'read_file') {
|
|
21
|
-
const path = String(parsed.path || '');
|
|
22
|
-
const size = parsed.size ? ` (${parsed.size} bytes)` : '';
|
|
23
|
-
return `Read ${path}${size}`;
|
|
24
|
-
}
|
|
25
|
-
if (tool === 'write_file') {
|
|
26
|
-
const path = String(parsed.path || '');
|
|
27
|
-
const action = String(parsed.action || 'written');
|
|
28
|
-
return `Wrote ${path} (${action})`;
|
|
29
|
-
}
|
|
30
|
-
if (tool === 'list_directory') {
|
|
31
|
-
const path = String(parsed.path || '');
|
|
32
|
-
const entries = Array.isArray(parsed.entries) ? parsed.entries.length : 0;
|
|
33
|
-
return `Listed ${path} (${entries} items)`;
|
|
34
|
-
}
|
|
35
|
-
if (tool === 'run_command') {
|
|
36
|
-
const cwd = String(parsed.cwd || '');
|
|
37
|
-
const cmd = String(parsed.command || '');
|
|
38
|
-
return `Ran ${cmd}${cwd ? ` (cwd ${cwd})` : ''}`;
|
|
39
|
-
}
|
|
40
|
-
if (tool === 'search') {
|
|
41
|
-
const count = parsed.count ? String(parsed.count) : '0';
|
|
42
|
-
const path = String(parsed.path || '');
|
|
43
|
-
return `Found ${count} matches${path ? ` in ${path}` : ''}`;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
const fallback = raw.trim();
|
|
47
|
-
return fallback.length > 140 ? `${fallback.slice(0, 140)}...` : fallback || 'Done';
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
export function summarizeToolArgs(args: Record<string, unknown>): string {
|
|
51
|
-
const entries = Object.entries(args)
|
|
52
|
-
.map(([key, value]) => `${key}=${String(value)}`)
|
|
53
|
-
.slice(0, 3);
|
|
54
|
-
if (entries.length === 0) return '';
|
|
55
|
-
return `(${entries.join(', ')}${Object.keys(args).length > 3 ? ', ...' : ''})`;
|
|
56
|
-
}
|
package/src/utils/tool_trace.ts
DELETED
|
@@ -1,346 +0,0 @@
|
|
|
1
|
-
import { getTraceStyle } from '../emulation/trace_style.js';
|
|
2
|
-
import { formatTracePath, formatBytes, formatDuration } from './path_format.js';
|
|
3
|
-
|
|
4
|
-
const toolLabels: Record<string, string> = {
|
|
5
|
-
run_command: 'Bash',
|
|
6
|
-
read_file: 'Read',
|
|
7
|
-
write_file: 'Update',
|
|
8
|
-
list_directory: 'List',
|
|
9
|
-
search: 'Search',
|
|
10
|
-
screenshot: 'Screenshot',
|
|
11
|
-
list_windows: 'ListWindows',
|
|
12
|
-
run_and_capture: 'RunCapture'
|
|
13
|
-
};
|
|
14
|
-
|
|
15
|
-
function formatArgs(tool: string, args: Record<string, unknown>): string {
|
|
16
|
-
// Compact arg formatting - just show the key values
|
|
17
|
-
if (tool === 'run_command') {
|
|
18
|
-
const command = args.command ? String(args.command) : '';
|
|
19
|
-
// Truncate long commands
|
|
20
|
-
const truncated = command.length > 60 ? command.slice(0, 57) + '...' : command;
|
|
21
|
-
return `(${truncated})`;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
if (tool === 'search') {
|
|
25
|
-
const pattern = args.pattern ? String(args.pattern) : '';
|
|
26
|
-
const path = args.path ? formatTracePath(String(args.path)) : '';
|
|
27
|
-
if (path) {
|
|
28
|
-
return `(${pattern}, ${path})`;
|
|
29
|
-
}
|
|
30
|
-
return `(${pattern})`;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
if (tool === 'read_file' || tool === 'write_file' || tool === 'list_directory') {
|
|
34
|
-
const path = args.path ? formatTracePath(String(args.path)) : '';
|
|
35
|
-
return `(${path})`;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
if (tool === 'screenshot') {
|
|
39
|
-
if (args.app) return `(app: "${args.app}")`;
|
|
40
|
-
if (args.pid) return `(pid: ${args.pid})`;
|
|
41
|
-
if (args.windowId) return `(windowId: ${args.windowId})`;
|
|
42
|
-
return '';
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
if (tool === 'list_windows') {
|
|
46
|
-
if (args.filter) return `(filter: "${args.filter}")`;
|
|
47
|
-
return '';
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
// Generic: show first few args compactly
|
|
51
|
-
const entries = Object.entries(args).slice(0, 3);
|
|
52
|
-
if (entries.length === 0) return '';
|
|
53
|
-
|
|
54
|
-
const parts = entries.map(([key, value]) => {
|
|
55
|
-
if (typeof value === 'string') {
|
|
56
|
-
const v = value.length > 30 ? value.slice(0, 27) + '...' : value;
|
|
57
|
-
return `${key}: "${v}"`;
|
|
58
|
-
}
|
|
59
|
-
return `${key}: ${value}`;
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
return `(${parts.join(', ')})`;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
function parseResult(raw: string): Record<string, unknown> | null {
|
|
66
|
-
try {
|
|
67
|
-
const data = JSON.parse(raw) as Record<string, unknown>;
|
|
68
|
-
if (data && typeof data === 'object') return data;
|
|
69
|
-
} catch {
|
|
70
|
-
return null;
|
|
71
|
-
}
|
|
72
|
-
return null;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
interface ParsedDiff {
|
|
76
|
-
addedLines: number;
|
|
77
|
-
removedLines: number;
|
|
78
|
-
hunks: Array<{
|
|
79
|
-
header: string;
|
|
80
|
-
lines: Array<{ type: 'add' | 'remove' | 'context'; lineNum?: number; text: string }>;
|
|
81
|
-
}>;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
function parseDiff(diff: string): ParsedDiff | null {
|
|
85
|
-
if (!diff || !diff.includes('@@')) return null;
|
|
86
|
-
|
|
87
|
-
const lines = diff.split('\n');
|
|
88
|
-
let addedLines = 0;
|
|
89
|
-
let removedLines = 0;
|
|
90
|
-
const hunks: ParsedDiff['hunks'] = [];
|
|
91
|
-
let currentHunk: ParsedDiff['hunks'][0] | null = null;
|
|
92
|
-
|
|
93
|
-
for (const line of lines) {
|
|
94
|
-
if (line.startsWith('@@')) {
|
|
95
|
-
if (currentHunk) hunks.push(currentHunk);
|
|
96
|
-
currentHunk = { header: line, lines: [] };
|
|
97
|
-
continue;
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
if (!currentHunk) continue;
|
|
101
|
-
|
|
102
|
-
if (line.startsWith('+') && !line.startsWith('+++')) {
|
|
103
|
-
addedLines++;
|
|
104
|
-
currentHunk.lines.push({ type: 'add', text: line.slice(1) });
|
|
105
|
-
} else if (line.startsWith('-') && !line.startsWith('---')) {
|
|
106
|
-
removedLines++;
|
|
107
|
-
currentHunk.lines.push({ type: 'remove', text: line.slice(1) });
|
|
108
|
-
} else if (line.startsWith(' ')) {
|
|
109
|
-
currentHunk.lines.push({ type: 'context', text: line.slice(1) });
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
if (currentHunk) hunks.push(currentHunk);
|
|
114
|
-
|
|
115
|
-
return { addedLines, removedLines, hunks };
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
function formatOutcome(tool: string, result: string, durationMs?: number): string {
|
|
119
|
-
const parsed = parseResult(result);
|
|
120
|
-
const duration = typeof durationMs === 'number' ? ` in ${formatDuration(durationMs)}` : '';
|
|
121
|
-
|
|
122
|
-
if (parsed && tool === 'search') {
|
|
123
|
-
const count = typeof parsed.count === 'number' ? parsed.count : 0;
|
|
124
|
-
const label = count === 1 ? 'match' : 'matches';
|
|
125
|
-
return `Found ${count} ${label}${duration}`;
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
if (parsed && tool === 'run_command') {
|
|
129
|
-
const stdout = String(parsed.stdout || '');
|
|
130
|
-
const stderr = String(parsed.stderr || '');
|
|
131
|
-
const lines = (stdout || stderr).trim().split('\n').filter(Boolean).length;
|
|
132
|
-
const label = lines === 1 ? 'line' : 'lines';
|
|
133
|
-
return lines > 0 ? `Output ${lines} ${label}${duration}` : `Done${duration}`;
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
if (parsed && tool === 'read_file') {
|
|
137
|
-
const path = formatTracePath(String(parsed.path || ''));
|
|
138
|
-
const size = typeof parsed.size === 'number' ? formatBytes(parsed.size) : '';
|
|
139
|
-
const truncated = parsed.truncated ? ' (truncated)' : '';
|
|
140
|
-
return `${path} (${size})${duration}${truncated}`;
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
if (parsed && tool === 'write_file') {
|
|
144
|
-
const path = formatTracePath(String(parsed.path || ''));
|
|
145
|
-
const diff = parseDiff(String(parsed.diff || ''));
|
|
146
|
-
if (diff) {
|
|
147
|
-
const parts: string[] = [];
|
|
148
|
-
if (diff.addedLines > 0) parts.push(`+${diff.addedLines}`);
|
|
149
|
-
if (diff.removedLines > 0) parts.push(`-${diff.removedLines}`);
|
|
150
|
-
const summary = parts.length > 0 ? ` (${parts.join(', ')} lines)` : '';
|
|
151
|
-
return `${path}${summary}${duration}`;
|
|
152
|
-
}
|
|
153
|
-
return `${path}${duration}`;
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
if (parsed && tool === 'list_directory') {
|
|
157
|
-
const path = formatTracePath(String(parsed.path || ''));
|
|
158
|
-
const entries = Array.isArray(parsed.entries) ? parsed.entries.length : 0;
|
|
159
|
-
return `${path} (${entries} items)${duration}`;
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
if (parsed && tool === 'screenshot') {
|
|
163
|
-
const desc = String(parsed.description || 'Screenshot captured');
|
|
164
|
-
return `${desc}${duration}`;
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
return `Done${duration}`;
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
function buildOutputLines(tool: string, result: string): { lines: string[]; truncated: boolean } {
|
|
171
|
-
const parsed = parseResult(result);
|
|
172
|
-
if (!parsed) {
|
|
173
|
-
return { lines: [], truncated: false };
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
if (tool === 'run_command') {
|
|
177
|
-
const stdout = String(parsed.stdout || '');
|
|
178
|
-
const stderr = String(parsed.stderr || '');
|
|
179
|
-
const combined = [stdout, stderr].filter(Boolean).join('\n');
|
|
180
|
-
const lines = combined.split('\n').filter(Boolean);
|
|
181
|
-
return { lines, truncated: Boolean(parsed.truncated) };
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
if (tool === 'search') {
|
|
185
|
-
const matches = Array.isArray(parsed.matches) ? parsed.matches : [];
|
|
186
|
-
const lines = matches.map((entry: unknown) => {
|
|
187
|
-
if (!entry || typeof entry !== 'object') return '';
|
|
188
|
-
const row = entry as Record<string, unknown>;
|
|
189
|
-
const file = formatTracePath(String(row.path || ''));
|
|
190
|
-
const line = row.line ? `:${row.line}` : '';
|
|
191
|
-
const text = String(row.text || '');
|
|
192
|
-
return `${file}${line} ${text}`.trim();
|
|
193
|
-
}).filter(Boolean);
|
|
194
|
-
return { lines, truncated: Boolean(parsed.truncated) };
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
if (tool === 'read_file') {
|
|
198
|
-
const content = String(parsed.content || '');
|
|
199
|
-
const lines = content.split('\n');
|
|
200
|
-
return { lines, truncated: Boolean(parsed.truncated) };
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
if (tool === 'write_file') {
|
|
204
|
-
// Return formatted diff lines
|
|
205
|
-
const diff = parseDiff(String(parsed.diff || ''));
|
|
206
|
-
if (diff && diff.hunks.length > 0) {
|
|
207
|
-
const lines: string[] = [];
|
|
208
|
-
for (const hunk of diff.hunks) {
|
|
209
|
-
for (const line of hunk.lines) {
|
|
210
|
-
if (line.type === 'add') {
|
|
211
|
-
lines.push(`+ ${line.text}`);
|
|
212
|
-
} else if (line.type === 'remove') {
|
|
213
|
-
lines.push(`- ${line.text}`);
|
|
214
|
-
} else {
|
|
215
|
-
lines.push(` ${line.text}`);
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
return { lines, truncated: Boolean(parsed.truncated) };
|
|
220
|
-
}
|
|
221
|
-
return { lines: [], truncated: false };
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
return { lines: [], truncated: false };
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
export function formatToolStart(tool: string, args: Record<string, unknown>, emulationId?: string): string {
|
|
228
|
-
const style = getTraceStyle(emulationId);
|
|
229
|
-
const label = toolLabels[tool] || tool;
|
|
230
|
-
const argsText = formatArgs(tool, args);
|
|
231
|
-
|
|
232
|
-
if (style === 'claude_code') {
|
|
233
|
-
return `⏺ ${label}${argsText}`;
|
|
234
|
-
}
|
|
235
|
-
if (style === 'codex') {
|
|
236
|
-
if (tool === 'run_command') {
|
|
237
|
-
const command = args.command ? String(args.command) : '';
|
|
238
|
-
return `! ${command}`;
|
|
239
|
-
}
|
|
240
|
-
return `⏺ ${label}${argsText}`;
|
|
241
|
-
}
|
|
242
|
-
return `> ${label}${argsText}`;
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
export function formatToolEnd(tool: string, result: string, durationMs?: number, emulationId?: string): string {
|
|
246
|
-
const style = getTraceStyle(emulationId);
|
|
247
|
-
const summary = formatOutcome(tool, result, durationMs);
|
|
248
|
-
|
|
249
|
-
if (style === 'claude_code') {
|
|
250
|
-
return `⎿ ${summary}`;
|
|
251
|
-
}
|
|
252
|
-
if (style === 'codex') {
|
|
253
|
-
if (tool === 'run_command') {
|
|
254
|
-
const { lines } = buildOutputLines(tool, result);
|
|
255
|
-
if (lines.length === 0) {
|
|
256
|
-
return ` ⎿ (No content)`;
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
|
-
return ` ⎿ ${summary}`;
|
|
260
|
-
}
|
|
261
|
-
return `< ${summary}`;
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
export function formatToolError(tool: string, error: string, emulationId?: string): string {
|
|
265
|
-
const style = getTraceStyle(emulationId);
|
|
266
|
-
const label = toolLabels[tool] || tool;
|
|
267
|
-
|
|
268
|
-
// Provide helpful error messages
|
|
269
|
-
let displayError = error;
|
|
270
|
-
if (error.includes('Screen Recording permission')) {
|
|
271
|
-
displayError = 'Screen Recording permission required (System Settings > Privacy & Security)';
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
if (style === 'claude_code') {
|
|
275
|
-
return `⎿ ${label} failed: ${displayError}`;
|
|
276
|
-
}
|
|
277
|
-
if (style === 'codex') {
|
|
278
|
-
return ` ${label} failed: ${displayError}`;
|
|
279
|
-
}
|
|
280
|
-
return `< ${label} failed: ${displayError}`;
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
export interface ToolOutputMessage {
|
|
284
|
-
preview: string;
|
|
285
|
-
full: string;
|
|
286
|
-
truncated: boolean;
|
|
287
|
-
diffLines?: Array<{ type: 'add' | 'remove' | 'context'; text: string }>;
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
export function buildToolOutputMessage(
|
|
291
|
-
tool: string,
|
|
292
|
-
result: string,
|
|
293
|
-
durationMs?: number,
|
|
294
|
-
emulationId?: string
|
|
295
|
-
): ToolOutputMessage {
|
|
296
|
-
const { lines, truncated } = buildOutputLines(tool, result);
|
|
297
|
-
const parsed = parseResult(result);
|
|
298
|
-
|
|
299
|
-
// For write_file, extract diff info
|
|
300
|
-
let diffLines: ToolOutputMessage['diffLines'];
|
|
301
|
-
if (tool === 'write_file' && parsed) {
|
|
302
|
-
const diff = parseDiff(String(parsed.diff || ''));
|
|
303
|
-
if (diff && diff.hunks.length > 0) {
|
|
304
|
-
diffLines = [];
|
|
305
|
-
for (const hunk of diff.hunks) {
|
|
306
|
-
for (const line of hunk.lines) {
|
|
307
|
-
diffLines.push({ type: line.type, text: line.text });
|
|
308
|
-
}
|
|
309
|
-
}
|
|
310
|
-
}
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
if (lines.length === 0 && !diffLines) {
|
|
314
|
-
const summary = formatToolEnd(tool, result, durationMs, emulationId);
|
|
315
|
-
return { preview: summary, full: summary, truncated: false };
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
const maxLines = 8;
|
|
319
|
-
const previewLines = lines.slice(0, maxLines);
|
|
320
|
-
const remaining = lines.length - previewLines.length;
|
|
321
|
-
const expandHint = truncated || remaining > 0 ? ' (ctrl+o to expand)' : '';
|
|
322
|
-
|
|
323
|
-
const summary = formatToolEnd(tool, result, durationMs, emulationId);
|
|
324
|
-
const indent = ' ';
|
|
325
|
-
|
|
326
|
-
const previewBlock = [summary];
|
|
327
|
-
for (const line of previewLines) {
|
|
328
|
-
previewBlock.push(`${indent}${line}`);
|
|
329
|
-
}
|
|
330
|
-
if (expandHint) {
|
|
331
|
-
const extraLabel = remaining > 0 ? `… +${remaining} lines` : '…';
|
|
332
|
-
previewBlock.push(`${indent}${extraLabel}${expandHint}`);
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
const fullBlock = [summary];
|
|
336
|
-
for (const line of lines) {
|
|
337
|
-
fullBlock.push(`${indent}${line}`);
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
return {
|
|
341
|
-
preview: previewBlock.join('\n'),
|
|
342
|
-
full: fullBlock.join('\n'),
|
|
343
|
-
truncated: Boolean(expandHint),
|
|
344
|
-
diffLines
|
|
345
|
-
};
|
|
346
|
-
}
|
package/src/utils/update.ts
DELETED
|
@@ -1,44 +0,0 @@
|
|
|
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
|
-
}
|
package/src/utils/version.ts
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
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
|
-
}
|