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.
Files changed (147) hide show
  1. package/dist/App.d.ts.map +1 -1
  2. package/dist/App.js +183 -19
  3. package/dist/App.js.map +1 -1
  4. package/dist/agent/agent.d.ts.map +1 -1
  5. package/dist/agent/agent.js +3 -1
  6. package/dist/agent/agent.js.map +1 -1
  7. package/dist/agent/commands/config.d.ts.map +1 -1
  8. package/dist/agent/commands/config.js +68 -2
  9. package/dist/agent/commands/config.js.map +1 -1
  10. package/dist/agent/commands/index.d.ts.map +1 -1
  11. package/dist/agent/commands/index.js +4 -1
  12. package/dist/agent/commands/index.js.map +1 -1
  13. package/dist/agent/commands/input_mode.d.ts +3 -0
  14. package/dist/agent/commands/input_mode.d.ts.map +1 -0
  15. package/dist/agent/commands/input_mode.js +21 -0
  16. package/dist/agent/commands/input_mode.js.map +1 -0
  17. package/dist/agent/commands/keybindings.d.ts +3 -0
  18. package/dist/agent/commands/keybindings.d.ts.map +1 -0
  19. package/dist/agent/commands/keybindings.js +38 -0
  20. package/dist/agent/commands/keybindings.js.map +1 -0
  21. package/dist/agent/commands/types.d.ts +2 -0
  22. package/dist/agent/commands/types.d.ts.map +1 -1
  23. package/dist/agent/commands/update.d.ts +3 -0
  24. package/dist/agent/commands/update.d.ts.map +1 -0
  25. package/dist/agent/commands/update.js +33 -0
  26. package/dist/agent/commands/update.js.map +1 -0
  27. package/dist/cli.js +68 -16
  28. package/dist/cli.js.map +1 -1
  29. package/dist/components/ActivityLine.d.ts +11 -0
  30. package/dist/components/ActivityLine.d.ts.map +1 -0
  31. package/dist/components/ActivityLine.js +9 -0
  32. package/dist/components/ActivityLine.js.map +1 -0
  33. package/dist/components/FullScreen.d.ts +1 -0
  34. package/dist/components/FullScreen.d.ts.map +1 -1
  35. package/dist/components/FullScreen.js +30 -30
  36. package/dist/components/FullScreen.js.map +1 -1
  37. package/dist/components/InputArea.d.ts.map +1 -1
  38. package/dist/components/InputArea.js +476 -19
  39. package/dist/components/InputArea.js.map +1 -1
  40. package/dist/components/MessageList.d.ts +2 -1
  41. package/dist/components/MessageList.d.ts.map +1 -1
  42. package/dist/components/MessageList.js +41 -2
  43. package/dist/components/MessageList.js.map +1 -1
  44. package/dist/components/SingleMessage.d.ts +9 -0
  45. package/dist/components/SingleMessage.d.ts.map +1 -0
  46. package/dist/components/SingleMessage.js +27 -0
  47. package/dist/components/SingleMessage.js.map +1 -0
  48. package/dist/components/StatusBar.d.ts +2 -0
  49. package/dist/components/StatusBar.d.ts.map +1 -1
  50. package/dist/components/StatusBar.js +3 -1
  51. package/dist/components/StatusBar.js.map +1 -1
  52. package/dist/components/index.d.ts +2 -0
  53. package/dist/components/index.d.ts.map +1 -1
  54. package/dist/components/index.js +2 -0
  55. package/dist/components/index.js.map +1 -1
  56. package/dist/config/types.d.ts +1 -0
  57. package/dist/config/types.d.ts.map +1 -1
  58. package/dist/config.d.ts.map +1 -1
  59. package/dist/config.js +8 -0
  60. package/dist/config.js.map +1 -1
  61. package/dist/types.d.ts +1 -0
  62. package/dist/types.d.ts.map +1 -1
  63. package/dist/ui/core/input_segments.d.ts +1 -0
  64. package/dist/ui/core/input_segments.d.ts.map +1 -1
  65. package/dist/ui/core/input_segments.js +46 -14
  66. package/dist/ui/core/input_segments.js.map +1 -1
  67. package/dist/ui/core/types.d.ts +1 -0
  68. package/dist/ui/core/types.d.ts.map +1 -1
  69. package/dist/ui/ink/render.d.ts +3 -1
  70. package/dist/ui/ink/render.d.ts.map +1 -1
  71. package/dist/ui/ink/render.js +7 -5
  72. package/dist/ui/ink/render.js.map +1 -1
  73. package/dist/ui/views/activity_line.d.ts +11 -0
  74. package/dist/ui/views/activity_line.d.ts.map +1 -0
  75. package/dist/ui/views/activity_line.js +20 -0
  76. package/dist/ui/views/activity_line.js.map +1 -0
  77. package/dist/ui/views/app.d.ts +5 -1
  78. package/dist/ui/views/app.d.ts.map +1 -1
  79. package/dist/ui/views/app.js +18 -14
  80. package/dist/ui/views/app.js.map +1 -1
  81. package/dist/ui/views/header.d.ts.map +1 -1
  82. package/dist/ui/views/header.js +7 -5
  83. package/dist/ui/views/header.js.map +1 -1
  84. package/dist/ui/views/input_area.d.ts.map +1 -1
  85. package/dist/ui/views/input_area.js +25 -12
  86. package/dist/ui/views/input_area.js.map +1 -1
  87. package/dist/ui/views/message_list.d.ts +3 -2
  88. package/dist/ui/views/message_list.d.ts.map +1 -1
  89. package/dist/ui/views/message_list.js +33 -19
  90. package/dist/ui/views/message_list.js.map +1 -1
  91. package/dist/ui/views/status_bar.d.ts +3 -1
  92. package/dist/ui/views/status_bar.d.ts.map +1 -1
  93. package/dist/ui/views/status_bar.js +8 -2
  94. package/dist/ui/views/status_bar.js.map +1 -1
  95. package/dist/utils/spinner_frames.d.ts +2 -0
  96. package/dist/utils/spinner_frames.d.ts.map +1 -0
  97. package/dist/utils/spinner_frames.js +2 -0
  98. package/dist/utils/spinner_frames.js.map +1 -0
  99. package/dist/utils/spinner_verbs.d.ts +4 -0
  100. package/dist/utils/spinner_verbs.d.ts.map +1 -0
  101. package/dist/utils/spinner_verbs.js +22 -0
  102. package/dist/utils/spinner_verbs.js.map +1 -0
  103. package/dist/utils/tool_trace.d.ts.map +1 -1
  104. package/dist/utils/tool_trace.js +12 -2
  105. package/dist/utils/tool_trace.js.map +1 -1
  106. package/dist/utils/update.d.ts +9 -0
  107. package/dist/utils/update.d.ts.map +1 -0
  108. package/dist/utils/update.js +37 -0
  109. package/dist/utils/update.js.map +1 -0
  110. package/dist/utils/version.d.ts +2 -0
  111. package/dist/utils/version.d.ts.map +1 -0
  112. package/dist/utils/version.js +16 -0
  113. package/dist/utils/version.js.map +1 -0
  114. package/package.json +1 -1
  115. package/src/App.tsx +226 -32
  116. package/src/agent/agent.ts +3 -1
  117. package/src/agent/commands/config.ts +76 -2
  118. package/src/agent/commands/index.ts +6 -0
  119. package/src/agent/commands/input_mode.ts +22 -0
  120. package/src/agent/commands/keybindings.ts +40 -0
  121. package/src/agent/commands/types.ts +2 -0
  122. package/src/agent/commands/update.ts +32 -0
  123. package/src/cli.tsx +77 -15
  124. package/src/components/ActivityLine.tsx +23 -0
  125. package/src/components/FullScreen.tsx +41 -35
  126. package/src/components/InputArea.tsx +489 -19
  127. package/src/components/MessageList.tsx +52 -6
  128. package/src/components/SingleMessage.tsx +59 -0
  129. package/src/components/StatusBar.tsx +6 -0
  130. package/src/components/index.tsx +3 -1
  131. package/src/config/types.ts +1 -0
  132. package/src/config.ts +8 -0
  133. package/src/types.ts +1 -0
  134. package/src/ui/core/input_segments.ts +49 -14
  135. package/src/ui/core/types.ts +1 -0
  136. package/src/ui/ink/render.tsx +16 -5
  137. package/src/ui/views/activity_line.ts +33 -0
  138. package/src/ui/views/app.ts +25 -13
  139. package/src/ui/views/header.ts +7 -5
  140. package/src/ui/views/input_area.ts +28 -17
  141. package/src/ui/views/message_list.ts +36 -20
  142. package/src/ui/views/status_bar.ts +11 -1
  143. package/src/utils/spinner_frames.ts +1 -0
  144. package/src/utils/spinner_verbs.ts +23 -0
  145. package/src/utils/tool_trace.ts +12 -2
  146. package/src/utils/update.ts +44 -0
  147. package/src/utils/version.ts +15 -0
@@ -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: number;
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
- const visibleMessages: Message[] = [];
391
- let remaining = height;
392
- for (let i = recentMessages.length - 1; i >= 0; i -= 1) {
393
- const msg = recentMessages[i];
394
- const estimate = estimateMessageLines(msg);
395
- if (estimate <= remaining) {
396
- visibleMessages.unshift(msg);
397
- remaining -= estimate;
398
- continue;
399
- }
400
- if (visibleMessages.length === 0) {
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
  });
@@ -11,7 +11,9 @@ interface StatusBarProps {
11
11
  provider?: string;
12
12
  model?: string;
13
13
  emulationId?: string;
14
+ inputMode?: 'queue' | 'interrupt';
14
15
  toast?: string | null;
16
+ spinnerLabel?: string | null;
15
17
  debug?: boolean;
16
18
  }
17
19
 
@@ -36,11 +38,15 @@ export function buildStatusBarView({
36
38
  provider,
37
39
  model,
38
40
  emulationId,
41
+ inputMode,
39
42
  toast,
43
+ spinnerLabel,
40
44
  debug = false
41
45
  }: StatusBarProps): LayoutNode {
42
46
  const config = getStatusConfig(state);
43
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;
44
50
 
45
51
  const connectionColors = {
46
52
  connected: 'green',
@@ -62,7 +68,7 @@ export function buildStatusBarView({
62
68
  return box([
63
69
  box([
64
70
  text(isActive ? '⠋ ' : `${config.icon} `, { color: config.color }),
65
- text(config.label, { color: config.color }),
71
+ text(label, { color: config.color }),
66
72
  state.error ? text(` - ${state.error}`, { color: 'red' }) : text('', {}),
67
73
  toastLabel ? text(' • ', { color: 'gray', dimColor: true }) : text('', {}),
68
74
  toastLabel ? text(toastLabel, { color: 'yellow' }) : text('', {})
@@ -78,6 +84,8 @@ export function buildStatusBarView({
78
84
  (provider || model) ? text(' • ', { color: 'gray', dimColor: true }) : text('', {}),
79
85
  emulationId ? text(`emu:${emulationId}`, { color: 'gray', dimColor: true }) : text('', {}),
80
86
  emulationId ? text(' • ', { color: 'gray', dimColor: true }) : text('', {}),
87
+ inputMode ? text(`mode:${inputMode}`, { color: 'gray', dimColor: true }) : text('', {}),
88
+ inputMode ? text(' • ', { color: 'gray', dimColor: true }) : text('', {}),
81
89
  state.tokensUsed !== undefined && state.tokensUsed > 0
82
90
  ? text(`${state.tokensUsed.toLocaleString()} tok`, { color: 'gray', dimColor: true })
83
91
  : text('', {}),
@@ -98,6 +106,8 @@ export function buildStatusBarView({
98
106
  flexDirection: 'row',
99
107
  justifyContent: 'space-between',
100
108
  paddingX: 1,
109
+ height: 1,
110
+ flexShrink: 0,
101
111
  borderStyle: debug ? 'single' : undefined,
102
112
  borderColor: debug ? 'gray' : undefined
103
113
  });
@@ -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
+ }
@@ -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
- return `• ${label}${argsText}`;
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
- return ` ${summary}`;
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
+ }