claude-ps 0.2.3 → 0.2.5
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/chunk-RZG4BATR.js +351 -0
- package/dist/index.js +24 -153
- package/dist/session-TGVDWUTY.js +15 -0
- package/package.json +1 -1
|
@@ -0,0 +1,351 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/utils/session.ts
|
|
4
|
+
import { readFile, readdir } from "fs/promises";
|
|
5
|
+
import { homedir } from "os";
|
|
6
|
+
import { join } from "path";
|
|
7
|
+
function cwdToProjectDir(cwd) {
|
|
8
|
+
return cwd.replace(/\//g, "-").replace(/^-/, "-");
|
|
9
|
+
}
|
|
10
|
+
async function getSessionPath(cwd, startTime) {
|
|
11
|
+
const projectDir = cwdToProjectDir(cwd);
|
|
12
|
+
const sessionsDir = join(homedir(), ".claude", "projects", projectDir);
|
|
13
|
+
try {
|
|
14
|
+
const files = await readdir(sessionsDir);
|
|
15
|
+
const jsonlFiles = files.filter((f) => f.endsWith(".jsonl"));
|
|
16
|
+
if (process.env.DEBUG_SESSION) {
|
|
17
|
+
console.error(`[DEBUG] cwd: ${cwd}`);
|
|
18
|
+
console.error(`[DEBUG] projectDir: ${projectDir}`);
|
|
19
|
+
console.error(`[DEBUG] sessionsDir: ${sessionsDir}`);
|
|
20
|
+
console.error(`[DEBUG] found ${jsonlFiles.length} jsonl files`);
|
|
21
|
+
}
|
|
22
|
+
if (jsonlFiles.length === 0) return "";
|
|
23
|
+
const { stat } = await import("fs/promises");
|
|
24
|
+
const fileStats = await Promise.all(
|
|
25
|
+
jsonlFiles.map(async (f) => {
|
|
26
|
+
const path = join(sessionsDir, f);
|
|
27
|
+
const s = await stat(path);
|
|
28
|
+
return {
|
|
29
|
+
path,
|
|
30
|
+
birthtime: s.birthtime,
|
|
31
|
+
mtimeMs: s.mtimeMs,
|
|
32
|
+
size: s.size
|
|
33
|
+
};
|
|
34
|
+
})
|
|
35
|
+
);
|
|
36
|
+
if (process.env.DEBUG_SESSION) {
|
|
37
|
+
console.error(`[DEBUG] total files: ${fileStats.length}`);
|
|
38
|
+
for (const f of fileStats) {
|
|
39
|
+
console.error(
|
|
40
|
+
`[DEBUG] ${f.path.split("/").pop()}: ${f.size} bytes, birth: ${f.birthtime.toISOString()}, mtime: ${new Date(f.mtimeMs).toISOString()}`
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
if (startTime) {
|
|
44
|
+
console.error(`[DEBUG] startTime: ${startTime.toISOString()}`);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
if (startTime && fileStats.length > 0) {
|
|
48
|
+
const startMs = startTime.getTime();
|
|
49
|
+
const birthtimeThreshold = 3e5;
|
|
50
|
+
const birthtimeMatched = fileStats.filter(
|
|
51
|
+
(f) => Math.abs(f.birthtime.getTime() - startMs) < birthtimeThreshold
|
|
52
|
+
).sort((a, b) => b.mtimeMs - a.mtimeMs);
|
|
53
|
+
if (birthtimeMatched.length > 0) {
|
|
54
|
+
if (process.env.DEBUG_SESSION) {
|
|
55
|
+
console.error(
|
|
56
|
+
`[DEBUG] matched by birthtime: ${birthtimeMatched[0].path}`
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
return birthtimeMatched[0].path;
|
|
60
|
+
}
|
|
61
|
+
const mtimeThreshold = 6e5;
|
|
62
|
+
const mtimeMatched = fileStats.filter((f) => {
|
|
63
|
+
const mtimeDiff = f.mtimeMs - startMs;
|
|
64
|
+
return Math.abs(mtimeDiff) < mtimeThreshold;
|
|
65
|
+
}).sort((a, b) => Math.abs(a.mtimeMs - startMs) - Math.abs(b.mtimeMs - startMs));
|
|
66
|
+
if (mtimeMatched.length > 0) {
|
|
67
|
+
if (process.env.DEBUG_SESSION) {
|
|
68
|
+
console.error(`[DEBUG] matched by mtime: ${mtimeMatched[0].path}`);
|
|
69
|
+
}
|
|
70
|
+
return mtimeMatched[0].path;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
fileStats.sort((a, b) => b.mtimeMs - a.mtimeMs);
|
|
74
|
+
if (process.env.DEBUG_SESSION && fileStats[0]) {
|
|
75
|
+
console.error(`[DEBUG] fallback to latest: ${fileStats[0].path}`);
|
|
76
|
+
}
|
|
77
|
+
return fileStats[0]?.path || "";
|
|
78
|
+
} catch {
|
|
79
|
+
return "";
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
async function getRecentMessages(sessionPath, limit = 5) {
|
|
83
|
+
if (!sessionPath) return [];
|
|
84
|
+
try {
|
|
85
|
+
const content = await readFile(sessionPath, "utf-8");
|
|
86
|
+
const lines = content.trim().split("\n");
|
|
87
|
+
const messages = [];
|
|
88
|
+
for (const line of lines) {
|
|
89
|
+
try {
|
|
90
|
+
const entry = JSON.parse(line);
|
|
91
|
+
if (entry.type === "user" && entry.message?.content) {
|
|
92
|
+
const text = extractUserText(entry.message.content);
|
|
93
|
+
if (text && !isMetaMessage(text)) {
|
|
94
|
+
messages.push({
|
|
95
|
+
role: "user",
|
|
96
|
+
content: truncate(text, 100),
|
|
97
|
+
timestamp: entry.timestamp || ""
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
} else if (entry.type === "assistant" && entry.message?.content) {
|
|
101
|
+
const text = extractAssistantText(entry.message.content);
|
|
102
|
+
if (text) {
|
|
103
|
+
messages.push({
|
|
104
|
+
role: "assistant",
|
|
105
|
+
content: truncate(text, 100),
|
|
106
|
+
timestamp: entry.timestamp || ""
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
} catch {
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
return messages.slice(-limit);
|
|
114
|
+
} catch {
|
|
115
|
+
return [];
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
async function getNewMessages(sessionPath, fromLine) {
|
|
119
|
+
if (!sessionPath) return { messages: [], totalLines: 0 };
|
|
120
|
+
try {
|
|
121
|
+
const content = await readFile(sessionPath, "utf-8");
|
|
122
|
+
const lines = content.trim().split("\n");
|
|
123
|
+
const totalLines = lines.length;
|
|
124
|
+
const newLines = lines.slice(fromLine);
|
|
125
|
+
const messages = [];
|
|
126
|
+
for (const line of newLines) {
|
|
127
|
+
try {
|
|
128
|
+
const entry = JSON.parse(line);
|
|
129
|
+
if (entry.type === "user" && entry.message?.content) {
|
|
130
|
+
const text = extractUserText(entry.message.content);
|
|
131
|
+
if (text && !isMetaMessage(text)) {
|
|
132
|
+
messages.push({
|
|
133
|
+
role: "user",
|
|
134
|
+
content: truncate(text, 100),
|
|
135
|
+
timestamp: entry.timestamp || ""
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
} else if (entry.type === "assistant" && entry.message?.content) {
|
|
139
|
+
const text = extractAssistantText(entry.message.content);
|
|
140
|
+
if (text) {
|
|
141
|
+
messages.push({
|
|
142
|
+
role: "assistant",
|
|
143
|
+
content: truncate(text, 100),
|
|
144
|
+
timestamp: entry.timestamp || ""
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
} catch {
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
return { messages, totalLines };
|
|
152
|
+
} catch {
|
|
153
|
+
return { messages: [], totalLines: 0 };
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
async function getAllMessages(sessionPath) {
|
|
157
|
+
if (!sessionPath) return [];
|
|
158
|
+
try {
|
|
159
|
+
const content = await readFile(sessionPath, "utf-8");
|
|
160
|
+
const lines = content.trim().split("\n");
|
|
161
|
+
const messages = [];
|
|
162
|
+
for (const line of lines) {
|
|
163
|
+
try {
|
|
164
|
+
const entry = JSON.parse(line);
|
|
165
|
+
if (entry.type === "user" && entry.message?.content) {
|
|
166
|
+
const text = extractUserText(entry.message.content);
|
|
167
|
+
if (text && !isMetaMessage(text)) {
|
|
168
|
+
messages.push({
|
|
169
|
+
role: "user",
|
|
170
|
+
content: truncate(text, 100),
|
|
171
|
+
timestamp: entry.timestamp || ""
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
} else if (entry.type === "assistant" && entry.message?.content) {
|
|
175
|
+
const text = extractAssistantText(entry.message.content);
|
|
176
|
+
if (text) {
|
|
177
|
+
messages.push({
|
|
178
|
+
role: "assistant",
|
|
179
|
+
content: truncate(text, 100),
|
|
180
|
+
timestamp: entry.timestamp || ""
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
} catch {
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
return messages;
|
|
188
|
+
} catch {
|
|
189
|
+
return [];
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
function extractUserText(content) {
|
|
193
|
+
if (typeof content === "string") {
|
|
194
|
+
return content;
|
|
195
|
+
}
|
|
196
|
+
return "";
|
|
197
|
+
}
|
|
198
|
+
function extractAssistantText(content) {
|
|
199
|
+
if (!Array.isArray(content)) return "";
|
|
200
|
+
const textParts = content.filter((item) => item.type === "text" && item.text).map((item) => item.text);
|
|
201
|
+
return textParts.join("\n");
|
|
202
|
+
}
|
|
203
|
+
function isMetaMessage(text) {
|
|
204
|
+
return text.startsWith("<local-command") || text.startsWith("<command-name>") || text.startsWith("<command-message>");
|
|
205
|
+
}
|
|
206
|
+
function truncate(text, maxLen) {
|
|
207
|
+
const clean = text.replace(/\s+/g, " ").trim();
|
|
208
|
+
if (clean.length <= maxLen) return clean;
|
|
209
|
+
return `${clean.slice(0, maxLen - 3)}...`;
|
|
210
|
+
}
|
|
211
|
+
function formatSize(bytes) {
|
|
212
|
+
if (bytes < 1024) return `${bytes} B`;
|
|
213
|
+
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
|
|
214
|
+
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
215
|
+
}
|
|
216
|
+
function formatTimeDiff(date, referenceDate) {
|
|
217
|
+
const diffMs = date.getTime() - referenceDate.getTime();
|
|
218
|
+
const diffSec = Math.abs(diffMs / 1e3);
|
|
219
|
+
if (diffSec < 60) {
|
|
220
|
+
return diffMs >= 0 ? `\u542F\u52A8\u540E ${diffSec.toFixed(0)} \u79D2` : `\u542F\u52A8\u524D ${diffSec.toFixed(0)} \u79D2`;
|
|
221
|
+
}
|
|
222
|
+
const diffMin = diffSec / 60;
|
|
223
|
+
return diffMs >= 0 ? `\u542F\u52A8\u540E ${diffMin.toFixed(1)} \u5206\u949F` : `\u542F\u52A8\u524D ${diffMin.toFixed(1)} \u5206\u949F`;
|
|
224
|
+
}
|
|
225
|
+
async function debugSessionMatching(processes) {
|
|
226
|
+
const output = [];
|
|
227
|
+
output.push("=== Claude Code \u4F1A\u8BDD\u8C03\u8BD5\u4FE1\u606F ===\n");
|
|
228
|
+
if (processes.length === 0) {
|
|
229
|
+
output.push("\u672A\u627E\u5230\u8FD0\u884C\u4E2D\u7684 Claude Code \u8FDB\u7A0B\n");
|
|
230
|
+
return output.join("\n");
|
|
231
|
+
}
|
|
232
|
+
let successCount = 0;
|
|
233
|
+
for (let i = 0; i < processes.length; i++) {
|
|
234
|
+
const proc = processes[i];
|
|
235
|
+
output.push(`\u8FDB\u7A0B #${i + 1} (PID: ${proc.pid})`);
|
|
236
|
+
output.push(` \u5DE5\u4F5C\u76EE\u5F55: ${proc.cwd}`);
|
|
237
|
+
output.push(` \u542F\u52A8\u65F6\u95F4: ${proc.startTime.toISOString()}`);
|
|
238
|
+
const projectDir = cwdToProjectDir(proc.cwd);
|
|
239
|
+
const sessionsDir = join(homedir(), ".claude", "projects", projectDir);
|
|
240
|
+
output.push(` \u9879\u76EE\u76EE\u5F55: ${projectDir}`);
|
|
241
|
+
output.push(` \u4F1A\u8BDD\u76EE\u5F55: ${sessionsDir}`);
|
|
242
|
+
output.push("");
|
|
243
|
+
try {
|
|
244
|
+
const files = await readdir(sessionsDir);
|
|
245
|
+
const jsonlFiles = files.filter((f) => f.endsWith(".jsonl"));
|
|
246
|
+
if (jsonlFiles.length === 0) {
|
|
247
|
+
output.push(" \u26A0\uFE0F \u672A\u627E\u5230\u4F1A\u8BDD\u6587\u4EF6");
|
|
248
|
+
output.push("");
|
|
249
|
+
continue;
|
|
250
|
+
}
|
|
251
|
+
output.push(" \u627E\u5230\u7684\u4F1A\u8BDD\u6587\u4EF6:");
|
|
252
|
+
const { stat } = await import("fs/promises");
|
|
253
|
+
const fileStats = await Promise.all(
|
|
254
|
+
jsonlFiles.map(async (f) => {
|
|
255
|
+
const path = join(sessionsDir, f);
|
|
256
|
+
const s = await stat(path);
|
|
257
|
+
return {
|
|
258
|
+
name: f,
|
|
259
|
+
path,
|
|
260
|
+
birthtime: s.birthtime,
|
|
261
|
+
mtime: new Date(s.mtimeMs),
|
|
262
|
+
size: s.size
|
|
263
|
+
};
|
|
264
|
+
})
|
|
265
|
+
);
|
|
266
|
+
for (const file of fileStats) {
|
|
267
|
+
const ignored = file.size < 1024;
|
|
268
|
+
const prefix = ignored ? " \u2717" : " \u2713";
|
|
269
|
+
output.push(`${prefix} ${file.name}`);
|
|
270
|
+
output.push(` \u5927\u5C0F: ${formatSize(file.size)}`);
|
|
271
|
+
if (ignored) {
|
|
272
|
+
output.push(" (< 1KB, \u5DF2\u5FFD\u7565)");
|
|
273
|
+
}
|
|
274
|
+
output.push(
|
|
275
|
+
` \u521B\u5EFA: ${file.birthtime.toISOString()} (${formatTimeDiff(file.birthtime, proc.startTime)})`
|
|
276
|
+
);
|
|
277
|
+
output.push(
|
|
278
|
+
` \u4FEE\u6539: ${file.mtime.toISOString()} (${formatTimeDiff(file.mtime, proc.startTime)})`
|
|
279
|
+
);
|
|
280
|
+
output.push("");
|
|
281
|
+
}
|
|
282
|
+
const startMs = proc.startTime.getTime();
|
|
283
|
+
const validFiles = fileStats.filter((f) => f.size >= 1024);
|
|
284
|
+
let matchedFile = null;
|
|
285
|
+
let matchStrategy = "";
|
|
286
|
+
const birthtimeThreshold = 3e5;
|
|
287
|
+
const birthtimeMatched = validFiles.filter(
|
|
288
|
+
(f) => Math.abs(f.birthtime.getTime() - startMs) < birthtimeThreshold
|
|
289
|
+
).sort((a, b) => b.mtime.getTime() - a.mtime.getTime());
|
|
290
|
+
if (birthtimeMatched.length > 0) {
|
|
291
|
+
matchedFile = birthtimeMatched[0];
|
|
292
|
+
matchStrategy = "\u521B\u5EFA\u65F6\u95F4\u5339\u914D \u2713";
|
|
293
|
+
} else {
|
|
294
|
+
const mtimeThreshold = 6e5;
|
|
295
|
+
const mtimeMatched = validFiles.filter((f) => {
|
|
296
|
+
const mtimeDiff = f.mtime.getTime() - startMs;
|
|
297
|
+
return Math.abs(mtimeDiff) < mtimeThreshold;
|
|
298
|
+
}).sort(
|
|
299
|
+
(a, b) => Math.abs(a.mtime.getTime() - startMs) - Math.abs(b.mtime.getTime() - startMs)
|
|
300
|
+
);
|
|
301
|
+
if (mtimeMatched.length > 0) {
|
|
302
|
+
matchedFile = mtimeMatched[0];
|
|
303
|
+
matchStrategy = "\u4FEE\u6539\u65F6\u95F4\u5339\u914D \u2713";
|
|
304
|
+
} else {
|
|
305
|
+
const sorted = [...validFiles].sort(
|
|
306
|
+
(a, b) => b.mtime.getTime() - a.mtime.getTime()
|
|
307
|
+
);
|
|
308
|
+
if (sorted.length > 0) {
|
|
309
|
+
matchedFile = sorted[0];
|
|
310
|
+
matchStrategy = "\u56DE\u9000\u5230\u6700\u65B0\u6587\u4EF6";
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
output.push(" \u5339\u914D\u7ED3\u679C:");
|
|
315
|
+
output.push(` \u7B56\u7565: ${matchStrategy}`);
|
|
316
|
+
if (matchedFile) {
|
|
317
|
+
output.push(` \u9009\u62E9: ${matchedFile.name}`);
|
|
318
|
+
try {
|
|
319
|
+
const messages = await getAllMessages(matchedFile.path);
|
|
320
|
+
output.push(` \u6D88\u606F\u6570: ${messages.length} \u6761`);
|
|
321
|
+
successCount++;
|
|
322
|
+
} catch {
|
|
323
|
+
output.push(" \u6D88\u606F\u6570: \u8BFB\u53D6\u5931\u8D25");
|
|
324
|
+
}
|
|
325
|
+
} else {
|
|
326
|
+
output.push(" \u9009\u62E9: \u65E0");
|
|
327
|
+
}
|
|
328
|
+
output.push("");
|
|
329
|
+
output.push("---");
|
|
330
|
+
output.push("");
|
|
331
|
+
} catch (error) {
|
|
332
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
333
|
+
output.push(` \u274C \u9519\u8BEF: ${errorMsg}`);
|
|
334
|
+
output.push("");
|
|
335
|
+
output.push("---");
|
|
336
|
+
output.push("");
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
output.push(
|
|
340
|
+
`\u603B\u8BA1: ${processes.length} \u4E2A\u8FDB\u7A0B, ${successCount} \u4E2A\u6210\u529F\u5339\u914D\u4F1A\u8BDD`
|
|
341
|
+
);
|
|
342
|
+
return output.join("\n");
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
export {
|
|
346
|
+
getSessionPath,
|
|
347
|
+
getRecentMessages,
|
|
348
|
+
getNewMessages,
|
|
349
|
+
getAllMessages,
|
|
350
|
+
debugSessionMatching
|
|
351
|
+
};
|
package/dist/index.js
CHANGED
|
@@ -3,6 +3,11 @@ import {
|
|
|
3
3
|
getClaudeProcesses,
|
|
4
4
|
killProcess
|
|
5
5
|
} from "./chunk-EZHIVMX4.js";
|
|
6
|
+
import {
|
|
7
|
+
getAllMessages,
|
|
8
|
+
getNewMessages,
|
|
9
|
+
getSessionPath
|
|
10
|
+
} from "./chunk-RZG4BATR.js";
|
|
6
11
|
|
|
7
12
|
// src/index.tsx
|
|
8
13
|
import { withFullScreen } from "fullscreen-ink";
|
|
@@ -193,7 +198,10 @@ function formatElapsed(elapsed) {
|
|
|
193
198
|
|
|
194
199
|
// src/components/ui/DetailPanel.tsx
|
|
195
200
|
import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
196
|
-
function DetailPanel({
|
|
201
|
+
function DetailPanel({
|
|
202
|
+
process: proc,
|
|
203
|
+
isLive = true
|
|
204
|
+
}) {
|
|
197
205
|
if (!proc) {
|
|
198
206
|
return /* @__PURE__ */ jsx4(EmptyPrompt, { message: "\u9009\u62E9\u4E00\u4E2A\u8FDB\u7A0B\u67E5\u770B\u8BE6\u60C5" });
|
|
199
207
|
}
|
|
@@ -302,157 +310,9 @@ function ProcessList({
|
|
|
302
310
|
// src/hooks/useProcesses.ts
|
|
303
311
|
import { useCallback, useEffect as useEffect2, useMemo, useRef as useRef2, useState } from "react";
|
|
304
312
|
|
|
305
|
-
// src/utils/session.ts
|
|
306
|
-
import { readFile, readdir } from "fs/promises";
|
|
307
|
-
import { homedir } from "os";
|
|
308
|
-
import { join } from "path";
|
|
309
|
-
function cwdToProjectDir(cwd) {
|
|
310
|
-
return cwd.replace(/\//g, "-").replace(/^-/, "-");
|
|
311
|
-
}
|
|
312
|
-
async function getSessionPath(cwd, startTime) {
|
|
313
|
-
const projectDir = cwdToProjectDir(cwd);
|
|
314
|
-
const sessionsDir = join(homedir(), ".claude", "projects", projectDir);
|
|
315
|
-
try {
|
|
316
|
-
const files = await readdir(sessionsDir);
|
|
317
|
-
const jsonlFiles = files.filter((f) => f.endsWith(".jsonl"));
|
|
318
|
-
if (jsonlFiles.length === 0) return "";
|
|
319
|
-
const { stat } = await import("fs/promises");
|
|
320
|
-
const fileStats = await Promise.all(
|
|
321
|
-
jsonlFiles.map(async (f) => {
|
|
322
|
-
const path = join(sessionsDir, f);
|
|
323
|
-
const s = await stat(path);
|
|
324
|
-
return {
|
|
325
|
-
path,
|
|
326
|
-
birthtime: s.birthtime,
|
|
327
|
-
mtimeMs: s.mtimeMs,
|
|
328
|
-
size: s.size
|
|
329
|
-
};
|
|
330
|
-
})
|
|
331
|
-
);
|
|
332
|
-
const minSize = 1024;
|
|
333
|
-
const validFiles = fileStats.filter((f) => f.size >= minSize);
|
|
334
|
-
if (startTime && validFiles.length > 0) {
|
|
335
|
-
const startMs = startTime.getTime();
|
|
336
|
-
const birthtimeThreshold = 1e4;
|
|
337
|
-
const birthtimeMatched = validFiles.filter(
|
|
338
|
-
(f) => Math.abs(f.birthtime.getTime() - startMs) < birthtimeThreshold
|
|
339
|
-
).sort((a, b) => b.mtimeMs - a.mtimeMs);
|
|
340
|
-
if (birthtimeMatched.length > 0) {
|
|
341
|
-
return birthtimeMatched[0].path;
|
|
342
|
-
}
|
|
343
|
-
const mtimeThreshold = 6e4;
|
|
344
|
-
const mtimeMatched = validFiles.filter((f) => {
|
|
345
|
-
const mtimeDiff = f.mtimeMs - startMs;
|
|
346
|
-
return mtimeDiff >= 0 && mtimeDiff < mtimeThreshold;
|
|
347
|
-
}).sort((a, b) => a.mtimeMs - b.mtimeMs);
|
|
348
|
-
if (mtimeMatched.length > 0) {
|
|
349
|
-
return mtimeMatched[0].path;
|
|
350
|
-
}
|
|
351
|
-
}
|
|
352
|
-
const fallbackFiles = validFiles.length > 0 ? validFiles : fileStats;
|
|
353
|
-
fallbackFiles.sort((a, b) => b.mtimeMs - a.mtimeMs);
|
|
354
|
-
return fallbackFiles[0]?.path || "";
|
|
355
|
-
} catch {
|
|
356
|
-
return "";
|
|
357
|
-
}
|
|
358
|
-
}
|
|
359
|
-
async function getNewMessages(sessionPath, fromLine) {
|
|
360
|
-
if (!sessionPath) return { messages: [], totalLines: 0 };
|
|
361
|
-
try {
|
|
362
|
-
const content = await readFile(sessionPath, "utf-8");
|
|
363
|
-
const lines = content.trim().split("\n");
|
|
364
|
-
const totalLines = lines.length;
|
|
365
|
-
const newLines = lines.slice(fromLine);
|
|
366
|
-
const messages = [];
|
|
367
|
-
for (const line of newLines) {
|
|
368
|
-
try {
|
|
369
|
-
const entry = JSON.parse(line);
|
|
370
|
-
if (entry.type === "user" && entry.message?.content) {
|
|
371
|
-
const text = extractUserText(entry.message.content);
|
|
372
|
-
if (text && !isMetaMessage(text)) {
|
|
373
|
-
messages.push({
|
|
374
|
-
role: "user",
|
|
375
|
-
content: truncate(text, 100),
|
|
376
|
-
timestamp: entry.timestamp || ""
|
|
377
|
-
});
|
|
378
|
-
}
|
|
379
|
-
} else if (entry.type === "assistant" && entry.message?.content) {
|
|
380
|
-
const text = extractAssistantText(entry.message.content);
|
|
381
|
-
if (text) {
|
|
382
|
-
messages.push({
|
|
383
|
-
role: "assistant",
|
|
384
|
-
content: truncate(text, 100),
|
|
385
|
-
timestamp: entry.timestamp || ""
|
|
386
|
-
});
|
|
387
|
-
}
|
|
388
|
-
}
|
|
389
|
-
} catch {
|
|
390
|
-
}
|
|
391
|
-
}
|
|
392
|
-
return { messages, totalLines };
|
|
393
|
-
} catch {
|
|
394
|
-
return { messages: [], totalLines: 0 };
|
|
395
|
-
}
|
|
396
|
-
}
|
|
397
|
-
async function getAllMessages(sessionPath) {
|
|
398
|
-
if (!sessionPath) return [];
|
|
399
|
-
try {
|
|
400
|
-
const content = await readFile(sessionPath, "utf-8");
|
|
401
|
-
const lines = content.trim().split("\n");
|
|
402
|
-
const messages = [];
|
|
403
|
-
for (const line of lines) {
|
|
404
|
-
try {
|
|
405
|
-
const entry = JSON.parse(line);
|
|
406
|
-
if (entry.type === "user" && entry.message?.content) {
|
|
407
|
-
const text = extractUserText(entry.message.content);
|
|
408
|
-
if (text && !isMetaMessage(text)) {
|
|
409
|
-
messages.push({
|
|
410
|
-
role: "user",
|
|
411
|
-
content: truncate(text, 100),
|
|
412
|
-
timestamp: entry.timestamp || ""
|
|
413
|
-
});
|
|
414
|
-
}
|
|
415
|
-
} else if (entry.type === "assistant" && entry.message?.content) {
|
|
416
|
-
const text = extractAssistantText(entry.message.content);
|
|
417
|
-
if (text) {
|
|
418
|
-
messages.push({
|
|
419
|
-
role: "assistant",
|
|
420
|
-
content: truncate(text, 100),
|
|
421
|
-
timestamp: entry.timestamp || ""
|
|
422
|
-
});
|
|
423
|
-
}
|
|
424
|
-
}
|
|
425
|
-
} catch {
|
|
426
|
-
}
|
|
427
|
-
}
|
|
428
|
-
return messages;
|
|
429
|
-
} catch {
|
|
430
|
-
return [];
|
|
431
|
-
}
|
|
432
|
-
}
|
|
433
|
-
function extractUserText(content) {
|
|
434
|
-
if (typeof content === "string") {
|
|
435
|
-
return content;
|
|
436
|
-
}
|
|
437
|
-
return "";
|
|
438
|
-
}
|
|
439
|
-
function extractAssistantText(content) {
|
|
440
|
-
if (!Array.isArray(content)) return "";
|
|
441
|
-
const textParts = content.filter((item) => item.type === "text" && item.text).map((item) => item.text);
|
|
442
|
-
return textParts.join("\n");
|
|
443
|
-
}
|
|
444
|
-
function isMetaMessage(text) {
|
|
445
|
-
return text.startsWith("<local-command") || text.startsWith("<command-name>") || text.startsWith("<command-message>");
|
|
446
|
-
}
|
|
447
|
-
function truncate(text, maxLen) {
|
|
448
|
-
const clean = text.replace(/\s+/g, " ").trim();
|
|
449
|
-
if (clean.length <= maxLen) return clean;
|
|
450
|
-
return `${clean.slice(0, maxLen - 3)}...`;
|
|
451
|
-
}
|
|
452
|
-
|
|
453
313
|
// src/hooks/useSessionWatcher.ts
|
|
454
|
-
import { useEffect, useRef } from "react";
|
|
455
314
|
import chokidar from "chokidar";
|
|
315
|
+
import { useEffect, useRef } from "react";
|
|
456
316
|
function useSessionWatcher(sessionPaths, onFileChange) {
|
|
457
317
|
const watcherRef = useRef(null);
|
|
458
318
|
const debounceTimersRef = useRef(/* @__PURE__ */ new Map());
|
|
@@ -750,6 +610,7 @@ var cli = meow(
|
|
|
750
610
|
Options
|
|
751
611
|
-l, --list \u975E\u4EA4\u4E92\u6A21\u5F0F\uFF0C\u4EC5\u5217\u51FA\u8FDB\u7A0B
|
|
752
612
|
-j, --json JSON \u683C\u5F0F\u8F93\u51FA\uFF08\u914D\u5408 --list\uFF09
|
|
613
|
+
-d, --debug \u663E\u793A\u4F1A\u8BDD\u5339\u914D\u8C03\u8BD5\u4FE1\u606F
|
|
753
614
|
-i, --interval \u5237\u65B0\u95F4\u9694\u79D2\u6570\uFF08\u9ED8\u8BA4 2\uFF09
|
|
754
615
|
-v, --version \u663E\u793A\u7248\u672C
|
|
755
616
|
-h, --help \u663E\u793A\u5E2E\u52A9
|
|
@@ -758,6 +619,7 @@ var cli = meow(
|
|
|
758
619
|
$ claude-ps \u542F\u52A8 TUI
|
|
759
620
|
$ claude-ps --list \u5217\u51FA\u8FDB\u7A0B\u540E\u9000\u51FA
|
|
760
621
|
$ claude-ps --json JSON \u683C\u5F0F\u8F93\u51FA
|
|
622
|
+
$ claude-ps --debug \u663E\u793A\u4F1A\u8BDD\u5339\u914D\u8BE6\u60C5
|
|
761
623
|
$ claude-ps -i 5 \u8BBE\u7F6E\u5237\u65B0\u95F4\u9694\u4E3A 5 \u79D2
|
|
762
624
|
`,
|
|
763
625
|
{
|
|
@@ -778,6 +640,11 @@ var cli = meow(
|
|
|
778
640
|
shortFlag: "j",
|
|
779
641
|
default: false
|
|
780
642
|
},
|
|
643
|
+
debug: {
|
|
644
|
+
type: "boolean",
|
|
645
|
+
shortFlag: "d",
|
|
646
|
+
default: false
|
|
647
|
+
},
|
|
781
648
|
interval: {
|
|
782
649
|
type: "number",
|
|
783
650
|
shortFlag: "i",
|
|
@@ -791,11 +658,15 @@ var cli = meow(
|
|
|
791
658
|
}
|
|
792
659
|
}
|
|
793
660
|
);
|
|
794
|
-
var { list, json, interval } = cli.flags;
|
|
795
|
-
if (list || json) {
|
|
661
|
+
var { list, json, debug, interval } = cli.flags;
|
|
662
|
+
if (list || json || debug) {
|
|
796
663
|
const { getClaudeProcesses: getClaudeProcesses2 } = await import("./process-AUO5UVTV.js");
|
|
797
664
|
const processes = await getClaudeProcesses2();
|
|
798
|
-
if (
|
|
665
|
+
if (debug) {
|
|
666
|
+
const { debugSessionMatching } = await import("./session-TGVDWUTY.js");
|
|
667
|
+
const debugInfo = await debugSessionMatching(processes);
|
|
668
|
+
console.log(debugInfo);
|
|
669
|
+
} else if (json) {
|
|
799
670
|
console.log(JSON.stringify(processes, null, 2));
|
|
800
671
|
} else {
|
|
801
672
|
console.log("PID TTY CWD");
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
debugSessionMatching,
|
|
4
|
+
getAllMessages,
|
|
5
|
+
getNewMessages,
|
|
6
|
+
getRecentMessages,
|
|
7
|
+
getSessionPath
|
|
8
|
+
} from "./chunk-RZG4BATR.js";
|
|
9
|
+
export {
|
|
10
|
+
debugSessionMatching,
|
|
11
|
+
getAllMessages,
|
|
12
|
+
getNewMessages,
|
|
13
|
+
getRecentMessages,
|
|
14
|
+
getSessionPath
|
|
15
|
+
};
|