@kkelly-offical/kkcode 0.1.7 → 0.2.1
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/LICENSE +674 -674
- package/README.md +452 -387
- package/package.json +50 -46
- package/src/agent/agent.mjs +228 -220
- package/src/agent/custom-agent-loader.mjs +6 -3
- package/src/agent/generator.mjs +2 -2
- package/src/agent/prompt/assistant.txt +12 -0
- package/src/agent/prompt/bug-hunter.txt +89 -89
- package/src/agent/prompt/frontend-designer.txt +58 -58
- package/src/agent/prompt/guide.txt +1 -1
- package/src/agent/prompt/longagent-blueprint-agent.txt +83 -83
- package/src/agent/prompt/longagent-coding-agent.txt +37 -37
- package/src/agent/prompt/longagent-debugging-agent.txt +46 -46
- package/src/agent/prompt/longagent-preview-agent.txt +63 -63
- package/src/command/custom-commands.mjs +2 -2
- package/src/commands/agent.mjs +1 -1
- package/src/commands/background.mjs +145 -4
- package/src/commands/chat.mjs +117 -76
- package/src/commands/config.mjs +148 -1
- package/src/commands/doctor.mjs +30 -6
- package/src/commands/init.mjs +32 -6
- package/src/commands/longagent.mjs +117 -0
- package/src/commands/mcp.mjs +275 -43
- package/src/commands/permission.mjs +1 -1
- package/src/commands/session.mjs +195 -140
- package/src/commands/skill.mjs +63 -0
- package/src/commands/theme.mjs +1 -1
- package/src/config/defaults.mjs +280 -260
- package/src/config/import-config.mjs +1 -1
- package/src/config/load-config.mjs +61 -4
- package/src/config/schema.mjs +591 -574
- package/src/context.mjs +4 -1
- package/src/core/constants.mjs +97 -91
- package/src/core/types.mjs +1 -1
- package/src/github/api.mjs +78 -78
- package/src/github/auth.mjs +294 -286
- package/src/github/flow.mjs +298 -298
- package/src/github/workspace.mjs +225 -212
- package/src/index.mjs +84 -82
- package/src/knowledge/frontend-aesthetics.txt +38 -38
- package/src/mcp/client-http.mjs +139 -141
- package/src/mcp/client-sse.mjs +297 -288
- package/src/mcp/client-stdio.mjs +534 -533
- package/src/mcp/constants.mjs +2 -2
- package/src/mcp/registry.mjs +498 -479
- package/src/mcp/stdio-framing.mjs +135 -133
- package/src/mcp/tool-result.mjs +24 -24
- package/src/observability/edit-diagnostics.mjs +449 -0
- package/src/observability/index.mjs +42 -42
- package/src/observability/metrics.mjs +165 -137
- package/src/observability/tracer.mjs +137 -137
- package/src/onboarding.mjs +209 -0
- package/src/orchestration/background-manager.mjs +567 -372
- package/src/orchestration/background-worker.mjs +419 -305
- package/src/orchestration/interruption-reason.mjs +21 -0
- package/src/orchestration/longagent-manager.mjs +197 -171
- package/src/orchestration/stage-scheduler.mjs +733 -728
- package/src/orchestration/subagent-router.mjs +7 -1
- package/src/orchestration/task-scheduler.mjs +219 -7
- package/src/permission/engine.mjs +1 -1
- package/src/permission/exec-policy.mjs +370 -370
- package/src/permission/file-edit-policy.mjs +108 -0
- package/src/permission/prompt.mjs +1 -1
- package/src/permission/rules.mjs +116 -7
- package/src/plugin/builtin-hooks/post-edit-format.mjs +2 -1
- package/src/plugin/builtin-hooks/post-edit-typecheck.mjs +104 -40
- package/src/plugin/hook-bus.mjs +19 -5
- package/src/plugin/manifest-loader.mjs +222 -0
- package/src/provider/anthropic.mjs +396 -390
- package/src/provider/ollama.mjs +7 -1
- package/src/provider/openai.mjs +382 -340
- package/src/provider/retry-policy.mjs +74 -68
- package/src/provider/router.mjs +242 -241
- package/src/provider/sse.mjs +104 -104
- package/src/provider/wizard.mjs +556 -0
- package/src/repl/capability-facade.mjs +30 -0
- package/src/repl/command-surface.mjs +23 -0
- package/src/repl/controller-entry.mjs +40 -0
- package/src/repl/core-shell.mjs +208 -0
- package/src/repl/dialog-router.mjs +87 -0
- package/src/repl/input-engine.mjs +76 -0
- package/src/repl/keymap.mjs +7 -0
- package/src/repl/operator-surface.mjs +15 -0
- package/src/repl/permission-flow.mjs +49 -0
- package/src/repl/runtime-facade.mjs +36 -0
- package/src/repl/slash-router.mjs +62 -0
- package/src/repl/state-store.mjs +29 -0
- package/src/repl/turn-controller.mjs +58 -0
- package/src/repl/verification.mjs +23 -0
- package/src/repl.mjs +3368 -2981
- package/src/rules/load-rules.mjs +3 -3
- package/src/runtime.mjs +1 -1
- package/src/session/agent-transaction.mjs +86 -0
- package/src/session/checkpoint.mjs +302 -302
- package/src/session/compaction.mjs +298 -298
- package/src/session/engine.mjs +417 -232
- package/src/session/longagent-4stage.mjs +467 -460
- package/src/session/longagent-hybrid.mjs +1344 -1097
- package/src/session/longagent-plan.mjs +376 -365
- package/src/session/longagent-project-memory.mjs +53 -53
- package/src/session/longagent-scaffold.mjs +291 -291
- package/src/session/longagent-task-bus.mjs +138 -54
- package/src/session/longagent-utils.mjs +828 -472
- package/src/session/longagent.mjs +911 -900
- package/src/session/loop.mjs +1005 -930
- package/src/session/prompt/agent.txt +25 -25
- package/src/session/prompt/anthropic.txt +150 -150
- package/src/session/prompt/beast.txt +1 -1
- package/src/session/prompt/plan.txt +31 -31
- package/src/session/prompt/qwen.txt +46 -46
- package/src/session/recovery.mjs +21 -0
- package/src/session/rollback.mjs +196 -195
- package/src/session/routing-observability.mjs +72 -0
- package/src/session/runtime-state.mjs +47 -0
- package/src/session/store.mjs +523 -519
- package/src/session/system-prompt.mjs +308 -273
- package/src/session/task-validator.mjs +267 -267
- package/src/session/usability-gates.mjs +2 -2
- package/src/skill/builtin/commit.mjs +64 -64
- package/src/skill/builtin/design.mjs +76 -76
- package/src/skill/generator.mjs +18 -2
- package/src/skill/registry.mjs +642 -390
- package/src/storage/audit-store.mjs +18 -11
- package/src/storage/event-log.mjs +7 -1
- package/src/storage/ghost-commit-store.mjs +243 -245
- package/src/storage/paths.mjs +13 -0
- package/src/theme/default-theme.mjs +1 -1
- package/src/theme/markdown.mjs +4 -0
- package/src/theme/schema.mjs +1 -1
- package/src/theme/status-bar.mjs +162 -158
- package/src/tool/audit-wrapper.mjs +18 -2
- package/src/tool/edit-transaction.mjs +23 -0
- package/src/tool/executor.mjs +26 -1
- package/src/tool/file-read-state.mjs +65 -0
- package/src/tool/git-auto.mjs +526 -526
- package/src/tool/git-full-auto.mjs +487 -478
- package/src/tool/mutation-guard.mjs +54 -0
- package/src/tool/prompt/edit.txt +3 -3
- package/src/tool/prompt/multiedit.txt +1 -0
- package/src/tool/prompt/notebookedit.txt +2 -1
- package/src/tool/prompt/patch.txt +25 -24
- package/src/tool/prompt/read.txt +3 -3
- package/src/tool/prompt/sysinfo.txt +29 -0
- package/src/tool/prompt/task.txt +66 -4
- package/src/tool/prompt/write.txt +2 -2
- package/src/tool/question-prompt.mjs +99 -93
- package/src/tool/registry.mjs +1701 -1343
- package/src/tool/task-tool.mjs +14 -6
- package/src/ui/activity-renderer.mjs +667 -664
- package/src/ui/repl-background-panel.mjs +7 -0
- package/src/ui/repl-capability-panel.mjs +9 -0
- package/src/ui/repl-dashboard.mjs +54 -4
- package/src/ui/repl-help.mjs +110 -0
- package/src/ui/repl-operator-panel.mjs +12 -0
- package/src/ui/repl-route-feedback.mjs +35 -0
- package/src/ui/repl-status-view.mjs +76 -0
- package/src/ui/repl-task-panel.mjs +5 -0
- package/src/ui/repl-transcript-panel.mjs +56 -0
- package/src/ui/repl-turn-summary.mjs +135 -0
- package/src/usage/pricing.mjs +122 -121
- package/src/usage/usage-meter.mjs +1 -0
- package/src/util/git.mjs +562 -519
- package/src/util/template.mjs +6 -1
|
@@ -1,664 +1,667 @@
|
|
|
1
|
-
import { EventBus } from "../core/events.mjs"
|
|
2
|
-
import { EVENT_TYPES } from "../core/constants.mjs"
|
|
3
|
-
import { paint } from "../theme/color.mjs"
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
function
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
case "
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
case "
|
|
69
|
-
return ` ${prefix} ${name} ${paint(shortPath(args?.path
|
|
70
|
-
case "
|
|
71
|
-
case "
|
|
72
|
-
return ` ${prefix} ${name} ${paint(
|
|
73
|
-
case "
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
const
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
return ` ${paint(`${
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
const
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
return
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
const
|
|
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
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
const
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
}
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
payload.
|
|
501
|
-
payload.
|
|
502
|
-
payload.
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
1
|
+
import { EventBus } from "../core/events.mjs"
|
|
2
|
+
import { EVENT_TYPES } from "../core/constants.mjs"
|
|
3
|
+
import { paint } from "../theme/color.mjs"
|
|
4
|
+
|
|
5
|
+
const ANSI_RE = /\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~]|\][^\x07]*\x07)/g
|
|
6
|
+
function stripAnsi(text) { return String(text || "").replace(ANSI_RE, "") }
|
|
7
|
+
|
|
8
|
+
let _theme = null
|
|
9
|
+
function diffAdd(theme) { return (theme ?? _theme)?.components?.diff_add || "green" }
|
|
10
|
+
function diffDel(theme) { return (theme ?? _theme)?.components?.diff_del || "red" }
|
|
11
|
+
|
|
12
|
+
// ── Symbols ──────────────────────────────────────────────
|
|
13
|
+
export const SYM = {
|
|
14
|
+
dot: "●",
|
|
15
|
+
dotHollow: "○",
|
|
16
|
+
toolOk: "✓",
|
|
17
|
+
toolErr: "✗",
|
|
18
|
+
stage: "◆",
|
|
19
|
+
iteration: "↻",
|
|
20
|
+
phase: "●",
|
|
21
|
+
plan: "☐",
|
|
22
|
+
planDone: "☑",
|
|
23
|
+
recovery: "⟳",
|
|
24
|
+
alert: "!",
|
|
25
|
+
thinking: "▶",
|
|
26
|
+
thinkingOpen: "▼",
|
|
27
|
+
search: "*",
|
|
28
|
+
arrow: "→",
|
|
29
|
+
write: "◇"
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// ── Helpers ──────────────────────────────────────────────
|
|
33
|
+
|
|
34
|
+
function clipText(text, max) {
|
|
35
|
+
const s = String(text || "").trim()
|
|
36
|
+
if (s.length <= max) return s
|
|
37
|
+
return s.slice(0, max - 3) + "..."
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function shortPath(p) {
|
|
41
|
+
const s = String(p || "").trim()
|
|
42
|
+
// Show last 2-3 segments for readability
|
|
43
|
+
const parts = s.replace(/\\/g, "/").split("/")
|
|
44
|
+
if (parts.length <= 3) return s
|
|
45
|
+
return ".../" + parts.slice(-3).join("/")
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// ── Tool Display Formatters ──────────────────────────────
|
|
49
|
+
|
|
50
|
+
export function formatToolStart(toolName, args) {
|
|
51
|
+
// Compact single-line dim format (OpenCode style)
|
|
52
|
+
const sym = toolName === "grep" || toolName === "glob" || toolName === "websearch"
|
|
53
|
+
? SYM.search
|
|
54
|
+
: toolName === "write" || toolName === "edit" || toolName === "notebookedit"
|
|
55
|
+
? SYM.write
|
|
56
|
+
: SYM.arrow
|
|
57
|
+
const prefix = paint(sym, "#666666")
|
|
58
|
+
const name = paint(toolName.charAt(0).toUpperCase() + toolName.slice(1), null, { dim: true })
|
|
59
|
+
|
|
60
|
+
switch (toolName) {
|
|
61
|
+
case "bash": {
|
|
62
|
+
const desc = clipText(args?.description || args?.command, 80)
|
|
63
|
+
return ` ${prefix} ${name} ${paint(desc, null, { dim: true })}`
|
|
64
|
+
}
|
|
65
|
+
case "write":
|
|
66
|
+
case "edit":
|
|
67
|
+
return ` ${prefix} ${name} ${paint(shortPath(args?.path), null, { dim: true })}`
|
|
68
|
+
case "notebookedit":
|
|
69
|
+
return ` ${prefix} ${name} ${paint(shortPath(args?.path), null, { dim: true })} ${paint(`cell ${args?.cell_number ?? 0}`, null, { dim: true })}`
|
|
70
|
+
case "read":
|
|
71
|
+
case "list":
|
|
72
|
+
return ` ${prefix} ${name} ${paint(shortPath(args?.path || "."), null, { dim: true })}`
|
|
73
|
+
case "grep":
|
|
74
|
+
case "glob":
|
|
75
|
+
return ` ${prefix} ${name} ${paint(clipText(args?.pattern, 60), null, { dim: true })}`
|
|
76
|
+
case "task":
|
|
77
|
+
return ` ${prefix} ${name} ${paint(clipText(args?.description || args?.prompt, 60), null, { dim: true })}`
|
|
78
|
+
case "todowrite":
|
|
79
|
+
return null // handled by result preview only
|
|
80
|
+
case "webfetch":
|
|
81
|
+
return ` ${prefix} ${name} ${paint(clipText(args?.url, 60), null, { dim: true })}`
|
|
82
|
+
case "websearch":
|
|
83
|
+
return ` ${prefix} ${name} ${paint(clipText(args?.query, 60), null, { dim: true })}`
|
|
84
|
+
case "question":
|
|
85
|
+
return ` ~ ${paint("Asking questions...", null, { dim: true })}`
|
|
86
|
+
case "enter_plan":
|
|
87
|
+
return ` ${paint(SYM.plan, "magenta")} ${paint("Enter Plan", "magenta")}`
|
|
88
|
+
case "exit_plan":
|
|
89
|
+
return ` ${paint(SYM.planDone, "green")} ${paint("Submit Plan", "green")}`
|
|
90
|
+
default:
|
|
91
|
+
return ` ${prefix} ${name} ${paint(clipText(args ? Object.keys(args).slice(0, 3).join(", ") : "", 40), null, { dim: true })}`
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export function formatToolFinish(toolName, status, durationMs, args) {
|
|
96
|
+
if (status === "error") {
|
|
97
|
+
return ` ${paint(SYM.toolErr, "red")} ${paint(toolName, null, { dim: true })} ${paint("error", "red")}${durationMs ? paint(` ${durationMs}ms`, null, { dim: true }) : ""}`
|
|
98
|
+
}
|
|
99
|
+
// For completed tools, return null — the start line + result preview is enough
|
|
100
|
+
return null
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export function formatToolResultPreview(toolName, output, status, args) {
|
|
104
|
+
if (status !== "completed") return null
|
|
105
|
+
const text = String(output || "").trim()
|
|
106
|
+
|
|
107
|
+
switch (toolName) {
|
|
108
|
+
case "bash": {
|
|
109
|
+
const lines = stripAnsi(text).split("\n").filter(Boolean)
|
|
110
|
+
if (!lines.length) return null
|
|
111
|
+
const first = clipText(lines[0], 90)
|
|
112
|
+
const suffix = lines.length > 1 ? paint(` (+${lines.length - 1} lines)`, null, { dim: true }) : ""
|
|
113
|
+
return ` ${paint(first, null, { dim: true })}${suffix}`
|
|
114
|
+
}
|
|
115
|
+
case "write": {
|
|
116
|
+
const n = String(args?.content || "").split("\n").filter(Boolean).length
|
|
117
|
+
return ` ${paint(`+${n} lines`, diffAdd(), { dim: true })}`
|
|
118
|
+
}
|
|
119
|
+
case "edit": {
|
|
120
|
+
const added = String(args?.new_string || "").split("\n").filter(Boolean).length
|
|
121
|
+
const removed = String(args?.old_string || "").split("\n").filter(Boolean).length
|
|
122
|
+
const parts = []
|
|
123
|
+
if (added > 0) parts.push(paint(`+${added}`, diffAdd()))
|
|
124
|
+
if (removed > 0) parts.push(paint(`-${removed}`, diffDel()))
|
|
125
|
+
return parts.length ? ` ${parts.join(" ")} ${paint("lines", null, { dim: true })}` : null
|
|
126
|
+
}
|
|
127
|
+
case "grep": {
|
|
128
|
+
const lines = text.split("\n").filter(Boolean)
|
|
129
|
+
if (text === "no matches" || !lines.length) return ` ${paint("no matches", null, { dim: true })}`
|
|
130
|
+
return ` ${paint(`${lines.length} matches`, null, { dim: true })}`
|
|
131
|
+
}
|
|
132
|
+
case "read":
|
|
133
|
+
return ` ${paint(`${text.split("\n").length} lines`, null, { dim: true })}`
|
|
134
|
+
case "glob": {
|
|
135
|
+
const lines = text.split("\n").filter(Boolean)
|
|
136
|
+
if (!lines.length) return ` ${paint("no files", null, { dim: true })}`
|
|
137
|
+
return ` ${paint(`${lines.length} files`, null, { dim: true })}`
|
|
138
|
+
}
|
|
139
|
+
case "todowrite": {
|
|
140
|
+
const todos = Array.isArray(args?.todos) ? args.todos : []
|
|
141
|
+
if (!todos.length) return null
|
|
142
|
+
const result = []
|
|
143
|
+
for (const t of todos.slice(0, 8)) {
|
|
144
|
+
const s = t.status || "pending"
|
|
145
|
+
const dot = s === "completed" ? paint(SYM.toolOk, "green")
|
|
146
|
+
: s === "in_progress" ? paint(SYM.dot, "yellow")
|
|
147
|
+
: paint(SYM.dotHollow, "#666666")
|
|
148
|
+
const color = s === "completed" ? "green" : s === "in_progress" ? "yellow" : null
|
|
149
|
+
const label = s === "in_progress" && t.activeForm ? t.activeForm : t.content
|
|
150
|
+
result.push(` ${dot} ${paint(label || "", color, { dim: s === "completed" })}`)
|
|
151
|
+
}
|
|
152
|
+
if (todos.length > 8) result.push(paint(` ... +${todos.length - 8} more`, null, { dim: true }))
|
|
153
|
+
return result
|
|
154
|
+
}
|
|
155
|
+
default:
|
|
156
|
+
return null
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function formatToolError(error) {
|
|
161
|
+
if (!error) return null
|
|
162
|
+
return ` ${paint(clipText(stripAnsi(error), 120), "red", { dim: true })}`
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// ── Thinking Formatter ──────────────────────────────────
|
|
166
|
+
|
|
167
|
+
export function formatThinkingHeader() {
|
|
168
|
+
return `${paint(SYM.dot, "#666666")} ${paint("Thinking", null, { italic: true, dim: true })} ${paint("∨", null, { dim: true })}`
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// ── LongAgent Display Formatters ─────────────────────────
|
|
172
|
+
|
|
173
|
+
export function formatPhaseChange(prevPhase, nextPhase, reason) {
|
|
174
|
+
const arrow = paint("→", null, { dim: true })
|
|
175
|
+
const reasonText = reason ? paint(reason, null, { dim: true }) : ""
|
|
176
|
+
return `${paint(SYM.phase, "magenta")} ${paint("phase", "magenta", { bold: true })} ${paint(prevPhase, null, { dim: true })} ${arrow} ${paint(nextPhase, "magenta", { bold: true })} ${reasonText}`
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
export function formatStageStarted(stageId, taskCount) {
|
|
180
|
+
return `${paint(SYM.stage, "#fb923c", { bold: true })} ${paint("stage", "#fb923c", { bold: true })} ${paint(stageId, "white", { bold: true })} ${paint(`(${taskCount} tasks)`, null, { dim: true })}`
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
export function formatStageFinished(stageId, successCount, failCount) {
|
|
184
|
+
const status = failCount === 0
|
|
185
|
+
? paint("PASS", "green", { bold: true })
|
|
186
|
+
: paint(`FAIL (${failCount})`, "red", { bold: true })
|
|
187
|
+
return `${paint(SYM.stage, "#fb923c")} ${paint("stage", "#fb923c")} ${paint(stageId, "white")} ${status} ${paint(`(${successCount} ok)`, null, { dim: true })}`
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
export function formatTaskDispatched(_stageId, taskId, attempt) {
|
|
191
|
+
const attemptLabel = attempt > 1 ? paint(` retry#${attempt}`, "yellow") : ""
|
|
192
|
+
return ` ${paint(SYM.dot, "#666666")} ${paint("task", "cyan")} ${paint(taskId, null, { dim: true })}${attemptLabel}`
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
export function formatTaskFinished(taskId, status) {
|
|
196
|
+
const dot = status === "completed" ? paint(SYM.dot, "green") : paint(SYM.dot, "red")
|
|
197
|
+
const color = status === "completed" ? "green" : "red"
|
|
198
|
+
return ` ${dot} ${paint(taskId, null, { dim: true })} ${paint(status, color)}`
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
export function formatHeartbeat(iteration, maxIterations, phase, gate, progress, elapsed) {
|
|
202
|
+
const iterLabel = maxIterations > 0 ? `${iteration}/${maxIterations}` : String(iteration)
|
|
203
|
+
const progressLabel = progress?.percentage !== null && progress?.percentage !== undefined
|
|
204
|
+
? paint(`${progress.percentage}%`, "green")
|
|
205
|
+
: paint("...", null, { dim: true })
|
|
206
|
+
const elapsedLabel = elapsed !== undefined ? paint(`${elapsed}s`, null, { dim: true }) : ""
|
|
207
|
+
return `${paint(SYM.iteration, "#fb923c")} ${paint("iter", "#fb923c")} ${paint(iterLabel, "white", { bold: true })} phase=${paint(phase || "-", "magenta")} gate=${paint(gate || "-", "cyan")} progress=${progressLabel} ${elapsedLabel}`
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
export function formatPlanFrozen(planId, stageCount) {
|
|
211
|
+
return `${paint(SYM.planDone, "green", { bold: true })} ${paint("plan frozen", "green", { bold: true })} ${paint(planId || "", null, { dim: true })} ${paint(`${stageCount} stage(s)`, null, { dim: true })}`
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
export function formatRecovery(reason, recoveryCount) {
|
|
215
|
+
return `${paint(SYM.recovery, "yellow", { bold: true })} ${paint("recovery", "yellow", { bold: true })} #${recoveryCount} ${paint(reason || "", null, { dim: true })}`
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
export function formatAlert(kind, message) {
|
|
219
|
+
return `${paint(SYM.alert, "red", { bold: true })} ${paint("alert", "red", { bold: true })} [${kind}] ${paint(message || "", null, { dim: true })}`
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
export function formatIntakeStarted(objective) {
|
|
223
|
+
const preview = clipText(objective, 80)
|
|
224
|
+
return `${paint(SYM.phase, "magenta")} ${paint("intake", "magenta", { bold: true })} ${paint(preview, null, { dim: true })}`
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
export function formatGateChecked(gate, status) {
|
|
228
|
+
const dot = status === "pass" ? paint(SYM.dot, "green") : paint(SYM.dot, "yellow")
|
|
229
|
+
return ` ${dot} gate=${paint(gate || "-", "cyan")} ${paint(status || "-", status === "pass" ? "green" : "yellow")}`
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// ── Hybrid Stage Formatters ──────────────────────────────
|
|
233
|
+
|
|
234
|
+
function hybridBanner(label, color) {
|
|
235
|
+
const line = paint("━".repeat(40), color, { dim: true })
|
|
236
|
+
return `${line}\n${paint(label, color, { bold: true })}`
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
export function formatHybridPreviewStart(objective) {
|
|
240
|
+
const preview = clipText(objective, 70)
|
|
241
|
+
return `${hybridBanner("H1 Preview", "#3b82f6")}\n ${paint(preview, null, { dim: true })}`
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
export function formatHybridPreviewComplete(findingsLength) {
|
|
245
|
+
return ` ${paint(SYM.toolOk, "green")} ${paint("preview complete", "green")} ${paint(`(${findingsLength} chars)`, null, { dim: true })}`
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
export function formatHybridBlueprintStart() {
|
|
249
|
+
return hybridBanner("H2 Blueprint", "#a855f7")
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
export function formatHybridBlueprintComplete(planId, stageCount) {
|
|
253
|
+
return ` ${paint(SYM.toolOk, "green")} ${paint("blueprint complete", "green")} ${paint(planId || "", null, { dim: true })} ${paint(`${stageCount} stage(s)`, null, { dim: true })}`
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
export function formatHybridBlueprintReview(planId) {
|
|
257
|
+
return ` ${paint("⏳", "yellow")} ${paint("awaiting blueprint review", "yellow")} ${paint(planId || "", null, { dim: true })}`
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
export function formatHybridBlueprintValidated(totalTasks, totalFiles, valid) {
|
|
261
|
+
const status = valid ? paint("PASS", "green") : paint("WARN", "yellow")
|
|
262
|
+
return ` ${paint(SYM.dot, valid ? "green" : "yellow")} ${paint("blueprint validation", null, { dim: true })} ${status} ${paint(`${totalTasks} tasks, ${totalFiles} files`, null, { dim: true })}`
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
// ── Hybrid Debugging/Rollback Formatters ─────────────────
|
|
266
|
+
|
|
267
|
+
export function formatHybridDebuggingStart(codingRollbackCount) {
|
|
268
|
+
const suffix = codingRollbackCount > 0 ? ` ${paint(`(rollback #${codingRollbackCount})`, "yellow")}` : ""
|
|
269
|
+
return `${hybridBanner("H5 Debugging", "#fb923c")}${suffix}`
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
export function formatHybridDebuggingComplete(debugIter, rollback) {
|
|
273
|
+
const status = rollback
|
|
274
|
+
? paint("ROLLBACK", "yellow", { bold: true })
|
|
275
|
+
: paint("PASS", "green", { bold: true })
|
|
276
|
+
return ` ${paint(SYM.dot, rollback ? "yellow" : "green")} ${paint("debugging", null, { dim: true })} ${status} ${paint(`(${debugIter} iters)`, null, { dim: true })}`
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
export function formatHybridReturnToCoding(rollbackCount, failedTaskIds) {
|
|
280
|
+
const tasks = failedTaskIds?.length ? paint(` [${failedTaskIds.join(", ")}]`, null, { dim: true }) : ""
|
|
281
|
+
return ` ${paint(SYM.recovery, "yellow")} ${paint(`rollback to coding #${rollbackCount}`, "yellow")}${tasks}`
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
export function formatHybridCrossReview(fileCount) {
|
|
285
|
+
return ` ${paint(SYM.dot, "cyan")} ${paint("cross-review", "cyan")} ${paint(`${fileCount} file(s)`, null, { dim: true })}`
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
// ── Hybrid Incremental/Budget/Context Formatters ─────────
|
|
289
|
+
|
|
290
|
+
export function formatHybridIncrementalGate(stageId, passed) {
|
|
291
|
+
const dot = passed ? paint(SYM.dot, "green") : paint(SYM.dot, "yellow")
|
|
292
|
+
const status = passed ? paint("pass", "green") : paint("warn", "yellow")
|
|
293
|
+
return ` ${dot} ${paint("gate", null, { dim: true })} ${paint(stageId, "cyan")} ${status}`
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
export function formatHybridContextCompressed(newLength) {
|
|
297
|
+
return ` ${paint(SYM.dot, "#666666")} ${paint(`context compressed → ${newLength} chars`, null, { dim: true })}`
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
export function formatHybridBudgetWarning(totalTokens, budgetLimit, percentage) {
|
|
301
|
+
const color = percentage >= 100 ? "red" : "yellow"
|
|
302
|
+
return ` ${paint(SYM.alert, color)} ${paint("budget", color, { bold: true })} ${paint(`${percentage}%`, color)} ${paint(`(${totalTokens}/${budgetLimit})`, null, { dim: true })}`
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
export function formatHybridCheckpointResumed(stageIndex, iteration) {
|
|
306
|
+
return ` ${paint(SYM.dot, "cyan")} ${paint("checkpoint resumed", "cyan")} ${paint(`stage ${stageIndex}, iter ${iteration}`, null, { dim: true })}`
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
export function formatHybridReplan(newStageCount) {
|
|
310
|
+
return ` ${paint(SYM.dot, "#a855f7")} ${paint("replan", "#a855f7", { bold: true })} ${paint(`→ ${newStageCount} stage(s)`, null, { dim: true })}`
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
// ── Hybrid Memory Formatters ─────────────────────────────
|
|
314
|
+
|
|
315
|
+
export function formatHybridMemoryLoaded(techStack) {
|
|
316
|
+
const items = Array.isArray(techStack) ? techStack.slice(0, 5).join(", ") : ""
|
|
317
|
+
return ` ${paint(SYM.dot, "#666666")} ${paint("memory loaded", null, { dim: true })} ${paint(items, null, { dim: true })}`
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
export function formatHybridMemorySaved(techStackCount) {
|
|
321
|
+
return ` ${paint(SYM.dot, "#666666")} ${paint(`memory saved (${techStackCount} items)`, null, { dim: true })}`
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
// ── Git Formatters ───────────────────────────────────────
|
|
325
|
+
|
|
326
|
+
export function formatGitBranchCreated(branch, baseBranch) {
|
|
327
|
+
return ` ${paint(SYM.dot, "green")} ${paint("git branch", "green")} ${paint(branch, "white", { bold: true })} ${paint(`← ${baseBranch}`, null, { dim: true })}`
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
export function formatGitStageCommitted(stageId, message) {
|
|
331
|
+
return ` ${paint(SYM.dot, "#666666")} ${paint("commit", null, { dim: true })} ${paint(clipText(message || stageId, 60), null, { dim: true })}`
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
export function formatGitMerged(branch, baseBranch) {
|
|
335
|
+
return ` ${paint(SYM.toolOk, "green")} ${paint("git merged", "green", { bold: true })} ${paint(branch, null, { dim: true })} ${paint("→", null, { dim: true })} ${paint(baseBranch, "white")}`
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
// ── Plan Progress Formatter ──────────────────────────────
|
|
339
|
+
|
|
340
|
+
export function formatPlanProgress(taskProgress) {
|
|
341
|
+
if (!taskProgress || typeof taskProgress !== "object") return []
|
|
342
|
+
const entries = Object.entries(taskProgress)
|
|
343
|
+
if (!entries.length) return []
|
|
344
|
+
|
|
345
|
+
const lines = [paint("Plan Progress:", "cyan", { bold: true })]
|
|
346
|
+
for (const [taskId, tp] of entries) {
|
|
347
|
+
const status = tp?.status || "pending"
|
|
348
|
+
const dot = status === "completed"
|
|
349
|
+
? paint(SYM.dot, "green")
|
|
350
|
+
: status === "error"
|
|
351
|
+
? paint(SYM.dot, "red")
|
|
352
|
+
: paint(SYM.dotHollow, "#666666")
|
|
353
|
+
const color = status === "completed" ? "green" : status === "error" ? "red" : "white"
|
|
354
|
+
lines.push(` ${dot} ${taskId} ${paint(status, color)}`)
|
|
355
|
+
}
|
|
356
|
+
return lines
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
// ── Recovery Suggestions Formatter ───────────────────────
|
|
360
|
+
|
|
361
|
+
export function formatRecoverySuggestions(recovery) {
|
|
362
|
+
if (!recovery) return []
|
|
363
|
+
const lines = []
|
|
364
|
+
lines.push(paint("Recovery Suggestions:", "yellow", { bold: true }))
|
|
365
|
+
|
|
366
|
+
if (recovery.summary) {
|
|
367
|
+
lines.push(` ${paint(recovery.summary, null, { dim: true })}`)
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
if (recovery.suggestions?.length) {
|
|
371
|
+
for (const s of recovery.suggestions) {
|
|
372
|
+
lines.push(` ${paint(SYM.alert, "yellow")} ${paint(s, "yellow")}`)
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
if (recovery.failedTasks?.length) {
|
|
377
|
+
lines.push(paint(" Failed Tasks:", "red"))
|
|
378
|
+
for (const t of recovery.failedTasks.slice(0, 5)) {
|
|
379
|
+
lines.push(` ${paint(SYM.dot, "red")} ${t.taskId} [${t.category}]: ${paint(t.error || "", null, { dim: true })}`)
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
if (recovery.manualSteps?.length) {
|
|
384
|
+
lines.push(paint(" Manual Steps:", "cyan"))
|
|
385
|
+
for (const step of recovery.manualSteps.slice(0, 5)) {
|
|
386
|
+
lines.push(` ${paint(SYM.arrow, "cyan")} ${paint(step, null, { dim: true })}`)
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
if (recovery.resumeHint) {
|
|
391
|
+
lines.push(` ${paint(SYM.dot, "green")} ${paint(recovery.resumeHint, "green")}`)
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
return lines
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
// ── Renderer ─────────────────────────────────────────────
|
|
398
|
+
|
|
399
|
+
export function createActivityRenderer({ output, theme = null }) {
|
|
400
|
+
_theme = theme
|
|
401
|
+
const log = typeof output?.appendLog === "function"
|
|
402
|
+
? output.appendLog
|
|
403
|
+
: (text) => console.log(text)
|
|
404
|
+
|
|
405
|
+
const toolTimers = new Map()
|
|
406
|
+
let timerCounter = 0
|
|
407
|
+
let unsubscribe = null
|
|
408
|
+
|
|
409
|
+
function timerKey(sessionId, turnId, toolName) {
|
|
410
|
+
return `${sessionId || ""}:${turnId || ""}:${toolName}:${timerCounter++}`
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
// Track the latest timer key per tool invocation
|
|
414
|
+
const activeToolKeys = new Map()
|
|
415
|
+
// Track tool args for finish formatting
|
|
416
|
+
const activeToolArgs = new Map()
|
|
417
|
+
|
|
418
|
+
function handleEvent(event) {
|
|
419
|
+
const { type, payload, sessionId, turnId } = event
|
|
420
|
+
|
|
421
|
+
switch (type) {
|
|
422
|
+
case EVENT_TYPES.TOOL_START: {
|
|
423
|
+
const key = timerKey(sessionId, turnId, payload.tool)
|
|
424
|
+
const lookupKey = `${sessionId}:${turnId}:${payload.tool}`
|
|
425
|
+
toolTimers.set(key, Date.now())
|
|
426
|
+
activeToolKeys.set(lookupKey, key)
|
|
427
|
+
activeToolArgs.set(lookupKey, payload.args)
|
|
428
|
+
// Show tool call inline (compact dim line)
|
|
429
|
+
const startLine = formatToolStart(payload.tool, payload.args)
|
|
430
|
+
if (startLine) log(startLine)
|
|
431
|
+
break
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
case EVENT_TYPES.TOOL_FINISH: {
|
|
435
|
+
const lookupKey = `${sessionId}:${turnId}:${payload.tool}`
|
|
436
|
+
const key = activeToolKeys.get(lookupKey)
|
|
437
|
+
const savedArgs = activeToolArgs.get(lookupKey) || payload.args
|
|
438
|
+
if (key) {
|
|
439
|
+
toolTimers.delete(key)
|
|
440
|
+
activeToolKeys.delete(lookupKey)
|
|
441
|
+
activeToolArgs.delete(lookupKey)
|
|
442
|
+
}
|
|
443
|
+
const finishLine = formatToolFinish(payload.tool, payload.status, 0, savedArgs)
|
|
444
|
+
if (finishLine) log(finishLine)
|
|
445
|
+
const preview = formatToolResultPreview(payload.tool, payload.output, payload.status, savedArgs)
|
|
446
|
+
if (preview) {
|
|
447
|
+
if (Array.isArray(preview)) {
|
|
448
|
+
for (const line of preview) log(line)
|
|
449
|
+
} else {
|
|
450
|
+
log(preview)
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
// Blank line after tool for visual spacing
|
|
454
|
+
if (payload.tool !== "todowrite") log("")
|
|
455
|
+
break
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
case EVENT_TYPES.TOOL_ERROR: {
|
|
459
|
+
const lookupKey = `${sessionId}:${turnId}:${payload.tool}`
|
|
460
|
+
const key = activeToolKeys.get(lookupKey)
|
|
461
|
+
const savedArgs = activeToolArgs.get(lookupKey) || payload.args
|
|
462
|
+
if (key) {
|
|
463
|
+
toolTimers.delete(key)
|
|
464
|
+
activeToolKeys.delete(lookupKey)
|
|
465
|
+
activeToolArgs.delete(lookupKey)
|
|
466
|
+
}
|
|
467
|
+
log(formatToolFinish(payload.tool, payload.status || "error", 0, savedArgs))
|
|
468
|
+
const errLine = formatToolError(payload.error)
|
|
469
|
+
if (errLine) log(errLine)
|
|
470
|
+
break
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
case EVENT_TYPES.LONGAGENT_PHASE_CHANGED: {
|
|
474
|
+
log(formatPhaseChange(payload.prevPhase, payload.nextPhase, payload.reason))
|
|
475
|
+
break
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
case EVENT_TYPES.LONGAGENT_STAGE_STARTED: {
|
|
479
|
+
log(formatStageStarted(payload.stageId, payload.taskCount))
|
|
480
|
+
break
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
case EVENT_TYPES.LONGAGENT_STAGE_FINISHED: {
|
|
484
|
+
log(formatStageFinished(payload.stageId, payload.successCount, payload.failCount))
|
|
485
|
+
break
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
case EVENT_TYPES.LONGAGENT_STAGE_TASK_DISPATCHED: {
|
|
489
|
+
log(formatTaskDispatched(payload.stageId, payload.taskId, payload.attempt))
|
|
490
|
+
break
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
case EVENT_TYPES.LONGAGENT_STAGE_TASK_FINISHED: {
|
|
494
|
+
log(formatTaskFinished(payload.taskId, payload.status))
|
|
495
|
+
break
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
case EVENT_TYPES.LONGAGENT_HEARTBEAT: {
|
|
499
|
+
log(formatHeartbeat(
|
|
500
|
+
payload.iteration,
|
|
501
|
+
payload.maxIterations,
|
|
502
|
+
payload.phase,
|
|
503
|
+
payload.gate,
|
|
504
|
+
payload.progress,
|
|
505
|
+
payload.elapsed
|
|
506
|
+
))
|
|
507
|
+
break
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
case EVENT_TYPES.LONGAGENT_PLAN_FROZEN: {
|
|
511
|
+
log(formatPlanFrozen(payload.planId, payload.stageCount))
|
|
512
|
+
break
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
case EVENT_TYPES.LONGAGENT_RECOVERY_ENTERED: {
|
|
516
|
+
log(formatRecovery(payload.reason, payload.recoveryCount))
|
|
517
|
+
break
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
case EVENT_TYPES.LONGAGENT_ALERT: {
|
|
521
|
+
log(formatAlert(payload.kind, payload.message))
|
|
522
|
+
break
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
case EVENT_TYPES.LONGAGENT_INTAKE_STARTED: {
|
|
526
|
+
log(formatIntakeStarted(payload.objective))
|
|
527
|
+
break
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
case EVENT_TYPES.LONGAGENT_GATE_CHECKED: {
|
|
531
|
+
log(formatGateChecked(payload.gate, payload.status))
|
|
532
|
+
break
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
// ── Hybrid Events ──────────────────────────────
|
|
536
|
+
case EVENT_TYPES.LONGAGENT_HYBRID_PREVIEW_START: {
|
|
537
|
+
log(formatHybridPreviewStart(payload.objective))
|
|
538
|
+
break
|
|
539
|
+
}
|
|
540
|
+
case EVENT_TYPES.LONGAGENT_HYBRID_PREVIEW_COMPLETE: {
|
|
541
|
+
log(formatHybridPreviewComplete(payload.findingsLength))
|
|
542
|
+
break
|
|
543
|
+
}
|
|
544
|
+
case EVENT_TYPES.LONGAGENT_HYBRID_BLUEPRINT_START: {
|
|
545
|
+
log(formatHybridBlueprintStart())
|
|
546
|
+
break
|
|
547
|
+
}
|
|
548
|
+
case EVENT_TYPES.LONGAGENT_HYBRID_BLUEPRINT_COMPLETE: {
|
|
549
|
+
log(formatHybridBlueprintComplete(payload.planId, payload.stageCount))
|
|
550
|
+
break
|
|
551
|
+
}
|
|
552
|
+
case EVENT_TYPES.LONGAGENT_HYBRID_BLUEPRINT_REVIEW: {
|
|
553
|
+
log(formatHybridBlueprintReview(payload.planId))
|
|
554
|
+
break
|
|
555
|
+
}
|
|
556
|
+
case EVENT_TYPES.LONGAGENT_HYBRID_BLUEPRINT_VALIDATED: {
|
|
557
|
+
log(formatHybridBlueprintValidated(payload.totalTasks, payload.totalFiles, payload.valid))
|
|
558
|
+
break
|
|
559
|
+
}
|
|
560
|
+
case EVENT_TYPES.LONGAGENT_HYBRID_DEBUGGING_START: {
|
|
561
|
+
log(formatHybridDebuggingStart(payload.codingRollbackCount))
|
|
562
|
+
break
|
|
563
|
+
}
|
|
564
|
+
case EVENT_TYPES.LONGAGENT_HYBRID_DEBUGGING_COMPLETE: {
|
|
565
|
+
log(formatHybridDebuggingComplete(payload.debugIter, payload.rollback))
|
|
566
|
+
break
|
|
567
|
+
}
|
|
568
|
+
case EVENT_TYPES.LONGAGENT_HYBRID_RETURN_TO_CODING: {
|
|
569
|
+
log(formatHybridReturnToCoding(payload.rollbackCount, payload.failedTaskIds))
|
|
570
|
+
break
|
|
571
|
+
}
|
|
572
|
+
case EVENT_TYPES.LONGAGENT_HYBRID_CROSS_REVIEW: {
|
|
573
|
+
log(formatHybridCrossReview(payload.fileCount))
|
|
574
|
+
break
|
|
575
|
+
}
|
|
576
|
+
case EVENT_TYPES.LONGAGENT_HYBRID_INCREMENTAL_GATE: {
|
|
577
|
+
log(formatHybridIncrementalGate(payload.stageId, payload.passed))
|
|
578
|
+
break
|
|
579
|
+
}
|
|
580
|
+
case EVENT_TYPES.LONGAGENT_HYBRID_CONTEXT_COMPRESSED: {
|
|
581
|
+
log(formatHybridContextCompressed(payload.newLength))
|
|
582
|
+
break
|
|
583
|
+
}
|
|
584
|
+
case EVENT_TYPES.LONGAGENT_HYBRID_BUDGET_WARNING: {
|
|
585
|
+
log(formatHybridBudgetWarning(payload.totalTokens, payload.budgetLimit, payload.percentage))
|
|
586
|
+
break
|
|
587
|
+
}
|
|
588
|
+
case EVENT_TYPES.LONGAGENT_HYBRID_CHECKPOINT_RESUMED: {
|
|
589
|
+
log(formatHybridCheckpointResumed(payload.stageIndex, payload.iteration))
|
|
590
|
+
break
|
|
591
|
+
}
|
|
592
|
+
case EVENT_TYPES.LONGAGENT_HYBRID_REPLAN: {
|
|
593
|
+
log(formatHybridReplan(payload.newStageCount))
|
|
594
|
+
break
|
|
595
|
+
}
|
|
596
|
+
case EVENT_TYPES.LONGAGENT_HYBRID_MEMORY_LOADED: {
|
|
597
|
+
log(formatHybridMemoryLoaded(payload.techStack))
|
|
598
|
+
break
|
|
599
|
+
}
|
|
600
|
+
case EVENT_TYPES.LONGAGENT_HYBRID_MEMORY_SAVED: {
|
|
601
|
+
log(formatHybridMemorySaved(payload.techStackCount))
|
|
602
|
+
break
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
// ── New Fault Recovery Events ──────────────────
|
|
606
|
+
case EVENT_TYPES.LONGAGENT_DEGRADATION_APPLIED: {
|
|
607
|
+
log(formatAlert("degradation", `${payload.strategy} applied in ${payload.phase}${payload.reason ? ` (${payload.reason})` : ""}`))
|
|
608
|
+
break
|
|
609
|
+
}
|
|
610
|
+
case EVENT_TYPES.LONGAGENT_WRITE_LOOP_DETECTED: {
|
|
611
|
+
log(formatAlert("write_loop", payload.message || "write loop detected"))
|
|
612
|
+
break
|
|
613
|
+
}
|
|
614
|
+
case EVENT_TYPES.LONGAGENT_SEMANTIC_ERROR_REPEATED: {
|
|
615
|
+
log(formatAlert("semantic_error", `repeated ${payload.count}x: ${(payload.error || "").slice(0, 80)}`))
|
|
616
|
+
break
|
|
617
|
+
}
|
|
618
|
+
case EVENT_TYPES.LONGAGENT_PHASE_TIMEOUT: {
|
|
619
|
+
log(formatAlert("phase_timeout", `${payload.phase} timed out after ${Math.round((payload.elapsed || 0) / 1000)}s`))
|
|
620
|
+
break
|
|
621
|
+
}
|
|
622
|
+
case EVENT_TYPES.LONGAGENT_GIT_CONFLICT_RESOLUTION: {
|
|
623
|
+
log(formatAlert("git_conflict", `resolving conflicts in ${(payload.files || []).length} file(s)`))
|
|
624
|
+
break
|
|
625
|
+
}
|
|
626
|
+
case EVENT_TYPES.LONGAGENT_CHECKPOINT_CLEANED: {
|
|
627
|
+
log(` ${paint(SYM.dot, "#666666")} ${paint(`checkpoints cleaned (${payload.removed} removed)`, null, { dim: true })}`)
|
|
628
|
+
break
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
// ── Git Events ─────────────────────────────────
|
|
632
|
+
case EVENT_TYPES.LONGAGENT_GIT_BRANCH_CREATED: {
|
|
633
|
+
log(formatGitBranchCreated(payload.branch, payload.baseBranch))
|
|
634
|
+
break
|
|
635
|
+
}
|
|
636
|
+
case EVENT_TYPES.LONGAGENT_GIT_STAGE_COMMITTED: {
|
|
637
|
+
log(formatGitStageCommitted(payload.stageId, payload.message))
|
|
638
|
+
break
|
|
639
|
+
}
|
|
640
|
+
case EVENT_TYPES.LONGAGENT_GIT_MERGED: {
|
|
641
|
+
log(formatGitMerged(payload.branch, payload.baseBranch))
|
|
642
|
+
break
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
case EVENT_TYPES.SESSION_COMPACTED: {
|
|
646
|
+
log(`${paint(SYM.phase, "magenta")} ${paint("context compacted", "magenta", { dim: true })}`)
|
|
647
|
+
break
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
return {
|
|
653
|
+
start() {
|
|
654
|
+
if (unsubscribe) return
|
|
655
|
+
unsubscribe = EventBus.subscribe(handleEvent)
|
|
656
|
+
},
|
|
657
|
+
stop() {
|
|
658
|
+
if (unsubscribe) {
|
|
659
|
+
unsubscribe()
|
|
660
|
+
unsubscribe = null
|
|
661
|
+
}
|
|
662
|
+
toolTimers.clear()
|
|
663
|
+
activeToolKeys.clear()
|
|
664
|
+
activeToolArgs.clear()
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
}
|