zerg-ztc 0.1.3 → 0.1.5
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 +183 -19
- package/dist/App.js.map +1 -1
- package/dist/agent/agent.d.ts.map +1 -1
- package/dist/agent/agent.js +3 -1
- 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 +4 -1
- package/dist/agent/commands/index.js.map +1 -1
- package/dist/agent/commands/input_mode.d.ts +3 -0
- package/dist/agent/commands/input_mode.d.ts.map +1 -0
- package/dist/agent/commands/input_mode.js +21 -0
- package/dist/agent/commands/input_mode.js.map +1 -0
- package/dist/agent/commands/keybindings.d.ts +3 -0
- package/dist/agent/commands/keybindings.d.ts.map +1 -0
- package/dist/agent/commands/keybindings.js +38 -0
- package/dist/agent/commands/keybindings.js.map +1 -0
- package/dist/agent/commands/types.d.ts +2 -0
- package/dist/agent/commands/types.d.ts.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/cli.js +68 -16
- 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 +30 -30
- package/dist/components/FullScreen.js.map +1 -1
- package/dist/components/InputArea.d.ts.map +1 -1
- package/dist/components/InputArea.js +476 -19
- package/dist/components/InputArea.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 +2 -0
- package/dist/components/StatusBar.d.ts.map +1 -1
- package/dist/components/StatusBar.js +3 -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/types.d.ts +1 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/ui/core/input_segments.d.ts +1 -0
- package/dist/ui/core/input_segments.d.ts.map +1 -1
- package/dist/ui/core/input_segments.js +46 -14
- package/dist/ui/core/input_segments.js.map +1 -1
- package/dist/ui/core/types.d.ts +1 -0
- package/dist/ui/core/types.d.ts.map +1 -1
- package/dist/ui/ink/render.d.ts +3 -1
- package/dist/ui/ink/render.d.ts.map +1 -1
- package/dist/ui/ink/render.js +7 -5
- package/dist/ui/ink/render.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 -1
- package/dist/ui/views/app.d.ts.map +1 -1
- package/dist/ui/views/app.js +18 -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 +7 -5
- 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 +3 -1
- package/dist/ui/views/status_bar.d.ts.map +1 -1
- package/dist/ui/views/status_bar.js +8 -2
- package/dist/ui/views/status_bar.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 +226 -32
- package/src/agent/agent.ts +3 -1
- package/src/agent/commands/config.ts +76 -2
- package/src/agent/commands/index.ts +6 -0
- package/src/agent/commands/input_mode.ts +22 -0
- package/src/agent/commands/keybindings.ts +40 -0
- package/src/agent/commands/types.ts +2 -0
- package/src/agent/commands/update.ts +32 -0
- package/src/cli.tsx +77 -15
- package/src/components/ActivityLine.tsx +23 -0
- package/src/components/FullScreen.tsx +41 -35
- package/src/components/InputArea.tsx +489 -19
- package/src/components/MessageList.tsx +52 -6
- package/src/components/SingleMessage.tsx +59 -0
- package/src/components/StatusBar.tsx +6 -0
- package/src/components/index.tsx +3 -1
- package/src/config/types.ts +1 -0
- package/src/config.ts +8 -0
- package/src/types.ts +1 -0
- package/src/ui/core/input_segments.ts +49 -14
- package/src/ui/core/types.ts +1 -0
- package/src/ui/ink/render.tsx +16 -5
- package/src/ui/views/activity_line.ts +33 -0
- package/src/ui/views/app.ts +25 -13
- package/src/ui/views/header.ts +7 -5
- 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 +11 -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
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { Command } from './types.js';
|
|
2
|
+
import { getVersion } from '../../utils/version.js';
|
|
3
|
+
import { checkForUpdate } from '../../utils/update.js';
|
|
4
|
+
|
|
5
|
+
export const updateCommand: Command = {
|
|
6
|
+
name: 'update',
|
|
7
|
+
description: 'Check for updates and install the latest version',
|
|
8
|
+
handler: async (_args, ctx) => {
|
|
9
|
+
try {
|
|
10
|
+
ctx.addMessage({ role: 'system', content: 'Checking for updates...' });
|
|
11
|
+
const current = getVersion();
|
|
12
|
+
const info = await checkForUpdate(current);
|
|
13
|
+
if (!info.hasUpdate) {
|
|
14
|
+
ctx.addMessage({ role: 'system', content: `ZTC is up to date (v${current}).` });
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
ctx.addMessage({ role: 'system', content: `Updating to v${info.latest}...` });
|
|
18
|
+
const result = await ctx.shell.run('npm install -g zerg-ztc@latest');
|
|
19
|
+
if (result.exitCode === 0) {
|
|
20
|
+
ctx.addMessage({ role: 'system', content: `✓ Updated to v${info.latest}. Restart ZTC to use the new version.` });
|
|
21
|
+
} else {
|
|
22
|
+
ctx.addMessage({
|
|
23
|
+
role: 'system',
|
|
24
|
+
content: `Update failed (exit ${result.exitCode}).\n${result.stderr || result.stdout || ''}`.trim()
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
} catch (err) {
|
|
28
|
+
const message = err instanceof Error ? err.message : 'Update failed';
|
|
29
|
+
ctx.addMessage({ role: 'system', content: `Update failed: ${message}` });
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
};
|
package/src/cli.tsx
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import React from 'react';
|
|
3
3
|
import { render } from 'ink';
|
|
4
|
-
import { resolve
|
|
5
|
-
import { statSync
|
|
6
|
-
import { fileURLToPath } from 'url';
|
|
4
|
+
import { resolve } from 'path';
|
|
5
|
+
import { statSync } from 'fs';
|
|
7
6
|
import { App } from './App.js';
|
|
8
7
|
import { configStore } from './config.js';
|
|
8
|
+
import { getVersion } from './utils/version.js';
|
|
9
9
|
|
|
10
10
|
// --- CLI Entry Point ---
|
|
11
11
|
|
|
@@ -75,12 +75,84 @@ if (cwdIndex >= 0) {
|
|
|
75
75
|
async function main(): Promise<void> {
|
|
76
76
|
await configStore.load(true);
|
|
77
77
|
|
|
78
|
+
const isTty = Boolean(process.stdout.isTTY);
|
|
79
|
+
const useAltScreen = isTty
|
|
80
|
+
&& process.env.ZTC_ALT_SCREEN === '1'
|
|
81
|
+
&& process.env.ZTC_NO_ALT_SCREEN !== '1'
|
|
82
|
+
&& process.env.ZTC_SCROLLBACK !== '1';
|
|
83
|
+
|
|
84
|
+
if (useAltScreen) {
|
|
85
|
+
process.stdout.write('\x1b[?1049h');
|
|
86
|
+
process.stdout.write('\x1b[?25l');
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if (isTty) {
|
|
90
|
+
// Clear screen and move cursor home for a clean start
|
|
91
|
+
process.stdout.write('\x1b[2J');
|
|
92
|
+
process.stdout.write('\x1b[H');
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// In scrollback mode, print a static header once at startup
|
|
96
|
+
const scrollback = !useAltScreen;
|
|
97
|
+
if (scrollback && isTty) {
|
|
98
|
+
const version = getVersion();
|
|
99
|
+
const dateLabel = new Date().toLocaleDateString('en-US', {
|
|
100
|
+
month: 'short',
|
|
101
|
+
day: '2-digit',
|
|
102
|
+
year: 'numeric'
|
|
103
|
+
});
|
|
104
|
+
const cols = process.stdout.columns || 80;
|
|
105
|
+
const title = 'Zerg Terminal Client';
|
|
106
|
+
const right = `${dateLabel} • v${version} • Ctrl+C exit • /help`;
|
|
107
|
+
const padding = Math.max(0, cols - title.length - right.length - 4);
|
|
108
|
+
const borderLine = '─'.repeat(cols - 2);
|
|
109
|
+
|
|
110
|
+
console.log(`┌${borderLine}┐`);
|
|
111
|
+
console.log(`│ ${title}${' '.repeat(padding)}${right} │`);
|
|
112
|
+
console.log(`└${borderLine}┘`);
|
|
113
|
+
console.log(''); // Extra line after header
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Enable bracketed paste mode so we can detect paste boundaries
|
|
117
|
+
process.stdout.write('\x1b[?2004h');
|
|
118
|
+
|
|
119
|
+
// Enable Kitty keyboard protocol (progressive enhancement)
|
|
120
|
+
// This allows detection of Cmd+V and other modifier combinations
|
|
121
|
+
// Flags: 1 = disambiguate escape codes, 2 = report event types, 4 = report alternate keys, 8 = report all keys
|
|
122
|
+
// Using flags=1 for basic disambiguation which reports Cmd+V as CSI sequence
|
|
123
|
+
process.stdout.write('\x1b[>1u');
|
|
124
|
+
|
|
125
|
+
if (useAltScreen) {
|
|
126
|
+
// Wrap stdout.write to use synchronized output (reduces flicker)
|
|
127
|
+
// DECSM/DECRM 2026 tells terminal to batch updates
|
|
128
|
+
const originalWrite = process.stdout.write.bind(process.stdout);
|
|
129
|
+
let syncPending = false;
|
|
130
|
+
process.stdout.write = function(chunk: any, encoding?: any, callback?: any): boolean {
|
|
131
|
+
if (!syncPending) {
|
|
132
|
+
syncPending = true;
|
|
133
|
+
originalWrite('\x1b[?2026h'); // Begin synchronized update
|
|
134
|
+
setImmediate(() => {
|
|
135
|
+
originalWrite('\x1b[?2026l'); // End synchronized update
|
|
136
|
+
syncPending = false;
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
return originalWrite(chunk, encoding, callback);
|
|
140
|
+
} as typeof process.stdout.write;
|
|
141
|
+
}
|
|
142
|
+
|
|
78
143
|
// Render the app
|
|
79
|
-
// Note: FullScreen component handles alternate screen buffer
|
|
80
144
|
const { waitUntilExit } = render(<App />);
|
|
81
145
|
|
|
82
146
|
// Handle clean exit
|
|
83
147
|
waitUntilExit().then(() => {
|
|
148
|
+
// Disable Kitty keyboard protocol
|
|
149
|
+
process.stdout.write('\x1b[<u');
|
|
150
|
+
// Disable bracketed paste mode
|
|
151
|
+
process.stdout.write('\x1b[?2004l');
|
|
152
|
+
if (useAltScreen) {
|
|
153
|
+
process.stdout.write('\x1b[?25h');
|
|
154
|
+
process.stdout.write('\x1b[?1049l');
|
|
155
|
+
}
|
|
84
156
|
console.log('Goodbye from ZTC! 👋');
|
|
85
157
|
process.exit(0);
|
|
86
158
|
});
|
|
@@ -88,14 +160,4 @@ async function main(): Promise<void> {
|
|
|
88
160
|
|
|
89
161
|
void main();
|
|
90
162
|
|
|
91
|
-
|
|
92
|
-
try {
|
|
93
|
-
const here = dirname(fileURLToPath(import.meta.url));
|
|
94
|
-
const pkgPath = resolve(here, '../package.json');
|
|
95
|
-
const raw = readFileSync(pkgPath, 'utf-8');
|
|
96
|
-
const parsed = JSON.parse(raw) as { version?: string };
|
|
97
|
-
return parsed.version || '0.0.0';
|
|
98
|
-
} catch {
|
|
99
|
-
return '0.0.0';
|
|
100
|
-
}
|
|
101
|
-
}
|
|
163
|
+
// getVersion moved to utils/version.ts
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { AgentState } from '../types.js';
|
|
3
|
+
import { InkNode } from '../ui/ink/index.js';
|
|
4
|
+
import { buildActivityLineView } from '../ui/views/activity_line.js';
|
|
5
|
+
|
|
6
|
+
interface ActivityLineProps {
|
|
7
|
+
state: AgentState;
|
|
8
|
+
spinnerLabel?: string | null;
|
|
9
|
+
spinnerFrame?: string | null;
|
|
10
|
+
inputMode?: 'queue' | 'interrupt';
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export const ActivityLine: React.FC<ActivityLineProps> = ({
|
|
14
|
+
state,
|
|
15
|
+
spinnerLabel,
|
|
16
|
+
spinnerFrame,
|
|
17
|
+
inputMode
|
|
18
|
+
}) => {
|
|
19
|
+
const node = buildActivityLineView({ state, spinnerLabel, spinnerFrame, inputMode });
|
|
20
|
+
return <InkNode node={node} />;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export default ActivityLine;
|
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
import React, {
|
|
1
|
+
import React, { PropsWithChildren, useState, useEffect } from 'react';
|
|
2
2
|
import { Box } from 'ink';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* FullScreen component that uses the alternate screen buffer.
|
|
6
|
-
*
|
|
6
|
+
*
|
|
7
7
|
* This prevents flickering by:
|
|
8
8
|
* 1. Switching to the alternate screen buffer (like vim, htop, less do)
|
|
9
9
|
* 2. Tracking terminal resize events
|
|
10
10
|
* 3. Restoring the original terminal state on exit
|
|
11
|
-
*
|
|
11
|
+
*
|
|
12
12
|
* ANSI escape codes:
|
|
13
13
|
* - \x1b[?1049h - Enter alternate screen buffer
|
|
14
14
|
* - \x1b[?1049l - Exit alternate screen buffer
|
|
@@ -18,43 +18,25 @@ import { Box } from 'ink';
|
|
|
18
18
|
|
|
19
19
|
interface FullScreenProps extends PropsWithChildren {
|
|
20
20
|
debug?: boolean;
|
|
21
|
+
scrollback?: boolean;
|
|
21
22
|
}
|
|
22
23
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
const isTty = Boolean(process.stdout.isTTY);
|
|
30
|
-
const useAltScreen = isTty && process.env.ZTC_ALT_SCREEN === '1';
|
|
31
|
-
|
|
32
|
-
useEffect(() => {
|
|
33
|
-
if (!useAltScreen) {
|
|
34
|
-
return;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
// Enter alternate screen buffer
|
|
38
|
-
process.stdout.write('\x1b[?1049h');
|
|
39
|
-
// Hide cursor (we'll draw our own)
|
|
40
|
-
process.stdout.write('\x1b[?25l');
|
|
41
|
-
// Clear the alternate screen
|
|
42
|
-
process.stdout.write('\x1b[2J');
|
|
43
|
-
// Move cursor to top-left
|
|
44
|
-
process.stdout.write('\x1b[H');
|
|
24
|
+
function getTerminalSize() {
|
|
25
|
+
return {
|
|
26
|
+
columns: process.stdout.columns || 80,
|
|
27
|
+
rows: process.stdout.rows || 24,
|
|
28
|
+
};
|
|
29
|
+
}
|
|
45
30
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
// Show cursor
|
|
49
|
-
process.stdout.write('\x1b[?25h');
|
|
50
|
-
// Exit alternate screen buffer (restores original content)
|
|
51
|
-
process.stdout.write('\x1b[?1049l');
|
|
52
|
-
};
|
|
53
|
-
}, [useAltScreen]);
|
|
31
|
+
export const FullScreen: React.FC<FullScreenProps> = ({ children, debug = false, scrollback = false }) => {
|
|
32
|
+
const { rows, columns } = useScreenSize();
|
|
54
33
|
|
|
55
34
|
return (
|
|
56
|
-
<Box
|
|
35
|
+
<Box
|
|
57
36
|
flexDirection="column"
|
|
37
|
+
width={columns}
|
|
38
|
+
height={scrollback ? undefined : rows}
|
|
39
|
+
overflow={scrollback ? undefined : 'hidden'}
|
|
58
40
|
borderStyle={debug ? 'single' : undefined}
|
|
59
41
|
borderColor={debug ? 'red' : undefined}
|
|
60
42
|
>
|
|
@@ -67,7 +49,31 @@ export const FullScreen: React.FC<FullScreenProps> = ({ children, debug = false
|
|
|
67
49
|
* Hook to get current screen size with resize tracking
|
|
68
50
|
*/
|
|
69
51
|
export function useScreenSize() {
|
|
70
|
-
|
|
52
|
+
const [size, setSize] = useState(getTerminalSize);
|
|
53
|
+
|
|
54
|
+
useEffect(() => {
|
|
55
|
+
const updateSize = () => {
|
|
56
|
+
const current = getTerminalSize();
|
|
57
|
+
setSize(prev => {
|
|
58
|
+
if (prev.rows !== current.rows || prev.columns !== current.columns) {
|
|
59
|
+
return current;
|
|
60
|
+
}
|
|
61
|
+
return prev;
|
|
62
|
+
});
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
process.stdout.on('resize', updateSize);
|
|
66
|
+
|
|
67
|
+
// Also check on an interval as a fallback for some terminals
|
|
68
|
+
const interval = setInterval(updateSize, 1000);
|
|
69
|
+
|
|
70
|
+
return () => {
|
|
71
|
+
process.stdout.off('resize', updateSize);
|
|
72
|
+
clearInterval(interval);
|
|
73
|
+
};
|
|
74
|
+
}, []);
|
|
75
|
+
|
|
76
|
+
return size;
|
|
71
77
|
}
|
|
72
78
|
|
|
73
79
|
export default FullScreen;
|