@cmdctrl/claude-code 0.1.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/dist/adapter/claude-cli.d.ts +41 -0
- package/dist/adapter/claude-cli.d.ts.map +1 -0
- package/dist/adapter/claude-cli.js +525 -0
- package/dist/adapter/claude-cli.js.map +1 -0
- package/dist/adapter/events.d.ts +52 -0
- package/dist/adapter/events.d.ts.map +1 -0
- package/dist/adapter/events.js +134 -0
- package/dist/adapter/events.js.map +1 -0
- package/dist/client/messages.d.ts +140 -0
- package/dist/client/messages.d.ts.map +1 -0
- package/dist/client/messages.js +6 -0
- package/dist/client/messages.js.map +1 -0
- package/dist/client/websocket.d.ts +115 -0
- package/dist/client/websocket.d.ts.map +1 -0
- package/dist/client/websocket.js +434 -0
- package/dist/client/websocket.js.map +1 -0
- package/dist/commands/register.d.ts +10 -0
- package/dist/commands/register.d.ts.map +1 -0
- package/dist/commands/register.js +175 -0
- package/dist/commands/register.js.map +1 -0
- package/dist/commands/start.d.ts +9 -0
- package/dist/commands/start.d.ts.map +1 -0
- package/dist/commands/start.js +54 -0
- package/dist/commands/start.js.map +1 -0
- package/dist/commands/status.d.ts +5 -0
- package/dist/commands/status.d.ts.map +1 -0
- package/dist/commands/status.js +38 -0
- package/dist/commands/status.js.map +1 -0
- package/dist/commands/stop.d.ts +5 -0
- package/dist/commands/stop.d.ts.map +1 -0
- package/dist/commands/stop.js +59 -0
- package/dist/commands/stop.js.map +1 -0
- package/dist/commands/unregister.d.ts +5 -0
- package/dist/commands/unregister.d.ts.map +1 -0
- package/dist/commands/unregister.js +28 -0
- package/dist/commands/unregister.js.map +1 -0
- package/dist/config/config.d.ts +68 -0
- package/dist/config/config.d.ts.map +1 -0
- package/dist/config/config.js +193 -0
- package/dist/config/config.js.map +1 -0
- package/dist/handlers/context-handler.d.ts +37 -0
- package/dist/handlers/context-handler.d.ts.map +1 -0
- package/dist/handlers/context-handler.js +303 -0
- package/dist/handlers/context-handler.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +39 -0
- package/dist/index.js.map +1 -0
- package/dist/message-reader.d.ts +25 -0
- package/dist/message-reader.d.ts.map +1 -0
- package/dist/message-reader.js +454 -0
- package/dist/message-reader.js.map +1 -0
- package/dist/session-discovery.d.ts +48 -0
- package/dist/session-discovery.d.ts.map +1 -0
- package/dist/session-discovery.js +496 -0
- package/dist/session-discovery.js.map +1 -0
- package/dist/session-watcher.d.ts +92 -0
- package/dist/session-watcher.d.ts.map +1 -0
- package/dist/session-watcher.js +494 -0
- package/dist/session-watcher.js.map +1 -0
- package/dist/session-watcher.test.d.ts +9 -0
- package/dist/session-watcher.test.d.ts.map +1 -0
- package/dist/session-watcher.test.js +149 -0
- package/dist/session-watcher.test.js.map +1 -0
- package/jest.config.js +8 -0
- package/package.json +42 -0
- package/src/adapter/claude-cli.ts +591 -0
- package/src/adapter/events.ts +186 -0
- package/src/client/messages.ts +193 -0
- package/src/client/websocket.ts +509 -0
- package/src/commands/register.ts +201 -0
- package/src/commands/start.ts +70 -0
- package/src/commands/status.ts +47 -0
- package/src/commands/stop.ts +58 -0
- package/src/commands/unregister.ts +30 -0
- package/src/config/config.ts +163 -0
- package/src/handlers/context-handler.ts +337 -0
- package/src/index.ts +45 -0
- package/src/message-reader.ts +485 -0
- package/src/session-discovery.ts +557 -0
- package/src/session-watcher.test.ts +141 -0
- package/src/session-watcher.ts +560 -0
- package/tsconfig.json +19 -0
|
@@ -0,0 +1,496 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.discoverSessions = discoverSessions;
|
|
37
|
+
exports.groupByProject = groupByProject;
|
|
38
|
+
exports.discoverProjects = discoverProjects;
|
|
39
|
+
const fs = __importStar(require("fs"));
|
|
40
|
+
const path = __importStar(require("path"));
|
|
41
|
+
const os = __importStar(require("os"));
|
|
42
|
+
const ACTIVE_THRESHOLD_MS = 30 * 1000; // 30 seconds
|
|
43
|
+
const TAIL_BYTES = 65536; // 64KB - only used as fallback
|
|
44
|
+
// Cache for sessions parsed from disk (not in index)
|
|
45
|
+
// Maps session_id -> { session, fileMtime }
|
|
46
|
+
const parsedSessionCache = new Map();
|
|
47
|
+
let lastDiscoveryTime = 0;
|
|
48
|
+
/**
|
|
49
|
+
* Generate a title from message content (first line, truncated)
|
|
50
|
+
*/
|
|
51
|
+
function generateTitle(message) {
|
|
52
|
+
if (!message)
|
|
53
|
+
return '';
|
|
54
|
+
const firstLine = message.split('\n')[0].trim();
|
|
55
|
+
if (firstLine.length === 0)
|
|
56
|
+
return '';
|
|
57
|
+
if (firstLine.length <= 50)
|
|
58
|
+
return firstLine;
|
|
59
|
+
// Truncate at word boundary
|
|
60
|
+
const truncated = firstLine.slice(0, 50);
|
|
61
|
+
const lastSpace = truncated.lastIndexOf(' ');
|
|
62
|
+
if (lastSpace > 30) {
|
|
63
|
+
return truncated.slice(0, lastSpace) + '...';
|
|
64
|
+
}
|
|
65
|
+
return truncated + '...';
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Extract readable text from message content (handles string or array of content blocks)
|
|
69
|
+
*/
|
|
70
|
+
function extractReadableText(content) {
|
|
71
|
+
// Simple string
|
|
72
|
+
if (typeof content === 'string') {
|
|
73
|
+
return content.trim();
|
|
74
|
+
}
|
|
75
|
+
// Array of content blocks (Claude format)
|
|
76
|
+
if (Array.isArray(content)) {
|
|
77
|
+
const textParts = [];
|
|
78
|
+
for (const block of content) {
|
|
79
|
+
if (typeof block === 'string') {
|
|
80
|
+
textParts.push(block);
|
|
81
|
+
}
|
|
82
|
+
else if (block && typeof block === 'object') {
|
|
83
|
+
// Text block: { type: 'text', text: '...' }
|
|
84
|
+
if (block.type === 'text' && typeof block.text === 'string') {
|
|
85
|
+
textParts.push(block.text);
|
|
86
|
+
}
|
|
87
|
+
// Skip tool_use, tool_result, image blocks etc.
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return textParts.join(' ').trim();
|
|
91
|
+
}
|
|
92
|
+
// Object with text property
|
|
93
|
+
if (content && typeof content === 'object' && 'text' in content) {
|
|
94
|
+
const text = content.text;
|
|
95
|
+
if (typeof text === 'string') {
|
|
96
|
+
return text.trim();
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
return '';
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Decode a project directory name to a path
|
|
103
|
+
* e.g., "-Users-mrwoof-src-cmdctrl" -> "/Users/mrwoof/src/cmdctrl"
|
|
104
|
+
*
|
|
105
|
+
* The encoding is ambiguous: hyphens in directory names look the same as path separators.
|
|
106
|
+
* e.g., "-Users-mrwoof-src-cmdctrl-admin-interface" could be:
|
|
107
|
+
* /Users/mrwoof/src/cmdctrl-admin-interface (correct - worktree)
|
|
108
|
+
* /Users/mrwoof/src/cmdctrl/admin/interface (wrong - doesn't exist)
|
|
109
|
+
*
|
|
110
|
+
* We solve this by trying all possible decodings and returning the one that:
|
|
111
|
+
* 1. Actually exists on the filesystem
|
|
112
|
+
* 2. Has the most path components (to prefer /a/b-c over /a/b/c when both exist)
|
|
113
|
+
*
|
|
114
|
+
* If no valid path is found, fall back to replacing all hyphens with slashes.
|
|
115
|
+
*/
|
|
116
|
+
function decodeProjectPath(dirName) {
|
|
117
|
+
if (!dirName || dirName.length === 0)
|
|
118
|
+
return '';
|
|
119
|
+
// Remove leading dash and split by dashes
|
|
120
|
+
const parts = dirName.slice(1).split('-');
|
|
121
|
+
if (parts.length === 0)
|
|
122
|
+
return '/';
|
|
123
|
+
// Generate all possible path interpretations using recursion
|
|
124
|
+
const candidates = [];
|
|
125
|
+
function generatePaths(index, currentPath) {
|
|
126
|
+
if (index >= parts.length) {
|
|
127
|
+
candidates.push(currentPath);
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
// Try combining remaining parts with hyphens (longer combinations first)
|
|
131
|
+
for (let end = parts.length; end > index; end--) {
|
|
132
|
+
const component = parts.slice(index, end).join('-');
|
|
133
|
+
const newPath = currentPath + '/' + component;
|
|
134
|
+
generatePaths(end, newPath);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
generatePaths(0, '');
|
|
138
|
+
// Find candidates that exist, preferring fewer path components (more hyphens preserved)
|
|
139
|
+
// Sort by number of slashes (ascending) to prefer paths with hyphens in names
|
|
140
|
+
candidates.sort((a, b) => {
|
|
141
|
+
const slashesA = (a.match(/\//g) || []).length;
|
|
142
|
+
const slashesB = (b.match(/\//g) || []).length;
|
|
143
|
+
return slashesA - slashesB;
|
|
144
|
+
});
|
|
145
|
+
for (const candidate of candidates) {
|
|
146
|
+
if (fs.existsSync(candidate)) {
|
|
147
|
+
return candidate;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
// Fallback: replace all hyphens with slashes (original behavior)
|
|
151
|
+
return '/' + parts.join('/');
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Extract project name from full path
|
|
155
|
+
*/
|
|
156
|
+
function projectNameFromPath(projectPath) {
|
|
157
|
+
return path.basename(projectPath);
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Discover all Claude Code sessions on this device
|
|
161
|
+
*
|
|
162
|
+
* Uses sessions-index.json for efficiency when available (one file read per project
|
|
163
|
+
* instead of 64KB per session). Falls back to parsing individual files if index
|
|
164
|
+
* is missing or stale.
|
|
165
|
+
*/
|
|
166
|
+
async function discoverSessions(excludeSessionIDs = new Set()) {
|
|
167
|
+
const claudeDir = path.join(os.homedir(), '.claude', 'projects');
|
|
168
|
+
const sessionMap = new Map();
|
|
169
|
+
// Check if directory exists
|
|
170
|
+
if (!fs.existsSync(claudeDir)) {
|
|
171
|
+
return [];
|
|
172
|
+
}
|
|
173
|
+
// Read project directories
|
|
174
|
+
const entries = fs.readdirSync(claudeDir, { withFileTypes: true });
|
|
175
|
+
for (const entry of entries) {
|
|
176
|
+
if (!entry.isDirectory())
|
|
177
|
+
continue;
|
|
178
|
+
const projectDir = path.join(claudeDir, entry.name);
|
|
179
|
+
const projectPath = decodeProjectPath(entry.name);
|
|
180
|
+
const projectName = projectNameFromPath(projectPath);
|
|
181
|
+
// Try to use sessions-index.json first (much more efficient)
|
|
182
|
+
const indexPath = path.join(projectDir, 'sessions-index.json');
|
|
183
|
+
if (fs.existsSync(indexPath)) {
|
|
184
|
+
try {
|
|
185
|
+
const indexContent = fs.readFileSync(indexPath, 'utf-8');
|
|
186
|
+
const index = JSON.parse(indexContent);
|
|
187
|
+
// Track which sessions we need to re-parse due to stale index
|
|
188
|
+
const staleSessionPaths = [];
|
|
189
|
+
for (const indexEntry of index.entries) {
|
|
190
|
+
// Skip sidechains, excluded sessions, and empty sessions
|
|
191
|
+
if (indexEntry.isSidechain)
|
|
192
|
+
continue;
|
|
193
|
+
if (excludeSessionIDs.has(indexEntry.sessionId))
|
|
194
|
+
continue;
|
|
195
|
+
if (indexEntry.messageCount === 0)
|
|
196
|
+
continue;
|
|
197
|
+
// Check if the actual file has been modified since the index was updated
|
|
198
|
+
// If so, the index data is stale and we need to re-parse the file
|
|
199
|
+
try {
|
|
200
|
+
const stat = fs.statSync(indexEntry.fullPath);
|
|
201
|
+
const actualMtimeMs = stat.mtimeMs;
|
|
202
|
+
// Index fileMtime is in milliseconds
|
|
203
|
+
if (actualMtimeMs > indexEntry.fileMtime + 1000) {
|
|
204
|
+
// File is newer than index - mark for re-parsing
|
|
205
|
+
staleSessionPaths.push(indexEntry.fullPath);
|
|
206
|
+
continue;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
catch {
|
|
210
|
+
// File doesn't exist or can't stat, skip
|
|
211
|
+
continue;
|
|
212
|
+
}
|
|
213
|
+
const modifiedDate = new Date(indexEntry.modified);
|
|
214
|
+
const isActive = Date.now() - modifiedDate.getTime() < ACTIVE_THRESHOLD_MS;
|
|
215
|
+
// Use customTitle > summary > firstPrompt for title
|
|
216
|
+
let title = indexEntry.customTitle || indexEntry.summary || '';
|
|
217
|
+
if (!title && indexEntry.firstPrompt && indexEntry.firstPrompt !== 'No prompt') {
|
|
218
|
+
title = generateTitle(indexEntry.firstPrompt);
|
|
219
|
+
}
|
|
220
|
+
if (!title) {
|
|
221
|
+
title = indexEntry.sessionId.slice(0, 8);
|
|
222
|
+
}
|
|
223
|
+
const session = {
|
|
224
|
+
session_id: indexEntry.sessionId,
|
|
225
|
+
slug: '', // Not in index, but we don't really use it
|
|
226
|
+
title,
|
|
227
|
+
// Always use directory-derived projectPath, not indexEntry.projectPath
|
|
228
|
+
// The index stores cwd at session start, which can be a subdirectory
|
|
229
|
+
project: projectPath,
|
|
230
|
+
project_name: projectNameFromPath(projectPath),
|
|
231
|
+
file_path: indexEntry.fullPath,
|
|
232
|
+
last_message: indexEntry.firstPrompt !== 'No prompt' ? generateTitle(indexEntry.firstPrompt) : '',
|
|
233
|
+
last_activity: indexEntry.modified, // This is the correct message timestamp!
|
|
234
|
+
is_active: isActive,
|
|
235
|
+
message_count: indexEntry.messageCount,
|
|
236
|
+
};
|
|
237
|
+
// Keep most recently active version
|
|
238
|
+
const existing = sessionMap.get(session.session_id);
|
|
239
|
+
if (!existing || new Date(session.last_activity) > new Date(existing.last_activity)) {
|
|
240
|
+
sessionMap.set(session.session_id, session);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
// Re-parse stale sessions from index
|
|
244
|
+
for (const stalePath of staleSessionPaths) {
|
|
245
|
+
try {
|
|
246
|
+
const session = await parseSessionFile(stalePath, projectPath, projectName);
|
|
247
|
+
if (session.message_count === 0)
|
|
248
|
+
continue;
|
|
249
|
+
const existing = sessionMap.get(session.session_id);
|
|
250
|
+
if (!existing || new Date(session.last_activity) > new Date(existing.last_activity)) {
|
|
251
|
+
sessionMap.set(session.session_id, session);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
catch {
|
|
255
|
+
// Failed to parse, skip
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
// After processing index, check for files not in index (index can be stale)
|
|
259
|
+
const indexedSessionIds = new Set(index.entries.map(e => e.sessionId));
|
|
260
|
+
let missingFiles;
|
|
261
|
+
try {
|
|
262
|
+
missingFiles = fs.readdirSync(projectDir)
|
|
263
|
+
.filter(f => f.endsWith('.jsonl') && !f.startsWith('agent-'))
|
|
264
|
+
.map(f => path.join(projectDir, f));
|
|
265
|
+
}
|
|
266
|
+
catch (err) {
|
|
267
|
+
continue; // Can't read directory, skip
|
|
268
|
+
}
|
|
269
|
+
for (const jsonlPath of missingFiles) {
|
|
270
|
+
const sessionId = path.basename(jsonlPath, '.jsonl');
|
|
271
|
+
// Skip if already in index or excluded
|
|
272
|
+
if (indexedSessionIds.has(sessionId))
|
|
273
|
+
continue;
|
|
274
|
+
if (excludeSessionIDs.has(sessionId))
|
|
275
|
+
continue;
|
|
276
|
+
try {
|
|
277
|
+
const stat = fs.statSync(jsonlPath);
|
|
278
|
+
const fileMtime = stat.mtimeMs;
|
|
279
|
+
const cached = parsedSessionCache.get(sessionId);
|
|
280
|
+
let session;
|
|
281
|
+
if (cached && cached.fileMtime === fileMtime) {
|
|
282
|
+
// File unchanged, use cached session (update is_active)
|
|
283
|
+
session = { ...cached.session };
|
|
284
|
+
session.is_active = Date.now() - new Date(session.last_activity).getTime() < ACTIVE_THRESHOLD_MS;
|
|
285
|
+
}
|
|
286
|
+
else {
|
|
287
|
+
// File is new or modified, parse it
|
|
288
|
+
session = await parseSessionFile(jsonlPath, projectPath, projectName);
|
|
289
|
+
if (session.message_count === 0)
|
|
290
|
+
continue;
|
|
291
|
+
parsedSessionCache.set(sessionId, { session, fileMtime });
|
|
292
|
+
}
|
|
293
|
+
const existing = sessionMap.get(session.session_id);
|
|
294
|
+
if (!existing || new Date(session.last_activity) > new Date(existing.last_activity)) {
|
|
295
|
+
sessionMap.set(session.session_id, session);
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
catch (err) {
|
|
299
|
+
continue; // Skip unparseable files
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
continue; // Done with this project
|
|
303
|
+
}
|
|
304
|
+
catch (err) {
|
|
305
|
+
// Index parsing failed, fall back to file parsing
|
|
306
|
+
console.warn(`Failed to parse sessions-index.json in ${projectDir}, falling back to file parsing:`, err);
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
// Fallback: parse individual .jsonl files (slower but always works)
|
|
310
|
+
let jsonlFiles;
|
|
311
|
+
try {
|
|
312
|
+
jsonlFiles = fs.readdirSync(projectDir)
|
|
313
|
+
.filter(f => f.endsWith('.jsonl') && !f.startsWith('agent-'))
|
|
314
|
+
.map(f => path.join(projectDir, f));
|
|
315
|
+
}
|
|
316
|
+
catch (err) {
|
|
317
|
+
console.warn(`Failed to read project directory ${projectDir}:`, err);
|
|
318
|
+
continue;
|
|
319
|
+
}
|
|
320
|
+
for (const jsonlPath of jsonlFiles) {
|
|
321
|
+
try {
|
|
322
|
+
const session = await parseSessionFile(jsonlPath, projectPath, projectName);
|
|
323
|
+
// Skip if excluded or no messages
|
|
324
|
+
if (excludeSessionIDs.has(session.session_id))
|
|
325
|
+
continue;
|
|
326
|
+
if (session.message_count === 0)
|
|
327
|
+
continue;
|
|
328
|
+
// Keep most recently active version
|
|
329
|
+
const existing = sessionMap.get(session.session_id);
|
|
330
|
+
if (!existing || new Date(session.last_activity) > new Date(existing.last_activity)) {
|
|
331
|
+
sessionMap.set(session.session_id, session);
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
catch (err) {
|
|
335
|
+
// Skip files that can't be parsed
|
|
336
|
+
continue;
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
// Convert to array and sort by last activity (most recent first)
|
|
341
|
+
const sessions = Array.from(sessionMap.values());
|
|
342
|
+
sessions.sort((a, b) => new Date(b.last_activity).getTime() - new Date(a.last_activity).getTime());
|
|
343
|
+
return sessions;
|
|
344
|
+
}
|
|
345
|
+
/**
|
|
346
|
+
* Parse a session JSONL file to extract metadata
|
|
347
|
+
*/
|
|
348
|
+
async function parseSessionFile(filePath, projectPath, projectName) {
|
|
349
|
+
const stat = fs.statSync(filePath);
|
|
350
|
+
const lastActivity = stat.mtime;
|
|
351
|
+
const isActive = Date.now() - lastActivity.getTime() < ACTIVE_THRESHOLD_MS;
|
|
352
|
+
const session = {
|
|
353
|
+
session_id: '',
|
|
354
|
+
slug: '',
|
|
355
|
+
title: '',
|
|
356
|
+
project: projectPath,
|
|
357
|
+
project_name: projectName,
|
|
358
|
+
file_path: filePath,
|
|
359
|
+
last_message: '',
|
|
360
|
+
last_activity: lastActivity.toISOString(),
|
|
361
|
+
is_active: isActive,
|
|
362
|
+
message_count: 0,
|
|
363
|
+
};
|
|
364
|
+
// Read the tail of the file
|
|
365
|
+
const fileSize = stat.size;
|
|
366
|
+
const fd = fs.openSync(filePath, 'r');
|
|
367
|
+
try {
|
|
368
|
+
const seekPos = Math.max(0, fileSize - TAIL_BYTES);
|
|
369
|
+
const buffer = Buffer.alloc(Math.min(TAIL_BYTES, fileSize));
|
|
370
|
+
fs.readSync(fd, buffer, 0, buffer.length, seekPos);
|
|
371
|
+
let content = buffer.toString('utf-8');
|
|
372
|
+
// If we seeked into middle, skip first partial line
|
|
373
|
+
if (seekPos > 0) {
|
|
374
|
+
const newlineIdx = content.indexOf('\n');
|
|
375
|
+
if (newlineIdx >= 0) {
|
|
376
|
+
content = content.slice(newlineIdx + 1);
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
const lines = content.split('\n').filter(l => l.trim());
|
|
380
|
+
let firstUserMessage = '';
|
|
381
|
+
let lastUserMessage = '';
|
|
382
|
+
let messageCount = 0;
|
|
383
|
+
let foundSessionId = false;
|
|
384
|
+
let foundSlug = false;
|
|
385
|
+
let lastMessageTimestamp = ''; // Track actual last message timestamp
|
|
386
|
+
for (const line of lines) {
|
|
387
|
+
try {
|
|
388
|
+
const entry = JSON.parse(line);
|
|
389
|
+
// Extract session ID and slug
|
|
390
|
+
if (!foundSessionId && entry.sessionId) {
|
|
391
|
+
session.session_id = entry.sessionId;
|
|
392
|
+
foundSessionId = true;
|
|
393
|
+
}
|
|
394
|
+
if (!foundSlug && entry.slug) {
|
|
395
|
+
session.slug = entry.slug;
|
|
396
|
+
foundSlug = true;
|
|
397
|
+
}
|
|
398
|
+
// NOTE: Do NOT override project with entry.cwd - the project path must come from
|
|
399
|
+
// the directory where the session file is stored, not from cwd in JSONL entries.
|
|
400
|
+
// The cwd can change during a session (e.g., when Claude changes to a subdirectory),
|
|
401
|
+
// but the session file stays in its original project directory.
|
|
402
|
+
// Count messages and track last message timestamp
|
|
403
|
+
if (entry.type === 'user' || entry.type === 'assistant') {
|
|
404
|
+
messageCount++;
|
|
405
|
+
// Track timestamp of actual user/assistant messages (not system messages)
|
|
406
|
+
if (entry.timestamp) {
|
|
407
|
+
lastMessageTimestamp = entry.timestamp;
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
// Track first and last user messages (extract readable text only)
|
|
411
|
+
if (entry.type === 'user' && entry.message?.content) {
|
|
412
|
+
const text = extractReadableText(entry.message.content);
|
|
413
|
+
if (text) {
|
|
414
|
+
if (!firstUserMessage) {
|
|
415
|
+
firstUserMessage = text;
|
|
416
|
+
}
|
|
417
|
+
lastUserMessage = text;
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
catch {
|
|
422
|
+
continue;
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
// Use actual last message timestamp if available, otherwise fall back to file mtime
|
|
426
|
+
if (lastMessageTimestamp) {
|
|
427
|
+
session.last_activity = lastMessageTimestamp;
|
|
428
|
+
session.is_active = Date.now() - new Date(lastMessageTimestamp).getTime() < ACTIVE_THRESHOLD_MS;
|
|
429
|
+
}
|
|
430
|
+
session.message_count = messageCount;
|
|
431
|
+
// Fallback session ID from filename
|
|
432
|
+
if (!session.session_id) {
|
|
433
|
+
session.session_id = path.basename(filePath, '.jsonl');
|
|
434
|
+
}
|
|
435
|
+
// Generate title from first user message, falling back to slug then session ID
|
|
436
|
+
if (firstUserMessage) {
|
|
437
|
+
session.title = generateTitle(firstUserMessage);
|
|
438
|
+
}
|
|
439
|
+
if (!session.title && session.slug) {
|
|
440
|
+
session.title = session.slug;
|
|
441
|
+
}
|
|
442
|
+
if (!session.title) {
|
|
443
|
+
session.title = session.session_id.slice(0, 8);
|
|
444
|
+
}
|
|
445
|
+
// Truncate last message for preview
|
|
446
|
+
if (lastUserMessage.length > 100) {
|
|
447
|
+
session.last_message = lastUserMessage.slice(0, 100) + '...';
|
|
448
|
+
}
|
|
449
|
+
else {
|
|
450
|
+
session.last_message = lastUserMessage;
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
finally {
|
|
454
|
+
fs.closeSync(fd);
|
|
455
|
+
}
|
|
456
|
+
return session;
|
|
457
|
+
}
|
|
458
|
+
/**
|
|
459
|
+
* Group sessions by project
|
|
460
|
+
*/
|
|
461
|
+
function groupByProject(sessions) {
|
|
462
|
+
const projectMap = new Map();
|
|
463
|
+
const projectOrder = [];
|
|
464
|
+
for (const session of sessions) {
|
|
465
|
+
if (!projectMap.has(session.project)) {
|
|
466
|
+
projectMap.set(session.project, {
|
|
467
|
+
project: session.project,
|
|
468
|
+
project_name: session.project_name,
|
|
469
|
+
sessions: [],
|
|
470
|
+
});
|
|
471
|
+
projectOrder.push(session.project);
|
|
472
|
+
}
|
|
473
|
+
projectMap.get(session.project).sessions.push(session);
|
|
474
|
+
}
|
|
475
|
+
return projectOrder.map(p => projectMap.get(p));
|
|
476
|
+
}
|
|
477
|
+
/**
|
|
478
|
+
* Discover projects (directories in ~/.claude/projects/)
|
|
479
|
+
*/
|
|
480
|
+
function discoverProjects() {
|
|
481
|
+
const claudeDir = path.join(os.homedir(), '.claude', 'projects');
|
|
482
|
+
if (!fs.existsSync(claudeDir)) {
|
|
483
|
+
return [];
|
|
484
|
+
}
|
|
485
|
+
const entries = fs.readdirSync(claudeDir, { withFileTypes: true });
|
|
486
|
+
const projects = [];
|
|
487
|
+
for (const entry of entries) {
|
|
488
|
+
if (!entry.isDirectory())
|
|
489
|
+
continue;
|
|
490
|
+
const projectPath = decodeProjectPath(entry.name);
|
|
491
|
+
const projectName = projectNameFromPath(projectPath);
|
|
492
|
+
projects.push({ path: projectPath, name: projectName });
|
|
493
|
+
}
|
|
494
|
+
return projects;
|
|
495
|
+
}
|
|
496
|
+
//# sourceMappingURL=session-discovery.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-discovery.js","sourceRoot":"","sources":["../src/session-discovery.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Session file watcher for monitoring JSONL session files
|
|
3
|
+
*
|
|
4
|
+
* Watches JSONL files and emits typed events for each new entry:
|
|
5
|
+
* - AGENT_RESPONSE: assistant entries with text content
|
|
6
|
+
* - VERBOSE: tool_use, thinking, tool_result entries
|
|
7
|
+
* - USER_MESSAGE: user entries (for passive observers)
|
|
8
|
+
*
|
|
9
|
+
* This is the single source of truth for session content events.
|
|
10
|
+
*/
|
|
11
|
+
export interface SessionEvent {
|
|
12
|
+
type: 'AGENT_RESPONSE' | 'VERBOSE' | 'USER_MESSAGE';
|
|
13
|
+
sessionId: string;
|
|
14
|
+
uuid: string;
|
|
15
|
+
content: string;
|
|
16
|
+
timestamp: string;
|
|
17
|
+
isToolResult?: boolean;
|
|
18
|
+
}
|
|
19
|
+
type EventCallback = (event: SessionEvent) => void;
|
|
20
|
+
export interface CompletionEvent {
|
|
21
|
+
sessionId: string;
|
|
22
|
+
filePath: string;
|
|
23
|
+
lastMessage: string;
|
|
24
|
+
messageCount: number;
|
|
25
|
+
}
|
|
26
|
+
type CompletionCallback = (event: CompletionEvent) => void;
|
|
27
|
+
export declare class SessionWatcher {
|
|
28
|
+
private watchedSessions;
|
|
29
|
+
private completionTimers;
|
|
30
|
+
private onEvent;
|
|
31
|
+
private onCompletion;
|
|
32
|
+
private pollTimer;
|
|
33
|
+
constructor(onEvent: EventCallback, onCompletion?: CompletionCallback);
|
|
34
|
+
/**
|
|
35
|
+
* Start watching a session file for changes
|
|
36
|
+
*/
|
|
37
|
+
watchSession(sessionId: string, filePath: string): void;
|
|
38
|
+
/**
|
|
39
|
+
* Stop watching a session file
|
|
40
|
+
*/
|
|
41
|
+
unwatchSession(sessionId: string): void;
|
|
42
|
+
/**
|
|
43
|
+
* Stop watching all sessions
|
|
44
|
+
*/
|
|
45
|
+
unwatchAll(): void;
|
|
46
|
+
/**
|
|
47
|
+
* Initialize processed UUIDs from existing file content
|
|
48
|
+
* This prevents emitting events for entries that existed before we started watching
|
|
49
|
+
*/
|
|
50
|
+
private initializeFromFile;
|
|
51
|
+
/**
|
|
52
|
+
* Start the polling loop
|
|
53
|
+
*/
|
|
54
|
+
private startPolling;
|
|
55
|
+
/**
|
|
56
|
+
* Poll all watched sessions for changes
|
|
57
|
+
*/
|
|
58
|
+
private pollAllSessions;
|
|
59
|
+
/**
|
|
60
|
+
* Check a single session for changes
|
|
61
|
+
*/
|
|
62
|
+
private checkSession;
|
|
63
|
+
/**
|
|
64
|
+
* Read only NEW entries appended since lastSize
|
|
65
|
+
* Reads from lastSize offset forward, avoiding full file reads
|
|
66
|
+
*/
|
|
67
|
+
private readNewEntries;
|
|
68
|
+
/**
|
|
69
|
+
* Convert a JSONL entry to a SessionEvent
|
|
70
|
+
*/
|
|
71
|
+
private entryToEvent;
|
|
72
|
+
/**
|
|
73
|
+
* Format a tool_use block for verbose display
|
|
74
|
+
*/
|
|
75
|
+
private formatToolUse;
|
|
76
|
+
get watchCount(): number;
|
|
77
|
+
/**
|
|
78
|
+
* Check if an entry contains tool_use blocks (agent is making a tool call)
|
|
79
|
+
*/
|
|
80
|
+
private entryHasToolUse;
|
|
81
|
+
/**
|
|
82
|
+
* Start a completion timer for a session
|
|
83
|
+
* If no tool call arrives within COMPLETION_DELAY_MS, fire the completion callback
|
|
84
|
+
*/
|
|
85
|
+
private startCompletionTimer;
|
|
86
|
+
/**
|
|
87
|
+
* Cancel a pending completion timer for a session
|
|
88
|
+
*/
|
|
89
|
+
private cancelCompletionTimer;
|
|
90
|
+
}
|
|
91
|
+
export {};
|
|
92
|
+
//# sourceMappingURL=session-watcher.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-watcher.d.ts","sourceRoot":"","sources":["../src/session-watcher.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAKH,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,gBAAgB,GAAG,SAAS,GAAG,cAAc,CAAC;IACpD,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAElB,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB;AAYD,KAAK,aAAa,GAAG,CAAC,KAAK,EAAE,YAAY,KAAK,IAAI,CAAC;AAGnD,MAAM,WAAW,eAAe;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,KAAK,kBAAkB,GAAG,CAAC,KAAK,EAAE,eAAe,KAAK,IAAI,CAAC;AAY3D,qBAAa,cAAc;IACzB,OAAO,CAAC,eAAe,CAA0C;IACjE,OAAO,CAAC,gBAAgB,CAA0C;IAClE,OAAO,CAAC,OAAO,CAAgB;IAC/B,OAAO,CAAC,YAAY,CAAmC;IACvD,OAAO,CAAC,SAAS,CAA+B;gBAEpC,OAAO,EAAE,aAAa,EAAE,YAAY,CAAC,EAAE,kBAAkB;IAKrE;;OAEG;IACH,YAAY,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI;IAoCvD;;OAEG;IACH,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IAevC;;OAEG;IACH,UAAU,IAAI,IAAI;IAelB;;;OAGG;IACH,OAAO,CAAC,kBAAkB;IAuC1B;;OAEG;IACH,OAAO,CAAC,YAAY;IAMpB;;OAEG;IACH,OAAO,CAAC,eAAe;IAMvB;;OAEG;IACH,OAAO,CAAC,YAAY;IA+DpB;;;OAGG;IACH,OAAO,CAAC,cAAc;IA2CtB;;OAEG;IACH,OAAO,CAAC,YAAY;IAwJpB;;OAEG;IACH,OAAO,CAAC,aAAa;IA4BrB,IAAI,UAAU,IAAI,MAAM,CAEvB;IAED;;OAEG;IACH,OAAO,CAAC,eAAe;IAWvB;;;OAGG;IACH,OAAO,CAAC,oBAAoB;IAwB5B;;OAEG;IACH,OAAO,CAAC,qBAAqB;CAO9B"}
|