agentgui 1.0.236 → 1.0.238
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/gmgui.cjs +15 -5
- package/lib/claude-runner.js +17 -5
- package/lib/speech.js +16 -5
- package/package.json +1 -1
- package/server.js +25 -9
- package/static/js/conversations.js +25 -10
- package/static/js/streaming-renderer.js +33 -18
- package/telemetry-id +1 -0
package/bin/gmgui.cjs
CHANGED
|
@@ -16,19 +16,29 @@ async function gmgui(args = []) {
|
|
|
16
16
|
const installer = 'npm';
|
|
17
17
|
|
|
18
18
|
// Ensure dependencies are installed only if node_modules is missing
|
|
19
|
-
// Skip this for bunx which
|
|
19
|
+
// Skip this for bunx/npx which manage dependencies independently
|
|
20
20
|
const nodeModulesPath = path.join(projectRoot, 'node_modules');
|
|
21
|
-
const
|
|
21
|
+
const execPath = process.env.npm_execpath || '';
|
|
22
|
+
const isBunx = execPath.includes('bun') || process.env.BUN_INSTALL;
|
|
23
|
+
const isNpx = execPath.includes('npx') || process.env._.includes('npx');
|
|
24
|
+
|
|
25
|
+
// Also skip if running from temp/cache directory (bunx/npm cache)
|
|
26
|
+
const isFromCache = projectRoot.includes('node_modules') &&
|
|
27
|
+
(projectRoot.includes('.bun') || projectRoot.includes('_npx') || projectRoot.includes('npm-cache'));
|
|
22
28
|
|
|
23
|
-
if (!isBunx && !fs.existsSync(nodeModulesPath)) {
|
|
29
|
+
if (!isBunx && !isNpx && !isFromCache && !fs.existsSync(nodeModulesPath)) {
|
|
24
30
|
console.log(`Installing dependencies with ${installer}...`);
|
|
25
31
|
const installResult = spawnSync(installer, ['install'], {
|
|
26
32
|
cwd: projectRoot,
|
|
27
|
-
stdio: 'inherit'
|
|
33
|
+
stdio: 'inherit',
|
|
34
|
+
shell: true
|
|
28
35
|
});
|
|
29
|
-
if (installResult.status !== 0) {
|
|
36
|
+
if (installResult.status !== 0 && installResult.status !== null) {
|
|
30
37
|
throw new Error(`${installer} install failed with code ${installResult.status}`);
|
|
31
38
|
}
|
|
39
|
+
if (installResult.error) {
|
|
40
|
+
throw new Error(`${installer} install failed: ${installResult.error.message}`);
|
|
41
|
+
}
|
|
32
42
|
}
|
|
33
43
|
|
|
34
44
|
const port = process.env.PORT || 3000;
|
package/lib/claude-runner.js
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
import { spawn } from 'child_process';
|
|
2
2
|
|
|
3
|
+
const isWindows = process.platform === 'win32';
|
|
4
|
+
|
|
5
|
+
function getSpawnOptions(cwd, additionalOptions = {}) {
|
|
6
|
+
const options = { cwd, ...additionalOptions };
|
|
7
|
+
if (isWindows) {
|
|
8
|
+
options.shell = true;
|
|
9
|
+
}
|
|
10
|
+
return options;
|
|
11
|
+
}
|
|
12
|
+
|
|
3
13
|
/**
|
|
4
14
|
* Agent Framework
|
|
5
15
|
* Extensible registry for AI agent CLI integrations
|
|
@@ -51,7 +61,7 @@ class AgentRunner {
|
|
|
51
61
|
} = config;
|
|
52
62
|
|
|
53
63
|
const args = this.buildArgs(prompt, config);
|
|
54
|
-
const proc = spawn(this.command, args,
|
|
64
|
+
const proc = spawn(this.command, args, getSpawnOptions(cwd));
|
|
55
65
|
|
|
56
66
|
if (config.onPid) {
|
|
57
67
|
try { config.onPid(proc.pid); } catch (e) {}
|
|
@@ -211,7 +221,7 @@ class AgentRunner {
|
|
|
211
221
|
const baseArgs = this.requiresAdapter && this.adapterCommand ? this.adapterArgs : this.buildArgs(prompt, config);
|
|
212
222
|
const args = [...baseArgs];
|
|
213
223
|
|
|
214
|
-
const proc = spawn(cmd, args,
|
|
224
|
+
const proc = spawn(cmd, args, getSpawnOptions(cwd));
|
|
215
225
|
|
|
216
226
|
if (config.onPid) {
|
|
217
227
|
try { config.onPid(proc.pid); } catch (e) {}
|
|
@@ -474,13 +484,15 @@ class AgentRegistry {
|
|
|
474
484
|
|
|
475
485
|
listACPAvailable() {
|
|
476
486
|
const { spawnSync } = require('child_process');
|
|
487
|
+
const isWindows = process.platform === 'win32';
|
|
477
488
|
return this.list().filter(agent => {
|
|
478
489
|
try {
|
|
479
|
-
const
|
|
490
|
+
const whichCmd = isWindows ? 'where' : 'which';
|
|
491
|
+
const which = spawnSync(whichCmd, [agent.command], { encoding: 'utf-8', timeout: 3000 });
|
|
480
492
|
if (which.status !== 0) return false;
|
|
481
|
-
const binPath = (which.stdout || '').trim();
|
|
493
|
+
const binPath = (which.stdout || '').trim().split('\n')[0].trim();
|
|
482
494
|
if (!binPath) return false;
|
|
483
|
-
const check = spawnSync(binPath, ['--version'], { encoding: 'utf-8', timeout: 10000 });
|
|
495
|
+
const check = spawnSync(binPath, ['--version'], { encoding: 'utf-8', timeout: 10000, shell: isWindows });
|
|
484
496
|
return check.status === 0 && (check.stdout || '').trim().length > 0;
|
|
485
497
|
} catch {
|
|
486
498
|
return false;
|
package/lib/speech.js
CHANGED
|
@@ -29,7 +29,14 @@ const POCKET_TTS_VOICES = [
|
|
|
29
29
|
const PREDEFINED_IDS = new Set(POCKET_TTS_VOICES.filter(v => v.id !== 'default').map(v => v.id));
|
|
30
30
|
const POCKET_PORT = 8787;
|
|
31
31
|
|
|
32
|
-
|
|
32
|
+
function safeGetVoices(extraDirs) {
|
|
33
|
+
if (typeof serverTTS.getVoices === 'function') {
|
|
34
|
+
return serverTTS.getVoices(extraDirs || []);
|
|
35
|
+
}
|
|
36
|
+
return [];
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const needsPatch = !safeGetVoices(EXTRA_VOICE_DIRS).some(v => v.id === 'alba' && !v.isCustom);
|
|
33
40
|
|
|
34
41
|
function synthesizeDirect(text, voiceId) {
|
|
35
42
|
const voicePath = serverTTS.findVoiceFile(voiceId, EXTRA_VOICE_DIRS);
|
|
@@ -96,7 +103,7 @@ function synthesizeStream(text, voiceId) {
|
|
|
96
103
|
}
|
|
97
104
|
|
|
98
105
|
function getVoices() {
|
|
99
|
-
const upstream =
|
|
106
|
+
const upstream = safeGetVoices(EXTRA_VOICE_DIRS);
|
|
100
107
|
const custom = upstream.filter(v => v.isCustom);
|
|
101
108
|
return [...POCKET_TTS_VOICES, ...custom];
|
|
102
109
|
}
|
|
@@ -116,6 +123,10 @@ function getStatus() {
|
|
|
116
123
|
}
|
|
117
124
|
|
|
118
125
|
function preloadTTS() {
|
|
126
|
+
if (typeof serverTTS.findVoiceFile !== 'function' || typeof serverTTS.start !== 'function') {
|
|
127
|
+
console.log('[TTS] pocket-tts functions not available');
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
119
130
|
const defaultVoice = serverTTS.findVoiceFile('custom_cleetus', EXTRA_VOICE_DIRS) || '/config/voices/cleetus.wav';
|
|
120
131
|
const voicePath = fs.existsSync(defaultVoice) ? defaultVoice : null;
|
|
121
132
|
serverTTS.start(voicePath, {}).then(ok => {
|
|
@@ -127,15 +138,15 @@ function preloadTTS() {
|
|
|
127
138
|
}
|
|
128
139
|
|
|
129
140
|
function ttsCacheKey(text, voiceId) {
|
|
130
|
-
return serverTTS.ttsCacheKey(text, voiceId);
|
|
141
|
+
return typeof serverTTS.ttsCacheKey === 'function' ? serverTTS.ttsCacheKey(text, voiceId) : null;
|
|
131
142
|
}
|
|
132
143
|
|
|
133
144
|
function ttsCacheGet(key) {
|
|
134
|
-
return serverTTS.ttsCacheGet(key);
|
|
145
|
+
return typeof serverTTS.ttsCacheGet === 'function' ? serverTTS.ttsCacheGet(key) : null;
|
|
135
146
|
}
|
|
136
147
|
|
|
137
148
|
function splitSentences(text) {
|
|
138
|
-
return serverTTS.splitSentences(text);
|
|
149
|
+
return typeof serverTTS.splitSentences === 'function' ? serverTTS.splitSentences(text) : [text];
|
|
139
150
|
}
|
|
140
151
|
|
|
141
152
|
export { transcribe, synthesize, synthesizeStream, getSTT, getStatus, getVoices, preloadTTS, ttsCacheKey, ttsCacheGet, splitSentences };
|
package/package.json
CHANGED
package/server.js
CHANGED
|
@@ -38,9 +38,17 @@ const modelDownloadState = {
|
|
|
38
38
|
async function ensureModelsDownloaded() {
|
|
39
39
|
const { createRequire: cr } = await import('module');
|
|
40
40
|
const r = cr(import.meta.url);
|
|
41
|
-
const
|
|
41
|
+
const models = r('sttttsmodels');
|
|
42
|
+
const checkAllFilesExist = models.checkAllFilesExist;
|
|
43
|
+
const downloadModels = models.downloadModels;
|
|
42
44
|
|
|
43
|
-
if (checkAllFilesExist()) {
|
|
45
|
+
if (checkAllFilesExist && checkAllFilesExist()) {
|
|
46
|
+
modelDownloadState.complete = true;
|
|
47
|
+
return true;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (!downloadModels) {
|
|
51
|
+
console.log('[MODELS] Download function not available, skipping');
|
|
44
52
|
modelDownloadState.complete = true;
|
|
45
53
|
return true;
|
|
46
54
|
}
|
|
@@ -1260,7 +1268,8 @@ const server = http.createServer(async (req, res) => {
|
|
|
1260
1268
|
delete childEnv.PORT;
|
|
1261
1269
|
delete childEnv.BASE_URL;
|
|
1262
1270
|
delete childEnv.HOT_RELOAD;
|
|
1263
|
-
const
|
|
1271
|
+
const isWindows = os.platform() === 'win32';
|
|
1272
|
+
const child = spawn('npm', ['run', script], { cwd: wd, stdio: ['ignore', 'pipe', 'pipe'], detached: true, env: childEnv, shell: isWindows });
|
|
1264
1273
|
activeScripts.set(conversationId, { process: child, script, startTime: Date.now() });
|
|
1265
1274
|
broadcastSync({ type: 'script_started', conversationId, script, timestamp: Date.now() });
|
|
1266
1275
|
|
|
@@ -1508,7 +1517,8 @@ const server = http.createServer(async (req, res) => {
|
|
|
1508
1517
|
|
|
1509
1518
|
const child = spawn(authCmd.cmd, authCmd.args, {
|
|
1510
1519
|
stdio: ['pipe', 'pipe', 'pipe'],
|
|
1511
|
-
env: { ...process.env, FORCE_COLOR: '1' }
|
|
1520
|
+
env: { ...process.env, FORCE_COLOR: '1' },
|
|
1521
|
+
shell: os.platform() === 'win32'
|
|
1512
1522
|
});
|
|
1513
1523
|
activeScripts.set(conversationId, { process: child, script: 'auth-' + agentId, startTime: Date.now() });
|
|
1514
1524
|
broadcastSync({ type: 'script_started', conversationId, script: 'auth-' + agentId, agentId, timestamp: Date.now() });
|
|
@@ -1721,12 +1731,14 @@ const server = http.createServer(async (req, res) => {
|
|
|
1721
1731
|
return;
|
|
1722
1732
|
}
|
|
1723
1733
|
try {
|
|
1724
|
-
|
|
1734
|
+
const isWindows = os.platform() === 'win32';
|
|
1735
|
+
execSync('git clone https://github.com/' + repo + '.git', {
|
|
1725
1736
|
cwd: cloneDir,
|
|
1726
1737
|
encoding: 'utf-8',
|
|
1727
1738
|
timeout: 120000,
|
|
1728
1739
|
stdio: ['pipe', 'pipe', 'pipe'],
|
|
1729
|
-
env: { ...process.env, GIT_TERMINAL_PROMPT: '0' }
|
|
1740
|
+
env: { ...process.env, GIT_TERMINAL_PROMPT: '0' },
|
|
1741
|
+
shell: isWindows
|
|
1730
1742
|
});
|
|
1731
1743
|
sendJSON(req, res, 200, { ok: true, repo, path: targetPath, name: repoName });
|
|
1732
1744
|
} catch (err) {
|
|
@@ -1756,7 +1768,8 @@ const server = http.createServer(async (req, res) => {
|
|
|
1756
1768
|
|
|
1757
1769
|
if (pathOnly === '/api/git/check-remote-ownership' && req.method === 'GET') {
|
|
1758
1770
|
try {
|
|
1759
|
-
const
|
|
1771
|
+
const isWindows = os.platform() === 'win32';
|
|
1772
|
+
const result = execSync('git remote get-url origin' + (isWindows ? '' : ' 2>/dev/null'), { encoding: 'utf-8', cwd: STARTUP_CWD, shell: isWindows });
|
|
1760
1773
|
const remoteUrl = result.trim();
|
|
1761
1774
|
const statusResult = execSync('git status --porcelain', { encoding: 'utf-8', cwd: STARTUP_CWD });
|
|
1762
1775
|
const hasChanges = statusResult.trim().length > 0;
|
|
@@ -1770,7 +1783,8 @@ const server = http.createServer(async (req, res) => {
|
|
|
1770
1783
|
|
|
1771
1784
|
if (pathOnly === '/api/git/push' && req.method === 'POST') {
|
|
1772
1785
|
try {
|
|
1773
|
-
|
|
1786
|
+
const isWindows = os.platform() === 'win32';
|
|
1787
|
+
execSync('git add -A && git commit -m "Auto-commit" && git push', { encoding: 'utf-8', cwd: STARTUP_CWD, shell: isWindows });
|
|
1774
1788
|
sendJSON(req, res, 200, { success: true });
|
|
1775
1789
|
} catch (err) {
|
|
1776
1790
|
sendJSON(req, res, 500, { error: err.message });
|
|
@@ -1784,7 +1798,9 @@ const server = http.createServer(async (req, res) => {
|
|
|
1784
1798
|
const expandedPath = decodedPath.startsWith('~') ?
|
|
1785
1799
|
decodedPath.replace('~', os.homedir()) : decodedPath;
|
|
1786
1800
|
const normalizedPath = path.normalize(expandedPath);
|
|
1787
|
-
|
|
1801
|
+
const isWindows = os.platform() === 'win32';
|
|
1802
|
+
const isAbsolute = isWindows ? /^[A-Za-z]:[\\\/]/.test(normalizedPath) : normalizedPath.startsWith('/');
|
|
1803
|
+
if (!isAbsolute || normalizedPath.includes('..')) {
|
|
1788
1804
|
res.writeHead(403); res.end('Forbidden'); return;
|
|
1789
1805
|
}
|
|
1790
1806
|
try {
|
|
@@ -4,6 +4,15 @@
|
|
|
4
4
|
* Includes folder browser for selecting working directory on new conversation
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
+
function pathSplit(p) {
|
|
8
|
+
return p.split(/[\/\\]/).filter(Boolean);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function pathBasename(p) {
|
|
12
|
+
const parts = pathSplit(p);
|
|
13
|
+
return parts.length ? parts.pop() : '';
|
|
14
|
+
}
|
|
15
|
+
|
|
7
16
|
class ConversationManager {
|
|
8
17
|
constructor() {
|
|
9
18
|
this.conversations = [];
|
|
@@ -176,7 +185,8 @@ class ConversationManager {
|
|
|
176
185
|
li.innerHTML = `<span class="folder-list-item-icon">📁</span><span class="folder-list-item-name">${this.escapeHtml(folder.name)}</span>`;
|
|
177
186
|
li.addEventListener('click', () => {
|
|
178
187
|
const expandedBase = dirPath === '~' ? this.folderBrowser.homePath : dirPath;
|
|
179
|
-
const
|
|
188
|
+
const separator = expandedBase.includes('\\') ? '\\' : '/';
|
|
189
|
+
const newPath = expandedBase + separator + folder.name;
|
|
180
190
|
this.loadFolders(newPath);
|
|
181
191
|
});
|
|
182
192
|
this.folderBrowser.listEl.appendChild(li);
|
|
@@ -189,26 +199,31 @@ class ConversationManager {
|
|
|
189
199
|
|
|
190
200
|
getParentPath(dirPath) {
|
|
191
201
|
const expanded = dirPath === '~' ? this.folderBrowser.homePath : dirPath;
|
|
192
|
-
const parts = expanded
|
|
193
|
-
if (parts.length <= 1)
|
|
202
|
+
const parts = pathSplit(expanded);
|
|
203
|
+
if (parts.length <= 1) {
|
|
204
|
+
const separator = expanded.includes('\\') ? '\\' : '/';
|
|
205
|
+
return separator;
|
|
206
|
+
}
|
|
194
207
|
parts.pop();
|
|
195
|
-
|
|
208
|
+
const separator = expanded.includes('\\') ? '\\' : '/';
|
|
209
|
+
return separator + parts.join(separator);
|
|
196
210
|
}
|
|
197
211
|
|
|
198
212
|
renderBreadcrumb(dirPath) {
|
|
199
213
|
if (!this.folderBrowser.breadcrumbEl) return;
|
|
200
214
|
|
|
201
215
|
const expanded = dirPath === '~' ? this.folderBrowser.homePath : dirPath;
|
|
202
|
-
const parts = expanded
|
|
216
|
+
const parts = pathSplit(expanded);
|
|
217
|
+
const separator = expanded.includes('\\') ? '\\' : '/';
|
|
203
218
|
|
|
204
219
|
let html = '';
|
|
205
|
-
html +=
|
|
220
|
+
html += `<span class="folder-breadcrumb-segment" data-path="${separator}">${separator} </span>`;
|
|
206
221
|
|
|
207
222
|
let accumulated = '';
|
|
208
223
|
for (let i = 0; i < parts.length; i++) {
|
|
209
|
-
accumulated +=
|
|
224
|
+
accumulated += separator + parts[i];
|
|
210
225
|
const isLast = i === parts.length - 1;
|
|
211
|
-
html +=
|
|
226
|
+
html += `<span class="folder-breadcrumb-separator">${separator}</span>`;
|
|
212
227
|
html += `<span class="folder-breadcrumb-segment${isLast ? '' : ''}" data-path="${this.escapeHtml(accumulated)}">${this.escapeHtml(parts[i])}</span>`;
|
|
213
228
|
}
|
|
214
229
|
|
|
@@ -227,7 +242,7 @@ class ConversationManager {
|
|
|
227
242
|
const expanded = currentPath === '~' ? this.folderBrowser.homePath : currentPath;
|
|
228
243
|
this.closeFolderBrowser();
|
|
229
244
|
|
|
230
|
-
const dirName = expanded
|
|
245
|
+
const dirName = pathBasename(expanded) || 'root';
|
|
231
246
|
window.dispatchEvent(new CustomEvent('create-new-conversation', {
|
|
232
247
|
detail: { workingDirectory: expanded, title: dirName }
|
|
233
248
|
}));
|
|
@@ -398,7 +413,7 @@ class ConversationManager {
|
|
|
398
413
|
const timestamp = conv.created_at ? new Date(conv.created_at).toLocaleDateString() : 'Unknown';
|
|
399
414
|
const agent = this.getAgentDisplayName(conv.agentType);
|
|
400
415
|
const modelLabel = conv.model ? ` (${conv.model})` : '';
|
|
401
|
-
const wd = conv.workingDirectory ? conv.workingDirectory
|
|
416
|
+
const wd = conv.workingDirectory ? pathBasename(conv.workingDirectory) : '';
|
|
402
417
|
const metaParts = [agent + modelLabel, timestamp];
|
|
403
418
|
if (wd) metaParts.push(wd);
|
|
404
419
|
|
|
@@ -4,6 +4,15 @@
|
|
|
4
4
|
* for Claude Code streaming execution display
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
+
function pathSplit(p) {
|
|
8
|
+
return p.split(/[\/\\]/).filter(Boolean);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function pathBasename(p) {
|
|
12
|
+
const parts = pathSplit(p);
|
|
13
|
+
return parts.length ? parts.pop() : '';
|
|
14
|
+
}
|
|
15
|
+
|
|
7
16
|
class StreamingRenderer {
|
|
8
17
|
constructor(config = {}) {
|
|
9
18
|
// Configuration
|
|
@@ -523,7 +532,7 @@ class StreamingRenderer {
|
|
|
523
532
|
*/
|
|
524
533
|
renderFilePath(filePath) {
|
|
525
534
|
if (!filePath) return '';
|
|
526
|
-
const parts = filePath
|
|
535
|
+
const parts = pathSplit(filePath);
|
|
527
536
|
const fileName = parts.pop();
|
|
528
537
|
const dir = parts.join('/');
|
|
529
538
|
return `<div class="tool-param-file"><span class="file-icon">📄</span>${dir ? `<span class="file-dir">${this.escapeHtml(dir)}/</span>` : ''}<span class="file-name">${this.escapeHtml(fileName)}</span></div>`;
|
|
@@ -660,18 +669,16 @@ class StreamingRenderer {
|
|
|
660
669
|
getToolUseTitle(toolName, input) {
|
|
661
670
|
const normalizedName = toolName.replace(/^mcp__[^_]+__/, '');
|
|
662
671
|
if (normalizedName === 'Edit' && input.file_path) {
|
|
663
|
-
const parts = input.file_path
|
|
672
|
+
const parts = pathSplit(input.file_path);
|
|
664
673
|
const fileName = parts.pop();
|
|
665
674
|
const dir = parts.slice(-2).join('/');
|
|
666
675
|
return dir ? `${dir}/${fileName}` : fileName;
|
|
667
676
|
}
|
|
668
677
|
if (normalizedName === 'Read' && input.file_path) {
|
|
669
|
-
|
|
670
|
-
return parts.pop();
|
|
678
|
+
return pathBasename(input.file_path);
|
|
671
679
|
}
|
|
672
680
|
if (normalizedName === 'Write' && input.file_path) {
|
|
673
|
-
|
|
674
|
-
return parts.pop();
|
|
681
|
+
return pathBasename(input.file_path);
|
|
675
682
|
}
|
|
676
683
|
if (normalizedName === 'Bash' || normalizedName === 'bash') {
|
|
677
684
|
const cmd = input.command || input.commands || '';
|
|
@@ -684,7 +691,7 @@ class StreamingRenderer {
|
|
|
684
691
|
try { return new URL(input.url).hostname; } catch (e) { return input.url.substring(0, 40); }
|
|
685
692
|
}
|
|
686
693
|
if (normalizedName === 'WebSearch' && input.query) return input.query.substring(0, 50);
|
|
687
|
-
if (input.file_path) return input.file_path
|
|
694
|
+
if (input.file_path) return pathBasename(input.file_path);
|
|
688
695
|
if (input.command) {
|
|
689
696
|
const c = typeof input.command === 'string' ? input.command : JSON.stringify(input.command);
|
|
690
697
|
return c.length > 50 ? c.substring(0, 47) + '...' : c;
|
|
@@ -748,11 +755,14 @@ class StreamingRenderer {
|
|
|
748
755
|
}
|
|
749
756
|
|
|
750
757
|
const lines = trimmed.split('\n');
|
|
751
|
-
const allFilePaths = lines.length > 1 && lines.every(l =>
|
|
758
|
+
const allFilePaths = lines.length > 1 && lines.every(l => {
|
|
759
|
+
const t = l.trim();
|
|
760
|
+
return t === '' || t.startsWith('/') || /^[A-Za-z]:[\\\/]/.test(t);
|
|
761
|
+
});
|
|
752
762
|
if (allFilePaths && lines.filter(l => l.trim()).length > 0) {
|
|
753
763
|
const fileHtml = lines.filter(l => l.trim()).map(l => {
|
|
754
764
|
const p = l.trim();
|
|
755
|
-
const parts = p
|
|
765
|
+
const parts = pathSplit(p);
|
|
756
766
|
const name = parts.pop();
|
|
757
767
|
const dir = parts.join('/');
|
|
758
768
|
return `<div style="display:flex;align-items:center;gap:0.375rem;padding:0.1875rem 0;font-family:'Monaco','Menlo','Ubuntu Mono',monospace;font-size:0.75rem"><span style="opacity:0.5">📄</span><span style="color:var(--color-text-secondary)">${this.escapeHtml(dir)}/</span><span style="font-weight:600">${this.escapeHtml(name)}</span></div>`;
|
|
@@ -785,7 +795,8 @@ class StreamingRenderer {
|
|
|
785
795
|
const lines = data.split('\n').length;
|
|
786
796
|
return `<div style="font-family:'Monaco','Menlo','Ubuntu Mono',monospace;font-size:0.75rem;white-space:pre-wrap;word-break:break-all;max-height:200px;overflow-y:auto;background:var(--color-bg-code);color:#d1d5db;padding:0.5rem;border-radius:0.375rem;line-height:1.5">${this.escapeHtml(data.substring(0, 1000))}${data.length > 1000 ? '\n... (' + (data.length - 1000) + ' more chars, ' + lines + ' lines)' : ''}</div>`;
|
|
787
797
|
}
|
|
788
|
-
|
|
798
|
+
const looksLikePath = data.startsWith('/') || /^[A-Za-z]:[\\\/]/.test(data);
|
|
799
|
+
if (looksLikePath && !data.includes(' ') && data.includes('.')) return this.renderFilePath(data);
|
|
789
800
|
return `<span style="color:var(--color-text-primary)">${this.escapeHtml(data)}</span>`;
|
|
790
801
|
}
|
|
791
802
|
|
|
@@ -986,11 +997,14 @@ class StreamingRenderer {
|
|
|
986
997
|
return html;
|
|
987
998
|
}
|
|
988
999
|
|
|
989
|
-
const allFilePaths = lines.length > 1 && lines.every(l =>
|
|
1000
|
+
const allFilePaths = lines.length > 1 && lines.every(l => {
|
|
1001
|
+
const t = l.trim();
|
|
1002
|
+
return t === '' || t.startsWith('/') || /^[A-Za-z]:[\\\/]/.test(t);
|
|
1003
|
+
});
|
|
990
1004
|
if (allFilePaths && lines.filter(l => l.trim()).length > 0) {
|
|
991
1005
|
const fileHtml = lines.filter(l => l.trim()).map(l => {
|
|
992
1006
|
const p = l.trim();
|
|
993
|
-
const parts = p
|
|
1007
|
+
const parts = pathSplit(p);
|
|
994
1008
|
const name = parts.pop();
|
|
995
1009
|
const dir = parts.join('/');
|
|
996
1010
|
return `<div style="display:flex;align-items:center;gap:0.375rem;padding:0.1875rem 0;font-family:'Monaco','Menlo','Ubuntu Mono',monospace;font-size:0.75rem"><span style="opacity:0.5">📄</span><span style="color:var(--color-text-secondary)">${esc(dir)}/</span><span style="font-weight:600">${esc(name)}</span></div>`;
|
|
@@ -1103,15 +1117,15 @@ class StreamingRenderer {
|
|
|
1103
1117
|
|
|
1104
1118
|
static getToolTitle(toolName, input) {
|
|
1105
1119
|
const n = toolName.replace(/^mcp__[^_]+__/, '');
|
|
1106
|
-
if (n === 'Edit' && input.file_path) { const p = input.file_path
|
|
1107
|
-
if (n === 'Read' && input.file_path) return input.file_path
|
|
1108
|
-
if (n === 'Write' && input.file_path) return input.file_path
|
|
1120
|
+
if (n === 'Edit' && input.file_path) { const p = pathSplit(input.file_path); const f = p.pop(); const d = p.slice(-2).join('/'); return d ? d+'/'+f : f; }
|
|
1121
|
+
if (n === 'Read' && input.file_path) return pathBasename(input.file_path);
|
|
1122
|
+
if (n === 'Write' && input.file_path) return pathBasename(input.file_path);
|
|
1109
1123
|
if ((n === 'Bash' || n === 'bash') && (input.command || input.commands)) { const c = typeof (input.command||input.commands) === 'string' ? (input.command||input.commands) : JSON.stringify(input.command||input.commands); return c.length > 60 ? c.substring(0,57)+'...' : c; }
|
|
1110
1124
|
if (n === 'Glob' && input.pattern) return input.pattern;
|
|
1111
1125
|
if (n === 'Grep' && input.pattern) return input.pattern;
|
|
1112
1126
|
if (n === 'WebFetch' && input.url) { try { return new URL(input.url).hostname; } catch(e) { return input.url.substring(0,40); } }
|
|
1113
1127
|
if (n === 'WebSearch' && input.query) return input.query.substring(0,50);
|
|
1114
|
-
if (input.file_path) return input.file_path
|
|
1128
|
+
if (input.file_path) return pathBasename(input.file_path);
|
|
1115
1129
|
if (input.command) { const c = typeof input.command === 'string' ? input.command : JSON.stringify(input.command); return c.length > 50 ? c.substring(0,47)+'...' : c; }
|
|
1116
1130
|
if (input.query) return input.query.substring(0,50);
|
|
1117
1131
|
return '';
|
|
@@ -1134,8 +1148,9 @@ class StreamingRenderer {
|
|
|
1134
1148
|
if (data.length > 500) {
|
|
1135
1149
|
return `<div style="font-family:'Monaco','Menlo','Ubuntu Mono',monospace;font-size:0.75rem;white-space:pre-wrap;word-break:break-all;max-height:200px;overflow-y:auto;background:var(--color-bg-code);color:#d1d5db;padding:0.5rem;border-radius:0.375rem;line-height:1.5">${esc(data.substring(0, 1000))}${data.length > 1000 ? '\n... (' + (data.length - 1000) + ' more chars)' : ''}</div>`;
|
|
1136
1150
|
}
|
|
1137
|
-
|
|
1138
|
-
|
|
1151
|
+
const looksLikePath = /^[A-Za-z]:[\\\/]/.test(data) || data.startsWith('/');
|
|
1152
|
+
if (looksLikePath && !data.includes(' ') && data.includes('.')) {
|
|
1153
|
+
const parts = pathSplit(data);
|
|
1139
1154
|
const name = parts.pop();
|
|
1140
1155
|
const dir = parts.join('/');
|
|
1141
1156
|
return `<div style="display:flex;align-items:center;gap:0.375rem;font-family:'Monaco','Menlo','Ubuntu Mono',monospace;font-size:0.8rem"><span style="opacity:0.5">📄</span><span style="color:var(--color-text-secondary)">${esc(dir)}/</span><span style="font-weight:600">${esc(name)}</span></div>`;
|
package/telemetry-id
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
814cd4b3-ffd4-44c7-abbd-b2d9e757f0fc
|