@playcraft/cli 0.0.56 → 0.0.58
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 +41 -26
- package/REMIX.md +17 -5
- package/dist/cli-root-help.js +1 -0
- package/dist/commands/build-all.js +10 -9
- package/dist/commands/build.js +13 -12
- package/dist/commands/create.js +26 -33
- package/dist/commands/platform-skills.generated.js +19 -0
- package/dist/commands/remix.js +48 -2
- package/dist/commands/skills.js +2 -0
- package/dist/commands/tools-generation.js +1 -1
- package/dist/commands/workspace-runtime.js +54 -0
- package/dist/index.js +3 -1
- package/dist/project-skills/commands.js +72 -0
- package/dist/project-skills/lifecycle.js +28 -0
- package/dist/project-skills/local-run.js +112 -0
- package/dist/project-skills/local-store.js +149 -0
- package/dist/project-skills/messages.js +72 -0
- package/dist/project-skills/reconcile.js +62 -0
- package/dist/project-skills/remote-cache.js +51 -0
- package/dist/project-skills/validation.js +19 -0
- package/dist/remix/clone.js +4 -1
- package/dist/remix/init-template.js +2 -0
- package/dist/remix/local-git.js +56 -0
- package/dist/remix/pull.js +66 -0
- package/dist/remix/push.js +90 -0
- package/dist/utils/agent-api-client.js +54 -17
- package/dist/utils/tool-operation-journal.js +54 -0
- package/dist/workspace-runtime/codex/app-server.js +395 -0
- package/dist/workspace-runtime/codex/config-toml.js +25 -0
- package/dist/workspace-runtime/codex/jsonrpc-stdio.js +106 -0
- package/dist/workspace-runtime/codex/loopback.js +59 -0
- package/dist/workspace-runtime/codex/native-adapter.js +3 -0
- package/dist/workspace-runtime/main.js +10 -0
- package/dist/workspace-runtime/persistence/journal.js +350 -0
- package/dist/workspace-runtime/processes/managed-writes.js +69 -0
- package/dist/workspace-runtime/serve.js +111 -0
- package/dist/workspace-runtime/server/auth.js +21 -0
- package/dist/workspace-runtime/server/dispatch.js +861 -0
- package/dist/workspace-runtime/server/execution-group.js +88 -0
- package/dist/workspace-runtime/server/http.js +561 -0
- package/dist/workspace-runtime/server/lock.js +53 -0
- package/dist/workspace-runtime/server/types.js +1 -0
- package/dist/workspace-runtime/workspaces/context-error.js +2 -0
- package/dist/workspace-runtime/workspaces/file-snapshot.js +213 -0
- package/dist/workspace-runtime/workspaces/files.js +143 -0
- package/dist/workspace-runtime/workspaces/git.js +242 -0
- package/dist/workspace-runtime/workspaces/json5-edit.js +170 -0
- package/dist/workspace-runtime/workspaces/parameters.js +272 -0
- package/dist/workspace-runtime/workspaces/prepare.js +364 -0
- package/dist/workspace-runtime/workspaces/registry.js +107 -0
- package/dist/workspace-runtime/workspaces/revisions.js +31 -0
- package/package.json +6 -3
- package/project-template-v2/.claude/agents/artist.md +82 -0
- package/project-template-v2/.claude/agents/developer.md +153 -0
- package/project-template-v2/.claude/agents/game-designer.md +264 -0
- package/project-template-v2/.claude/agents/refs/artist-art-style-catalog.md +533 -0
- package/project-template-v2/.claude/agents/refs/artist-color-audio-recipes.md +153 -0
- package/project-template-v2/.claude/agents/refs/artist-dimension-axis.md +27 -0
- package/project-template-v2/.claude/agents/refs/artist-master-composite-recipes.md +208 -0
- package/project-template-v2/.claude/agents/refs/atom-skill-library.md +81 -0
- package/project-template-v2/.claude/agents/refs/developer-impl-cookbook.md +432 -0
- package/project-template-v2/.claude/agents/refs/framework-5-component-filter.md +252 -0
- package/project-template-v2/.claude/agents/refs/framework-game-feel-juice.md +266 -0
- package/project-template-v2/.claude/agents/refs/framework-mda.md +147 -0
- package/project-template-v2/.claude/agents/refs/game-designer-gameplay-sufficiency.md +123 -0
- package/project-template-v2/.claude/agents/refs/ta-3d-flip-recipe.md +88 -0
- package/project-template-v2/.claude/agents/refs/ta-atlas-deliverable-standard.md +67 -0
- package/project-template-v2/.claude/agents/refs/ta-batch-pipeline-recipes.md +120 -0
- package/project-template-v2/.claude/agents/refs/ta-image-generation-detail.md +300 -0
- package/project-template-v2/.claude/agents/refs/ta-image-ops-reference.md +495 -0
- package/project-template-v2/.claude/agents/refs/ta-pipeline-cookbook.md +1141 -0
- package/project-template-v2/.claude/agents/refs/ta-tools-reference.md +111 -0
- package/project-template-v2/.claude/agents/refs/ta-vfx-preset-catalog.md +365 -0
- package/project-template-v2/.claude/agents/refs/threejs-cannon-pitfalls.md +412 -0
- package/project-template-v2/.claude/agents/reviewer.md +75 -0
- package/project-template-v2/.claude/agents/technical-artist.md +86 -0
- package/project-template-v2/.claude/hooks/snapshot-milestone.mjs +243 -0
- package/project-template-v2/.claude/hooks/user-prompt.mjs +133 -0
- package/project-template-v2/.claude/settings.json +33 -0
- package/project-template-v2/.claude/skills/brainstorming/SKILL.md +161 -0
- package/project-template-v2/.claude/skills/brainstorming/scripts/frame-template.html +270 -0
- package/project-template-v2/.claude/skills/brainstorming/scripts/helper.js +177 -0
- package/project-template-v2/.claude/skills/brainstorming/scripts/server.cjs +354 -0
- package/project-template-v2/.claude/skills/brainstorming/scripts/start-server.sh +148 -0
- package/project-template-v2/.claude/skills/brainstorming/scripts/stop-server.sh +56 -0
- package/project-template-v2/.claude/skills/brainstorming/scripts/wait-for-selection.sh +62 -0
- package/project-template-v2/.claude/skills/brainstorming/spec-document-reviewer-prompt.md +49 -0
- package/project-template-v2/.claude/skills/brainstorming/visual-companion.md +309 -0
- package/project-template-v2/.claude/skills/playcraft-ad-psychology/SKILL.md +182 -0
- package/project-template-v2/.claude/skills/playcraft-art-style-guide/SKILL.md +123 -0
- package/project-template-v2/.claude/skills/playcraft-asset-state-sheet/SKILL.md +205 -0
- package/project-template-v2/.claude/skills/playcraft-audio-generation/SKILL.md +280 -0
- package/project-template-v2/.claude/skills/playcraft-batch-pipeline/SKILL.md +184 -0
- package/project-template-v2/.claude/skills/playcraft-build-optimizer/SKILL.md +306 -0
- package/project-template-v2/.claude/skills/playcraft-image-generation/SKILL.md +298 -0
- package/project-template-v2/.claude/skills/playcraft-image-generation/reference/build-sprite-sheet.template.mjs +123 -0
- package/project-template-v2/.claude/skills/playcraft-image-generation/reference/compare-style.template.mjs +254 -0
- package/project-template-v2/.claude/skills/playcraft-image-generation/reference/gen-batch-sprite.template.mjs +324 -0
- package/project-template-v2/.claude/skills/playcraft-image-generation/reference/gen-batch.template.mjs +97 -0
- package/project-template-v2/.claude/skills/playcraft-image-generation/reference/gen-edit-variants.template.mjs +118 -0
- package/project-template-v2/.claude/skills/playcraft-image-generation/reference/process-batch.template.mjs +137 -0
- package/project-template-v2/.claude/skills/playcraft-image-generation/reference/prompt-cookbook.md +397 -0
- package/project-template-v2/.claude/skills/playcraft-image-generation/reference/validate-sprite-sheet.template.mjs +296 -0
- package/project-template-v2/.claude/skills/playcraft-image-ops/SKILL.md +122 -0
- package/project-template-v2/.claude/skills/playcraft-image-processing/SKILL.md +219 -0
- package/project-template-v2/.claude/skills/playcraft-masking/SKILL.md +373 -0
- package/project-template-v2/.claude/skills/playcraft-playable-optimization/SKILL.md +161 -0
- package/project-template-v2/.claude/skills/playcraft-research/SKILL.md +215 -0
- package/project-template-v2/.claude/skills/playcraft-skill-recommender/SKILL.md +382 -0
- package/project-template-v2/.claude/skills/playcraft-sprite-generation/SKILL.md +423 -0
- package/project-template-v2/.claude/skills/playcraft-sprite-remix/SKILL.md +158 -0
- package/project-template-v2/.claude/skills/playcraft-sprite-sheet/SKILL.md +100 -0
- package/project-template-v2/.claude/skills/playcraft-storyboard/SKILL.md +167 -0
- package/project-template-v2/.claude/skills/playcraft-style-qa/SKILL.md +270 -0
- package/project-template-v2/.claude/skills/playcraft-text-rendering/SKILL.md +236 -0
- package/project-template-v2/.claude/skills/playcraft-vfx-animation/SKILL.md +130 -0
- package/project-template-v2/.claude/skills/playwright-cli/SKILL.md +390 -0
- package/project-template-v2/.claude/skills/playwright-cli/references/element-attributes.md +23 -0
- package/project-template-v2/.claude/skills/playwright-cli/references/playwright-tests.md +39 -0
- package/project-template-v2/.claude/skills/playwright-cli/references/request-mocking.md +87 -0
- package/project-template-v2/.claude/skills/playwright-cli/references/running-code.md +240 -0
- package/project-template-v2/.claude/skills/playwright-cli/references/session-management.md +226 -0
- package/project-template-v2/.claude/skills/playwright-cli/references/spec-driven-testing.md +308 -0
- package/project-template-v2/.claude/skills/playwright-cli/references/storage-state.md +275 -0
- package/project-template-v2/.claude/skills/playwright-cli/references/test-generation.md +134 -0
- package/project-template-v2/.claude/skills/playwright-cli/references/tracing.md +142 -0
- package/project-template-v2/.claude/skills/playwright-cli/references/video-recording.md +153 -0
- package/project-template-v2/.claude/skills/session-analyzer/SKILL.md +386 -0
- package/project-template-v2/.claude/skills/session-analyzer/scripts/execution-breakdown.mjs +182 -0
- package/project-template-v2/.claude/skills/session-analyzer/scripts/find-turns.mjs +72 -0
- package/project-template-v2/.claude/skills/session-analyzer/scripts/heavy-output.mjs +121 -0
- package/project-template-v2/.claude/skills/session-analyzer/scripts/resolve-session.mjs +102 -0
- package/project-template-v2/.claude/skills/session-analyzer/scripts/subagent-stats.mjs +127 -0
- package/project-template-v2/.claude/skills/session-analyzer/scripts/subagent-tool-timeline.mjs +106 -0
- package/project-template-v2/.claude/skills/session-analyzer/scripts/time-gaps.mjs +128 -0
- package/project-template-v2/.claude/skills/session-analyzer/scripts/turn-timeline.mjs +67 -0
- package/project-template-v2/.claude/snapshot.mjs +263 -0
- package/project-template-v2/.playcraft/skills.lock.json +152 -0
- package/project-template-v2/CLAUDE.md +146 -0
- package/project-template-v2/assets/audio/bgm/.gitkeep +0 -0
- package/project-template-v2/assets/audio/sfx/.gitkeep +0 -0
- package/project-template-v2/assets/bundles/.gitkeep +0 -0
- package/project-template-v2/assets/images/bg/.gitkeep +0 -0
- package/project-template-v2/assets/images/reference/.gitkeep +0 -0
- package/project-template-v2/assets/images/storyboard/.gitkeep +0 -0
- package/project-template-v2/assets/images/tiles/.gitkeep +0 -0
- package/project-template-v2/assets/images/ui/.gitkeep +0 -0
- package/project-template-v2/assets/images/vfx/.gitkeep +0 -0
- package/project-template-v2/assets/models/.gitkeep +0 -0
- package/project-template-v2/docs/harness/iteration-1/context-flow.md +254 -0
- package/project-template-v2/docs/harness/iteration-1/generate-flow.md +91 -0
- package/project-template-v2/docs/harness/iteration-1/ideate-flow.md +214 -0
- package/project-template-v2/docs/harness/iteration-1/optimize-flow.md +75 -0
- package/project-template-v2/docs/harness/iteration-1/wrapup-flow.md +63 -0
- package/project-template-v2/docs/harness/iteration-2/context-flow.md +223 -0
- package/project-template-v2/docs/harness/iteration-2/generate-flow.md +129 -0
- package/project-template-v2/docs/harness/iteration-2/ideate-flow.md +267 -0
- package/project-template-v2/docs/harness/iteration-2/optimize-flow.md +164 -0
- package/project-template-v2/docs/harness/iteration-2/wrapup-flow.md +115 -0
- package/project-template-v2/docs/harness/orchestrator-flow.md +364 -0
- package/project-template-v2/docs/project-state.json +60 -0
- package/project-template-v2/docs/project-state.md +72 -0
- package/project-template-v2/docs/standards/README.md +225 -0
- package/project-template-v2/docs/standards/agent-behavior-standards.md +174 -0
- package/project-template-v2/docs/standards/artifacts/design-brief.md +19 -0
- package/project-template-v2/docs/standards/artifacts/design.md +22 -0
- package/project-template-v2/docs/standards/artifacts/game-code.md +41 -0
- package/project-template-v2/docs/standards/artifacts/todo-list.md +41 -0
- package/project-template-v2/docs/standards/iter1-agent-behavior-standards.md +343 -0
- package/project-template-v2/game/index.ts +18 -0
- package/project-template-v2/globals.d.ts +51 -0
- package/project-template-v2/index.css +34 -0
- package/project-template-v2/index.html +18 -0
- package/project-template-v2/main.ts +9 -0
- package/project-template-v2/package.json +46 -0
- package/project-template-v2/skills/_shared/scripts/dispatch-clear.mjs +31 -0
- package/project-template-v2/skills/_shared/scripts/dispatch-set.mjs +86 -0
- package/project-template-v2/skills/_shared/scripts/dod-check.mjs +153 -0
- package/project-template-v2/skills/_shared/scripts/handoff-append.mjs +70 -0
- package/project-template-v2/skills/_shared/scripts/lib/validator-artifacts.mjs +91 -0
- package/project-template-v2/skills/_shared/scripts/pipeline/dod-config.mjs +131 -0
- package/project-template-v2/skills/_shared/scripts/pipeline/index.mjs +80 -0
- package/project-template-v2/skills/_shared/scripts/pipeline/iteration-1-core.mjs +90 -0
- package/project-template-v2/skills/_shared/scripts/pipeline/iteration-2-wrap.mjs +93 -0
- package/project-template-v2/skills/_shared/scripts/pipeline/iteration-3-visual.mjs +23 -0
- package/project-template-v2/skills/_shared/scripts/render-project-state.mjs +230 -0
- package/project-template-v2/skills/_shared/scripts/state-advance-stage.mjs +58 -0
- package/project-template-v2/skills/_shared/scripts/state-get.mjs +339 -0
- package/project-template-v2/skills/_shared/scripts/state-handoff.mjs +39 -0
- package/project-template-v2/skills/_shared/scripts/state-set.mjs +94 -0
- package/project-template-v2/skills/_shared/scripts/state-store.mjs +783 -0
- package/project-template-v2/skills/_shared/scripts/todo-add.mjs +88 -0
- package/project-template-v2/skills/_shared/scripts/todo-get.mjs +130 -0
- package/project-template-v2/skills/_shared/scripts/todo-remove.mjs +33 -0
- package/project-template-v2/skills/_shared/scripts/todo-set.mjs +47 -0
- package/project-template-v2/skills/_shared/scripts/verify-asset-code-sync.mjs +268 -0
- package/project-template-v2/skills/_shared/scripts/verify-env.mjs +285 -0
- package/project-template-v2/skills/_shared/scripts/verify-placeholders.mjs +161 -0
- package/project-template-v2/skills/playable-autoplay/SKILL.md +176 -0
- package/project-template-v2/skills/playable-autoplay/agents/openai.yaml +4 -0
- package/project-template-v2/skills/playable-debug/SKILL.md +116 -0
- package/project-template-v2/skills/playable-debug/agents/openai.yaml +4 -0
- package/project-template-v2/skills/playable-debug/references/debug-config.md +40 -0
- package/project-template-v2/skills/playable-record/SKILL.md +140 -0
- package/project-template-v2/skills/playable-record/scripts/lib/dev-server.mjs +104 -0
- package/project-template-v2/skills/playable-record/scripts/lib/record-audio-bridge.js +141 -0
- package/project-template-v2/skills/playable-record/scripts/record-playable.mjs +425 -0
- package/project-template-v2/skills/playable-record/scripts/verify-contract.mjs +261 -0
- package/project-template-v2/skills/playable-record/scripts/verify-firstwin.mjs +398 -0
- package/project-template-v2/skills/playable-record/scripts/verify-fusion.mjs +322 -0
- package/project-template-v2/skills/playable-record/scripts/verify-lifecycle.mjs +105 -0
- package/project-template-v2/skills/playable-record/scripts/verify-vlm-video.mjs +230 -0
- package/project-template-v2/skills/playable-validate/SKILL.md +61 -0
- package/project-template-v2/skills/playable-validate/validation-rules.md +14 -0
- package/project-template-v2/skills/playable-verify-ui/SKILL.md +63 -0
- package/project-template-v2/skills/playable-verify-ui/scripts/lib/__init__.py +1 -0
- package/project-template-v2/skills/playable-verify-ui/scripts/lib/config_expr.js +218 -0
- package/project-template-v2/skills/playable-verify-ui/scripts/lib/node_utils.js +51 -0
- package/project-template-v2/skills/playable-verify-ui/scripts/lib/profile_loader.py +59 -0
- package/project-template-v2/skills/playable-verify-ui/scripts/lib/report.py +106 -0
- package/project-template-v2/skills/playable-verify-ui/scripts/lib/runtime_contract.js +116 -0
- package/project-template-v2/skills/playable-verify-ui/scripts/verify_fetch_antipatterns.py +59 -0
- package/project-template-v2/skills/playable-verify-ui/scripts/verify_hardcoded_layout.js +64 -0
- package/project-template-v2/skills/playable-verify-ui/scripts/verify_runtime_contract.js +58 -0
- package/project-template-v2/skills/playable-verify-ui/scripts/verify_text_overlap.py +333 -0
- package/project-template-v2/ta-workspace/scripts/.gitkeep +0 -0
- package/project-template-v2/tsconfig.json +20 -0
- package/project-template-v2/vite.config.ts +27 -0
|
@@ -0,0 +1,386 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: session-analyzer
|
|
3
|
+
description: >
|
|
4
|
+
Navigate and analyze AI coding session logs (Claude Code / Claude Internal / CodeBuddy).
|
|
5
|
+
Use when asked to find specific tool calls, understand what happened in a turn,
|
|
6
|
+
trace subagent work, or answer "what did the agent do here?" questions.
|
|
7
|
+
triggers: session, turn, subagent, agent做了什么, 为什么这么多步骤, 查会话, 分析session, 读日志
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# Session Analyzer
|
|
11
|
+
|
|
12
|
+
Analyze AI coding assistant session logs stored as JSONL files in the session tracker project
|
|
13
|
+
(`D:\project\cli-tool-tracker`).
|
|
14
|
+
|
|
15
|
+
## Quick Reference: Directory Layout
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
~/.claude/projects/ ← Claude Code sessions
|
|
19
|
+
{project-slug}/
|
|
20
|
+
{session-uuid}.jsonl ← main session file
|
|
21
|
+
{session-uuid}/subagents/ ← subagent files
|
|
22
|
+
agent-{hex}.jsonl
|
|
23
|
+
agent-{hex}.meta.json
|
|
24
|
+
|
|
25
|
+
~/.claude-internal/projects/ ← Claude Internal sessions
|
|
26
|
+
{project-slug}/
|
|
27
|
+
{session-uuid}.jsonl ← main session file
|
|
28
|
+
{session-uuid}/subagents/
|
|
29
|
+
agent-{hex}.jsonl
|
|
30
|
+
agent-{hex}.meta.json
|
|
31
|
+
|
|
32
|
+
~/.codebuddy/projects/ ← CodeBuddy sessions
|
|
33
|
+
{project-slug}/
|
|
34
|
+
{session-uuid}.jsonl
|
|
35
|
+
{session-uuid}/subagents/
|
|
36
|
+
agent-{hex}.jsonl
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## How to Find a Specific Session
|
|
40
|
+
|
|
41
|
+
### Method 1: Use the Session Tracker UI
|
|
42
|
+
|
|
43
|
+
1. Start the tracker: `cd D:\project\cli-tool-tracker && pnpm run dev`
|
|
44
|
+
2. Select the source tab (CodeBuddy / Claude / Claude Internal)
|
|
45
|
+
3. Find the project in the Projects list → click to list sessions
|
|
46
|
+
4. Click a session to see its turns
|
|
47
|
+
5. Click a turn to see events (tool calls, messages, reasoning)
|
|
48
|
+
6. Click any event to see full context (input/output/raw JSON)
|
|
49
|
+
|
|
50
|
+
### Method 2: Command-line Search
|
|
51
|
+
|
|
52
|
+
```powershell
|
|
53
|
+
# Find project directories
|
|
54
|
+
ls ~\.claude-internal\projects\ | Where Name -like "*keyword*"
|
|
55
|
+
|
|
56
|
+
# List sessions in a project
|
|
57
|
+
ls ~\.claude-internal\projects\D--project-{name}\ | Where Name -like "*.jsonl"
|
|
58
|
+
|
|
59
|
+
# Find a specific session by date/size
|
|
60
|
+
ls ~\.claude-internal\projects\D--project-{name}\*.jsonl | Sort Length -Desc | Select -First 10 Name,Length
|
|
61
|
+
|
|
62
|
+
# Search JSONL for specific tool calls
|
|
63
|
+
Select-String -Path "path\to\session.jsonl" -Pattern '"name":"Agent"' | Select -First 10 LineNumber,Line
|
|
64
|
+
|
|
65
|
+
# Search for specific content in a session
|
|
66
|
+
Select-String -Path "path\to\session.jsonl" -Pattern "keyword" | Select LineNumber,@{n='Preview';e={$_.Line.Substring(0,200)}}
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## JSONL Line Types (Claude / Claude-Internal Format)
|
|
70
|
+
|
|
71
|
+
Each line is a JSON object with a `type` field:
|
|
72
|
+
|
|
73
|
+
| Line Type | Description | Key Fields |
|
|
74
|
+
| ------------------------------ | --------------------------- | --------------------------------------------------------------------------- |
|
|
75
|
+
| `"user"` | User message or tool result | `message.content` (text or tool_result blocks) |
|
|
76
|
+
| `"assistant"` | AI response | `message.content` (thinking/text/tool_use blocks), `message.usage` (tokens) |
|
|
77
|
+
| `"system"` | System event | `subtype`, `content` |
|
|
78
|
+
| `"attachment"` | Skill listing, permissions | `attachment.type`, `attachment.content` |
|
|
79
|
+
| `"file-history-snapshot"` | File tracking | `snapshot.trackedFileBackups` |
|
|
80
|
+
| `"ai-title"` | Auto-generated title | `aiTitle` |
|
|
81
|
+
| `"mode"` / `"permission-mode"` | Session config | `mode`, `permissionMode` |
|
|
82
|
+
|
|
83
|
+
### Assistant Message Content Blocks
|
|
84
|
+
|
|
85
|
+
Within an `"assistant"` line, `message.content[]` contains:
|
|
86
|
+
|
|
87
|
+
| Block Type | Description | Data Location |
|
|
88
|
+
| ------------ | -------------------------- | --------------------------------------------------------- |
|
|
89
|
+
| `"thinking"` | Model's reasoning/thinking | `block.thinking` (text), `block.signature` |
|
|
90
|
+
| `"text"` | Display text reply | `block.text` |
|
|
91
|
+
| `"tool_use"` | Tool call | `block.name`, `block.id` (callId), `block.input` (params) |
|
|
92
|
+
|
|
93
|
+
### Tool Result Content Blocks
|
|
94
|
+
|
|
95
|
+
Within a `"user"` line, `message.content[]` contains:
|
|
96
|
+
|
|
97
|
+
| Block Type | Description | Data Location |
|
|
98
|
+
| --------------- | --------------------- | -------------------------------------------------------------------------------- |
|
|
99
|
+
| `"tool_result"` | Tool execution result | `block.tool_use_id` (matches callId), `block.content` (output), `block.is_error` |
|
|
100
|
+
|
|
101
|
+
## Subagent Relationship
|
|
102
|
+
|
|
103
|
+
### How Subagents are Spawned
|
|
104
|
+
|
|
105
|
+
1. Main agent calls `Agent` tool → `function_call` event with `toolName: "Agent"`
|
|
106
|
+
2. Runtime spawns a subagent process → writes to `subagents/agent-{hex}.jsonl`
|
|
107
|
+
3. When subagent finishes → `function_call_result` with `toolUseResult.agentId: "{hex}"`
|
|
108
|
+
4. The `agentId` links the main session's Agent call to the subagent file
|
|
109
|
+
|
|
110
|
+
**Critical**: In Claude Internal format, `agentId` is at `toolUseResult.agentId` in the JSONL
|
|
111
|
+
(outer structure of the result line), NOT in the text content.
|
|
112
|
+
|
|
113
|
+
### Subagent File Format
|
|
114
|
+
|
|
115
|
+
Same as main JSONL but with:
|
|
116
|
+
|
|
117
|
+
- `"agentId": "a2834ad1c500a2f8c"` on user/assistant lines
|
|
118
|
+
- `"isSidechain": true` on all lines
|
|
119
|
+
- `"attributionAgent": "Explore"` on assistant lines (agent type)
|
|
120
|
+
- Each subagent file = one agent invocation (may have multiple turns internally)
|
|
121
|
+
|
|
122
|
+
### How to Trace Subagent Work
|
|
123
|
+
|
|
124
|
+
1. In the main session JSONL, find the Agent `tool_use` line → note `block.id` (callId)
|
|
125
|
+
2. Find the corresponding `tool_result` line (same `tool_use_id`)
|
|
126
|
+
3. Extract `agentId` from `toolUseResult.agentId`
|
|
127
|
+
4. The subagent file is `subagents/agent-{agentId}.jsonl`
|
|
128
|
+
5. Read that file to see what the subagent did
|
|
129
|
+
|
|
130
|
+
## Turn Structure
|
|
131
|
+
|
|
132
|
+
A **turn** starts when the user sends a message and ends when the AI finishes responding
|
|
133
|
+
(including all tool calls and their results). The turn splitter detects turns by:
|
|
134
|
+
|
|
135
|
+
- A `user` message (not a tool_result) → starts a new turn
|
|
136
|
+
- If `parentUuid` points to an assistant message → new turn
|
|
137
|
+
- If `parentUuid` points to another user message → continuation of current turn
|
|
138
|
+
|
|
139
|
+
Turns contain: user message → assistant thinking → assistant text → tool calls → results → more assistant responses → ...
|
|
140
|
+
|
|
141
|
+
## How to Analyze a Turn
|
|
142
|
+
|
|
143
|
+
### Step 1: Identify the Turn Boundaries
|
|
144
|
+
|
|
145
|
+
```powershell
|
|
146
|
+
# In the tracker UI: expand a turn to see its events
|
|
147
|
+
# OR manually in JSONL:
|
|
148
|
+
Select-String -Path "session.jsonl" -Pattern '"type":"user".*"role":"user"' | Select LineNumber
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
### Step 2: List All Tool Calls in a Turn
|
|
152
|
+
|
|
153
|
+
```powershell
|
|
154
|
+
Select-String -Path "session.jsonl" -Pattern '"type":"tool_use"' | Select LineNumber,@{n='Tool';e={if($_.Line -match '"name":"([^"]+)"'){$matches[1]}}}
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
### Step 3: Find Agent (Subagent) Calls
|
|
158
|
+
|
|
159
|
+
```powershell
|
|
160
|
+
Select-String -Path "session.jsonl" -Pattern '"name":"Agent"' -Context 0,5 | Select LineNumber
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
### Step 4: Check Subagent Results
|
|
164
|
+
|
|
165
|
+
```powershell
|
|
166
|
+
# Find results of a specific tool_use_id
|
|
167
|
+
Select-String -Path "session.jsonl" -Pattern "TOOL_USE_ID_HERE" | Select LineNumber
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
### Step 5: Read Subagent Files
|
|
171
|
+
|
|
172
|
+
```powershell
|
|
173
|
+
ls "session-dir\subagents\agent-*.jsonl" | ForEach { Write-Host "--- $($_.Name) ---"; Get-Content $_.FullName -Head 3 }
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
## Reusable Analysis Scripts
|
|
177
|
+
|
|
178
|
+
All scripts are in the `scripts/` directory relative to this skill. Run with Node.js (ESM).
|
|
179
|
+
|
|
180
|
+
### Quick Reference
|
|
181
|
+
|
|
182
|
+
| Script | Purpose | Usage |
|
|
183
|
+
| ---------------------------- | ------------------------------------------ | --------------------------------------------------------------- |
|
|
184
|
+
| `resolve-session.mjs` | Find session file path by project keyword | `node scripts/resolve-session.mjs <keyword> [uuid]` |
|
|
185
|
+
| `find-turns.mjs` | List all user-initiated turns in a session | `node scripts/find-turns.mjs <session.jsonl>` |
|
|
186
|
+
| `turn-timeline.mjs` | Show event timeline for a line range | `node scripts/turn-timeline.mjs <session.jsonl> <start> [end]` |
|
|
187
|
+
| `subagent-stats.mjs` | Extract stats for all Agent calls | `node scripts/subagent-stats.mjs <session.jsonl> [start] [end]` |
|
|
188
|
+
| `subagent-tool-timeline.mjs` | Show tool call sequence in a subagent | `node scripts/subagent-tool-timeline.mjs <subagent.jsonl>` |
|
|
189
|
+
| `time-gaps.mjs` | Find performance bottleneck (time gaps) | `node scripts/time-gaps.mjs <file.jsonl> [--threshold 5]` |
|
|
190
|
+
| `heavy-output.mjs` | Find heaviest output turns | `node scripts/heavy-output.mjs <file.jsonl> [--min-kb 3]` |
|
|
191
|
+
| `execution-breakdown.mjs` | Auto-detect phases & time allocation | `node scripts/execution-breakdown.mjs <subagent.jsonl>` |
|
|
192
|
+
|
|
193
|
+
### Typical Analysis Workflow
|
|
194
|
+
|
|
195
|
+
```bash
|
|
196
|
+
# 1. Resolve session path
|
|
197
|
+
node scripts/resolve-session.mjs tencent-test10 55f43f72-...
|
|
198
|
+
|
|
199
|
+
# 2. Find turns
|
|
200
|
+
node scripts/find-turns.mjs /path/to/session.jsonl
|
|
201
|
+
|
|
202
|
+
# 3. Look at specific turn (e.g. turn starting at line 205, ending at 337)
|
|
203
|
+
node scripts/turn-timeline.mjs /path/to/session.jsonl 205 337
|
|
204
|
+
|
|
205
|
+
# 4. Get subagent stats (which agents ran, how long, how many tokens)
|
|
206
|
+
node scripts/subagent-stats.mjs /path/to/session.jsonl 205 337
|
|
207
|
+
|
|
208
|
+
# 5. Deep-dive into a slow subagent
|
|
209
|
+
node scripts/subagent-tool-timeline.mjs /path/to/subagents/agent-abc123.jsonl --compact
|
|
210
|
+
node scripts/time-gaps.mjs /path/to/subagents/agent-abc123.jsonl --threshold 5
|
|
211
|
+
node scripts/execution-breakdown.mjs /path/to/subagents/agent-abc123.jsonl
|
|
212
|
+
node scripts/heavy-output.mjs /path/to/subagents/agent-abc123.jsonl --min-kb 3
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
### Script Details
|
|
216
|
+
|
|
217
|
+
#### resolve-session.mjs
|
|
218
|
+
|
|
219
|
+
Searches all three session stores (Claude Code / Claude Internal / CodeBuddy) by keyword.
|
|
220
|
+
|
|
221
|
+
- With just a keyword: lists all sessions in matching projects (sorted by size)
|
|
222
|
+
- With keyword + UUID: resolves to exact file path + lists subagent files
|
|
223
|
+
|
|
224
|
+
#### find-turns.mjs
|
|
225
|
+
|
|
226
|
+
Identifies turn boundaries by finding user messages with `promptSource` (typed/system).
|
|
227
|
+
|
|
228
|
+
- `--verbose`: show previews for all turns (not just typed)
|
|
229
|
+
- Distinguishes user-typed vs system-triggered turns
|
|
230
|
+
|
|
231
|
+
#### turn-timeline.mjs
|
|
232
|
+
|
|
233
|
+
Shows chronological events within a line range. Tags each event:
|
|
234
|
+
|
|
235
|
+
- `THINK [size]`: thinking blocks with size indicator
|
|
236
|
+
- `TOOL: <name>`: tool calls with input preview
|
|
237
|
+
- `RESULT(OK/ERR)`: tool results
|
|
238
|
+
- `NOTIFY`: system notifications
|
|
239
|
+
|
|
240
|
+
#### subagent-stats.mjs
|
|
241
|
+
|
|
242
|
+
For each Agent tool call in the range, extracts from `toolUseResult`:
|
|
243
|
+
|
|
244
|
+
- `agentId`, `agentType`, `status`
|
|
245
|
+
- `totalDurationMs`, `totalTokens`, `totalToolUseCount`
|
|
246
|
+
- `toolStats` breakdown (read/search/bash/edit counts, lines added/removed)
|
|
247
|
+
- Summary comparison table when multiple agents found
|
|
248
|
+
|
|
249
|
+
#### subagent-tool-timeline.mjs
|
|
250
|
+
|
|
251
|
+
Parses a subagent's JSONL to show every tool call in sequence.
|
|
252
|
+
|
|
253
|
+
- `--compact`: one line per call (good for overview)
|
|
254
|
+
- Shows file names for Read/Write/Edit, commands for Bash
|
|
255
|
+
- Includes tool count summary at the end
|
|
256
|
+
|
|
257
|
+
#### time-gaps.mjs
|
|
258
|
+
|
|
259
|
+
Finds the largest time gaps between events to identify bottlenecks.
|
|
260
|
+
|
|
261
|
+
- `--threshold <seconds>`: minimum gap to report (default: 10s)
|
|
262
|
+
- `--top <N>`: how many gaps to show (default: 10)
|
|
263
|
+
- Classifies each gap: LLM thinking, tool execution, bash command
|
|
264
|
+
- Shows gap category summary with percentages
|
|
265
|
+
|
|
266
|
+
#### heavy-output.mjs
|
|
267
|
+
|
|
268
|
+
Finds turns that generated the most content (thinking + text + file writes).
|
|
269
|
+
|
|
270
|
+
- `--min-kb <N>`: minimum output size to report (default: 5KB)
|
|
271
|
+
- `--top <N>`: how many to show (default: 15)
|
|
272
|
+
- Breaks down: thinking KB, text KB, write KB
|
|
273
|
+
- Shows output category breakdown (thinking vs text vs file writes)
|
|
274
|
+
|
|
275
|
+
#### execution-breakdown.mjs
|
|
276
|
+
|
|
277
|
+
Auto-detects "phases" in a subagent's execution and calculates time allocation.
|
|
278
|
+
|
|
279
|
+
- Groups by: thinking, reading files, writing files, bash commands, etc.
|
|
280
|
+
- Classifies bash commands: npm install, dev server, process debugging, etc.
|
|
281
|
+
- Shows time allocation with ASCII bar chart
|
|
282
|
+
- Useful for answering "where did the time go?"
|
|
283
|
+
|
|
284
|
+
## Legacy PowerShell Scripts (kept for reference)
|
|
285
|
+
|
|
286
|
+
### Helper Script: Find Subagent Files for a Session
|
|
287
|
+
|
|
288
|
+
```powershell
|
|
289
|
+
# Save as: find-subagents.ps1
|
|
290
|
+
param($SessionPath)
|
|
291
|
+
|
|
292
|
+
$dir = Split-Path $SessionPath -Parent
|
|
293
|
+
$sid = Split-Path $SessionPath -Leaf
|
|
294
|
+
$sid = $sid -replace '\.jsonl$',''
|
|
295
|
+
$subDir = Join-Path $dir $sid 'subagents'
|
|
296
|
+
if (Test-Path $subDir) {
|
|
297
|
+
Get-ChildItem $subDir -Filter "agent-*.jsonl" | ForEach-Object {
|
|
298
|
+
$first = Get-Content $_.FullName -Head 1 | ConvertFrom-Json
|
|
299
|
+
$agentType = $first.attributionAgent ?? $first.message?.role ?? "?"
|
|
300
|
+
$lines = (Get-Content $_.FullName | Measure-Object).Count
|
|
301
|
+
Write-Host "$($_.Name) | type: $agentType | lines: $lines"
|
|
302
|
+
}
|
|
303
|
+
} else {
|
|
304
|
+
Write-Host "No subagents directory at $subDir"
|
|
305
|
+
}
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
### Helper Script: Summarize a Turn
|
|
309
|
+
|
|
310
|
+
```powershell
|
|
311
|
+
# Save as: summarize-turn.ps1
|
|
312
|
+
# Usage: .\summarize-turn.ps1 -SessionPath "session.jsonl" -TurnStartLine 100
|
|
313
|
+
param($SessionPath, $TurnStartLine = 1, $MaxLines = 500)
|
|
314
|
+
|
|
315
|
+
$lines = Get-Content $SessionPath
|
|
316
|
+
$end = [Math]::Min($TurnStartLine + $MaxLines, $lines.Count)
|
|
317
|
+
|
|
318
|
+
for ($i = $TurnStartLine - 1; $i -lt $end; $i++) {
|
|
319
|
+
$line = $lines[$i]
|
|
320
|
+
try { $obj = $line | ConvertFrom-Json } catch { continue }
|
|
321
|
+
|
|
322
|
+
$ln = $i + 1
|
|
323
|
+
$ts = if ($obj.timestamp) { [DateTime]::Parse($obj.timestamp).ToString("HH:mm:ss") } else { "" }
|
|
324
|
+
|
|
325
|
+
if ($obj.type -eq 'user') {
|
|
326
|
+
$content = $obj.message.content
|
|
327
|
+
if ($content -is [string]) { Write-Host "[$ln] $ts USER: $content" }
|
|
328
|
+
elseif ($content[0].type -eq 'tool_result') {
|
|
329
|
+
$tid = $content[0].tool_use_id.Substring(0,20)
|
|
330
|
+
Write-Host "[$ln] $ts RESULT: $tid"
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
elseif ($obj.type -eq 'assistant') {
|
|
334
|
+
foreach ($b in $obj.message.content) {
|
|
335
|
+
if ($b.type -eq 'text') { Write-Host "[$ln] $ts TEXT: $($b.text.Substring(0,[Math]::Min(80,$b.text.Length)))" }
|
|
336
|
+
elseif ($b.type -eq 'thinking') { Write-Host "[$ln] $ts THINK: $($b.thinking.Substring(0,[Math]::Min(80,$b.thinking.Length)))" }
|
|
337
|
+
elseif ($b.type -eq 'tool_use') { Write-Host "[$ln] $ts TOOL: $($b.name) | $($b.id.Substring(0,20))" }
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
elseif ($obj.type -eq 'ai-title') { Write-Host "[$ln] $ts TITLE: $($obj.aiTitle)" }
|
|
341
|
+
}
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
## Understanding a Turn: Step-by-Step Analysis
|
|
345
|
+
|
|
346
|
+
When asked "what happened in this turn?" or "why did it do this?", follow this process:
|
|
347
|
+
|
|
348
|
+
1. **Read the turn title** (`ai-title` lines) for high-level summary
|
|
349
|
+
2. **Read the user message** — what was the request?
|
|
350
|
+
3. **Read the assistant thinking** — what was the model planning?
|
|
351
|
+
4. **List all tool calls** in order — what actions were taken?
|
|
352
|
+
5. **For each Agent call**: check the subagent file to see what it explored/did
|
|
353
|
+
6. **Read the tool results** — what came back?
|
|
354
|
+
7. **Read the assistant text replies** — what was communicated?
|
|
355
|
+
|
|
356
|
+
## Token Usage
|
|
357
|
+
|
|
358
|
+
Each assistant message has `message.usage`:
|
|
359
|
+
|
|
360
|
+
```json
|
|
361
|
+
"usage": {
|
|
362
|
+
"input_tokens": 25441,
|
|
363
|
+
"output_tokens": 186,
|
|
364
|
+
"cache_read_input_tokens": 10624,
|
|
365
|
+
"cache_creation_input_tokens": 0
|
|
366
|
+
}
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
- `input_tokens` + `output_tokens` = total tokens for this API call
|
|
370
|
+
- `cache_read_input_tokens` > 0 means cache was hit (saved cost)
|
|
371
|
+
- `cache_creation_input_tokens` > 0 means new cache entries were created
|
|
372
|
+
- Usage appears on ALL lines sharing the same `message.id`; the LAST line has the most complete data
|
|
373
|
+
|
|
374
|
+
## Quick Search Script
|
|
375
|
+
|
|
376
|
+
```powershell
|
|
377
|
+
# Save as: search-session.ps1
|
|
378
|
+
# Usage: .\search-session.ps1 -Path "session.jsonl" -Query "Agent"
|
|
379
|
+
param($Path, $Query)
|
|
380
|
+
|
|
381
|
+
Select-String -LiteralPath $Path -Pattern $Query | ForEach-Object {
|
|
382
|
+
$preview = $_.Line.Substring(0, [Math]::Min(300, $_.Line.Length))
|
|
383
|
+
Write-Host "[$($_.LineNumber)] $preview"
|
|
384
|
+
Write-Host "---"
|
|
385
|
+
}
|
|
386
|
+
```
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* execution-breakdown.mjs — Generate a phased execution breakdown for a subagent
|
|
4
|
+
*
|
|
5
|
+
* Automatically detects "phases" by grouping consecutive tool calls of similar type,
|
|
6
|
+
* then shows time allocation per phase. Useful for understanding where agent time was spent.
|
|
7
|
+
*
|
|
8
|
+
* Usage:
|
|
9
|
+
* node execution-breakdown.mjs <subagent.jsonl>
|
|
10
|
+
*
|
|
11
|
+
* Output: phase breakdown with duration, percentage, and tool counts
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { readFileSync } from 'fs';
|
|
15
|
+
|
|
16
|
+
const filePath = process.argv[2];
|
|
17
|
+
|
|
18
|
+
if (!filePath) {
|
|
19
|
+
console.error('Usage: node execution-breakdown.mjs <subagent.jsonl>');
|
|
20
|
+
process.exit(1);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const lines = readFileSync(filePath, 'utf8').trim().split('\n');
|
|
24
|
+
|
|
25
|
+
// Collect timestamped events with tool info
|
|
26
|
+
const events = [];
|
|
27
|
+
lines.forEach((line, idx) => {
|
|
28
|
+
try {
|
|
29
|
+
const obj = JSON.parse(line);
|
|
30
|
+
if (!obj.timestamp) return;
|
|
31
|
+
const ts = new Date(obj.timestamp).getTime();
|
|
32
|
+
|
|
33
|
+
if (obj.type === 'assistant') {
|
|
34
|
+
const blocks = obj.message?.content || [];
|
|
35
|
+
for (const b of blocks) {
|
|
36
|
+
if (b.type === 'tool_use') {
|
|
37
|
+
let detail = '';
|
|
38
|
+
if (b.name === 'Write' || b.name === 'Edit' || b.name === 'Read') {
|
|
39
|
+
detail = b.input?.file_path?.split(/[/\\]/).pop() || '';
|
|
40
|
+
} else if (b.name === 'Bash') {
|
|
41
|
+
detail = (b.input?.command || '').substring(0, 50);
|
|
42
|
+
}
|
|
43
|
+
events.push({ line: idx + 1, ts, type: 'tool', name: b.name, detail });
|
|
44
|
+
} else if (b.type === 'thinking') {
|
|
45
|
+
events.push({ line: idx + 1, ts, type: 'thinking', size: b.thinking.length });
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
} else if (obj.type === 'user') {
|
|
49
|
+
const content = obj.message?.content;
|
|
50
|
+
if (Array.isArray(content) && content[0]?.type === 'tool_result') {
|
|
51
|
+
events.push({ line: idx + 1, ts, type: 'result', isError: content[0].is_error || false });
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
} catch (e) { /* skip */ }
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
if (events.length < 2) {
|
|
58
|
+
console.log('Not enough events to analyze.');
|
|
59
|
+
process.exit(0);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const totalDuration = events[events.length - 1].ts - events[0].ts;
|
|
63
|
+
|
|
64
|
+
// Auto-detect phases: a "phase" changes when there's a significant thinking block (>5KB)
|
|
65
|
+
// or when tool type shifts from read→write, write→bash, etc.
|
|
66
|
+
const phases = [];
|
|
67
|
+
let currentPhase = { start: events[0], events: [events[0]], label: '' };
|
|
68
|
+
|
|
69
|
+
function classifyPhase(phaseEvents) {
|
|
70
|
+
const toolCounts = {};
|
|
71
|
+
let bigThink = 0;
|
|
72
|
+
phaseEvents.forEach(e => {
|
|
73
|
+
if (e.type === 'tool') toolCounts[e.name] = (toolCounts[e.name] || 0) + 1;
|
|
74
|
+
if (e.type === 'thinking' && e.size > 5000) bigThink++;
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
const dominant = Object.entries(toolCounts).sort((a, b) => b[1] - a[1])[0];
|
|
78
|
+
if (bigThink > 0 && !dominant) return 'Planning/Thinking';
|
|
79
|
+
if (!dominant) return 'Thinking';
|
|
80
|
+
|
|
81
|
+
const name = dominant[0];
|
|
82
|
+
const files = phaseEvents.filter(e => e.type === 'tool').map(e => e.detail).filter(Boolean);
|
|
83
|
+
const uniqueFiles = [...new Set(files)];
|
|
84
|
+
|
|
85
|
+
if (name === 'Read' && toolCounts.Read > toolCounts.Write) return 'Reading files';
|
|
86
|
+
if (name === 'Write') return `Writing files (${uniqueFiles.length} files)`;
|
|
87
|
+
if (name === 'Edit') return `Editing files (${uniqueFiles.length} files)`;
|
|
88
|
+
if (name === 'Bash') {
|
|
89
|
+
const cmds = phaseEvents.filter(e => e.type === 'tool' && e.name === 'Bash').map(e => e.detail);
|
|
90
|
+
if (cmds.some(c => c.includes('npm install') || c.includes('pnpm install'))) return 'Installing dependencies';
|
|
91
|
+
if (cmds.some(c => c.includes('http-server') || c.includes('dev') || c.includes('curl'))) return 'Dev server setup/verification';
|
|
92
|
+
if (cmds.some(c => c.includes('kill') || c.includes('netstat') || c.includes('lsof'))) return 'Process/port debugging';
|
|
93
|
+
return 'Running commands';
|
|
94
|
+
}
|
|
95
|
+
if (name === 'Grep' || name === 'Glob') return 'Searching codebase';
|
|
96
|
+
return `${name} operations`;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Split into phases by significant gaps (>15s) or thinking blocks >10KB
|
|
100
|
+
for (let i = 1; i < events.length; i++) {
|
|
101
|
+
const gap = events[i].ts - events[i - 1].ts;
|
|
102
|
+
const isBigThink = events[i].type === 'thinking' && events[i].size > 10000;
|
|
103
|
+
const isLongGap = gap > 15000;
|
|
104
|
+
|
|
105
|
+
if (isBigThink || isLongGap) {
|
|
106
|
+
// Finalize current phase
|
|
107
|
+
currentPhase.end = events[i - 1];
|
|
108
|
+
phases.push(currentPhase);
|
|
109
|
+
// Start new phase
|
|
110
|
+
currentPhase = { start: events[i], events: [events[i]], label: '' };
|
|
111
|
+
} else {
|
|
112
|
+
currentPhase.events.push(events[i]);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
// Finalize last phase
|
|
116
|
+
currentPhase.end = events[events.length - 1];
|
|
117
|
+
phases.push(currentPhase);
|
|
118
|
+
|
|
119
|
+
// Classify and calculate
|
|
120
|
+
phases.forEach(p => {
|
|
121
|
+
p.duration = (p.end?.ts || p.start.ts) - p.start.ts;
|
|
122
|
+
// If phase is just a start event, estimate from gap
|
|
123
|
+
if (p.duration === 0 && phases.indexOf(p) < phases.length - 1) {
|
|
124
|
+
const nextPhase = phases[phases.indexOf(p) + 1];
|
|
125
|
+
p.duration = nextPhase.start.ts - p.start.ts;
|
|
126
|
+
}
|
|
127
|
+
p.label = classifyPhase(p.events);
|
|
128
|
+
p.toolCount = p.events.filter(e => e.type === 'tool').length;
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
// Merge very short phases (< 5s) into previous
|
|
132
|
+
const mergedPhases = [];
|
|
133
|
+
for (const p of phases) {
|
|
134
|
+
if (mergedPhases.length > 0 && p.duration < 5000) {
|
|
135
|
+
const prev = mergedPhases[mergedPhases.length - 1];
|
|
136
|
+
prev.events.push(...p.events);
|
|
137
|
+
prev.end = p.end;
|
|
138
|
+
prev.duration += p.duration;
|
|
139
|
+
prev.toolCount += p.toolCount;
|
|
140
|
+
} else {
|
|
141
|
+
mergedPhases.push(p);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Re-classify merged phases
|
|
146
|
+
mergedPhases.forEach(p => {
|
|
147
|
+
p.label = classifyPhase(p.events);
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
// Output
|
|
151
|
+
console.log('=== EXECUTION BREAKDOWN ===');
|
|
152
|
+
console.log(`File: ${filePath}`);
|
|
153
|
+
console.log(`Total duration: ${(totalDuration / 1000).toFixed(1)}s (${(totalDuration / 1000 / 60).toFixed(1)} min)`);
|
|
154
|
+
console.log(`Total events: ${events.length} | Phases detected: ${mergedPhases.length}`);
|
|
155
|
+
console.log('');
|
|
156
|
+
|
|
157
|
+
console.log('#'.padEnd(4) + 'Duration'.padEnd(12) + '%'.padEnd(8) + 'Tools'.padEnd(8) + 'Phase');
|
|
158
|
+
console.log('-'.repeat(70));
|
|
159
|
+
|
|
160
|
+
mergedPhases.forEach((p, i) => {
|
|
161
|
+
const dur = (p.duration / 1000).toFixed(0) + 's';
|
|
162
|
+
const pct = (p.duration / totalDuration * 100).toFixed(1) + '%';
|
|
163
|
+
console.log(
|
|
164
|
+
String(i + 1).padEnd(4) +
|
|
165
|
+
dur.padEnd(12) +
|
|
166
|
+
pct.padEnd(8) +
|
|
167
|
+
String(p.toolCount).padEnd(8) +
|
|
168
|
+
p.label
|
|
169
|
+
);
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
// Time allocation
|
|
173
|
+
console.log('\n=== TIME ALLOCATION ===');
|
|
174
|
+
const categories = {};
|
|
175
|
+
mergedPhases.forEach(p => {
|
|
176
|
+
const cat = p.label.split(' (')[0]; // Remove file count details
|
|
177
|
+
categories[cat] = (categories[cat] || 0) + p.duration;
|
|
178
|
+
});
|
|
179
|
+
Object.entries(categories).sort((a, b) => b[1] - a[1]).forEach(([cat, ms]) => {
|
|
180
|
+
const bar = '#'.repeat(Math.round(ms / totalDuration * 40));
|
|
181
|
+
console.log(` ${cat.padEnd(30)} ${(ms / 1000).toFixed(0)}s (${(ms / totalDuration * 100).toFixed(1)}%) ${bar}`);
|
|
182
|
+
});
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* find-turns.mjs — Locate all user-initiated turns in a session JSONL
|
|
4
|
+
*
|
|
5
|
+
* Usage:
|
|
6
|
+
* node find-turns.mjs <session.jsonl>
|
|
7
|
+
* node find-turns.mjs <session.jsonl> --verbose
|
|
8
|
+
*
|
|
9
|
+
* Output: line number, timestamp, promptSource, and message preview for each turn start
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { readFileSync } from 'fs';
|
|
13
|
+
|
|
14
|
+
const sessionPath = process.argv[2];
|
|
15
|
+
const verbose = process.argv.includes('--verbose');
|
|
16
|
+
|
|
17
|
+
if (!sessionPath) {
|
|
18
|
+
console.error('Usage: node find-turns.mjs <session.jsonl> [--verbose]');
|
|
19
|
+
process.exit(1);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const lines = readFileSync(sessionPath, 'utf8').trim().split('\n');
|
|
23
|
+
const turns = [];
|
|
24
|
+
|
|
25
|
+
lines.forEach((line, idx) => {
|
|
26
|
+
try {
|
|
27
|
+
const obj = JSON.parse(line);
|
|
28
|
+
if (obj.type !== 'user') return;
|
|
29
|
+
|
|
30
|
+
const content = obj.message?.content;
|
|
31
|
+
// Skip tool_result lines — they're continuations, not new turns
|
|
32
|
+
if (Array.isArray(content) && content[0]?.type === 'tool_result') return;
|
|
33
|
+
// Skip system notifications unless they have promptSource
|
|
34
|
+
if (obj.origin?.kind === 'task-notification' && !obj.promptSource) return;
|
|
35
|
+
|
|
36
|
+
const ts = obj.timestamp || '';
|
|
37
|
+
const promptSource = obj.promptSource || 'tool_result';
|
|
38
|
+
const isTyped = promptSource === 'typed';
|
|
39
|
+
const isSystem = promptSource === 'system';
|
|
40
|
+
|
|
41
|
+
let preview = '';
|
|
42
|
+
if (typeof content === 'string') {
|
|
43
|
+
preview = content.substring(0, 100);
|
|
44
|
+
} else if (Array.isArray(content) && content[0]?.text) {
|
|
45
|
+
preview = content[0].text.substring(0, 100);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
turns.push({ line: idx + 1, ts, promptSource, isTyped, preview });
|
|
49
|
+
} catch (e) { /* skip parse errors */ }
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
console.log(`=== TURNS FOUND: ${turns.length} ===\n`);
|
|
53
|
+
|
|
54
|
+
turns.forEach((t, i) => {
|
|
55
|
+
const marker = t.isTyped ? '>>> ' : ' ';
|
|
56
|
+
const srcTag = `[${t.promptSource}]`.padEnd(10);
|
|
57
|
+
console.log(`${marker}Turn ${i + 1} | Line ${t.line} | ${t.ts} ${srcTag}`);
|
|
58
|
+
if (verbose || t.isTyped) {
|
|
59
|
+
console.log(` ${t.preview}`);
|
|
60
|
+
}
|
|
61
|
+
console.log('');
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
// Print summary
|
|
65
|
+
const typed = turns.filter(t => t.isTyped).length;
|
|
66
|
+
const system = turns.filter(t => t.promptSource === 'system').length;
|
|
67
|
+
console.log(`--- Summary: ${typed} user-typed, ${system} system-triggered, ${turns.length} total ---`);
|
|
68
|
+
if (turns.length >= 2) {
|
|
69
|
+
const first = new Date(turns[0].ts).getTime();
|
|
70
|
+
const last = new Date(turns[turns.length - 1].ts).getTime();
|
|
71
|
+
console.log(`--- Session span: ${((last - first) / 1000 / 60).toFixed(1)} minutes ---`);
|
|
72
|
+
}
|