@phuc1403/musketeer 0.7.0 → 0.9.0

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 (84) hide show
  1. package/README.md +49 -49
  2. package/manifest.json +333 -301
  3. package/package.json +1 -1
  4. package/template/.claude/agents/code-reviewer.md +182 -166
  5. package/template/.claude/hooks/git-skill-reminder.cjs +53 -0
  6. package/template/.claude/hooks/inject-design-docs.cjs +13 -13
  7. package/template/.claude/hooks/inject-ubiquitous-language.cjs +52 -0
  8. package/template/.claude/hooks/lib/colors.cjs +180 -122
  9. package/template/.claude/hooks/lib/transcript-parser.cjs +300 -277
  10. package/template/.claude/skills/code-review/SKILL.md +201 -54
  11. package/template/.claude/skills/code-review/references/checklist-workflow.md +96 -0
  12. package/template/.claude/skills/code-review/references/checklists/api.md +52 -52
  13. package/template/.claude/skills/code-review/references/checklists/base.md +100 -100
  14. package/template/.claude/skills/code-review/references/checklists/web-app.md +54 -54
  15. package/template/.claude/skills/code-review/references/code-review-reception.md +113 -0
  16. package/template/.claude/skills/code-review/references/codebase-scan-workflow.md +30 -0
  17. package/template/.claude/skills/code-review/references/edge-case-scouting.md +119 -0
  18. package/template/.claude/skills/code-review/references/input-mode-resolution.md +135 -0
  19. package/template/.claude/skills/code-review/references/parallel-review-workflow.md +76 -0
  20. package/template/.claude/skills/code-review/references/requesting-code-review.md +116 -0
  21. package/template/.claude/skills/code-review/references/spec-compliance-review.md +43 -0
  22. package/template/.claude/skills/code-review/references/task-management-reviews.md +140 -0
  23. package/template/.claude/skills/code-review/references/verification-before-completion.md +139 -0
  24. package/template/.claude/skills/context-map/SKILL.md +1 -1
  25. package/template/.claude/skills/git/SKILL.md +131 -115
  26. package/template/.claude/skills/git/references/branch-management.md +88 -88
  27. package/template/.claude/skills/git/references/commit-standards.md +46 -46
  28. package/template/.claude/skills/git/references/context-efficiency.md +54 -0
  29. package/template/.claude/skills/git/references/gh-cli-guide.md +109 -109
  30. package/template/.claude/skills/git/references/safety-protocols.md +69 -69
  31. package/template/.claude/skills/git/references/workflow-commit.md +58 -58
  32. package/template/.claude/skills/git/references/workflow-merge-pr.md +136 -0
  33. package/template/.claude/skills/git/references/workflow-merge.md +48 -48
  34. package/template/.claude/skills/git/references/workflow-pr.md +58 -58
  35. package/template/.claude/skills/git/references/workflow-push.md +52 -52
  36. package/template/.claude/skills/knowledge-crunching/SKILL.md +56 -92
  37. package/template/.claude/skills/knowledge-crunching/assets/ubiquitous-language.template.md +3 -0
  38. package/template/.claude/skills/skill-creator/LICENSE.txt +201 -201
  39. package/template/.claude/skills/skill-creator/SKILL.md +154 -149
  40. package/template/.claude/skills/skill-creator/agents/analyzer.md +274 -274
  41. package/template/.claude/skills/skill-creator/agents/comparator.md +202 -202
  42. package/template/.claude/skills/skill-creator/agents/grader.md +223 -223
  43. package/template/.claude/skills/skill-creator/assets/eval_review.html +146 -146
  44. package/template/.claude/skills/skill-creator/eval-viewer/generate_review.py +471 -471
  45. package/template/.claude/skills/skill-creator/eval-viewer/viewer.html +1325 -1325
  46. package/template/.claude/skills/skill-creator/references/benchmark-optimization-guide.md +86 -86
  47. package/template/.claude/skills/skill-creator/references/distribution-guide.md +79 -79
  48. package/template/.claude/skills/skill-creator/references/eval-infrastructure-guide.md +129 -129
  49. package/template/.claude/skills/skill-creator/references/eval-schemas.md +121 -121
  50. package/template/.claude/skills/skill-creator/references/mcp-skills-integration.md +71 -71
  51. package/template/.claude/skills/skill-creator/references/metadata-quality-criteria.md +94 -94
  52. package/template/.claude/skills/skill-creator/references/plugin-marketplace-hosting.md +104 -104
  53. package/template/.claude/skills/skill-creator/references/plugin-marketplace-overview.md +89 -89
  54. package/template/.claude/skills/skill-creator/references/plugin-marketplace-schema.md +93 -93
  55. package/template/.claude/skills/skill-creator/references/plugin-marketplace-sources.md +103 -103
  56. package/template/.claude/skills/skill-creator/references/plugin-marketplace-troubleshooting.md +76 -76
  57. package/template/.claude/skills/skill-creator/references/script-quality-criteria.md +106 -106
  58. package/template/.claude/skills/skill-creator/references/skill-anatomy-and-requirements.md +77 -77
  59. package/template/.claude/skills/skill-creator/references/skill-creation-workflow.md +152 -151
  60. package/template/.claude/skills/skill-creator/references/skill-design-patterns.md +75 -75
  61. package/template/.claude/skills/skill-creator/references/skillmark-benchmark-criteria.md +102 -102
  62. package/template/.claude/skills/skill-creator/references/structure-organization-criteria.md +114 -114
  63. package/template/.claude/skills/skill-creator/references/testing-and-iteration.md +78 -78
  64. package/template/.claude/skills/skill-creator/references/token-efficiency-criteria.md +74 -74
  65. package/template/.claude/skills/skill-creator/references/troubleshooting-guide.md +81 -81
  66. package/template/.claude/skills/skill-creator/references/validation-checklist.md +83 -83
  67. package/template/.claude/skills/skill-creator/references/writing-effective-instructions.md +88 -88
  68. package/template/.claude/skills/skill-creator/references/yaml-frontmatter-reference.md +92 -92
  69. package/template/.claude/skills/skill-creator/scripts/aggregate_benchmark.py +401 -401
  70. package/template/.claude/skills/skill-creator/scripts/encoding_utils.py +36 -36
  71. package/template/.claude/skills/skill-creator/scripts/generate_report.py +326 -326
  72. package/template/.claude/skills/skill-creator/scripts/improve_description.py +248 -248
  73. package/template/.claude/skills/skill-creator/scripts/init_skill.py +360 -360
  74. package/template/.claude/skills/skill-creator/scripts/package_skill.py +143 -143
  75. package/template/.claude/skills/skill-creator/scripts/quick_validate.py +110 -110
  76. package/template/.claude/skills/skill-creator/scripts/run_eval.py +310 -310
  77. package/template/.claude/skills/skill-creator/scripts/run_loop.py +332 -332
  78. package/template/.claude/skills/skill-creator/scripts/utils.py +47 -47
  79. package/template/.claude/statusline.cjs +0 -0
  80. package/template/.claude/hooks/inject-context.cjs +0 -52
  81. package/template/.claude/skills/code-review/references/adversarial-review.md +0 -223
  82. package/template/.claude/skills/knowledge-crunching/assets/context.template.md +0 -59
  83. package/template/.claude/skills/knowledge-crunching/references/crunching-dialogue.md +0 -113
  84. /package/template/.claude/hooks/{usage-context-awareness.cjs → usage-quota-cache-refresh.cjs} +0 -0
@@ -1,277 +1,300 @@
1
- #!/usr/bin/env node
2
- 'use strict';
3
-
4
- /**
5
- * Transcript Parser - Extract tool/agent/todo state from session JSONL
6
- * @module transcript-parser
7
- */
8
-
9
- const fs = require('fs');
10
- const readline = require('readline');
11
-
12
- function isNativeTaskTodo(todo) {
13
- return Boolean(todo && todo._source === 'native_task');
14
- }
15
-
16
- function normalizeTodo(todo) {
17
- if (!todo || typeof todo !== 'object') return null;
18
- const normalized = {
19
- content: todo.content ?? '',
20
- status: todo.status ?? 'pending',
21
- activeForm: todo.activeForm ?? null
22
- };
23
- if (todo.id != null) normalized.id = todo.id;
24
- return normalized;
25
- }
26
-
27
- function extractTaskIdFromString(text) {
28
- if (!text || typeof text !== 'string') return null;
29
- const trimmed = text.trim();
30
- if (!trimmed) return null;
31
-
32
- try {
33
- const parsed = JSON.parse(trimmed);
34
- return extractTaskIdFromValue(parsed);
35
- } catch {
36
- // Not JSON, continue with regex extraction.
37
- }
38
-
39
- const match = trimmed.match(/["']?task[_-]?id["']?\s*[:=]\s*["']([^"']+)["']/i);
40
- if (match && match[1]) return match[1];
41
- return null;
42
- }
43
-
44
- function extractTaskIdFromValue(value) {
45
- if (value == null) return null;
46
-
47
- if (typeof value === 'string') {
48
- return extractTaskIdFromString(value);
49
- }
50
-
51
- if (typeof value !== 'object') return null;
52
-
53
- if (typeof value.taskId === 'string' || typeof value.taskId === 'number') {
54
- return String(value.taskId);
55
- }
56
- if (typeof value.task_id === 'string' || typeof value.task_id === 'number') {
57
- return String(value.task_id);
58
- }
59
-
60
- if (Array.isArray(value)) {
61
- for (const item of value) {
62
- const taskId = extractTaskIdFromValue(item);
63
- if (taskId) return taskId;
64
- }
65
- return null;
66
- }
67
-
68
- for (const fieldValue of Object.values(value)) {
69
- const taskId = extractTaskIdFromValue(fieldValue);
70
- if (taskId) return taskId;
71
- }
72
- return null;
73
- }
74
-
75
- /**
76
- * Parse transcript JSONL file
77
- * @param {string} transcriptPath - Path to transcript file
78
- * @returns {Promise<TranscriptData>}
79
- */
80
- async function parseTranscript(transcriptPath) {
81
- const result = {
82
- tools: [],
83
- agents: [],
84
- todos: [],
85
- sessionStart: null
86
- };
87
-
88
- if (!transcriptPath || !fs.existsSync(transcriptPath)) {
89
- return result;
90
- }
91
-
92
- const toolMap = new Map();
93
- const agentMap = new Map();
94
- let latestTodos = [];
95
-
96
- try {
97
- const fileStream = fs.createReadStream(transcriptPath);
98
- const rl = readline.createInterface({
99
- input: fileStream,
100
- crlfDelay: Infinity
101
- });
102
-
103
- for await (const line of rl) {
104
- if (!line.trim()) continue;
105
-
106
- try {
107
- const entry = JSON.parse(line);
108
- processEntry(entry, toolMap, agentMap, latestTodos, result);
109
- } catch {
110
- // Skip malformed lines
111
- }
112
- }
113
- } catch {
114
- // Return partial results on error
115
- }
116
-
117
- result.tools = Array.from(toolMap.values()).slice(-20);
118
- result.agents = Array.from(agentMap.values()).slice(-10);
119
- result.todos = latestTodos
120
- .map(normalizeTodo)
121
- .filter(Boolean);
122
-
123
- return result;
124
- }
125
-
126
- /**
127
- * Process single JSONL entry
128
- * @param {Object} entry - Parsed JSON line
129
- * @param {Map} toolMap - Tool tracking map
130
- * @param {Map} agentMap - Agent tracking map
131
- * @param {Array} latestTodos - Latest todo array reference
132
- * @param {Object} result - Result object
133
- */
134
- function processEntry(entry, toolMap, agentMap, latestTodos, result) {
135
- const timestamp = entry.timestamp ? new Date(entry.timestamp) : new Date();
136
-
137
- // Track session start
138
- if (!result.sessionStart && entry.timestamp) {
139
- result.sessionStart = timestamp;
140
- }
141
-
142
- const content = entry.message?.content;
143
- if (!content || !Array.isArray(content)) return;
144
-
145
- for (const block of content) {
146
- // Handle tool_use blocks
147
- if (block.type === 'tool_use' && block.id && block.name) {
148
- if (block.name === 'Task') {
149
- // Agent spawn
150
- agentMap.set(block.id, {
151
- id: block.id,
152
- type: block.input?.subagent_type ?? 'unknown',
153
- model: block.input?.model ?? null,
154
- description: block.input?.description ?? null,
155
- status: 'running',
156
- startTime: timestamp,
157
- endTime: null
158
- });
159
- } else if (block.name === 'TodoWrite') {
160
- // Legacy: Replace todo array (deprecated, kept for backwards compatibility)
161
- if (block.input?.todos && Array.isArray(block.input.todos)) {
162
- latestTodos.length = 0;
163
- latestTodos.push(
164
- ...block.input.todos.map(todo => ({
165
- ...todo,
166
- _source: 'legacy_todowrite'
167
- }))
168
- );
169
- }
170
- } else if (block.name === 'TaskCreate') {
171
- // Native Task API: add new task.
172
- // Track by tool_use id first; hydrate real task id from matching tool_result when present.
173
- if (block.input?.subject) {
174
- latestTodos.push({
175
- id: block.id,
176
- content: block.input.subject,
177
- status: 'pending',
178
- activeForm: block.input.activeForm || null,
179
- _source: 'native_task',
180
- _toolUseId: block.id
181
- });
182
- }
183
- } else if (block.name === 'TaskUpdate') {
184
- // Native Task API: Update existing task status
185
- // Match by taskId against native-task ids first.
186
- // Numeric fallback maps to native-task creation order only (not legacy TodoWrite items).
187
- if (block.input?.taskId && block.input?.status) {
188
- const taskId = String(block.input.taskId);
189
- const nativeTodos = latestTodos.filter(isNativeTaskTodo);
190
- let task = nativeTodos.find(t => String(t.id) === taskId);
191
- if (!task && /^\d+$/.test(taskId)) {
192
- const idx = Number(taskId) - 1;
193
- if (idx >= 0 && idx < nativeTodos.length) task = nativeTodos[idx];
194
- }
195
-
196
- if (task) {
197
- task.status = block.input.status;
198
- if (Object.prototype.hasOwnProperty.call(block.input, 'activeForm')) {
199
- task.activeForm = block.input.activeForm || null;
200
- }
201
- }
202
- }
203
- } else {
204
- // Regular tool
205
- toolMap.set(block.id, {
206
- id: block.id,
207
- name: block.name,
208
- target: extractTarget(block.name, block.input),
209
- status: 'running',
210
- startTime: timestamp,
211
- endTime: null
212
- });
213
- }
214
- }
215
-
216
- // Handle tool_result blocks
217
- if (block.type === 'tool_result' && block.tool_use_id) {
218
- const tool = toolMap.get(block.tool_use_id);
219
- if (tool) {
220
- tool.status = block.is_error ? 'error' : 'completed';
221
- tool.endTime = timestamp;
222
- }
223
-
224
- const agent = agentMap.get(block.tool_use_id);
225
- if (agent) {
226
- agent.status = 'completed';
227
- agent.endTime = timestamp;
228
- }
229
-
230
- const createdTask = latestTodos.find(
231
- todo => isNativeTaskTodo(todo) && todo._toolUseId === block.tool_use_id
232
- );
233
- if (createdTask) {
234
- const hydratedId = extractTaskIdFromValue(block.content);
235
- if (hydratedId) {
236
- createdTask.id = hydratedId;
237
- }
238
- }
239
- }
240
- }
241
- }
242
-
243
- /**
244
- * Extract target from tool input
245
- * @param {string} toolName - Tool name
246
- * @param {Object} input - Tool input object
247
- * @returns {string|null} - Extracted target
248
- */
249
- function extractTarget(toolName, input) {
250
- if (!input) return null;
251
-
252
- switch (toolName) {
253
- case 'Read':
254
- case 'Write':
255
- case 'Edit':
256
- return input.file_path ?? input.path ?? null;
257
-
258
- case 'Glob':
259
- case 'Grep':
260
- return input.pattern ?? null;
261
-
262
- case 'Bash':
263
- const cmd = input.command;
264
- if (!cmd) return null;
265
- return cmd.length > 30 ? cmd.slice(0, 30) + '...' : cmd;
266
-
267
- default:
268
- return null;
269
- }
270
- }
271
-
272
- module.exports = {
273
- parseTranscript,
274
- // Export for testing
275
- processEntry,
276
- extractTarget
277
- };
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ /**
5
+ * Transcript Parser - Extract tool/agent/todo state from session JSONL
6
+ * @module transcript-parser
7
+ */
8
+
9
+ const fs = require('fs');
10
+ const readline = require('readline');
11
+
12
+ function isNativeTaskTodo(todo) {
13
+ return Boolean(todo && todo._source === 'native_task');
14
+ }
15
+
16
+ function normalizeTodo(todo) {
17
+ if (!todo || typeof todo !== 'object') return null;
18
+ const normalized = {
19
+ content: todo.content ?? '',
20
+ status: todo.status ?? 'pending',
21
+ activeForm: todo.activeForm ?? null
22
+ };
23
+ if (todo.id != null) normalized.id = todo.id;
24
+ return normalized;
25
+ }
26
+
27
+ function extractTaskIdFromString(text) {
28
+ if (!text || typeof text !== 'string') return null;
29
+ const trimmed = text.trim();
30
+ if (!trimmed) return null;
31
+
32
+ try {
33
+ const parsed = JSON.parse(trimmed);
34
+ return extractTaskIdFromValue(parsed);
35
+ } catch {
36
+ // Not JSON, continue with regex extraction.
37
+ }
38
+
39
+ const match = trimmed.match(/["']?task[_-]?id["']?\s*[:=]\s*["']([^"']+)["']/i);
40
+ if (match && match[1]) return match[1];
41
+ return null;
42
+ }
43
+
44
+ function extractTaskIdFromValue(value) {
45
+ if (value == null) return null;
46
+
47
+ if (typeof value === 'string') {
48
+ return extractTaskIdFromString(value);
49
+ }
50
+
51
+ if (typeof value !== 'object') return null;
52
+
53
+ if (typeof value.taskId === 'string' || typeof value.taskId === 'number') {
54
+ return String(value.taskId);
55
+ }
56
+ if (typeof value.task_id === 'string' || typeof value.task_id === 'number') {
57
+ return String(value.task_id);
58
+ }
59
+
60
+ if (Array.isArray(value)) {
61
+ for (const item of value) {
62
+ const taskId = extractTaskIdFromValue(item);
63
+ if (taskId) return taskId;
64
+ }
65
+ return null;
66
+ }
67
+
68
+ for (const fieldValue of Object.values(value)) {
69
+ const taskId = extractTaskIdFromValue(fieldValue);
70
+ if (taskId) return taskId;
71
+ }
72
+ return null;
73
+ }
74
+
75
+ /**
76
+ * Parse transcript JSONL file
77
+ * @param {string} transcriptPath - Path to transcript file
78
+ * @returns {Promise<TranscriptData>}
79
+ */
80
+ async function parseTranscript(transcriptPath) {
81
+ const result = {
82
+ tools: [],
83
+ agents: [],
84
+ todos: [],
85
+ sessionStart: null,
86
+ statuslineActivityCount: 0,
87
+ invalidLineCount: 0,
88
+ lastValidEntryAt: null,
89
+ lastActivityAt: null
90
+ };
91
+
92
+ if (!transcriptPath || !fs.existsSync(transcriptPath)) {
93
+ return result;
94
+ }
95
+
96
+ const toolMap = new Map();
97
+ const agentMap = new Map();
98
+ let latestTodos = [];
99
+
100
+ try {
101
+ const fileStream = fs.createReadStream(transcriptPath);
102
+ const rl = readline.createInterface({
103
+ input: fileStream,
104
+ crlfDelay: Infinity
105
+ });
106
+
107
+ for await (const line of rl) {
108
+ if (!line.trim()) continue;
109
+
110
+ try {
111
+ const entry = JSON.parse(line);
112
+ processEntry(entry, toolMap, agentMap, latestTodos, result);
113
+ } catch {
114
+ result.invalidLineCount += 1;
115
+ }
116
+ }
117
+ } catch {
118
+ // Return partial results on error
119
+ }
120
+
121
+ result.tools = Array.from(toolMap.values()).slice(-20);
122
+ result.agents = Array.from(agentMap.values()).slice(-10);
123
+ result.todos = latestTodos
124
+ .map(normalizeTodo)
125
+ .filter(Boolean);
126
+
127
+ return result;
128
+ }
129
+
130
+ /**
131
+ * Process single JSONL entry
132
+ * @param {Object} entry - Parsed JSON line
133
+ * @param {Map} toolMap - Tool tracking map
134
+ * @param {Map} agentMap - Agent tracking map
135
+ * @param {Array} latestTodos - Latest todo array reference
136
+ * @param {Object} result - Result object
137
+ */
138
+ function processEntry(entry, toolMap, agentMap, latestTodos, result) {
139
+ const parsedTimestamp = entry.timestamp ? new Date(entry.timestamp) : new Date();
140
+ const timestamp = Number.isNaN(parsedTimestamp.getTime()) ? new Date() : parsedTimestamp;
141
+ const timestampIso = timestamp.toISOString();
142
+ let hadActivity = false;
143
+
144
+ result.lastValidEntryAt = timestampIso;
145
+
146
+ // Track session start
147
+ if (!result.sessionStart && entry.timestamp) {
148
+ result.sessionStart = timestamp;
149
+ }
150
+
151
+ const content = entry.message?.content;
152
+ if (!content || !Array.isArray(content)) return;
153
+
154
+ for (const block of content) {
155
+ // Handle tool_use blocks
156
+ if (block.type === 'tool_use' && block.id && block.name) {
157
+ if (block.name === 'Task') {
158
+ result.statuslineActivityCount += 1;
159
+ hadActivity = true;
160
+ // Agent spawn
161
+ agentMap.set(block.id, {
162
+ id: block.id,
163
+ type: block.input?.subagent_type ?? 'unknown',
164
+ model: block.input?.model ?? null,
165
+ description: block.input?.description ?? null,
166
+ status: 'running',
167
+ startTime: timestamp,
168
+ endTime: null
169
+ });
170
+ } else if (block.name === 'TodoWrite') {
171
+ result.statuslineActivityCount += 1;
172
+ hadActivity = true;
173
+ // Legacy: Replace todo array (deprecated, kept for backwards compatibility)
174
+ if (block.input?.todos && Array.isArray(block.input.todos)) {
175
+ latestTodos.length = 0;
176
+ latestTodos.push(
177
+ ...block.input.todos.map(todo => ({
178
+ ...todo,
179
+ _source: 'legacy_todowrite'
180
+ }))
181
+ );
182
+ }
183
+ } else if (block.name === 'TaskCreate') {
184
+ result.statuslineActivityCount += 1;
185
+ hadActivity = true;
186
+ // Native Task API: add new task.
187
+ // Track by tool_use id first; hydrate real task id from matching tool_result when present.
188
+ if (block.input?.subject) {
189
+ latestTodos.push({
190
+ id: block.id,
191
+ content: block.input.subject,
192
+ status: 'pending',
193
+ activeForm: block.input.activeForm || null,
194
+ _source: 'native_task',
195
+ _toolUseId: block.id
196
+ });
197
+ }
198
+ } else if (block.name === 'TaskUpdate') {
199
+ result.statuslineActivityCount += 1;
200
+ hadActivity = true;
201
+ // Native Task API: Update existing task status
202
+ // Match by taskId against native-task ids first.
203
+ // Numeric fallback maps to native-task creation order only (not legacy TodoWrite items).
204
+ if (block.input?.taskId && block.input?.status) {
205
+ const taskId = String(block.input.taskId);
206
+ const nativeTodos = latestTodos.filter(isNativeTaskTodo);
207
+ let task = nativeTodos.find(t => String(t.id) === taskId);
208
+ if (!task && /^\d+$/.test(taskId)) {
209
+ const idx = Number(taskId) - 1;
210
+ if (idx >= 0 && idx < nativeTodos.length) task = nativeTodos[idx];
211
+ }
212
+
213
+ if (task) {
214
+ task.status = block.input.status;
215
+ if (Object.prototype.hasOwnProperty.call(block.input, 'activeForm')) {
216
+ task.activeForm = block.input.activeForm || null;
217
+ }
218
+ }
219
+ }
220
+ } else {
221
+ // Regular tool
222
+ toolMap.set(block.id, {
223
+ id: block.id,
224
+ name: block.name,
225
+ target: extractTarget(block.name, block.input),
226
+ status: 'running',
227
+ startTime: timestamp,
228
+ endTime: null
229
+ });
230
+ }
231
+ }
232
+
233
+ // Handle tool_result blocks
234
+ if (block.type === 'tool_result' && block.tool_use_id) {
235
+ const tool = toolMap.get(block.tool_use_id);
236
+ if (tool) {
237
+ tool.status = block.is_error ? 'error' : 'completed';
238
+ tool.endTime = timestamp;
239
+ }
240
+
241
+ const agent = agentMap.get(block.tool_use_id);
242
+ if (agent) {
243
+ result.statuslineActivityCount += 1;
244
+ hadActivity = true;
245
+ agent.status = 'completed';
246
+ agent.endTime = timestamp;
247
+ }
248
+
249
+ const createdTask = latestTodos.find(
250
+ todo => isNativeTaskTodo(todo) && todo._toolUseId === block.tool_use_id
251
+ );
252
+ if (createdTask) {
253
+ const hydratedId = extractTaskIdFromValue(block.content);
254
+ if (hydratedId) {
255
+ createdTask.id = hydratedId;
256
+ }
257
+ }
258
+ }
259
+ }
260
+
261
+ if (hadActivity) {
262
+ result.lastActivityAt = timestampIso;
263
+ }
264
+ }
265
+
266
+ /**
267
+ * Extract target from tool input
268
+ * @param {string} toolName - Tool name
269
+ * @param {Object} input - Tool input object
270
+ * @returns {string|null} - Extracted target
271
+ */
272
+ function extractTarget(toolName, input) {
273
+ if (!input) return null;
274
+
275
+ switch (toolName) {
276
+ case 'Read':
277
+ case 'Write':
278
+ case 'Edit':
279
+ return input.file_path ?? input.path ?? null;
280
+
281
+ case 'Glob':
282
+ case 'Grep':
283
+ return input.pattern ?? null;
284
+
285
+ case 'Bash':
286
+ const cmd = input.command;
287
+ if (!cmd) return null;
288
+ return cmd.length > 30 ? cmd.slice(0, 30) + '...' : cmd;
289
+
290
+ default:
291
+ return null;
292
+ }
293
+ }
294
+
295
+ module.exports = {
296
+ parseTranscript,
297
+ // Export for testing
298
+ processEntry,
299
+ extractTarget
300
+ };