@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.
- package/README.md +49 -49
- package/manifest.json +333 -301
- package/package.json +1 -1
- package/template/.claude/agents/code-reviewer.md +182 -166
- package/template/.claude/hooks/git-skill-reminder.cjs +53 -0
- package/template/.claude/hooks/inject-design-docs.cjs +13 -13
- package/template/.claude/hooks/inject-ubiquitous-language.cjs +52 -0
- package/template/.claude/hooks/lib/colors.cjs +180 -122
- package/template/.claude/hooks/lib/transcript-parser.cjs +300 -277
- package/template/.claude/skills/code-review/SKILL.md +201 -54
- package/template/.claude/skills/code-review/references/checklist-workflow.md +96 -0
- package/template/.claude/skills/code-review/references/checklists/api.md +52 -52
- package/template/.claude/skills/code-review/references/checklists/base.md +100 -100
- package/template/.claude/skills/code-review/references/checklists/web-app.md +54 -54
- package/template/.claude/skills/code-review/references/code-review-reception.md +113 -0
- package/template/.claude/skills/code-review/references/codebase-scan-workflow.md +30 -0
- package/template/.claude/skills/code-review/references/edge-case-scouting.md +119 -0
- package/template/.claude/skills/code-review/references/input-mode-resolution.md +135 -0
- package/template/.claude/skills/code-review/references/parallel-review-workflow.md +76 -0
- package/template/.claude/skills/code-review/references/requesting-code-review.md +116 -0
- package/template/.claude/skills/code-review/references/spec-compliance-review.md +43 -0
- package/template/.claude/skills/code-review/references/task-management-reviews.md +140 -0
- package/template/.claude/skills/code-review/references/verification-before-completion.md +139 -0
- package/template/.claude/skills/context-map/SKILL.md +1 -1
- package/template/.claude/skills/git/SKILL.md +131 -115
- package/template/.claude/skills/git/references/branch-management.md +88 -88
- package/template/.claude/skills/git/references/commit-standards.md +46 -46
- package/template/.claude/skills/git/references/context-efficiency.md +54 -0
- package/template/.claude/skills/git/references/gh-cli-guide.md +109 -109
- package/template/.claude/skills/git/references/safety-protocols.md +69 -69
- package/template/.claude/skills/git/references/workflow-commit.md +58 -58
- package/template/.claude/skills/git/references/workflow-merge-pr.md +136 -0
- package/template/.claude/skills/git/references/workflow-merge.md +48 -48
- package/template/.claude/skills/git/references/workflow-pr.md +58 -58
- package/template/.claude/skills/git/references/workflow-push.md +52 -52
- package/template/.claude/skills/knowledge-crunching/SKILL.md +56 -92
- package/template/.claude/skills/knowledge-crunching/assets/ubiquitous-language.template.md +3 -0
- package/template/.claude/skills/skill-creator/LICENSE.txt +201 -201
- package/template/.claude/skills/skill-creator/SKILL.md +154 -149
- package/template/.claude/skills/skill-creator/agents/analyzer.md +274 -274
- package/template/.claude/skills/skill-creator/agents/comparator.md +202 -202
- package/template/.claude/skills/skill-creator/agents/grader.md +223 -223
- package/template/.claude/skills/skill-creator/assets/eval_review.html +146 -146
- package/template/.claude/skills/skill-creator/eval-viewer/generate_review.py +471 -471
- package/template/.claude/skills/skill-creator/eval-viewer/viewer.html +1325 -1325
- package/template/.claude/skills/skill-creator/references/benchmark-optimization-guide.md +86 -86
- package/template/.claude/skills/skill-creator/references/distribution-guide.md +79 -79
- package/template/.claude/skills/skill-creator/references/eval-infrastructure-guide.md +129 -129
- package/template/.claude/skills/skill-creator/references/eval-schemas.md +121 -121
- package/template/.claude/skills/skill-creator/references/mcp-skills-integration.md +71 -71
- package/template/.claude/skills/skill-creator/references/metadata-quality-criteria.md +94 -94
- package/template/.claude/skills/skill-creator/references/plugin-marketplace-hosting.md +104 -104
- package/template/.claude/skills/skill-creator/references/plugin-marketplace-overview.md +89 -89
- package/template/.claude/skills/skill-creator/references/plugin-marketplace-schema.md +93 -93
- package/template/.claude/skills/skill-creator/references/plugin-marketplace-sources.md +103 -103
- package/template/.claude/skills/skill-creator/references/plugin-marketplace-troubleshooting.md +76 -76
- package/template/.claude/skills/skill-creator/references/script-quality-criteria.md +106 -106
- package/template/.claude/skills/skill-creator/references/skill-anatomy-and-requirements.md +77 -77
- package/template/.claude/skills/skill-creator/references/skill-creation-workflow.md +152 -151
- package/template/.claude/skills/skill-creator/references/skill-design-patterns.md +75 -75
- package/template/.claude/skills/skill-creator/references/skillmark-benchmark-criteria.md +102 -102
- package/template/.claude/skills/skill-creator/references/structure-organization-criteria.md +114 -114
- package/template/.claude/skills/skill-creator/references/testing-and-iteration.md +78 -78
- package/template/.claude/skills/skill-creator/references/token-efficiency-criteria.md +74 -74
- package/template/.claude/skills/skill-creator/references/troubleshooting-guide.md +81 -81
- package/template/.claude/skills/skill-creator/references/validation-checklist.md +83 -83
- package/template/.claude/skills/skill-creator/references/writing-effective-instructions.md +88 -88
- package/template/.claude/skills/skill-creator/references/yaml-frontmatter-reference.md +92 -92
- package/template/.claude/skills/skill-creator/scripts/aggregate_benchmark.py +401 -401
- package/template/.claude/skills/skill-creator/scripts/encoding_utils.py +36 -36
- package/template/.claude/skills/skill-creator/scripts/generate_report.py +326 -326
- package/template/.claude/skills/skill-creator/scripts/improve_description.py +248 -248
- package/template/.claude/skills/skill-creator/scripts/init_skill.py +360 -360
- package/template/.claude/skills/skill-creator/scripts/package_skill.py +143 -143
- package/template/.claude/skills/skill-creator/scripts/quick_validate.py +110 -110
- package/template/.claude/skills/skill-creator/scripts/run_eval.py +310 -310
- package/template/.claude/skills/skill-creator/scripts/run_loop.py +332 -332
- package/template/.claude/skills/skill-creator/scripts/utils.py +47 -47
- package/template/.claude/statusline.cjs +0 -0
- package/template/.claude/hooks/inject-context.cjs +0 -52
- package/template/.claude/skills/code-review/references/adversarial-review.md +0 -223
- package/template/.claude/skills/knowledge-crunching/assets/context.template.md +0 -59
- package/template/.claude/skills/knowledge-crunching/references/crunching-dialogue.md +0 -113
- /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
|
-
|
|
89
|
-
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
*
|
|
132
|
-
* @param {Object}
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
//
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
}
|
|
170
|
-
} else if (block.name === '
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
}
|
|
183
|
-
} else if (block.name === '
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
//
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
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
|
+
};
|