claude-code-session-manager 0.37.0 → 0.37.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/dist/index.html
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
8
8
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
9
9
|
<link href="https://fonts.googleapis.com/css2?family=Newsreader:ital,opsz,wght@0,6..72,400;0,6..72,500;0,6..72,600;0,6..72,700;1,6..72,400&family=Geist:wght@300;400;500;600;700&family=IBM+Plex+Mono:wght@400;500;600&display=swap" rel="stylesheet">
|
|
10
|
-
<script type="module" crossorigin src="./assets/index
|
|
10
|
+
<script type="module" crossorigin src="./assets/index--a8m5_ET.js"></script>
|
|
11
11
|
<link rel="modulepreload" crossorigin href="./assets/monaco-editor-BW5C4Iv1.js">
|
|
12
12
|
<link rel="stylesheet" crossorigin href="./assets/monaco-editor-BTnBOi8r.css">
|
|
13
13
|
<link rel="stylesheet" crossorigin href="./assets/index-CTTjT08J.css">
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claude-code-session-manager",
|
|
3
|
-
"version": "0.37.
|
|
3
|
+
"version": "0.37.1",
|
|
4
4
|
"description": "Local cockpit for the Claude Code CLI — multi-tab terminal, full config surface, scheduler, voice dictation, and live observability.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/main/index.cjs",
|
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
".claude-plugin/",
|
|
13
13
|
"plugins/",
|
|
14
14
|
"scripts/postinstall.cjs",
|
|
15
|
+
"scripts/lib/",
|
|
15
16
|
"src/main/",
|
|
16
17
|
"src/preload/",
|
|
17
18
|
"dist/index.html",
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// activeSessions.cjs — returns distinct on-disk project cwds with an active
|
|
4
|
+
// session within maxAgeMin minutes. Used by the feedback sweep (PRD 102) to
|
|
5
|
+
// narrow scanning to projects the user is actually working in.
|
|
6
|
+
//
|
|
7
|
+
// Detection is transcript-based only (scans ~/.claude/projects/*/*.jsonl
|
|
8
|
+
// mtimes). It has zero dependency on ~/.claude/knowledge-log/prompts.jsonl,
|
|
9
|
+
// which PRD 356-retire purges along with its capture hook.
|
|
10
|
+
|
|
11
|
+
const fs = require('node:fs');
|
|
12
|
+
const os = require('node:os');
|
|
13
|
+
const path = require('node:path');
|
|
14
|
+
|
|
15
|
+
const HOME = os.homedir();
|
|
16
|
+
|
|
17
|
+
// Transcript reads only need the last line with a `cwd` field — a few dozen
|
|
18
|
+
// lines is always enough. 64 KB keeps peak RSS proportionate.
|
|
19
|
+
const TAIL_BYTES = 64 * 1024;
|
|
20
|
+
|
|
21
|
+
// Safety cap on distinct cwds to bound result set size. O(1) extra space.
|
|
22
|
+
const MAX_CWDS = 50;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* readTailLines(filePath, maxBytes) → string[]
|
|
26
|
+
* Reads at most maxBytes from the END of filePath and splits into non-empty lines.
|
|
27
|
+
* O(1) in file size (bounded read). Returns [] on any I/O error.
|
|
28
|
+
*/
|
|
29
|
+
function readTailLines(filePath, maxBytes) {
|
|
30
|
+
let buf;
|
|
31
|
+
try {
|
|
32
|
+
const stat = fs.statSync(filePath);
|
|
33
|
+
if (stat.size === 0) return [];
|
|
34
|
+
const readSize = Math.min(maxBytes, stat.size);
|
|
35
|
+
const fd = fs.openSync(filePath, 'r');
|
|
36
|
+
try {
|
|
37
|
+
buf = Buffer.alloc(readSize);
|
|
38
|
+
fs.readSync(fd, buf, 0, readSize, stat.size - readSize);
|
|
39
|
+
} finally {
|
|
40
|
+
fs.closeSync(fd);
|
|
41
|
+
}
|
|
42
|
+
} catch {
|
|
43
|
+
return [];
|
|
44
|
+
}
|
|
45
|
+
return buf.toString('utf8').split('\n').filter((l) => l.trim());
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* activeProjectCwds(maxAgeMin = 90, opts?) → string[]
|
|
50
|
+
*
|
|
51
|
+
* Returns distinct, on-disk project cwds that have an open/active session
|
|
52
|
+
* within the last maxAgeMin minutes.
|
|
53
|
+
*
|
|
54
|
+
* Sole detection path: scan ~/.claude/projects/*/ for transcript *.jsonl
|
|
55
|
+
* files modified within maxAgeMin, read `cwd` from the last parseable line
|
|
56
|
+
* of the newest transcript per project dir.
|
|
57
|
+
* Complexity: O(P × L) over P project dirs, each bounded-tail read (64 KB).
|
|
58
|
+
*
|
|
59
|
+
* opts (for testing):
|
|
60
|
+
* projectsDir — override the default ~/.claude/projects path
|
|
61
|
+
*/
|
|
62
|
+
function activeProjectCwds(maxAgeMin = 90, {
|
|
63
|
+
projectsDir = path.join(HOME, '.claude', 'projects'),
|
|
64
|
+
} = {}) {
|
|
65
|
+
const cutoffMs = Date.now() - maxAgeMin * 60 * 1000;
|
|
66
|
+
const seen = new Set();
|
|
67
|
+
const result = [];
|
|
68
|
+
|
|
69
|
+
function addCwd(cwd) {
|
|
70
|
+
if (!cwd || typeof cwd !== 'string') return;
|
|
71
|
+
if (seen.has(cwd) || result.length >= MAX_CWDS) return;
|
|
72
|
+
try { fs.statSync(cwd); } catch { return; } // must exist on disk
|
|
73
|
+
seen.add(cwd);
|
|
74
|
+
result.push(cwd);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Scan ~/.claude/projects/*/ transcript *.jsonl files.
|
|
78
|
+
let slugs;
|
|
79
|
+
try { slugs = fs.readdirSync(projectsDir); } catch { return result; }
|
|
80
|
+
|
|
81
|
+
for (const slug of slugs) {
|
|
82
|
+
if (result.length >= MAX_CWDS) break;
|
|
83
|
+
const projDir = path.join(projectsDir, slug);
|
|
84
|
+
let entries;
|
|
85
|
+
try {
|
|
86
|
+
entries = fs.readdirSync(projDir).filter((f) => f.endsWith('.jsonl'));
|
|
87
|
+
} catch { continue; }
|
|
88
|
+
|
|
89
|
+
// Find the most recently modified transcript in this project dir.
|
|
90
|
+
let newestPath = null;
|
|
91
|
+
let newestMtimeMs = 0;
|
|
92
|
+
for (const tf of entries) {
|
|
93
|
+
const fp = path.join(projDir, tf);
|
|
94
|
+
try {
|
|
95
|
+
const st = fs.statSync(fp);
|
|
96
|
+
if (st.mtimeMs > newestMtimeMs) {
|
|
97
|
+
newestMtimeMs = st.mtimeMs;
|
|
98
|
+
newestPath = fp;
|
|
99
|
+
}
|
|
100
|
+
} catch { continue; }
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
if (!newestPath || newestMtimeMs < cutoffMs) continue;
|
|
104
|
+
|
|
105
|
+
// Read `cwd` from the last parseable line of the transcript.
|
|
106
|
+
const tlines = readTailLines(newestPath, TAIL_BYTES);
|
|
107
|
+
for (let i = tlines.length - 1; i >= 0; i--) {
|
|
108
|
+
let row;
|
|
109
|
+
try { row = JSON.parse(tlines[i]); } catch { continue; }
|
|
110
|
+
if (row.cwd) { addCwd(row.cwd); break; }
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
return result;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
module.exports = { activeProjectCwds };
|