@qiaolei81/copilot-session-viewer 0.1.9 → 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/.nycrc +29 -0
- package/CHANGELOG.md +48 -0
- package/README.md +154 -15
- package/examples/parser-usage.js +114 -0
- package/lib/parsers/README.md +239 -0
- package/lib/parsers/base-parser.js +53 -0
- package/lib/parsers/claude-parser.js +181 -0
- package/lib/parsers/copilot-parser.js +143 -0
- package/lib/parsers/index.js +13 -0
- package/lib/parsers/parser-factory.js +77 -0
- package/lib/parsers/pi-mono-parser.js +119 -0
- package/package.json +12 -4
- package/server.js +17 -2
- package/src/app.js +45 -20
- package/src/controllers/insightController.js +44 -8
- package/src/controllers/sessionController.js +217 -3
- package/src/controllers/uploadController.js +447 -7
- package/src/middleware/rateLimiting.js +7 -1
- package/src/models/Session.js +26 -0
- package/src/schemas/event.schema.js +73 -0
- package/src/services/eventNormalizer.js +291 -0
- package/src/services/insightService.js +140 -48
- package/src/services/sessionRepository.js +584 -49
- package/src/services/sessionService.js +1588 -36
- package/src/utils/helpers.js +6 -1
- package/views/index.ejs +111 -4
- package/views/session-vue.ejs +272 -65
- package/views/time-analyze.ejs +127 -55
package/.nycrc
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"all": true,
|
|
3
|
+
"include": [
|
|
4
|
+
"server.js",
|
|
5
|
+
"src/**/*.js",
|
|
6
|
+
"public/**/*.js"
|
|
7
|
+
],
|
|
8
|
+
"exclude": [
|
|
9
|
+
"**/*.test.js",
|
|
10
|
+
"**/__tests__/**",
|
|
11
|
+
"**/node_modules/**",
|
|
12
|
+
"coverage/**",
|
|
13
|
+
".nyc_output/**",
|
|
14
|
+
"test-results/**",
|
|
15
|
+
"scripts/**",
|
|
16
|
+
"**/*.spec.js"
|
|
17
|
+
],
|
|
18
|
+
"reporter": [
|
|
19
|
+
"text",
|
|
20
|
+
"text-summary",
|
|
21
|
+
"html",
|
|
22
|
+
"lcov"
|
|
23
|
+
],
|
|
24
|
+
"report-dir": "./coverage/combined",
|
|
25
|
+
"temp-dir": "./.nyc_output",
|
|
26
|
+
"cache": true,
|
|
27
|
+
"sourceMap": false,
|
|
28
|
+
"instrument": false
|
|
29
|
+
}
|
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,54 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [0.2.1] - 2026-02-25
|
|
9
|
+
|
|
10
|
+
### Changed
|
|
11
|
+
- `.nyc_output/` and `coverage/` directories now properly ignored in `.gitignore`
|
|
12
|
+
|
|
13
|
+
### Removed
|
|
14
|
+
- NYC coverage intermediate files (`.nyc_output/`, 1.6 MB) from repository
|
|
15
|
+
- Removed 28 coverage data files that should not be committed
|
|
16
|
+
|
|
17
|
+
### Docs
|
|
18
|
+
- Translated `lib/parsers/README.md` from Chinese to English for international contributors
|
|
19
|
+
|
|
20
|
+
## [0.2.0] - 2026-02-25
|
|
21
|
+
|
|
22
|
+
### Added
|
|
23
|
+
- **Multi-Tool Support** - Full support for **Pi-Mono** sessions (now supports 3 tools: Copilot CLI, Claude Code, Pi-Mono)
|
|
24
|
+
- **Agent Review for All Tools** - AI-powered session analysis now works for all 3 supported tools
|
|
25
|
+
- **Pi-Mono Parser** - New `PiMonoSessionParser` with full strategy pattern implementation
|
|
26
|
+
- **Unified Event Format** - All 3 tools now use consistent event schema across backend and frontend
|
|
27
|
+
- **Backend-Generated Display Metadata** - Badge labels, source names, and display data generated in backend for consistency
|
|
28
|
+
|
|
29
|
+
### Changed
|
|
30
|
+
- **Default Filter to Copilot** - Homepage now defaults to Copilot filter instead of "All" (matches user behavior)
|
|
31
|
+
- **Removed "All" Filter** - Simplified UI by removing the "All" filter option
|
|
32
|
+
- **Type Transformation Strategy** - Backend now transforms event types for unified schema (Pi-Mono `message` → `assistant.message`)
|
|
33
|
+
- **Tool Result Merging** - Pi-Mono tool results now properly merged into parent assistant messages
|
|
34
|
+
- **Badge Logic Moved to Backend** - Frontend no longer generates badge labels (single source of truth)
|
|
35
|
+
|
|
36
|
+
### Fixed
|
|
37
|
+
- **Pi-Mono Agent Review Accuracy** - Fixed incorrect session data by explicitly specifying target file in prompt
|
|
38
|
+
- **Timeline Rendering for Old Copilot Sessions** - Old CLI format now properly expands to `assistant.message` events for timeline
|
|
39
|
+
- **Architecture Consistency** - Unified type transformation across all sources (no more "按了葫芦起了瓢")
|
|
40
|
+
- **CI Upload Directory Race Condition** - Tests now create directories defensively before file operations
|
|
41
|
+
- **Event Expansion Test Coverage** - Tests now verify `assistant.message` generation (frontend dependency)
|
|
42
|
+
|
|
43
|
+
### Docs
|
|
44
|
+
- **README Multi-Tool Emphasis** - Updated subtitle and descriptions to highlight multi-tool support
|
|
45
|
+
- **lib/parsers Documentation** - Translated Chinese README to English for international contributors
|
|
46
|
+
- **Project Cleanup** - Removed backup files (`time-analyze-v2.ejs`, `*.bak`) and 30 failed E2E test screenshot directories
|
|
47
|
+
|
|
48
|
+
### Performance
|
|
49
|
+
- **Agent Review Session Isolation** - Simplified from temporary directory approach to prompt-based file specification (6 lines vs 58 lines)
|
|
50
|
+
|
|
51
|
+
### Architecture
|
|
52
|
+
- **Strategy Pattern Complete** - All 3 parsers follow unified `BaseSessionParser` interface
|
|
53
|
+
- **Backend Normalization** - `eventNormalizer.js` handles all format differences, frontend renders uniformly
|
|
54
|
+
- **No Frontend Source Checks** - Frontend doesn't check `source` field, only renders normalized data
|
|
55
|
+
|
|
8
56
|
## [0.1.7] - 2026-02-16
|
|
9
57
|
|
|
10
58
|
### Changed
|
package/README.md
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
# 🤖 Copilot Session Viewer
|
|
2
2
|
|
|
3
3
|
[](https://www.npmjs.com/package/@qiaolei81/copilot-session-viewer)
|
|
4
|
+
[](https://github.com/qiaolei81/copilot-session-viewer/actions/workflows/ci.yml)
|
|
4
5
|
[](https://opensource.org/licenses/MIT)
|
|
5
6
|
[](https://nodejs.org/)
|
|
6
7
|
|
|
7
|
-
**
|
|
8
|
+
**Multi-Tool Session Log Viewer & Analyzer**
|
|
9
|
+
View and analyze AI coding assistant sessions from **GitHub Copilot CLI**, **Claude Code CLI**, and **Pi-Mono** with time analysis, virtual scrolling, and AI-powered insights.
|
|
8
10
|
|
|
9
|
-
A modern web-based viewer for analyzing
|
|
11
|
+
A modern web-based viewer for analyzing AI coding assistant session logs with virtual scrolling, infinite loading, time analysis, and AI-powered insights. Supports **GitHub Copilot CLI**, **Claude Code CLI**, and **Pi-Mono** sessions.
|
|
10
12
|
|
|
11
13
|
### Session List
|
|
12
14
|

|
|
@@ -39,7 +41,10 @@ copilot-session-viewer
|
|
|
39
41
|
### Requirements
|
|
40
42
|
|
|
41
43
|
- Node.js ≥ 18.0.0
|
|
42
|
-
-
|
|
44
|
+
- At least one AI coding assistant (optional for generating sessions):
|
|
45
|
+
- [GitHub Copilot CLI](https://github.com/cli/cli) (recommended)
|
|
46
|
+
- [Claude Code CLI](https://github.com/anthropics/claude-code)
|
|
47
|
+
- [Pi-Mono](https://github.com/badlogic/pi-mono)
|
|
43
48
|
|
|
44
49
|
---
|
|
45
50
|
|
|
@@ -52,37 +57,53 @@ copilot-session-viewer
|
|
|
52
57
|
- **🚀 Virtual Scrolling** - Handle 1000+ events smoothly
|
|
53
58
|
- **♾️ Infinite Scroll** - Progressive session loading for better performance
|
|
54
59
|
- **🤖 AI Insights** - LLM-powered session analysis
|
|
60
|
+
- **🎭 Multi-Format Support** - Copilot, Claude Code, and Pi-Mono sessions
|
|
55
61
|
|
|
56
62
|
### 🎨 **User Experience**
|
|
57
63
|
- **🌙 Dark Theme** - GitHub-inspired interface
|
|
58
64
|
- **📱 Responsive** - Works on desktop, tablet, and mobile
|
|
59
65
|
- **⚡ Fast** - Optimized virtual rendering and lazy loading
|
|
60
|
-
- **🔐 Secure** - Local-first with no data sharing
|
|
66
|
+
- **🔐 Secure** - Local-first with no data sharing, XSS protection, ZIP bomb defense
|
|
61
67
|
|
|
62
68
|
### 🛠️ **Technical Features**
|
|
63
69
|
- **Vue 3** - Reactive virtual scrolling
|
|
64
70
|
- **Express.js** - Robust backend API
|
|
65
|
-
- **ZIP Import/Export** - Session sharing capabilities
|
|
66
|
-
- **Multi-
|
|
71
|
+
- **ZIP Import/Export** - Session sharing capabilities with security validation
|
|
72
|
+
- **Multi-Source Support** - Copilot (`~/.copilot/session-state/`), Claude (`~/.claude/projects/`), Pi-Mono (`~/.pi/agent/sessions/`)
|
|
73
|
+
- **Unified Event Format** - Consistent schema across all sources
|
|
74
|
+
- **Memory Pagination** - Efficient handling of large sessions
|
|
75
|
+
- **XSS Protection** - DOMPurify-based HTML sanitization
|
|
76
|
+
- **ZIP Bomb Defense** - 4-layer protection (compressed size, uncompressed size, file count, depth)
|
|
67
77
|
|
|
68
78
|
---
|
|
69
79
|
|
|
70
80
|
## 🚀 How It Works
|
|
71
81
|
|
|
72
|
-
1. **Generate Sessions** - Use GitHub Copilot CLI to create session logs
|
|
73
|
-
2. **Auto-Discovery** - Sessions are automatically detected
|
|
82
|
+
1. **Generate Sessions** - Use GitHub Copilot CLI, Claude Code CLI, or Pi-Mono to create session logs
|
|
83
|
+
2. **Auto-Discovery** - Sessions are automatically detected from:
|
|
84
|
+
- Copilot: `~/.copilot/session-state/`
|
|
85
|
+
- Claude: `~/.claude/projects/`
|
|
86
|
+
- Pi-Mono: `~/.pi/agent/sessions/`
|
|
74
87
|
3. **Browse & Analyze** - View sessions with infinite scroll and detailed event streams
|
|
75
88
|
4. **Time Analysis** - Analyze turn durations, tool usage, and sub-agent performance
|
|
76
89
|
5. **AI Insights** - Generate comprehensive session analysis with Copilot
|
|
77
90
|
|
|
78
91
|
```bash
|
|
79
|
-
# Example: Generate
|
|
92
|
+
# Example: Generate sessions with different tools
|
|
93
|
+
|
|
94
|
+
# GitHub Copilot CLI
|
|
80
95
|
copilot --model claude-sonnet-4.5 -p "Help me refactor this code"
|
|
81
96
|
|
|
97
|
+
# Claude Code CLI
|
|
98
|
+
claude -p "Implement user authentication"
|
|
99
|
+
|
|
100
|
+
# Pi-Mono CLI
|
|
101
|
+
pi -p "Create a REST API endpoint"
|
|
102
|
+
|
|
82
103
|
# Start the viewer
|
|
83
104
|
npx @qiaolei81/copilot-session-viewer
|
|
84
105
|
|
|
85
|
-
# Browse sessions at http://localhost:3838
|
|
106
|
+
# Browse all sessions at http://localhost:3838
|
|
86
107
|
```
|
|
87
108
|
|
|
88
109
|
---
|
|
@@ -97,6 +118,53 @@ npx @qiaolei81/copilot-session-viewer
|
|
|
97
118
|
|
|
98
119
|
---
|
|
99
120
|
|
|
121
|
+
## 🧪 Testing & Quality
|
|
122
|
+
|
|
123
|
+
This project includes comprehensive unit and E2E test coverage with CI/CD integration.
|
|
124
|
+
|
|
125
|
+
### Test Coverage
|
|
126
|
+
|
|
127
|
+
- **470+ Tests** (411 unit + 59 E2E)
|
|
128
|
+
- **Unified Format Tests** - Mock data validation for all sources (Copilot, Claude, Pi-Mono)
|
|
129
|
+
- **Security Tests** - XSS prevention, ZIP bomb defense
|
|
130
|
+
- **Integration Tests** - Session import/export, file operations
|
|
131
|
+
- **CI-Friendly** - Mock data generation for reproducible tests
|
|
132
|
+
|
|
133
|
+
### Running Tests
|
|
134
|
+
|
|
135
|
+
```bash
|
|
136
|
+
# Unit tests only
|
|
137
|
+
npm test
|
|
138
|
+
|
|
139
|
+
# Unit tests with coverage
|
|
140
|
+
npm run test:coverage
|
|
141
|
+
|
|
142
|
+
# E2E tests only
|
|
143
|
+
npm run test:e2e
|
|
144
|
+
|
|
145
|
+
# Lint check
|
|
146
|
+
npm run lint:check
|
|
147
|
+
|
|
148
|
+
# Run all tests (unit + E2E)
|
|
149
|
+
npm run test:all
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
### CI/CD Pipeline
|
|
153
|
+
|
|
154
|
+
GitHub Actions workflow includes:
|
|
155
|
+
1. **Linting** - ESLint code quality checks
|
|
156
|
+
2. **Unit Tests** - 411 Jest tests with coverage
|
|
157
|
+
3. **Mock Data Generation** - Reproducible test session fixtures
|
|
158
|
+
4. **E2E Tests** - 59 Playwright tests with Chromium
|
|
159
|
+
5. **Artifact Upload** - Test results on failure
|
|
160
|
+
|
|
161
|
+
**Test Data Strategy:**
|
|
162
|
+
- ✅ CI uses generated mock data (fast, reliable, no external dependencies)
|
|
163
|
+
- ✅ Local development can use real sessions for integration testing
|
|
164
|
+
- ✅ Fixtures cover all event formats (Copilot, Claude, Pi-Mono)
|
|
165
|
+
|
|
166
|
+
---
|
|
167
|
+
|
|
100
168
|
## 🏗️ Architecture
|
|
101
169
|
|
|
102
170
|
```
|
|
@@ -105,23 +173,84 @@ npx @qiaolei81/copilot-session-viewer
|
|
|
105
173
|
│ • Virtual Scroller (vue-virtual-scroller) │
|
|
106
174
|
│ • Infinite Scroll (JavaScript) │
|
|
107
175
|
│ • GitHub-inspired Dark Theme │
|
|
176
|
+
│ • XSS Protection (DOMPurify) │
|
|
108
177
|
└─────────────────────────────────────────────────┘
|
|
109
178
|
↕ HTTP/API
|
|
110
179
|
┌─────────────────────────────────────────────────┐
|
|
111
180
|
│ Backend (Node.js + Express) │
|
|
112
|
-
│ • Session Repository
|
|
181
|
+
│ • Multi-Source Session Repository │
|
|
182
|
+
│ • Unified Event Format Normalizer │
|
|
113
183
|
│ • JSONL Streaming Parser │
|
|
114
184
|
│ • Paginated API Endpoints │
|
|
185
|
+
│ • ZIP Import/Export with Security Validation │
|
|
115
186
|
└─────────────────────────────────────────────────┘
|
|
116
187
|
↕ File System
|
|
117
188
|
┌─────────────────────────────────────────────────┐
|
|
118
|
-
│ Data Layer (
|
|
119
|
-
│ •
|
|
120
|
-
│ •
|
|
121
|
-
│ •
|
|
189
|
+
│ Data Layer (Multi-Source) │
|
|
190
|
+
│ • Copilot: ~/.copilot/session-state/ │
|
|
191
|
+
│ • Claude: ~/.claude/projects/ │
|
|
192
|
+
│ • Pi-Mono: ~/.pi/agent/sessions/ │
|
|
122
193
|
└─────────────────────────────────────────────────┘
|
|
123
194
|
```
|
|
124
195
|
|
|
196
|
+
### Unified Event Format
|
|
197
|
+
|
|
198
|
+
All session sources are normalized to a consistent schema:
|
|
199
|
+
|
|
200
|
+
```javascript
|
|
201
|
+
{
|
|
202
|
+
type: 'assistant.message',
|
|
203
|
+
timestamp: '2026-02-23T00:00:00.000Z',
|
|
204
|
+
data: {
|
|
205
|
+
message: 'Response text',
|
|
206
|
+
tools: [
|
|
207
|
+
{
|
|
208
|
+
id: 'tool-001',
|
|
209
|
+
name: 'read',
|
|
210
|
+
startTime: '2026-02-23T00:00:01.000Z',
|
|
211
|
+
endTime: '2026-02-23T00:00:02.000Z',
|
|
212
|
+
status: 'completed',
|
|
213
|
+
input: { path: 'file.js' },
|
|
214
|
+
result: { content: '...' },
|
|
215
|
+
error: null,
|
|
216
|
+
metadata: {
|
|
217
|
+
source: 'copilot', // or 'claude', 'pi-mono'
|
|
218
|
+
duration: 1000
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
]
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
**Benefits:**
|
|
227
|
+
- ✅ Consistent UI rendering across all sources
|
|
228
|
+
- ✅ Simplified frontend logic
|
|
229
|
+
- ✅ Easy to add new sources
|
|
230
|
+
|
|
231
|
+
---
|
|
232
|
+
|
|
233
|
+
## 🔒 Security
|
|
234
|
+
|
|
235
|
+
### XSS Protection
|
|
236
|
+
- **DOMPurify Sanitization** - All user-generated content is sanitized before rendering
|
|
237
|
+
- **Whitelist-based** - Only safe HTML tags and attributes are allowed
|
|
238
|
+
- **JavaScript URL Protection** - Blocks `javascript:`, `data:`, and `onclick` handlers
|
|
239
|
+
- **Tested** - Comprehensive E2E tests for XSS attack vectors
|
|
240
|
+
|
|
241
|
+
### ZIP Bomb Defense
|
|
242
|
+
4-layer protection against malicious archives:
|
|
243
|
+
1. **Compressed Size Limit** - 50 MB max upload
|
|
244
|
+
2. **Uncompressed Size Limit** - 200 MB max expansion
|
|
245
|
+
3. **File Count Limit** - 1000 files max
|
|
246
|
+
4. **Directory Depth Limit** - 5 levels max
|
|
247
|
+
|
|
248
|
+
### Local-First Design
|
|
249
|
+
- No external API calls for session data
|
|
250
|
+
- All processing happens locally
|
|
251
|
+
- Optional AI insights require user action
|
|
252
|
+
- No telemetry or tracking
|
|
253
|
+
|
|
125
254
|
---
|
|
126
255
|
|
|
127
256
|
## 🎯 Use Cases
|
|
@@ -172,6 +301,16 @@ MIT License - see [LICENSE](LICENSE) file for details
|
|
|
172
301
|
- [vue-virtual-scroller](https://github.com/Akryum/vue-virtual-scroller) - High-performance virtual scrolling
|
|
173
302
|
- [Express.js](https://expressjs.com/) - Web application framework
|
|
174
303
|
- [EJS](https://ejs.co/) - Templating engine
|
|
304
|
+
- [DOMPurify](https://github.com/cure53/DOMPurify) - XSS protection
|
|
305
|
+
- [Playwright](https://playwright.dev/) - E2E testing
|
|
306
|
+
|
|
307
|
+
**Recent Updates (v0.1.9+):**
|
|
308
|
+
- ✨ Multi-source support (Copilot, Claude, Pi-Mono)
|
|
309
|
+
- 🔒 XSS protection with DOMPurify
|
|
310
|
+
- 🛡️ ZIP bomb defense (4-layer validation)
|
|
311
|
+
- 📄 Memory pagination API
|
|
312
|
+
- 🧪 470+ tests with CI/CD integration
|
|
313
|
+
- 📚 Comprehensive documentation
|
|
175
314
|
|
|
176
315
|
---
|
|
177
316
|
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
const { ParserFactory } = require('../lib/parsers');
|
|
2
|
+
|
|
3
|
+
// Example: Parse Copilot CLI session
|
|
4
|
+
function parseCopilotSession() {
|
|
5
|
+
const copilotEvents = [
|
|
6
|
+
{
|
|
7
|
+
type: 'session.start',
|
|
8
|
+
data: {
|
|
9
|
+
sessionId: '12345',
|
|
10
|
+
startTime: '2026-02-20T00:00:00Z',
|
|
11
|
+
selectedModel: 'claude-sonnet-4.5',
|
|
12
|
+
copilotVersion: '0.0.411',
|
|
13
|
+
context: {
|
|
14
|
+
cwd: '/path/to/project',
|
|
15
|
+
branch: 'main'
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
id: 'evt-1',
|
|
19
|
+
timestamp: '2026-02-20T00:00:00Z'
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
type: 'user.message',
|
|
23
|
+
data: {
|
|
24
|
+
content: 'Hello',
|
|
25
|
+
transformedContent: 'Hello'
|
|
26
|
+
},
|
|
27
|
+
id: 'evt-2',
|
|
28
|
+
timestamp: '2026-02-20T00:00:01Z',
|
|
29
|
+
parentId: 'evt-1'
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
type: 'assistant.message',
|
|
33
|
+
data: {
|
|
34
|
+
messageId: 'msg-1',
|
|
35
|
+
content: 'Hi there!',
|
|
36
|
+
toolRequests: []
|
|
37
|
+
},
|
|
38
|
+
id: 'evt-3',
|
|
39
|
+
timestamp: '2026-02-20T00:00:02Z',
|
|
40
|
+
parentId: 'evt-2'
|
|
41
|
+
}
|
|
42
|
+
];
|
|
43
|
+
|
|
44
|
+
const factory = new ParserFactory();
|
|
45
|
+
const result = factory.parse(copilotEvents);
|
|
46
|
+
|
|
47
|
+
console.log('=== Copilot Session ===');
|
|
48
|
+
console.log('Parser type:', factory.getParserType(copilotEvents));
|
|
49
|
+
console.log('Metadata:', JSON.stringify(result.metadata, null, 2));
|
|
50
|
+
console.log('Turns:', result.turns.length);
|
|
51
|
+
console.log('First turn:', JSON.stringify(result.turns[0], null, 2));
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Example: Parse Claude Code session
|
|
55
|
+
function parseClaudeSession() {
|
|
56
|
+
const claudeEvents = [
|
|
57
|
+
{
|
|
58
|
+
type: 'user',
|
|
59
|
+
uuid: 'uuid-1',
|
|
60
|
+
parentUuid: null,
|
|
61
|
+
sessionId: 'session-123',
|
|
62
|
+
timestamp: '2026-02-20T00:00:00Z',
|
|
63
|
+
cwd: '/path/to/project',
|
|
64
|
+
gitBranch: 'main',
|
|
65
|
+
version: '2.1.42',
|
|
66
|
+
message: {
|
|
67
|
+
role: 'user',
|
|
68
|
+
content: 'Analyze this code'
|
|
69
|
+
}
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
type: 'assistant',
|
|
73
|
+
uuid: 'uuid-2',
|
|
74
|
+
parentUuid: 'uuid-1',
|
|
75
|
+
sessionId: 'session-123',
|
|
76
|
+
timestamp: '2026-02-20T00:00:01Z',
|
|
77
|
+
message: {
|
|
78
|
+
id: 'msg-1',
|
|
79
|
+
role: 'assistant',
|
|
80
|
+
model: 'claude-opus-4.6',
|
|
81
|
+
content: [
|
|
82
|
+
{ type: 'text', text: "I'll analyze the code." },
|
|
83
|
+
{
|
|
84
|
+
type: 'tool_use',
|
|
85
|
+
id: 'tool-1',
|
|
86
|
+
name: 'read_file',
|
|
87
|
+
input: { path: 'src/main.js' }
|
|
88
|
+
}
|
|
89
|
+
]
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
];
|
|
93
|
+
|
|
94
|
+
const factory = new ParserFactory();
|
|
95
|
+
const result = factory.parse(claudeEvents);
|
|
96
|
+
|
|
97
|
+
console.log('\n=== Claude Code Session ===');
|
|
98
|
+
console.log('Parser type:', factory.getParserType(claudeEvents));
|
|
99
|
+
console.log('Metadata:', JSON.stringify(result.metadata, null, 2));
|
|
100
|
+
console.log('Turns:', result.turns.length);
|
|
101
|
+
console.log('First turn:', JSON.stringify(result.turns[0], null, 2));
|
|
102
|
+
console.log('Tool calls:', result.toolCalls.length);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Run examples
|
|
106
|
+
if (require.main === module) {
|
|
107
|
+
parseCopilotSession();
|
|
108
|
+
parseClaudeSession();
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
module.exports = {
|
|
112
|
+
parseCopilotSession,
|
|
113
|
+
parseClaudeSession
|
|
114
|
+
};
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
# Session Event Parsers
|
|
2
|
+
|
|
3
|
+
Strategy pattern implementation for parsing session events from multiple formats.
|
|
4
|
+
|
|
5
|
+
## Architecture
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
lib/parsers/
|
|
9
|
+
├── base-parser.js # Base parser interface
|
|
10
|
+
├── copilot-parser.js # Copilot CLI format parser
|
|
11
|
+
├── claude-parser.js # Claude Code format parser
|
|
12
|
+
├── pi-mono-parser.js # Pi-Mono format parser
|
|
13
|
+
├── parser-factory.js # Parser factory (auto-detection)
|
|
14
|
+
└── index.js # Export all parsers
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Design Pattern
|
|
18
|
+
|
|
19
|
+
### Strategy Pattern
|
|
20
|
+
|
|
21
|
+
- **Strategy Interface**: `BaseSessionParser` defines methods all parsers must implement
|
|
22
|
+
- **Concrete Strategies**: `CopilotSessionParser`, `ClaudeSessionParser`, `PiMonoSessionParser` implement specific parsing logic
|
|
23
|
+
- **Context**: `ParserFactory` automatically selects the appropriate strategy
|
|
24
|
+
|
|
25
|
+
### Benefits
|
|
26
|
+
|
|
27
|
+
1. **Extensible**: Add new formats by simply implementing `BaseSessionParser`
|
|
28
|
+
2. **Decoupled**: Parsing logic is separated from consumers
|
|
29
|
+
3. **Auto-detection**: `ParserFactory` automatically identifies formats
|
|
30
|
+
4. **Unified Interface**: Different formats output the same data structure
|
|
31
|
+
|
|
32
|
+
## Usage
|
|
33
|
+
|
|
34
|
+
### Auto-detect Format
|
|
35
|
+
|
|
36
|
+
```javascript
|
|
37
|
+
const { ParserFactory } = require('./lib/parsers');
|
|
38
|
+
|
|
39
|
+
const events = [...]; // Events read from jsonl
|
|
40
|
+
const factory = new ParserFactory();
|
|
41
|
+
|
|
42
|
+
// Auto-detect and parse
|
|
43
|
+
const result = factory.parse(events);
|
|
44
|
+
|
|
45
|
+
// Get parser type
|
|
46
|
+
const parserType = factory.getParserType(events); // 'copilot', 'claude', or 'pi-mono'
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### Use Specific Parser Directly
|
|
50
|
+
|
|
51
|
+
```javascript
|
|
52
|
+
const { CopilotSessionParser, ClaudeSessionParser, PiMonoSessionParser } = require('./lib/parsers');
|
|
53
|
+
|
|
54
|
+
// Copilot CLI
|
|
55
|
+
const copilotParser = new CopilotSessionParser();
|
|
56
|
+
if (copilotParser.canParse(events)) {
|
|
57
|
+
const result = copilotParser.parse(events);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Claude Code
|
|
61
|
+
const claudeParser = new ClaudeSessionParser();
|
|
62
|
+
if (claudeParser.canParse(events)) {
|
|
63
|
+
const result = claudeParser.parse(events);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Pi-Mono
|
|
67
|
+
const piMonoParser = new PiMonoSessionParser();
|
|
68
|
+
if (piMonoParser.canParse(events)) {
|
|
69
|
+
const result = piMonoParser.parse(events);
|
|
70
|
+
}
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## Unified Output Format
|
|
74
|
+
|
|
75
|
+
All parsers output the same data structure:
|
|
76
|
+
|
|
77
|
+
```javascript
|
|
78
|
+
{
|
|
79
|
+
metadata: {
|
|
80
|
+
sessionId: "...",
|
|
81
|
+
startTime: "...",
|
|
82
|
+
model: "...",
|
|
83
|
+
version: "...",
|
|
84
|
+
cwd: "...",
|
|
85
|
+
branch: "...",
|
|
86
|
+
// ...
|
|
87
|
+
},
|
|
88
|
+
turns: [
|
|
89
|
+
{
|
|
90
|
+
turnId: "...",
|
|
91
|
+
userMessage: {
|
|
92
|
+
id: "...",
|
|
93
|
+
content: "...",
|
|
94
|
+
timestamp: "..."
|
|
95
|
+
},
|
|
96
|
+
assistantMessages: [
|
|
97
|
+
{
|
|
98
|
+
id: "...",
|
|
99
|
+
content: "...",
|
|
100
|
+
toolRequests: [...],
|
|
101
|
+
timestamp: "..."
|
|
102
|
+
}
|
|
103
|
+
],
|
|
104
|
+
toolCalls: [...]
|
|
105
|
+
}
|
|
106
|
+
],
|
|
107
|
+
toolCalls: [...],
|
|
108
|
+
allEvents: [...] // Raw events
|
|
109
|
+
}
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
## Supported Formats
|
|
113
|
+
|
|
114
|
+
### 1. Copilot CLI Format
|
|
115
|
+
|
|
116
|
+
**Characteristics:**
|
|
117
|
+
- Event types: `session.start`, `user.message`, `assistant.message`, `tool.execution_start`
|
|
118
|
+
- Structure: `{type, data: {...}, id, parentId}`
|
|
119
|
+
- Tree relationship: Connected via `parentId`
|
|
120
|
+
|
|
121
|
+
**Example:**
|
|
122
|
+
```json
|
|
123
|
+
{
|
|
124
|
+
"type": "session.start",
|
|
125
|
+
"data": {
|
|
126
|
+
"sessionId": "...",
|
|
127
|
+
"selectedModel": "claude-sonnet-4.5"
|
|
128
|
+
},
|
|
129
|
+
"id": "...",
|
|
130
|
+
"timestamp": "..."
|
|
131
|
+
}
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### 2. Claude Code Format
|
|
135
|
+
|
|
136
|
+
**Characteristics:**
|
|
137
|
+
- Event types: `user`, `assistant`, `file-history-snapshot`, `queue-operation`
|
|
138
|
+
- Structure: `{type, uuid, parentUuid, message: {...}}`
|
|
139
|
+
- Tree relationship: Connected via `parentUuid`
|
|
140
|
+
|
|
141
|
+
**Example:**
|
|
142
|
+
```json
|
|
143
|
+
{
|
|
144
|
+
"type": "user",
|
|
145
|
+
"uuid": "...",
|
|
146
|
+
"parentUuid": null,
|
|
147
|
+
"sessionId": "...",
|
|
148
|
+
"message": {
|
|
149
|
+
"role": "user",
|
|
150
|
+
"content": "..."
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
### 3. Pi-Mono Format
|
|
156
|
+
|
|
157
|
+
**Characteristics:**
|
|
158
|
+
- Event types: `message` (with role), `model_change`, `thinking_change`
|
|
159
|
+
- Structure: `{type, role, message, toolResult, timestamp}`
|
|
160
|
+
- Flat structure with parentId linkage for tool results
|
|
161
|
+
|
|
162
|
+
**Example:**
|
|
163
|
+
```json
|
|
164
|
+
{
|
|
165
|
+
"type": "message",
|
|
166
|
+
"role": "user",
|
|
167
|
+
"message": "...",
|
|
168
|
+
"timestamp": "...",
|
|
169
|
+
"id": "..."
|
|
170
|
+
}
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
## Adding New Formats
|
|
174
|
+
|
|
175
|
+
1. Extend `BaseSessionParser`
|
|
176
|
+
2. Implement all required methods
|
|
177
|
+
3. Register in `ParserFactory`
|
|
178
|
+
|
|
179
|
+
```javascript
|
|
180
|
+
const BaseSessionParser = require('./base-parser');
|
|
181
|
+
|
|
182
|
+
class MyCustomParser extends BaseSessionParser {
|
|
183
|
+
canParse(events) {
|
|
184
|
+
// Detection logic
|
|
185
|
+
return events.some(e => e.customField);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
parse(events) {
|
|
189
|
+
return {
|
|
190
|
+
metadata: this.getMetadata(events),
|
|
191
|
+
turns: this.extractTurns(events),
|
|
192
|
+
toolCalls: this.extractToolCalls(events),
|
|
193
|
+
allEvents: events
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
getMetadata(events) { /* ... */ }
|
|
198
|
+
extractTurns(events) { /* ... */ }
|
|
199
|
+
extractToolCalls(events) { /* ... */ }
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// Add to parser-factory.js
|
|
203
|
+
this.parsers.push(new MyCustomParser());
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
## Testing
|
|
207
|
+
|
|
208
|
+
Run example:
|
|
209
|
+
```bash
|
|
210
|
+
node examples/parser-usage.js
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
## API Documentation
|
|
214
|
+
|
|
215
|
+
### BaseSessionParser
|
|
216
|
+
|
|
217
|
+
Base class for all parsers.
|
|
218
|
+
|
|
219
|
+
#### Methods
|
|
220
|
+
|
|
221
|
+
- `canParse(events)` - Check if this format can be parsed
|
|
222
|
+
- `parse(events)` - Parse events and return unified format
|
|
223
|
+
- `getMetadata(events)` - Extract session metadata
|
|
224
|
+
- `extractTurns(events)` - Extract conversation turns
|
|
225
|
+
- `extractToolCalls(events)` - Extract tool calls
|
|
226
|
+
|
|
227
|
+
### ParserFactory
|
|
228
|
+
|
|
229
|
+
Parser factory for automatic format detection.
|
|
230
|
+
|
|
231
|
+
#### Methods
|
|
232
|
+
|
|
233
|
+
- `getParser(events)` - Return appropriate parser instance
|
|
234
|
+
- `parse(events)` - Auto-detect and parse
|
|
235
|
+
- `getParserType(events)` - Return parser type name
|
|
236
|
+
|
|
237
|
+
## License
|
|
238
|
+
|
|
239
|
+
MIT
|