claude-code-watch 0.0.14 → 0.0.15
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/package.json +1 -1
- package/public/index.html +74 -16
- package/src/parser/parser.js +56 -7
package/package.json
CHANGED
package/public/index.html
CHANGED
|
@@ -147,18 +147,27 @@ body {
|
|
|
147
147
|
|
|
148
148
|
/* ── Tree node styles ── */
|
|
149
149
|
.tree-row {
|
|
150
|
-
display: flex; align-items:
|
|
150
|
+
display: flex; align-items: flex-start;
|
|
151
|
+
}
|
|
152
|
+
.tree-content {
|
|
153
|
+
flex: 1; min-width: 0;
|
|
151
154
|
}
|
|
155
|
+
.tree-content:hover { background: rgba(255,255,255,0.05); }
|
|
156
|
+
.tree-row.selected > .tree-content { background: rgba(124,58,237,0.3); }
|
|
157
|
+
.tree-content.dim { opacity: 0.4; }
|
|
152
158
|
.tree-node {
|
|
153
|
-
|
|
159
|
+
display: flex; align-items: center;
|
|
154
160
|
padding: 3px 2px 3px 0;
|
|
155
161
|
cursor: pointer; white-space: nowrap; gap: 4px;
|
|
156
|
-
|
|
162
|
+
overflow: hidden;
|
|
157
163
|
}
|
|
158
|
-
.tree-node:hover { background: rgba(255,255,255,0.05); }
|
|
159
|
-
.tree-node.selected { background: rgba(124,58,237,0.3); }
|
|
160
|
-
.tree-node.dim { opacity: 0.4; }
|
|
161
164
|
.tree-prefix { color: var(--dim); font-size: 12px; flex-shrink: 0; letter-spacing: 0; font-family: monospace; }
|
|
165
|
+
.tree-activity {
|
|
166
|
+
font-size: 10px; color: var(--dim); white-space: nowrap;
|
|
167
|
+
overflow: hidden; text-overflow: ellipsis;
|
|
168
|
+
padding: 0 2px 2px; line-height: 1.2;
|
|
169
|
+
cursor: pointer;
|
|
170
|
+
}
|
|
162
171
|
.tree-node .ctx-pct { font-size: 10px; margin-left: 4px; flex-shrink: 0; }
|
|
163
172
|
.tree-node .ctx-pct.warn { color: var(--yellow); }
|
|
164
173
|
.tree-node .ctx-pct.danger { color: var(--red); }
|
|
@@ -249,8 +258,8 @@ body {
|
|
|
249
258
|
:root[data-theme="light"] .btn.on:hover { background: var(--purple2); color: #fff; }
|
|
250
259
|
:root[data-theme="light"] .btn.on:hover::after { background: var(--purple2); color: #fff; }
|
|
251
260
|
:root[data-theme="light"] .hljs { background: #f0f0f0 !important; }
|
|
252
|
-
:root[data-theme="light"] .tree-
|
|
253
|
-
:root[data-theme="light"] .tree-
|
|
261
|
+
:root[data-theme="light"] .tree-content:hover { background: rgba(0,0,0,0.06); }
|
|
262
|
+
:root[data-theme="light"] .tree-row.selected > .tree-content { background: rgba(124,58,237,0.2); }
|
|
254
263
|
:root[data-theme="light"] .tree-node .active-dot.off { color: #bbb; }
|
|
255
264
|
:root[data-theme="light"] #tree-resize-handle:hover,
|
|
256
265
|
:root[data-theme="light"] #tree-resize-handle.active { background: var(--purple); }
|
|
@@ -288,6 +297,7 @@ body {
|
|
|
288
297
|
<button class="btn btn-icon" onclick="selectAll()" data-tooltip="Show all sessions/agents">⊞</button>
|
|
289
298
|
<button class="btn btn-icon accent" onclick="soloSelected()" data-tooltip="Solo selected">⊙</button>
|
|
290
299
|
<button class="btn btn-icon danger" onclick="removeSelectedSession()" data-tooltip="Remove session">✕</button>
|
|
300
|
+
<button class="btn btn-icon on" id="btn-activity" onclick="toggleActivity()" data-tooltip="Toggle activity info">💬</button>
|
|
291
301
|
<span style="flex:1"></span>
|
|
292
302
|
<span id="tree-cursor-info" style="font-size:10px;color:var(--dim)"></span>
|
|
293
303
|
</div>
|
|
@@ -357,6 +367,9 @@ class LRUCache {
|
|
|
357
367
|
}
|
|
358
368
|
const seenToolIDs = new LRUCache(5000);
|
|
359
369
|
const toolNameMap = new LRUCache(2000);
|
|
370
|
+
const agentActivity = new Map(); // "sessionID:agentID" → { toolName, content }
|
|
371
|
+
const taskDescriptions = new Map(); // toolID → description string
|
|
372
|
+
const MAX_DESC_STORE = 200;
|
|
360
373
|
let filters = new Map();
|
|
361
374
|
|
|
362
375
|
let showThinking = true;
|
|
@@ -364,6 +377,7 @@ let showToolInput = true;
|
|
|
364
377
|
let showToolOutput = true;
|
|
365
378
|
let showText = true;
|
|
366
379
|
let showHook = true;
|
|
380
|
+
let showActivity = true;
|
|
367
381
|
let autoDiscovery = true;
|
|
368
382
|
|
|
369
383
|
let renderPending = false;
|
|
@@ -646,6 +660,20 @@ function pushItem(item) {
|
|
|
646
660
|
toolNameMap.set(item.toolID, item.toolName);
|
|
647
661
|
}
|
|
648
662
|
|
|
663
|
+
if (item.type === 'tool_input') {
|
|
664
|
+
// Main 代理不追踪工具调用,只显示用户 prompt
|
|
665
|
+
if (item.agentID) {
|
|
666
|
+
agentActivity.set(item.sessionID + ':' + item.agentID, { toolName: item.toolName || '', content: (item.content || '').slice(0, MAX_DESC_STORE) });
|
|
667
|
+
}
|
|
668
|
+
if (item.toolID) {
|
|
669
|
+
taskDescriptions.set(item.toolID, (item.content || '').slice(0, MAX_DESC_STORE));
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
if (item.type === 'user_text') {
|
|
674
|
+
agentActivity.set(item.sessionID + ':' + (item.agentID || ''), { toolName: '', content: (item.content || '').slice(0, MAX_DESC_STORE) });
|
|
675
|
+
}
|
|
676
|
+
|
|
649
677
|
if (item.toolID) {
|
|
650
678
|
const key = `${item.toolID}:${item.type}`;
|
|
651
679
|
if (seenToolIDs.has(key)) return;
|
|
@@ -671,6 +699,7 @@ function isItemVisible(item) {
|
|
|
671
699
|
case 'tool_output': return showToolOutput;
|
|
672
700
|
case 'text': return showText;
|
|
673
701
|
case 'hook_output': return showHook;
|
|
702
|
+
case 'user_text': return false;
|
|
674
703
|
default: return true;
|
|
675
704
|
}
|
|
676
705
|
}
|
|
@@ -694,14 +723,23 @@ function rebuildNodes() {
|
|
|
694
723
|
);
|
|
695
724
|
const lastTaskIdx = tasks.length - 1;
|
|
696
725
|
const hasTasks = tasks.length > 0;
|
|
697
|
-
|
|
726
|
+
const actKey = s.id + ':' + a.id;
|
|
727
|
+
const act = agentActivity.get(actKey);
|
|
728
|
+
treeNodes.push({
|
|
729
|
+
type: a.type, id: a.id, name: a.name, sessionID: s.id,
|
|
730
|
+
level: 1, isLast: isLastAgent && !hasTasks,
|
|
731
|
+
activityTool: act ? act.toolName : '',
|
|
732
|
+
activityDesc: act ? act.content : '',
|
|
733
|
+
});
|
|
698
734
|
for (let ti = 0; ti < tasks.length; ti++) {
|
|
699
735
|
const t = tasks[ti];
|
|
736
|
+
const tDesc = taskDescriptions.get(t.id);
|
|
700
737
|
treeNodes.push({
|
|
701
738
|
type: 'task', id: t.id, name: t.toolName,
|
|
702
739
|
sessionID: s.id, parentAgentID: t.parentAgentID,
|
|
703
740
|
outputPath: t.outputPath, isComplete: t.isComplete,
|
|
704
741
|
level: 2, isLast: isLastAgent && ti === lastTaskIdx,
|
|
742
|
+
description: tDesc || '',
|
|
705
743
|
});
|
|
706
744
|
}
|
|
707
745
|
}
|
|
@@ -736,10 +774,12 @@ function getNodeHTML(node, idx) {
|
|
|
736
774
|
const subInfo = parts.length > 0 ? ` <span style="color:#6b7280;font-size:10px">${parts.join(' · ')}</span>` : '';
|
|
737
775
|
const agentCount = node.agents ? node.agents.filter(a => a.type === 'agent').length : 0;
|
|
738
776
|
return `<div class="tree-row${selClass ? ' selected' : ''}">
|
|
739
|
-
<div class="tree-
|
|
740
|
-
<
|
|
741
|
-
|
|
742
|
-
|
|
777
|
+
<div class="tree-content" onclick="treeClick(${idx})" data-idx="${idx}">
|
|
778
|
+
<div class="tree-node">
|
|
779
|
+
<span class="tree-prefix">${treePrefix(node)}</span>${activeDot} ${node.collapsed ? '▸' : '▾'} ${esc(displayName)}
|
|
780
|
+
${node.collapsed && agentCount > 0 ? `(${esc(String(agentCount))})` : ''}
|
|
781
|
+
${subInfo}
|
|
782
|
+
</div>
|
|
743
783
|
</div>
|
|
744
784
|
<span class="tree-actions">
|
|
745
785
|
<button class="btn btn-icon accent" onclick="event.stopPropagation();selectIndex(${idx});soloSelected()" data-tooltip="Solo">⊙</button>
|
|
@@ -760,9 +800,19 @@ function getNodeHTML(node, idx) {
|
|
|
760
800
|
ctxPct = `<span class="ctx-pct ${cls}">${pct}%</span>`;
|
|
761
801
|
}
|
|
762
802
|
const activeDot = ctx && (Date.now() - ctx.lastActivity < 120000) ? '<span class="active-dot on">🟢</span>' : '<span class="active-dot off">⚪</span>';
|
|
803
|
+
const actIcon = node.type === 'main' ? '🗣' : '⚡';
|
|
804
|
+
const actText = showActivity && (node.activityTool || node.activityDesc)
|
|
805
|
+
? (node.activityTool && node.activityDesc ? `${node.activityTool}: ${node.activityDesc}` : (node.activityTool || node.activityDesc))
|
|
806
|
+
: '';
|
|
807
|
+
const activityHTML = actText
|
|
808
|
+
? `<div class="tree-activity">${actIcon} ${esc(actText)}</div>`
|
|
809
|
+
: '';
|
|
763
810
|
return `<div class="tree-row${selClass ? ' selected' : ''}">
|
|
764
|
-
<div class="tree-
|
|
811
|
+
<div class="tree-content${enabled ? '' : ' dim'}" onclick="treeClick(${idx})" data-idx="${idx}">
|
|
812
|
+
<div class="tree-node">
|
|
765
813
|
<span class="tree-prefix">${treePrefix(node)}</span>${activeDot} ${icon} ${esc(node.name || '')}${ctxPct}
|
|
814
|
+
</div>
|
|
815
|
+
${activityHTML}
|
|
766
816
|
</div>
|
|
767
817
|
<span class="tree-actions">
|
|
768
818
|
<button class="btn btn-icon accent" onclick="event.stopPropagation();selectIndex(${idx});soloSelected()" data-tooltip="Solo">⊙</button>
|
|
@@ -773,9 +823,15 @@ function getNodeHTML(node, idx) {
|
|
|
773
823
|
|
|
774
824
|
if (node.type === 'task') {
|
|
775
825
|
const icon = node.isComplete ? '✓' : '⏳';
|
|
826
|
+
const descHTML = showActivity && node.description
|
|
827
|
+
? `<div class="tree-activity">📋 ${esc(node.description)}</div>`
|
|
828
|
+
: '';
|
|
776
829
|
return `<div class="tree-row${selClass ? ' selected' : ''}">
|
|
777
|
-
<div class="tree-
|
|
830
|
+
<div class="tree-content dim" onclick="treeClick(${idx})" data-idx="${idx}">
|
|
831
|
+
<div class="tree-node">
|
|
778
832
|
<span class="tree-prefix">${treePrefix(node)}</span>${icon} ${esc(node.name || 'bg-task')}
|
|
833
|
+
</div>
|
|
834
|
+
${descHTML}
|
|
779
835
|
</div>
|
|
780
836
|
<span class="tree-actions">
|
|
781
837
|
<button class="btn btn-icon" onclick="event.stopPropagation();selectIndex(${idx});loadBgTask(${idx})" data-tooltip="Load output">▶</button>
|
|
@@ -799,7 +855,7 @@ function renderTree() {
|
|
|
799
855
|
treeEl.innerHTML = html;
|
|
800
856
|
|
|
801
857
|
// Scroll selected into view
|
|
802
|
-
const sel = treeEl.querySelector('.tree-
|
|
858
|
+
const sel = treeEl.querySelector('.tree-row.selected');
|
|
803
859
|
if (sel) sel.scrollIntoView({ block: 'nearest' });
|
|
804
860
|
|
|
805
861
|
treeCursorInfo.textContent = `${treeCursor + 1}/${treeNodes.length}`;
|
|
@@ -965,6 +1021,7 @@ function refreshButtons() {
|
|
|
965
1021
|
document.getElementById('btn-tool-output').classList.toggle('on', showToolOutput);
|
|
966
1022
|
document.getElementById('btn-text').classList.toggle('on', showText);
|
|
967
1023
|
document.getElementById('btn-hook').classList.toggle('on', showHook);
|
|
1024
|
+
document.getElementById('btn-activity').classList.toggle('on', showActivity);
|
|
968
1025
|
document.getElementById('btn-autoscroll').classList.toggle('on', autoScroll);
|
|
969
1026
|
document.getElementById('btn-tree-toggle').classList.toggle('on', showTree);
|
|
970
1027
|
document.getElementById('btn-autodisco').classList.toggle('on', autoDiscovery);
|
|
@@ -1144,6 +1201,7 @@ function toggleText() { showText = !showText; needsFullRender = true;
|
|
|
1144
1201
|
visibleDirty = true; renderStream(); refreshButtons(); }
|
|
1145
1202
|
function toggleHook() { showHook = !showHook; needsFullRender = true;
|
|
1146
1203
|
visibleDirty = true; renderStream(); refreshButtons(); }
|
|
1204
|
+
function toggleActivity() { showActivity = !showActivity; rebuildNodes(); scheduleRender(); refreshButtons(); }
|
|
1147
1205
|
function toggleAutoScroll() { autoScroll = !autoScroll; if (autoScroll) streamEl.scrollTop = streamEl.scrollHeight; renderAll(); }
|
|
1148
1206
|
function toggleTree() { showTree = !showTree; document.getElementById('tree-panel').classList.toggle('hidden', !showTree); }
|
|
1149
1207
|
function toggleAutoDiscovery() { sendCmd('toggleAutoDiscovery'); }
|
package/src/parser/parser.js
CHANGED
|
@@ -9,6 +9,7 @@ var StreamItemType = {
|
|
|
9
9
|
TOOL_INPUT: 'tool_input',
|
|
10
10
|
TOOL_OUTPUT: 'tool_output',
|
|
11
11
|
TEXT: 'text',
|
|
12
|
+
USER_TEXT: 'user_text',
|
|
12
13
|
TURN_MARKER: 'turn_marker',
|
|
13
14
|
COMPACT_MARKER: 'compact_marker',
|
|
14
15
|
HOOK_OUTPUT: 'hook_output',
|
|
@@ -368,7 +369,7 @@ function parseAssistantMessage(raw, timestamp) {
|
|
|
368
369
|
|
|
369
370
|
function parseUserMessage(raw, timestamp) {
|
|
370
371
|
const msg = raw.message;
|
|
371
|
-
if (!msg
|
|
372
|
+
if (!msg) return [];
|
|
372
373
|
|
|
373
374
|
// Parse toolUseResult for duration
|
|
374
375
|
let durationMs = 0;
|
|
@@ -379,20 +380,54 @@ function parseUserMessage(raw, timestamp) {
|
|
|
379
380
|
const items = [];
|
|
380
381
|
const name = agentDisplayName(raw.agentId);
|
|
381
382
|
|
|
382
|
-
|
|
383
|
-
|
|
383
|
+
// String content — user prompt
|
|
384
|
+
if (typeof msg.content === 'string' && msg.content) {
|
|
385
|
+
const text = stripNonUserContent(msg.content);
|
|
386
|
+
if (text) {
|
|
384
387
|
items.push(makeItem({
|
|
385
|
-
type: StreamItemType.
|
|
388
|
+
type: StreamItemType.USER_TEXT,
|
|
386
389
|
agentID: raw.agentId || '',
|
|
387
390
|
agentName: name,
|
|
388
|
-
content:
|
|
389
|
-
toolID: result.tool_use_id || '',
|
|
390
|
-
durationMs,
|
|
391
|
+
content: text,
|
|
391
392
|
timestamp,
|
|
392
393
|
}));
|
|
393
394
|
}
|
|
394
395
|
}
|
|
395
396
|
|
|
397
|
+
// Array content — mixed text blocks and tool_result blocks
|
|
398
|
+
if (Array.isArray(msg.content)) {
|
|
399
|
+
const textParts = [];
|
|
400
|
+
for (const block of msg.content) {
|
|
401
|
+
if (block.type === 'text' && block.text) {
|
|
402
|
+
const text = stripNonUserContent(block.text);
|
|
403
|
+
if (text) textParts.push(text);
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
if (textParts.length > 0) {
|
|
407
|
+
items.push(makeItem({
|
|
408
|
+
type: StreamItemType.USER_TEXT,
|
|
409
|
+
agentID: raw.agentId || '',
|
|
410
|
+
agentName: name,
|
|
411
|
+
content: textParts.join('\n'),
|
|
412
|
+
timestamp,
|
|
413
|
+
}));
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
for (const result of msg.content) {
|
|
417
|
+
if (result.type === 'tool_result') {
|
|
418
|
+
items.push(makeItem({
|
|
419
|
+
type: StreamItemType.TOOL_OUTPUT,
|
|
420
|
+
agentID: raw.agentId || '',
|
|
421
|
+
agentName: name,
|
|
422
|
+
content: extractToolResultContent(result.content),
|
|
423
|
+
toolID: result.tool_use_id || '',
|
|
424
|
+
durationMs,
|
|
425
|
+
timestamp,
|
|
426
|
+
}));
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
|
|
396
431
|
return items;
|
|
397
432
|
}
|
|
398
433
|
|
|
@@ -413,6 +448,19 @@ function extractToolResultContent(content) {
|
|
|
413
448
|
}
|
|
414
449
|
}
|
|
415
450
|
|
|
451
|
+
function stripNonUserContent(text) {
|
|
452
|
+
if (!text) return '';
|
|
453
|
+
// Remove tags that wrap non-user content
|
|
454
|
+
let s = text;
|
|
455
|
+
s = s.replace(/<local-command-caveat>[\s\S]*?<\/local-command-caveat>/g, '');
|
|
456
|
+
s = s.replace(/<command-name>[\s\S]*?<\/command-name>/g, '');
|
|
457
|
+
s = s.replace(/<command-message>[\s\S]*?<\/command-message>/g, '');
|
|
458
|
+
s = s.replace(/<command-args>[\s\S]*?<\/command-args>/g, '');
|
|
459
|
+
s = s.replace(/<local-command-stdout>[\s\S]*?<\/local-command-stdout>/g, '');
|
|
460
|
+
// Trim and return; empty string means no real user content
|
|
461
|
+
return s.trim();
|
|
462
|
+
}
|
|
463
|
+
|
|
416
464
|
// ============================================================================
|
|
417
465
|
// Tool Input Formatting
|
|
418
466
|
// ============================================================================
|
|
@@ -497,5 +545,6 @@ module.exports = {
|
|
|
497
545
|
formatToolInput,
|
|
498
546
|
prettyToolName,
|
|
499
547
|
agentDisplayName,
|
|
548
|
+
stripNonUserContent,
|
|
500
549
|
MAX_TOOL_INPUT_LENGTH,
|
|
501
550
|
};
|