zerg-ztc 0.1.11 → 0.1.12

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 (122) hide show
  1. package/bin/ztc-audio-darwin-arm64 +0 -0
  2. package/dist/utils/dictation_native.d.ts.map +1 -1
  3. package/dist/utils/dictation_native.js +43 -23
  4. package/dist/utils/dictation_native.js.map +1 -1
  5. package/package.json +5 -4
  6. package/packages/ztc-dictation/Cargo.toml +0 -43
  7. package/packages/ztc-dictation/README.md +0 -65
  8. package/packages/ztc-dictation/index.d.ts +0 -16
  9. package/packages/ztc-dictation/index.js +0 -74
  10. package/packages/ztc-dictation/package.json +0 -41
  11. package/packages/ztc-dictation/src/main.rs +0 -430
  12. package/src/App.tsx +0 -910
  13. package/src/agent/agent.ts +0 -534
  14. package/src/agent/backends/anthropic.ts +0 -86
  15. package/src/agent/backends/gemini.ts +0 -119
  16. package/src/agent/backends/inception.ts +0 -23
  17. package/src/agent/backends/index.ts +0 -17
  18. package/src/agent/backends/openai.ts +0 -23
  19. package/src/agent/backends/openai_compatible.ts +0 -143
  20. package/src/agent/backends/types.ts +0 -83
  21. package/src/agent/commands/clipboard.ts +0 -77
  22. package/src/agent/commands/config.ts +0 -204
  23. package/src/agent/commands/debug.ts +0 -23
  24. package/src/agent/commands/dictation.ts +0 -11
  25. package/src/agent/commands/emulation.ts +0 -80
  26. package/src/agent/commands/execution.ts +0 -9
  27. package/src/agent/commands/help.ts +0 -20
  28. package/src/agent/commands/history.ts +0 -13
  29. package/src/agent/commands/index.ts +0 -48
  30. package/src/agent/commands/input_mode.ts +0 -22
  31. package/src/agent/commands/keybindings.ts +0 -40
  32. package/src/agent/commands/model.ts +0 -11
  33. package/src/agent/commands/models.ts +0 -116
  34. package/src/agent/commands/permissions.ts +0 -64
  35. package/src/agent/commands/retry.ts +0 -9
  36. package/src/agent/commands/shell.ts +0 -68
  37. package/src/agent/commands/skills.ts +0 -54
  38. package/src/agent/commands/status.ts +0 -19
  39. package/src/agent/commands/types.ts +0 -88
  40. package/src/agent/commands/update.ts +0 -32
  41. package/src/agent/factory.ts +0 -60
  42. package/src/agent/index.ts +0 -20
  43. package/src/agent/runtime/capabilities.ts +0 -7
  44. package/src/agent/runtime/memory.ts +0 -23
  45. package/src/agent/runtime/policy.ts +0 -48
  46. package/src/agent/runtime/session.ts +0 -18
  47. package/src/agent/runtime/tracing.ts +0 -23
  48. package/src/agent/tools/file.ts +0 -178
  49. package/src/agent/tools/index.ts +0 -52
  50. package/src/agent/tools/screenshot.ts +0 -821
  51. package/src/agent/tools/search.ts +0 -138
  52. package/src/agent/tools/shell.ts +0 -69
  53. package/src/agent/tools/skills.ts +0 -28
  54. package/src/agent/tools/types.ts +0 -14
  55. package/src/agent/tools/zerg.ts +0 -50
  56. package/src/cli.tsx +0 -163
  57. package/src/components/ActivityLine.tsx +0 -23
  58. package/src/components/FullScreen.tsx +0 -79
  59. package/src/components/Header.tsx +0 -27
  60. package/src/components/InputArea.tsx +0 -1660
  61. package/src/components/MessageList.tsx +0 -71
  62. package/src/components/SingleMessage.tsx +0 -298
  63. package/src/components/StatusBar.tsx +0 -55
  64. package/src/components/index.tsx +0 -8
  65. package/src/config/types.ts +0 -19
  66. package/src/config.ts +0 -186
  67. package/src/debug/logger.ts +0 -14
  68. package/src/emulation/README.md +0 -24
  69. package/src/emulation/catalog.ts +0 -82
  70. package/src/emulation/trace_style.ts +0 -8
  71. package/src/emulation/types.ts +0 -7
  72. package/src/skills/index.ts +0 -36
  73. package/src/skills/loader.ts +0 -135
  74. package/src/skills/registry.ts +0 -6
  75. package/src/skills/types.ts +0 -10
  76. package/src/types.ts +0 -84
  77. package/src/ui/README.md +0 -44
  78. package/src/ui/core/factory.ts +0 -9
  79. package/src/ui/core/index.ts +0 -4
  80. package/src/ui/core/input.ts +0 -38
  81. package/src/ui/core/input_segments.ts +0 -410
  82. package/src/ui/core/input_state.ts +0 -17
  83. package/src/ui/core/layout_yoga.ts +0 -122
  84. package/src/ui/core/style.ts +0 -38
  85. package/src/ui/core/types.ts +0 -54
  86. package/src/ui/ink/index.tsx +0 -1
  87. package/src/ui/ink/render.tsx +0 -60
  88. package/src/ui/views/activity_line.ts +0 -33
  89. package/src/ui/views/app.ts +0 -111
  90. package/src/ui/views/header.ts +0 -44
  91. package/src/ui/views/input_area.ts +0 -255
  92. package/src/ui/views/message_list.ts +0 -443
  93. package/src/ui/views/status_bar.ts +0 -114
  94. package/src/ui/vue/index.ts +0 -53
  95. package/src/ui/web/frame_render.tsx +0 -148
  96. package/src/ui/web/index.tsx +0 -1
  97. package/src/ui/web/render.tsx +0 -41
  98. package/src/utils/clipboard.ts +0 -39
  99. package/src/utils/clipboard_image.ts +0 -40
  100. package/src/utils/dictation.ts +0 -467
  101. package/src/utils/dictation_native.ts +0 -258
  102. package/src/utils/diff.ts +0 -52
  103. package/src/utils/image_preview.ts +0 -36
  104. package/src/utils/models.ts +0 -98
  105. package/src/utils/path_complete.ts +0 -173
  106. package/src/utils/path_format.ts +0 -99
  107. package/src/utils/shell.ts +0 -72
  108. package/src/utils/spinner_frames.ts +0 -1
  109. package/src/utils/spinner_verbs.ts +0 -23
  110. package/src/utils/table.ts +0 -171
  111. package/src/utils/tool_summary.ts +0 -56
  112. package/src/utils/tool_trace.ts +0 -346
  113. package/src/utils/update.ts +0 -44
  114. package/src/utils/version.ts +0 -15
  115. package/src/web/index.html +0 -352
  116. package/src/web/mirror-favicon.svg +0 -4
  117. package/src/web/mirror.html +0 -641
  118. package/src/web/mirror_hook.ts +0 -25
  119. package/src/web/mirror_server.ts +0 -204
  120. package/tsconfig.json +0 -22
  121. package/vite.config.ts +0 -363
  122. /package/{packages/ztc-dictation/bin → bin}/.gitkeep +0 -0
package/src/utils/diff.ts DELETED
@@ -1,52 +0,0 @@
1
- interface DiffLine {
2
- type: 'context' | 'add' | 'remove';
3
- text: string;
4
- }
5
-
6
- export function buildSimpleDiff(before: string, after: string, maxLines = 120, context = 2): string {
7
- const beforeLines = before.split('\n');
8
- const afterLines = after.split('\n');
9
- const maxLen = Math.max(beforeLines.length, afterLines.length);
10
-
11
- const changes = new Set<number>();
12
- for (let i = 0; i < maxLen; i += 1) {
13
- const b = beforeLines[i];
14
- const a = afterLines[i];
15
- if (b !== a) {
16
- changes.add(i);
17
- }
18
- }
19
-
20
- const include = new Set<number>();
21
- for (const idx of changes) {
22
- for (let i = Math.max(0, idx - context); i <= idx + context; i += 1) {
23
- include.add(i);
24
- }
25
- }
26
-
27
- const lines: DiffLine[] = [];
28
- for (let i = 0; i < maxLen; i += 1) {
29
- if (!include.has(i)) continue;
30
- const b = beforeLines[i];
31
- const a = afterLines[i];
32
- if (b === a) {
33
- lines.push({ type: 'context', text: b || '' });
34
- } else {
35
- if (b !== undefined) lines.push({ type: 'remove', text: b || '' });
36
- if (a !== undefined) lines.push({ type: 'add', text: a || '' });
37
- }
38
- }
39
-
40
- const clipped = lines.slice(0, maxLines);
41
- if (lines.length > maxLines) {
42
- clipped.push({ type: 'context', text: '... diff truncated ...' });
43
- }
44
-
45
- return clipped
46
- .map(line => {
47
- if (line.type === 'add') return `+ ${line.text}`;
48
- if (line.type === 'remove') return `- ${line.text}`;
49
- return ` ${line.text}`;
50
- })
51
- .join('\n');
52
- }
@@ -1,36 +0,0 @@
1
- import { execFile } from 'child_process';
2
- import { promisify } from 'util';
3
-
4
- const execFileAsync = promisify(execFile);
5
- const cache = new Map<string, string[]>();
6
-
7
- async function hasCommand(command: string): Promise<boolean> {
8
- try {
9
- await execFileAsync('which', [command]);
10
- return true;
11
- } catch {
12
- return false;
13
- }
14
- }
15
-
16
- export async function renderImagePreview(path: string, width = 40, height = 20): Promise<string[] | null> {
17
- const key = `${path}:${width}x${height}`;
18
- const cached = cache.get(key);
19
- if (cached) return cached;
20
- if (!(await hasCommand('chafa'))) {
21
- return null;
22
- }
23
- try {
24
- const { stdout } = await execFileAsync('chafa', [
25
- '--colors=none',
26
- '--symbols=block',
27
- `--size=${width}x${height}`,
28
- path
29
- ]);
30
- const lines = stdout.split('\n').filter(line => line.length > 0);
31
- cache.set(key, lines);
32
- return lines;
33
- } catch {
34
- return null;
35
- }
36
- }
@@ -1,98 +0,0 @@
1
- interface ListModelsOptions {
2
- provider: string;
3
- apiKey?: string;
4
- baseUrl?: string;
5
- }
6
-
7
- async function parseList(response: Response): Promise<string[]> {
8
- const data = await response.json() as Record<string, unknown>;
9
- const array =
10
- (Array.isArray(data.data) && data.data) ||
11
- (Array.isArray(data.models) && data.models) ||
12
- (Array.isArray(data.items) && data.items) ||
13
- [];
14
-
15
- const models = array
16
- .map(item => {
17
- if (typeof item === 'string') return item;
18
- if (item && typeof item === 'object') {
19
- const obj = item as Record<string, unknown>;
20
- if (typeof obj.id === 'string') return obj.id;
21
- if (typeof obj.name === 'string') return obj.name;
22
- }
23
- return null;
24
- })
25
- .filter((value): value is string => typeof value === 'string');
26
-
27
- return models;
28
- }
29
-
30
- export async function listModels(options: ListModelsOptions): Promise<string[]> {
31
- const provider = options.provider;
32
- const apiKey = options.apiKey || '';
33
-
34
- if (!apiKey) {
35
- throw new Error(`No API key configured for ${provider}`);
36
- }
37
-
38
- if (provider === 'anthropic') {
39
- const res = await fetch('https://api.anthropic.com/v1/models', {
40
- headers: {
41
- 'x-api-key': apiKey,
42
- 'anthropic-version': '2023-06-01'
43
- }
44
- });
45
- if (!res.ok) {
46
- throw new Error(`Anthropic error (${res.status})`);
47
- }
48
- return parseList(res);
49
- }
50
-
51
- if (provider === 'openai') {
52
- const res = await fetch('https://api.openai.com/v1/models', {
53
- headers: {
54
- Authorization: `Bearer ${apiKey}`
55
- }
56
- });
57
- if (!res.ok) {
58
- throw new Error(`OpenAI error (${res.status})`);
59
- }
60
- return parseList(res);
61
- }
62
-
63
- if (provider === 'openai_compatible') {
64
- const baseUrl = options.baseUrl || 'https://api.openai.com/v1';
65
- const res = await fetch(`${baseUrl.replace(/\/$/, '')}/models`, {
66
- headers: {
67
- Authorization: `Bearer ${apiKey}`
68
- }
69
- });
70
- if (!res.ok) {
71
- throw new Error(`OpenAI-compatible error (${res.status})`);
72
- }
73
- return parseList(res);
74
- }
75
-
76
- if (provider === 'gemini') {
77
- const res = await fetch(`https://generativelanguage.googleapis.com/v1beta/models?key=${apiKey}`);
78
- if (!res.ok) {
79
- throw new Error(`Gemini error (${res.status})`);
80
- }
81
- return parseList(res);
82
- }
83
-
84
- if (provider === 'inception') {
85
- const baseUrl = options.baseUrl || 'https://api.inception.ai/v1';
86
- const res = await fetch(`${baseUrl.replace(/\/$/, '')}/models`, {
87
- headers: {
88
- Authorization: `Bearer ${apiKey}`
89
- }
90
- });
91
- if (!res.ok) {
92
- throw new Error(`Inception error (${res.status})`);
93
- }
94
- return parseList(res);
95
- }
96
-
97
- throw new Error(`Unknown provider: ${provider}`);
98
- }
@@ -1,173 +0,0 @@
1
- import { readdir } from 'fs/promises';
2
- import { resolve, dirname, basename } from 'path';
3
- import { homedir } from 'os';
4
-
5
- /**
6
- * Get path completions for tab autocomplete
7
- */
8
- export async function getPathCompletions(
9
- partial: string,
10
- cwd: string
11
- ): Promise<string[]> {
12
- // Expand tilde
13
- let expandedPartial = partial;
14
- if (partial.startsWith('~/')) {
15
- expandedPartial = resolve(homedir(), partial.slice(2));
16
- } else if (partial === '~') {
17
- expandedPartial = homedir();
18
- }
19
-
20
- // Resolve the path relative to cwd
21
- const fullPath = resolve(cwd, expandedPartial);
22
-
23
- // Determine the directory to list and the prefix to match
24
- let dirToList: string;
25
- let prefix: string;
26
-
27
- try {
28
- const { stat } = await import('fs/promises');
29
- const stats = await stat(fullPath);
30
- if (stats.isDirectory()) {
31
- // If it's a directory, list its contents
32
- dirToList = fullPath;
33
- prefix = '';
34
- } else {
35
- // It's a file, no completions
36
- return [];
37
- }
38
- } catch {
39
- // Path doesn't exist, so complete the last component
40
- dirToList = dirname(fullPath);
41
- prefix = basename(fullPath).toLowerCase();
42
- }
43
-
44
- try {
45
- const entries = await readdir(dirToList, { withFileTypes: true });
46
- const matches = entries
47
- .filter(entry => entry.name.toLowerCase().startsWith(prefix))
48
- .map(entry => {
49
- const name = entry.name;
50
- const suffix = entry.isDirectory() ? '/' : '';
51
- return name + suffix;
52
- })
53
- .sort();
54
-
55
- return matches;
56
- } catch {
57
- return [];
58
- }
59
- }
60
-
61
- /**
62
- * Complete a partial path, returning the completed path or null if no unique completion
63
- */
64
- export async function completePath(
65
- partial: string,
66
- cwd: string
67
- ): Promise<{ completed: string; isDirectory: boolean; alternatives: string[] } | null> {
68
- // Handle empty partial - list cwd contents
69
- if (!partial || partial.trim() === '') {
70
- try {
71
- const entries = await readdir(cwd, { withFileTypes: true });
72
- const matches = entries.filter(e => !e.name.startsWith('.')).sort();
73
- if (matches.length === 0) return null;
74
- if (matches.length === 1) {
75
- const entry = matches[0];
76
- const suffix = entry.isDirectory() ? '/' : '';
77
- return { completed: entry.name + suffix, isDirectory: entry.isDirectory(), alternatives: [] };
78
- }
79
- const alternatives = matches.map(e => e.name + (e.isDirectory() ? '/' : ''));
80
- return { completed: '', isDirectory: false, alternatives };
81
- } catch {
82
- return null;
83
- }
84
- }
85
-
86
- // Expand tilde for internal processing
87
- let expandedPartial = partial;
88
- let tildePrefix = '';
89
- if (partial.startsWith('~/')) {
90
- expandedPartial = resolve(homedir(), partial.slice(2));
91
- tildePrefix = '~/';
92
- } else if (partial === '~') {
93
- return { completed: '~/', isDirectory: true, alternatives: [] };
94
- }
95
-
96
- const fullPath = resolve(cwd, expandedPartial);
97
- let dirToList: string;
98
- let prefix: string;
99
- let basePath: string;
100
-
101
- try {
102
- const { stat } = await import('fs/promises');
103
- const stats = await stat(fullPath);
104
- if (stats.isDirectory()) {
105
- // Already a complete directory, append / if not present
106
- if (!partial.endsWith('/')) {
107
- return { completed: partial + '/', isDirectory: true, alternatives: [] };
108
- }
109
- // List directory contents for alternatives
110
- dirToList = fullPath;
111
- prefix = '';
112
- basePath = partial;
113
- } else {
114
- // Complete file path
115
- return { completed: partial, isDirectory: false, alternatives: [] };
116
- }
117
- } catch {
118
- // Path doesn't exist, complete the last component
119
- dirToList = dirname(fullPath);
120
- prefix = basename(fullPath).toLowerCase();
121
-
122
- // Calculate basePath - the directory portion to preserve
123
- if (tildePrefix) {
124
- const afterTilde = expandedPartial.slice(homedir().length + 1);
125
- const dirPart = dirname(afterTilde);
126
- basePath = dirPart === '.' ? tildePrefix : tildePrefix + dirPart + '/';
127
- } else {
128
- const dirPart = dirname(partial);
129
- // Don't add ./ prefix for simple filenames
130
- basePath = (dirPart === '.' || dirPart === '') ? '' : dirPart + '/';
131
- }
132
- }
133
-
134
- try {
135
- const entries = await readdir(dirToList, { withFileTypes: true });
136
- const matches = entries
137
- .filter(entry => entry.name.toLowerCase().startsWith(prefix))
138
- .sort();
139
-
140
- if (matches.length === 0) {
141
- return null;
142
- }
143
-
144
- if (matches.length === 1) {
145
- const entry = matches[0];
146
- const suffix = entry.isDirectory() ? '/' : '';
147
- const completed = basePath + entry.name + suffix;
148
- return { completed, isDirectory: entry.isDirectory(), alternatives: [] };
149
- }
150
-
151
- // Find common prefix among all matches
152
- const names = matches.map(e => e.name);
153
- let commonPrefix = names[0];
154
- for (const name of names.slice(1)) {
155
- let i = 0;
156
- while (i < commonPrefix.length && i < name.length && commonPrefix[i] === name[i]) {
157
- i++;
158
- }
159
- commonPrefix = commonPrefix.slice(0, i);
160
- }
161
-
162
- const alternatives = matches.map(e => e.name + (e.isDirectory() ? '/' : ''));
163
- const completed = basePath + commonPrefix;
164
-
165
- return {
166
- completed: completed.length > partial.length ? completed : partial,
167
- isDirectory: false,
168
- alternatives
169
- };
170
- } catch {
171
- return null;
172
- }
173
- }
@@ -1,99 +0,0 @@
1
- import { homedir } from 'os';
2
- import { basename, dirname } from 'path';
3
-
4
- /**
5
- * Shorten a path for display:
6
- * - Replace home directory with ~
7
- * - Truncate middle of long paths
8
- * - Show basename prominently
9
- */
10
- export function shortenPath(path: string, maxLength = 60): string {
11
- if (!path) return '';
12
-
13
- // Replace home directory with ~
14
- const home = homedir();
15
- let shortened = path;
16
- if (path.startsWith(home)) {
17
- shortened = '~' + path.slice(home.length);
18
- }
19
-
20
- // If short enough, return as-is
21
- if (shortened.length <= maxLength) {
22
- return shortened;
23
- }
24
-
25
- // Truncate middle, keeping start and end
26
- const base = basename(shortened);
27
- const dir = dirname(shortened);
28
-
29
- // Always show at least the basename
30
- if (base.length >= maxLength - 4) {
31
- return '…' + base.slice(-(maxLength - 1));
32
- }
33
-
34
- // Show as much of the directory as we can
35
- const availableForDir = maxLength - base.length - 2; // 2 for /…
36
- if (availableForDir < 10) {
37
- return '…/' + base;
38
- }
39
-
40
- // Split dir and keep start + end
41
- const startLen = Math.floor(availableForDir * 0.4);
42
- const endLen = availableForDir - startLen - 1; // 1 for …
43
-
44
- const dirStart = dir.slice(0, startLen);
45
- const dirEnd = dir.slice(-endLen);
46
-
47
- return `${dirStart}…${dirEnd}/${base}`;
48
- }
49
-
50
- /**
51
- * Format a path for trace display - very compact
52
- */
53
- export function formatTracePath(path: string): string {
54
- if (!path) return '""';
55
-
56
- const home = homedir();
57
- let formatted = path;
58
-
59
- // Replace home with ~
60
- if (path.startsWith(home)) {
61
- formatted = '~' + path.slice(home.length);
62
- }
63
-
64
- // For very long paths, just show basename with hint
65
- if (formatted.length > 50) {
66
- const base = basename(formatted);
67
- const dir = dirname(formatted);
68
- // Show abbreviated dir + full basename
69
- if (dir.length > 30) {
70
- const dirParts = dir.split('/').filter(Boolean);
71
- if (dirParts.length > 3) {
72
- const abbreviated = dirParts.slice(0, 2).join('/') + '/…/' + dirParts.slice(-1).join('/');
73
- return abbreviated + '/' + base;
74
- }
75
- }
76
- }
77
-
78
- return formatted;
79
- }
80
-
81
- /**
82
- * Format bytes in human readable form
83
- */
84
- export function formatBytes(bytes: number): string {
85
- if (bytes < 1024) return `${bytes} bytes`;
86
- if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
87
- return `${(bytes / 1024 / 1024).toFixed(1)} MB`;
88
- }
89
-
90
- /**
91
- * Format duration in human readable form
92
- */
93
- export function formatDuration(ms: number): string {
94
- if (ms < 1000) return `${ms}ms`;
95
- if (ms < 60000) return `${(ms / 1000).toFixed(1)}s`;
96
- const mins = Math.floor(ms / 60000);
97
- const secs = Math.round((ms % 60000) / 1000);
98
- return `${mins}m${secs}s`;
99
- }
@@ -1,72 +0,0 @@
1
- import { exec, ExecException } from 'child_process';
2
- import { promisify } from 'util';
3
- import { resolve } from 'path';
4
- import { stat } from 'fs/promises';
5
-
6
- import type { ShellResult } from '../agent/commands/types.js';
7
-
8
- const execAsync = promisify(exec);
9
-
10
- const MAX_BUFFER = 1024 * 1024;
11
- const MAX_OUTPUT = 10000;
12
- const DEFAULT_TIMEOUT_MS = 30000;
13
- const DANGEROUS = ['rm -rf /', 'mkfs', ':(){:|:&};:'];
14
-
15
- export async function runShellCommand(command: string, cwd: string): Promise<ShellResult> {
16
- if (DANGEROUS.some(entry => command.includes(entry))) {
17
- throw new Error('Command rejected for safety reasons');
18
- }
19
-
20
- const resolvedCwd = resolve(cwd);
21
-
22
- try {
23
- const { stdout, stderr } = await execAsync(command, {
24
- cwd: resolvedCwd,
25
- timeout: DEFAULT_TIMEOUT_MS,
26
- maxBuffer: MAX_BUFFER
27
- });
28
- const clippedStdout = stdout.slice(0, MAX_OUTPUT);
29
- const clippedStderr = stderr.slice(0, MAX_OUTPUT);
30
- return {
31
- command,
32
- cwd: resolvedCwd,
33
- stdout: clippedStdout,
34
- stderr: clippedStderr,
35
- exitCode: 0,
36
- truncated: stdout.length > MAX_OUTPUT || stderr.length > MAX_OUTPUT
37
- };
38
- } catch (err) {
39
- const error = err as ExecException & { stdout?: string; stderr?: string; code?: number };
40
- const stdout = String(error.stdout || '');
41
- const stderr = String(error.stderr || error.message || '');
42
- const clippedStdout = stdout.slice(0, MAX_OUTPUT);
43
- const clippedStderr = stderr.slice(0, MAX_OUTPUT);
44
- return {
45
- command,
46
- cwd: resolvedCwd,
47
- stdout: clippedStdout,
48
- stderr: clippedStderr,
49
- exitCode: typeof error.code === 'number' ? error.code : 1,
50
- truncated: stdout.length > MAX_OUTPUT || stderr.length > MAX_OUTPUT
51
- };
52
- }
53
- }
54
-
55
- export async function resolveWorkingDir(current: string, target?: string): Promise<string> {
56
- const base = current || process.cwd();
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;
67
- const info = await stat(next);
68
- if (!info.isDirectory()) {
69
- throw new Error('Not a directory');
70
- }
71
- return next;
72
- }
@@ -1 +0,0 @@
1
- export const SPINNER_FRAMES = ['-', '\\', '|', '/'];
@@ -1,23 +0,0 @@
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
- }