recall-player 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +238 -0
- package/backend/dist/db/connection.d.ts +47 -0
- package/backend/dist/db/connection.d.ts.map +1 -0
- package/backend/dist/db/connection.js +88 -0
- package/backend/dist/db/connection.js.map +1 -0
- package/backend/dist/db/queries.d.ts +172 -0
- package/backend/dist/db/queries.d.ts.map +1 -0
- package/backend/dist/db/queries.js +436 -0
- package/backend/dist/db/queries.js.map +1 -0
- package/backend/dist/db/schema.d.ts +100 -0
- package/backend/dist/db/schema.d.ts.map +1 -0
- package/backend/dist/db/schema.js +6 -0
- package/backend/dist/db/schema.js.map +1 -0
- package/backend/dist/db/transcript-connection.d.ts +57 -0
- package/backend/dist/db/transcript-connection.d.ts.map +1 -0
- package/backend/dist/db/transcript-connection.js +112 -0
- package/backend/dist/db/transcript-connection.js.map +1 -0
- package/backend/dist/db/transcript-queries.d.ts +152 -0
- package/backend/dist/db/transcript-queries.d.ts.map +1 -0
- package/backend/dist/db/transcript-queries.js +706 -0
- package/backend/dist/db/transcript-queries.js.map +1 -0
- package/backend/dist/db/transcript-schema.d.ts +143 -0
- package/backend/dist/db/transcript-schema.d.ts.map +1 -0
- package/backend/dist/db/transcript-schema.js +10 -0
- package/backend/dist/db/transcript-schema.js.map +1 -0
- package/backend/dist/index.d.ts +2 -0
- package/backend/dist/index.d.ts.map +1 -0
- package/backend/dist/index.js +96 -0
- package/backend/dist/index.js.map +1 -0
- package/backend/dist/parser/agent-detector.d.ts +76 -0
- package/backend/dist/parser/agent-detector.d.ts.map +1 -0
- package/backend/dist/parser/agent-detector.js +281 -0
- package/backend/dist/parser/agent-detector.js.map +1 -0
- package/backend/dist/parser/base-parser.d.ts +123 -0
- package/backend/dist/parser/base-parser.d.ts.map +1 -0
- package/backend/dist/parser/base-parser.js +364 -0
- package/backend/dist/parser/base-parser.js.map +1 -0
- package/backend/dist/parser/claude-parser.d.ts +78 -0
- package/backend/dist/parser/claude-parser.d.ts.map +1 -0
- package/backend/dist/parser/claude-parser.js +247 -0
- package/backend/dist/parser/claude-parser.js.map +1 -0
- package/backend/dist/parser/codex-parser.d.ts +128 -0
- package/backend/dist/parser/codex-parser.d.ts.map +1 -0
- package/backend/dist/parser/codex-parser.js +371 -0
- package/backend/dist/parser/codex-parser.js.map +1 -0
- package/backend/dist/parser/gemini-parser.d.ts +107 -0
- package/backend/dist/parser/gemini-parser.d.ts.map +1 -0
- package/backend/dist/parser/gemini-parser.js +360 -0
- package/backend/dist/parser/gemini-parser.js.map +1 -0
- package/backend/dist/parser/parser-factory.d.ts +83 -0
- package/backend/dist/parser/parser-factory.d.ts.map +1 -0
- package/backend/dist/parser/parser-factory.js +163 -0
- package/backend/dist/parser/parser-factory.js.map +1 -0
- package/backend/dist/parser/session-indexer.d.ts +79 -0
- package/backend/dist/parser/session-indexer.d.ts.map +1 -0
- package/backend/dist/parser/session-indexer.js +558 -0
- package/backend/dist/parser/session-indexer.js.map +1 -0
- package/backend/dist/parser/timeline-builder.d.ts +7 -0
- package/backend/dist/parser/timeline-builder.d.ts.map +1 -0
- package/backend/dist/parser/timeline-builder.js +334 -0
- package/backend/dist/parser/timeline-builder.js.map +1 -0
- package/backend/dist/parser/transcript-parser.d.ts +25 -0
- package/backend/dist/parser/transcript-parser.d.ts.map +1 -0
- package/backend/dist/parser/transcript-parser.js +137 -0
- package/backend/dist/parser/transcript-parser.js.map +1 -0
- package/backend/dist/routes/commentary.d.ts +33 -0
- package/backend/dist/routes/commentary.d.ts.map +1 -0
- package/backend/dist/routes/commentary.js +153 -0
- package/backend/dist/routes/commentary.js.map +1 -0
- package/backend/dist/routes/import.d.ts +3 -0
- package/backend/dist/routes/import.d.ts.map +1 -0
- package/backend/dist/routes/import.js +220 -0
- package/backend/dist/routes/import.js.map +1 -0
- package/backend/dist/routes/sessions.d.ts +3 -0
- package/backend/dist/routes/sessions.d.ts.map +1 -0
- package/backend/dist/routes/sessions.js +393 -0
- package/backend/dist/routes/sessions.js.map +1 -0
- package/backend/dist/server.d.ts +25 -0
- package/backend/dist/server.d.ts.map +1 -0
- package/backend/dist/server.js +110 -0
- package/backend/dist/server.js.map +1 -0
- package/backend/dist/services/example-import.d.ts +20 -0
- package/backend/dist/services/example-import.d.ts.map +1 -0
- package/backend/dist/services/example-import.js +93 -0
- package/backend/dist/services/example-import.js.map +1 -0
- package/backend/dist/services/file-watcher.d.ts +22 -0
- package/backend/dist/services/file-watcher.d.ts.map +1 -0
- package/backend/dist/services/file-watcher.js +141 -0
- package/backend/dist/services/file-watcher.js.map +1 -0
- package/backend/dist/services/import-cli.d.ts +11 -0
- package/backend/dist/services/import-cli.d.ts.map +1 -0
- package/backend/dist/services/import-cli.js +171 -0
- package/backend/dist/services/import-cli.js.map +1 -0
- package/backend/dist/services/transcript-importer.d.ts +98 -0
- package/backend/dist/services/transcript-importer.d.ts.map +1 -0
- package/backend/dist/services/transcript-importer.js +342 -0
- package/backend/dist/services/transcript-importer.js.map +1 -0
- package/backend/dist/types/transcript.d.ts +174 -0
- package/backend/dist/types/transcript.d.ts.map +1 -0
- package/backend/dist/types/transcript.js +8 -0
- package/backend/dist/types/transcript.js.map +1 -0
- package/backend/package.json +40 -0
- package/backend/public/.gitkeep +0 -0
- package/backend/public/assets/index-D8IP1zNL.css +1 -0
- package/backend/public/assets/index-GtkMiMqE.js +69 -0
- package/backend/public/index.html +14 -0
- package/bin/recall.js +43 -0
- package/package.json +54 -0
package/README.md
ADDED
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
# Recall
|
|
2
|
+
|
|
3
|
+
A local-first web application that lets you **replay AI coding sessions** like a video player. Watch how features were built, decisions made, and problems solved across multiple AI coding agents.
|
|
4
|
+
|
|
5
|
+
**Supported Agents:**
|
|
6
|
+
- **Claude Code** - Anthropic's CLI coding assistant
|
|
7
|
+
- **Codex CLI** - OpenAI's command-line coding tool
|
|
8
|
+
- **Gemini CLI** - Google's terminal-based AI assistant
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## Quick Start
|
|
13
|
+
|
|
14
|
+
### Prerequisites
|
|
15
|
+
|
|
16
|
+
- **Node.js 18+** (check with `node --version`)
|
|
17
|
+
- At least one AI coding agent with session history:
|
|
18
|
+
- Claude Code sessions in `~/.claude/projects/`
|
|
19
|
+
- Codex CLI sessions in `~/.codex/sessions/`
|
|
20
|
+
- Gemini CLI sessions in `~/.gemini/tmp/`
|
|
21
|
+
|
|
22
|
+
### Installation
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
# Clone the repository
|
|
26
|
+
git clone https://github.com/anthropics/recall.git
|
|
27
|
+
cd recall
|
|
28
|
+
|
|
29
|
+
# Install backend dependencies
|
|
30
|
+
cd backend
|
|
31
|
+
npm install
|
|
32
|
+
|
|
33
|
+
# Install frontend dependencies
|
|
34
|
+
cd ../frontend
|
|
35
|
+
npm install
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### Running the Application
|
|
39
|
+
|
|
40
|
+
You need to run both the backend and frontend servers:
|
|
41
|
+
|
|
42
|
+
**Terminal 1 - Backend:**
|
|
43
|
+
```bash
|
|
44
|
+
cd backend
|
|
45
|
+
npm run dev
|
|
46
|
+
```
|
|
47
|
+
Backend will start on http://localhost:3001
|
|
48
|
+
|
|
49
|
+
**Terminal 2 - Frontend:**
|
|
50
|
+
```bash
|
|
51
|
+
cd frontend
|
|
52
|
+
npm run dev
|
|
53
|
+
```
|
|
54
|
+
Frontend will start on http://localhost:5173 (or next available port)
|
|
55
|
+
|
|
56
|
+
### Using Recall
|
|
57
|
+
|
|
58
|
+
1. Open your browser to http://localhost:5173
|
|
59
|
+
2. You'll see a list of all your AI coding sessions
|
|
60
|
+
3. Use the filter tabs (All, Claude, Codex, Gemini) to filter by agent
|
|
61
|
+
4. Click on any session to open the replay player
|
|
62
|
+
5. Use the playback controls to step through the session
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
## Features
|
|
67
|
+
|
|
68
|
+
### Session Browser
|
|
69
|
+
- View all sessions across Claude, Codex, and Gemini
|
|
70
|
+
- Filter by agent type, date range, and duration
|
|
71
|
+
- Search sessions by project name or content
|
|
72
|
+
- See session metadata (duration, event count, first message)
|
|
73
|
+
|
|
74
|
+
### Session Player
|
|
75
|
+
- **Frame-by-frame playback** of coding sessions
|
|
76
|
+
- **Frame types:** User messages, AI responses, AI thinking, Tool executions
|
|
77
|
+
- **Filter controls** to show/hide specific frame types
|
|
78
|
+
- **Keyboard shortcuts** for navigation (arrow keys, space, etc.)
|
|
79
|
+
- **Search** within sessions to find specific content
|
|
80
|
+
|
|
81
|
+
### Multi-Agent Support
|
|
82
|
+
- Automatically detects and parses sessions from all supported agents
|
|
83
|
+
- Agent-specific badges and colors in the UI
|
|
84
|
+
- Normalized frame format for consistent playback experience
|
|
85
|
+
|
|
86
|
+
---
|
|
87
|
+
|
|
88
|
+
## Project Structure
|
|
89
|
+
|
|
90
|
+
```
|
|
91
|
+
recall/
|
|
92
|
+
├── backend/ # Node.js + Express + TypeScript
|
|
93
|
+
│ ├── src/
|
|
94
|
+
│ │ ├── parser/ # Agent-specific parsers
|
|
95
|
+
│ │ │ ├── agent-detector.ts # Detects agent from file path
|
|
96
|
+
│ │ │ ├── base-parser.ts # Abstract parser base class
|
|
97
|
+
│ │ │ ├── claude-parser.ts # Claude Code parser
|
|
98
|
+
│ │ │ ├── codex-parser.ts # Codex CLI parser
|
|
99
|
+
│ │ │ ├── gemini-parser.ts # Gemini CLI parser
|
|
100
|
+
│ │ │ └── parser-factory.ts # Parser selection factory
|
|
101
|
+
│ │ ├── routes/ # API endpoints
|
|
102
|
+
│ │ ├── db/ # Database layer
|
|
103
|
+
│ │ └── types/ # TypeScript types
|
|
104
|
+
│ └── package.json
|
|
105
|
+
│
|
|
106
|
+
├── frontend/ # React + Vite + TypeScript
|
|
107
|
+
│ ├── src/
|
|
108
|
+
│ │ ├── pages/ # Main pages (SessionList, SessionPlayer)
|
|
109
|
+
│ │ ├── components/ # Reusable components
|
|
110
|
+
│ │ ├── api/ # API client
|
|
111
|
+
│ │ └── types/ # TypeScript types
|
|
112
|
+
│ └── package.json
|
|
113
|
+
│
|
|
114
|
+
└── README.md
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
---
|
|
118
|
+
|
|
119
|
+
## API Reference
|
|
120
|
+
|
|
121
|
+
### List Sessions
|
|
122
|
+
```bash
|
|
123
|
+
# Get all sessions
|
|
124
|
+
curl 'http://localhost:3001/api/sessions'
|
|
125
|
+
|
|
126
|
+
# Filter by agent
|
|
127
|
+
curl 'http://localhost:3001/api/sessions?agent=claude'
|
|
128
|
+
curl 'http://localhost:3001/api/sessions?agent=codex'
|
|
129
|
+
curl 'http://localhost:3001/api/sessions?agent=gemini'
|
|
130
|
+
|
|
131
|
+
# Pagination
|
|
132
|
+
curl 'http://localhost:3001/api/sessions?limit=10&offset=20'
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
### Get Available Agents
|
|
136
|
+
```bash
|
|
137
|
+
curl 'http://localhost:3001/api/agents'
|
|
138
|
+
# Returns: { "agents": ["claude", "codex", "gemini"], "counts": {...} }
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### Get Session Details
|
|
142
|
+
```bash
|
|
143
|
+
curl 'http://localhost:3001/api/sessions/{sessionId}'
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
### Get Session Frames
|
|
147
|
+
```bash
|
|
148
|
+
curl 'http://localhost:3001/api/sessions/{sessionId}/frames'
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
---
|
|
152
|
+
|
|
153
|
+
## Keyboard Shortcuts (Session Player)
|
|
154
|
+
|
|
155
|
+
| Key | Action |
|
|
156
|
+
|-----|--------|
|
|
157
|
+
| `Space` | Play/Pause |
|
|
158
|
+
| `←` / `→` | Previous/Next frame |
|
|
159
|
+
| `Home` / `End` | First/Last frame |
|
|
160
|
+
| `n` / `p` | Next/Previous search match |
|
|
161
|
+
| `?` | Toggle help panel |
|
|
162
|
+
|
|
163
|
+
---
|
|
164
|
+
|
|
165
|
+
## Development
|
|
166
|
+
|
|
167
|
+
### Backend Development
|
|
168
|
+
```bash
|
|
169
|
+
cd backend
|
|
170
|
+
npm run dev # Development with hot reload
|
|
171
|
+
npm run build # Build for production
|
|
172
|
+
npm start # Run production build
|
|
173
|
+
npm test # Run tests
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
### Frontend Development
|
|
177
|
+
```bash
|
|
178
|
+
cd frontend
|
|
179
|
+
npm run dev # Development with hot reload
|
|
180
|
+
npm run build # Build for production
|
|
181
|
+
npm run lint # Run ESLint
|
|
182
|
+
npm run preview # Preview production build
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
---
|
|
186
|
+
|
|
187
|
+
## Session File Locations
|
|
188
|
+
|
|
189
|
+
Recall automatically scans these directories for sessions:
|
|
190
|
+
|
|
191
|
+
| Agent | Directory | File Format |
|
|
192
|
+
|-------|-----------|-------------|
|
|
193
|
+
| Claude Code | `~/.claude/projects/{project}/` | `*.jsonl` |
|
|
194
|
+
| Codex CLI | `~/.codex/sessions/` | `*.jsonl` (with date subdirs) |
|
|
195
|
+
| Gemini CLI | `~/.gemini/tmp/{hash}/chats/` | `session-*.json` |
|
|
196
|
+
|
|
197
|
+
---
|
|
198
|
+
|
|
199
|
+
## Troubleshooting
|
|
200
|
+
|
|
201
|
+
### No sessions showing up?
|
|
202
|
+
1. Make sure you have session files in one of the supported directories
|
|
203
|
+
2. Check the backend console for any errors
|
|
204
|
+
3. Try the API directly: `curl http://localhost:3001/api/agents`
|
|
205
|
+
|
|
206
|
+
### Backend won't start?
|
|
207
|
+
1. Make sure you're in the `backend` directory
|
|
208
|
+
2. Run `npm install` to install dependencies
|
|
209
|
+
3. Check that port 3001 is available
|
|
210
|
+
|
|
211
|
+
### Frontend won't start?
|
|
212
|
+
1. Make sure you're in the `frontend` directory
|
|
213
|
+
2. Run `npm install` to install dependencies
|
|
214
|
+
3. The frontend will automatically find an available port if 5173 is taken
|
|
215
|
+
|
|
216
|
+
---
|
|
217
|
+
|
|
218
|
+
## Security Notes
|
|
219
|
+
|
|
220
|
+
- **Local-only**: This app is designed for local use only
|
|
221
|
+
- **Read-only**: Session files are read but never modified
|
|
222
|
+
- **Sensitive data**: Session files may contain API keys, credentials, or sensitive code - do not expose this app to the internet
|
|
223
|
+
|
|
224
|
+
---
|
|
225
|
+
|
|
226
|
+
## License
|
|
227
|
+
|
|
228
|
+
MIT
|
|
229
|
+
|
|
230
|
+
---
|
|
231
|
+
|
|
232
|
+
## Credits
|
|
233
|
+
|
|
234
|
+
Built with:
|
|
235
|
+
- [Node.js](https://nodejs.org/) + [Express](https://expressjs.com/)
|
|
236
|
+
- [React](https://react.dev/) + [Vite](https://vitejs.dev/)
|
|
237
|
+
- [TypeScript](https://www.typescriptlang.org/)
|
|
238
|
+
- [Tailwind CSS](https://tailwindcss.com/)
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import Database from 'better-sqlite3';
|
|
2
|
+
/**
|
|
3
|
+
* Creates and configures a new SQLite database connection
|
|
4
|
+
*
|
|
5
|
+
* Opens the claude-mem database in read-only mode for safety.
|
|
6
|
+
* This prevents accidental modifications to the user's session history.
|
|
7
|
+
*
|
|
8
|
+
* @returns {Database.Database} Configured SQLite database instance
|
|
9
|
+
* @throws {Error} If database file doesn't exist at DB_PATH
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* const db = getDatabase();
|
|
13
|
+
* const result = db.prepare('SELECT COUNT(*) as count FROM sdk_sessions').get();
|
|
14
|
+
* console.log(`Found ${result.count} sessions`);
|
|
15
|
+
*/
|
|
16
|
+
export declare function getDatabase(): Database.Database;
|
|
17
|
+
/**
|
|
18
|
+
* Get the singleton database instance
|
|
19
|
+
*
|
|
20
|
+
* Creates a new connection on first call, then reuses it for subsequent calls.
|
|
21
|
+
* This is safe because SQLite handles concurrent reads well in read-only mode.
|
|
22
|
+
*
|
|
23
|
+
* @returns {Database.Database} Singleton database instance
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* import { getDbInstance } from './connection';
|
|
27
|
+
*
|
|
28
|
+
* const db = getDbInstance();
|
|
29
|
+
* const sessions = db.prepare('SELECT * FROM sdk_sessions LIMIT 10').all();
|
|
30
|
+
*/
|
|
31
|
+
export declare function getDbInstance(): Database.Database;
|
|
32
|
+
/**
|
|
33
|
+
* Close the database connection and clear the singleton
|
|
34
|
+
*
|
|
35
|
+
* Should be called during graceful shutdown (SIGTERM, SIGINT).
|
|
36
|
+
* Ensures the database file is properly closed.
|
|
37
|
+
*
|
|
38
|
+
* @example
|
|
39
|
+
* process.on('SIGTERM', () => {
|
|
40
|
+
* server.close(() => {
|
|
41
|
+
* closeDatabase();
|
|
42
|
+
* process.exit(0);
|
|
43
|
+
* });
|
|
44
|
+
* });
|
|
45
|
+
*/
|
|
46
|
+
export declare function closeDatabase(): void;
|
|
47
|
+
//# sourceMappingURL=connection.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"connection.d.ts","sourceRoot":"","sources":["../../src/db/connection.ts"],"names":[],"mappings":"AAAA,OAAO,QAAQ,MAAM,gBAAgB,CAAC;AAUtC;;;;;;;;;;;;;GAaG;AACH,wBAAgB,WAAW,IAAI,QAAQ,CAAC,QAAQ,CAc/C;AAQD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,aAAa,IAAI,QAAQ,CAAC,QAAQ,CAKjD;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,aAAa,IAAI,IAAI,CAKpC"}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.getDatabase = getDatabase;
|
|
7
|
+
exports.getDbInstance = getDbInstance;
|
|
8
|
+
exports.closeDatabase = closeDatabase;
|
|
9
|
+
const better_sqlite3_1 = __importDefault(require("better-sqlite3"));
|
|
10
|
+
const path_1 = __importDefault(require("path"));
|
|
11
|
+
const fs_1 = __importDefault(require("fs"));
|
|
12
|
+
/**
|
|
13
|
+
* Default path to the claude-mem database
|
|
14
|
+
* Located in user's home directory: ~/.claude-mem/claude-mem.db
|
|
15
|
+
*/
|
|
16
|
+
const DB_PATH = path_1.default.join(process.env.HOME || '', '.claude-mem', 'claude-mem.db');
|
|
17
|
+
/**
|
|
18
|
+
* Creates and configures a new SQLite database connection
|
|
19
|
+
*
|
|
20
|
+
* Opens the claude-mem database in read-only mode for safety.
|
|
21
|
+
* This prevents accidental modifications to the user's session history.
|
|
22
|
+
*
|
|
23
|
+
* @returns {Database.Database} Configured SQLite database instance
|
|
24
|
+
* @throws {Error} If database file doesn't exist at DB_PATH
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* const db = getDatabase();
|
|
28
|
+
* const result = db.prepare('SELECT COUNT(*) as count FROM sdk_sessions').get();
|
|
29
|
+
* console.log(`Found ${result.count} sessions`);
|
|
30
|
+
*/
|
|
31
|
+
function getDatabase() {
|
|
32
|
+
if (!fs_1.default.existsSync(DB_PATH)) {
|
|
33
|
+
throw new Error(`Database not found at ${DB_PATH}. Make sure claude-mem is installed and has recorded sessions.`);
|
|
34
|
+
}
|
|
35
|
+
const db = new better_sqlite3_1.default(DB_PATH, {
|
|
36
|
+
readonly: true, // Prevent accidental writes
|
|
37
|
+
fileMustExist: true // Fail if file doesn't exist
|
|
38
|
+
});
|
|
39
|
+
// Enable better error messages for foreign key violations
|
|
40
|
+
db.pragma('foreign_keys = ON');
|
|
41
|
+
return db;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Global database instance (singleton)
|
|
45
|
+
* Reused across all requests to avoid connection overhead
|
|
46
|
+
*/
|
|
47
|
+
let dbInstance = null;
|
|
48
|
+
/**
|
|
49
|
+
* Get the singleton database instance
|
|
50
|
+
*
|
|
51
|
+
* Creates a new connection on first call, then reuses it for subsequent calls.
|
|
52
|
+
* This is safe because SQLite handles concurrent reads well in read-only mode.
|
|
53
|
+
*
|
|
54
|
+
* @returns {Database.Database} Singleton database instance
|
|
55
|
+
*
|
|
56
|
+
* @example
|
|
57
|
+
* import { getDbInstance } from './connection';
|
|
58
|
+
*
|
|
59
|
+
* const db = getDbInstance();
|
|
60
|
+
* const sessions = db.prepare('SELECT * FROM sdk_sessions LIMIT 10').all();
|
|
61
|
+
*/
|
|
62
|
+
function getDbInstance() {
|
|
63
|
+
if (!dbInstance) {
|
|
64
|
+
dbInstance = getDatabase();
|
|
65
|
+
}
|
|
66
|
+
return dbInstance;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Close the database connection and clear the singleton
|
|
70
|
+
*
|
|
71
|
+
* Should be called during graceful shutdown (SIGTERM, SIGINT).
|
|
72
|
+
* Ensures the database file is properly closed.
|
|
73
|
+
*
|
|
74
|
+
* @example
|
|
75
|
+
* process.on('SIGTERM', () => {
|
|
76
|
+
* server.close(() => {
|
|
77
|
+
* closeDatabase();
|
|
78
|
+
* process.exit(0);
|
|
79
|
+
* });
|
|
80
|
+
* });
|
|
81
|
+
*/
|
|
82
|
+
function closeDatabase() {
|
|
83
|
+
if (dbInstance) {
|
|
84
|
+
dbInstance.close();
|
|
85
|
+
dbInstance = null;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
//# sourceMappingURL=connection.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"connection.js","sourceRoot":"","sources":["../../src/db/connection.ts"],"names":[],"mappings":";;;;;AAwBA,kCAcC;AAsBD,sCAKC;AAgBD,sCAKC;AAtFD,oEAAsC;AACtC,gDAAwB;AACxB,4CAAoB;AAEpB;;;GAGG;AACH,MAAM,OAAO,GAAG,cAAI,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,IAAI,EAAE,EAAE,aAAa,EAAE,eAAe,CAAC,CAAC;AAElF;;;;;;;;;;;;;GAaG;AACH,SAAgB,WAAW;IACzB,IAAI,CAAC,YAAE,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;QAC5B,MAAM,IAAI,KAAK,CAAC,yBAAyB,OAAO,gEAAgE,CAAC,CAAC;IACpH,CAAC;IAED,MAAM,EAAE,GAAG,IAAI,wBAAQ,CAAC,OAAO,EAAE;QAC/B,QAAQ,EAAE,IAAI,EAAO,4BAA4B;QACjD,aAAa,EAAE,IAAI,CAAE,6BAA6B;KACnD,CAAC,CAAC;IAEH,0DAA0D;IAC1D,EAAE,CAAC,MAAM,CAAC,mBAAmB,CAAC,CAAC;IAE/B,OAAO,EAAE,CAAC;AACZ,CAAC;AAED;;;GAGG;AACH,IAAI,UAAU,GAA6B,IAAI,CAAC;AAEhD;;;;;;;;;;;;;GAaG;AACH,SAAgB,aAAa;IAC3B,IAAI,CAAC,UAAU,EAAE,CAAC;QAChB,UAAU,GAAG,WAAW,EAAE,CAAC;IAC7B,CAAC;IACD,OAAO,UAAU,CAAC;AACpB,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,SAAgB,aAAa;IAC3B,IAAI,UAAU,EAAE,CAAC;QACf,UAAU,CAAC,KAAK,EAAE,CAAC;QACnB,UAAU,GAAG,IAAI,CAAC;IACpB,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import type { Session, SessionEvent, SessionListQuery, SessionEventsQuery } from './schema';
|
|
2
|
+
/**
|
|
3
|
+
* Get all sessions with optional filtering and pagination
|
|
4
|
+
*
|
|
5
|
+
* Fetches sessions from the claude-mem database with support for:
|
|
6
|
+
* - Pagination (offset/limit)
|
|
7
|
+
* - Project filtering
|
|
8
|
+
* - Date range filtering
|
|
9
|
+
*
|
|
10
|
+
* Results are ordered by most recent first (started_at_epoch DESC).
|
|
11
|
+
*
|
|
12
|
+
* @param {SessionListQuery} query - Query parameters
|
|
13
|
+
* @param {number} [query.offset=0] - Number of sessions to skip
|
|
14
|
+
* @param {number} [query.limit=20] - Maximum number of sessions to return
|
|
15
|
+
* @param {string} [query.project] - Filter by project name (exact match)
|
|
16
|
+
* @param {string} [query.dateStart] - Filter sessions started after this date (ISO 8601)
|
|
17
|
+
* @param {string} [query.dateEnd] - Filter sessions started before this date (ISO 8601)
|
|
18
|
+
* @returns {{ sessions: Session[]; total: number }} Paginated sessions and total count
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* // Get first 10 sessions
|
|
22
|
+
* const result = getSessions({ limit: 10, offset: 0 });
|
|
23
|
+
* console.log(`Found ${result.total} total sessions`);
|
|
24
|
+
* console.log(`Showing ${result.sessions.length} sessions`);
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* // Filter by project and date
|
|
28
|
+
* const result = getSessions({
|
|
29
|
+
* project: 'my-project',
|
|
30
|
+
* dateStart: '2026-02-01T00:00:00.000Z',
|
|
31
|
+
* limit: 20
|
|
32
|
+
* });
|
|
33
|
+
*/
|
|
34
|
+
export declare function getSessions(query: SessionListQuery): {
|
|
35
|
+
sessions: Session[];
|
|
36
|
+
total: number;
|
|
37
|
+
};
|
|
38
|
+
/**
|
|
39
|
+
* Get a single session by its UUID
|
|
40
|
+
*
|
|
41
|
+
* Fetches session metadata from the sdk_sessions table.
|
|
42
|
+
* Returns null if the session doesn't exist.
|
|
43
|
+
*
|
|
44
|
+
* @param {string} sessionId - Claude session UUID (claude_session_id)
|
|
45
|
+
* @returns {Session | null} Session object or null if not found
|
|
46
|
+
*
|
|
47
|
+
* @example
|
|
48
|
+
* const session = getSessionById('550e8400-e29b-41d4-a716-446655440000');
|
|
49
|
+
* if (session) {
|
|
50
|
+
* console.log(`Project: ${session.project}`);
|
|
51
|
+
* console.log(`Prompts: ${session.prompt_counter}`);
|
|
52
|
+
* }
|
|
53
|
+
*/
|
|
54
|
+
export declare function getSessionById(sessionId: string): Session | null;
|
|
55
|
+
/**
|
|
56
|
+
* Get statistics for a session
|
|
57
|
+
*
|
|
58
|
+
* Calculates the total number of events, prompts, and observations
|
|
59
|
+
* for a given session. Useful for displaying session metadata.
|
|
60
|
+
*
|
|
61
|
+
* @param {string} sessionId - Claude session UUID
|
|
62
|
+
* @returns {{ eventCount: number; promptCount: number; observationCount: number } | null}
|
|
63
|
+
* Statistics object or null if session not found
|
|
64
|
+
*
|
|
65
|
+
* @example
|
|
66
|
+
* const stats = getSessionStats('550e8400-e29b-41d4-a716-446655440000');
|
|
67
|
+
* if (stats) {
|
|
68
|
+
* console.log(`Total events: ${stats.eventCount}`);
|
|
69
|
+
* console.log(`Prompts: ${stats.promptCount}`);
|
|
70
|
+
* console.log(`Observations: ${stats.observationCount}`);
|
|
71
|
+
* }
|
|
72
|
+
*/
|
|
73
|
+
export declare function getSessionStats(sessionId: string): {
|
|
74
|
+
eventCount: number;
|
|
75
|
+
promptCount: number;
|
|
76
|
+
observationCount: number;
|
|
77
|
+
} | null;
|
|
78
|
+
/**
|
|
79
|
+
* Get session timeline events with TIME-FIRST ordering
|
|
80
|
+
*
|
|
81
|
+
* This is the core timeline reconstruction algorithm, validated in Phase 0.
|
|
82
|
+
* Events are ordered chronologically with prompts appearing before observations.
|
|
83
|
+
*
|
|
84
|
+
* **Ordering Algorithm:**
|
|
85
|
+
* 1. PRIMARY: Timestamp (ts ASC) - Chronological order
|
|
86
|
+
* 2. SECONDARY: Prompt number (COALESCE(prompt_number, 999999) ASC) - Group by prompt
|
|
87
|
+
* 3. TERTIARY: Kind rank (kind_rank ASC) - Prompts before observations
|
|
88
|
+
* 4. FINAL: Row ID (row_id ASC) - Stable tiebreaker
|
|
89
|
+
*
|
|
90
|
+
* **JSON Field Parsing:**
|
|
91
|
+
* The following fields are automatically parsed from JSON strings:
|
|
92
|
+
* - facts
|
|
93
|
+
* - concepts
|
|
94
|
+
* - files_read
|
|
95
|
+
* - files_modified
|
|
96
|
+
*
|
|
97
|
+
* **Validation:**
|
|
98
|
+
* This query has been validated on sessions with up to 902 events and passes
|
|
99
|
+
* all ordering checks (monotonic timestamps, prompt-before-observation, etc.)
|
|
100
|
+
*
|
|
101
|
+
* @param {string} sessionId - Claude session UUID
|
|
102
|
+
* @param {SessionEventsQuery} query - Filter and pagination options
|
|
103
|
+
* @param {number} [query.offset=0] - Number of events to skip
|
|
104
|
+
* @param {number} [query.limit=100] - Maximum number of events to return
|
|
105
|
+
* @param {string} [query.types] - Comma-separated observation types (e.g., "feature,bugfix")
|
|
106
|
+
* @param {number} [query.afterTs] - Only return events after this timestamp (epoch ms)
|
|
107
|
+
* @returns {{ events: SessionEvent[]; total: number }} Ordered events and total count
|
|
108
|
+
*
|
|
109
|
+
* @example
|
|
110
|
+
* // Get first 100 events
|
|
111
|
+
* const result = getSessionEvents(sessionId, { limit: 100, offset: 0 });
|
|
112
|
+
* console.log(`Total events: ${result.total}`);
|
|
113
|
+
* console.log(`Fetched: ${result.events.length}`);
|
|
114
|
+
*
|
|
115
|
+
* @example
|
|
116
|
+
* // Get only feature observations
|
|
117
|
+
* const result = getSessionEvents(sessionId, {
|
|
118
|
+
* types: 'feature',
|
|
119
|
+
* limit: 50
|
|
120
|
+
* });
|
|
121
|
+
*
|
|
122
|
+
* @example
|
|
123
|
+
* // Infinite scroll pagination
|
|
124
|
+
* const page1 = getSessionEvents(sessionId, { limit: 100, offset: 0 });
|
|
125
|
+
* const page2 = getSessionEvents(sessionId, { limit: 100, offset: 100 });
|
|
126
|
+
*/
|
|
127
|
+
export declare function getSessionEvents(sessionId: string, query: SessionEventsQuery): {
|
|
128
|
+
events: SessionEvent[];
|
|
129
|
+
total: number;
|
|
130
|
+
};
|
|
131
|
+
/**
|
|
132
|
+
* Get a single event by its type and ID
|
|
133
|
+
*
|
|
134
|
+
* Fetches either a prompt or observation by its database row ID.
|
|
135
|
+
* For observations, JSON fields are automatically parsed.
|
|
136
|
+
*
|
|
137
|
+
* @param {('prompt' | 'observation')} eventType - Type of event to fetch
|
|
138
|
+
* @param {number} eventId - Database row ID of the event
|
|
139
|
+
* @returns {SessionEvent | null} Event object or null if not found
|
|
140
|
+
*
|
|
141
|
+
* @example
|
|
142
|
+
* // Get a prompt
|
|
143
|
+
* const prompt = getEventById('prompt', 123);
|
|
144
|
+
* if (prompt) {
|
|
145
|
+
* console.log(`Prompt: ${prompt.text}`);
|
|
146
|
+
* }
|
|
147
|
+
*
|
|
148
|
+
* @example
|
|
149
|
+
* // Get an observation
|
|
150
|
+
* const obs = getEventById('observation', 456);
|
|
151
|
+
* if (obs) {
|
|
152
|
+
* console.log(`Type: ${obs.obs_type}`);
|
|
153
|
+
* console.log(`Title: ${obs.title}`);
|
|
154
|
+
* console.log(`Files modified: ${obs.files_modified?.join(', ')}`);
|
|
155
|
+
* }
|
|
156
|
+
*/
|
|
157
|
+
export declare function getEventById(eventType: 'prompt' | 'observation', eventId: number): SessionEvent | null;
|
|
158
|
+
/**
|
|
159
|
+
* Get a list of all unique project names
|
|
160
|
+
*
|
|
161
|
+
* Returns all distinct project names from the sessions table,
|
|
162
|
+
* sorted alphabetically. Useful for project filter dropdowns.
|
|
163
|
+
*
|
|
164
|
+
* @returns {string[]} Array of project names
|
|
165
|
+
*
|
|
166
|
+
* @example
|
|
167
|
+
* const projects = getProjects();
|
|
168
|
+
* console.log(`Found ${projects.length} projects:`);
|
|
169
|
+
* projects.forEach(p => console.log(` - ${p}`));
|
|
170
|
+
*/
|
|
171
|
+
export declare function getProjects(): string[];
|
|
172
|
+
//# sourceMappingURL=queries.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"queries.d.ts","sourceRoot":"","sources":["../../src/db/queries.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,OAAO,EACP,YAAY,EACZ,gBAAgB,EAChB,kBAAkB,EACnB,MAAM,UAAU,CAAC;AAalB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,gBAAgB,GAAG;IAAE,QAAQ,EAAE,OAAO,EAAE,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAuD3F;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,GAAG,IAAI,CAUhE;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG;IAClD,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,gBAAgB,EAAE,MAAM,CAAC;CAC1B,GAAG,IAAI,CA8BP;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgDG;AACH,wBAAgB,gBAAgB,CAC9B,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,kBAAkB,GACxB;IAAE,MAAM,EAAE,YAAY,EAAE,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAmI3C;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,YAAY,CAAC,SAAS,EAAE,QAAQ,GAAG,aAAa,EAAE,OAAO,EAAE,MAAM,GAAG,YAAY,GAAG,IAAI,CAoDtG;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,WAAW,IAAI,MAAM,EAAE,CAUtC"}
|