claude-code-session-manager 0.92.0 → 0.92.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/assets/{AgentLibrary-Bu-1eT_n.js → AgentLibrary-Ci6u03bq.js} +1 -1
- package/dist/assets/{DataModel-Bk9rkJ8x.js → DataModel-BIqcjsv2.js} +1 -1
- package/dist/assets/{History-BB6wpf02.js → History-BOR_fJNr.js} +1 -1
- package/dist/assets/{Hooks-zQZH56Sv.js → Hooks-BtD436iL.js} +1 -1
- package/dist/assets/{HostBilko-CW0pHoPS.js → HostBilko-CMe5cH3H.js} +1 -1
- package/dist/assets/{Library-DrGdaz9N.js → Library-DeKdY-tD.js} +1 -1
- package/dist/assets/{ListDetail-D4bxU_-2.js → ListDetail-CtVjrHKB.js} +1 -1
- package/dist/assets/{MarkdownEditor-H1ytN4Tk.js → MarkdownEditor-CbzC-Oqf.js} +1 -1
- package/dist/assets/{McpServers-CYSRCI-j.js → McpServers-DKN75Shn.js} +1 -1
- package/dist/assets/{Memory-zMNpJyRU.js → Memory-B68AJjg2.js} +1 -1
- package/dist/assets/{Panel-RMz3Acpu.js → Panel-C2YTW-qP.js} +1 -1
- package/dist/assets/{Permissions-Be6cIkPg.js → Permissions-Cr0RRvNy.js} +1 -1
- package/dist/assets/{Plugins-B8sTWf2K.js → Plugins-DbZkJs5l.js} +2 -2
- package/dist/assets/{ProvenanceBadge-CO-DZN38.js → ProvenanceBadge-kJ__BOQv.js} +1 -1
- package/dist/assets/{SaveBar-xQZ2EBOn.js → SaveBar-87ZJfscX.js} +1 -1
- package/dist/assets/{Scheduler-CF3zsJBw.js → Scheduler-DdEaxku8.js} +1 -1
- package/dist/assets/{ScopeSwitcher-BAfquN6Y.js → ScopeSwitcher-CKzpqjJ_.js} +1 -1
- package/dist/assets/{Settings-uPjmqutA.js → Settings-CjywVsNB.js} +1 -1
- package/dist/assets/{SkillReferenceGraph-BI1YtCbB.js → SkillReferenceGraph-D_-wg4r9.js} +1 -1
- package/dist/assets/{Skills-BEy9FDYd.js → Skills-Cd_yPO_l.js} +1 -1
- package/dist/assets/{SystemPrompt-BYkkw-LL.js → SystemPrompt-2z6eGPSF.js} +1 -1
- package/dist/assets/{TagLibrary-Kg3yOC5b.js → TagLibrary-DcaWQSp4.js} +1 -1
- package/dist/assets/{TiptapBody-BptXAVR9.js → TiptapBody-DYXIp8aG.js} +1 -1
- package/dist/assets/{Toggle-BwiClCSI.js → Toggle-CDXR5l3m.js} +1 -1
- package/dist/assets/{index-a2MVk8qh.js → index-D5H_H5wC.js} +3 -3
- package/dist/assets/{settingsSchema-DgxlStF5.js → settingsSchema-JRDfjgHb.js} +1 -1
- package/dist/index.html +1 -1
- package/package.json +1 -1
- package/src/main/__tests__/pty-epic-worktree-spawn-cwd.test.cjs +75 -10
- package/src/main/build-info.json +4 -4
- package/src/main/pty.cjs +16 -2
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-D5H_H5wC.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-D3P_jldk.css">
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claude-code-session-manager",
|
|
3
|
-
"version": "0.92.
|
|
3
|
+
"version": "0.92.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",
|
|
@@ -175,22 +175,87 @@ test('spawn() opens a merged-worktree Epic in the project cwd (dead worktree dir
|
|
|
175
175
|
manager.kill(sessionId);
|
|
176
176
|
});
|
|
177
177
|
|
|
178
|
-
|
|
179
|
-
const mainCwd = fs.mkdtempSync(path.join(tmpHome, 'sm-pty-closed-'));
|
|
180
|
-
const sessionId = 'epic-session-closed';
|
|
181
|
-
writeActiveIndexWithWorktree(mainCwd, { epicId: 'epic-c', claudeSessionId: sessionId, worktreeDir: mainCwd });
|
|
178
|
+
function setEpic(mainCwd, epicId, patch) {
|
|
182
179
|
const idxPath = path.join(mainCwd, 'session-manager-operations', 'prompt-sessions', 'active-index.json');
|
|
183
180
|
const idx = JSON.parse(fs.readFileSync(idxPath, 'utf8'));
|
|
184
|
-
idx.sessions[
|
|
181
|
+
const rec = idx.sessions[epicId];
|
|
182
|
+
if (patch.status) rec.status = patch.status;
|
|
183
|
+
if (patch.worktreeStatus) rec.worktree.status = patch.worktreeStatus;
|
|
185
184
|
fs.writeFileSync(idxPath, JSON.stringify(idx));
|
|
185
|
+
}
|
|
186
186
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
187
|
+
function captureSpawn() {
|
|
188
|
+
const out = { opts: null, count: 0 };
|
|
189
|
+
nodePty.spawn = (shell, args, opts) => {
|
|
190
|
+
out.opts = opts;
|
|
191
|
+
out.count += 1;
|
|
190
192
|
return fakeProc();
|
|
191
193
|
};
|
|
194
|
+
return out;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
function fakeWindow(sent) {
|
|
198
|
+
return {
|
|
199
|
+
isDestroyed: () => false,
|
|
200
|
+
webContents: { isDestroyed: () => false, isCrashed: () => false, send: (channel, payload) => sent.push({ channel, payload }) },
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
test('spawn() opens a completed Epic (merged worktree) as a shell in the project cwd with an informational line', () => {
|
|
205
|
+
const mainCwd = fs.mkdtempSync(path.join(tmpHome, 'sm-pty-closed-'));
|
|
206
|
+
const sessionId = 'epic-session-closed';
|
|
207
|
+
const deadDir = path.join(os.tmpdir(), 'sm-pty-closed-dead-worktree');
|
|
208
|
+
writeActiveIndexWithWorktree(mainCwd, { epicId: 'epic-c', claudeSessionId: sessionId, worktreeDir: deadDir });
|
|
209
|
+
setEpic(mainCwd, 'epic-c', { status: 'completed', worktreeStatus: 'merged' });
|
|
210
|
+
|
|
211
|
+
const cap = captureSpawn();
|
|
212
|
+
const sent = [];
|
|
213
|
+
manager.window = fakeWindow(sent);
|
|
214
|
+
try {
|
|
215
|
+
const result = manager.spawn({ tabId: sessionId, cwd: mainCwd, cols: 80, rows: 24 });
|
|
216
|
+
expect(cap.count).toBe(1);
|
|
217
|
+
expect(cap.opts.cwd).toBe(mainCwd);
|
|
218
|
+
expect(result.pid).toBe(4242);
|
|
219
|
+
expect(result.error).toBeUndefined();
|
|
220
|
+
const info = sent.find((m) => m.channel === `pty:data:${sessionId}`);
|
|
221
|
+
expect(info.payload).toMatch(/Epic is completed/);
|
|
222
|
+
expect(info.payload).toContain(mainCwd);
|
|
223
|
+
} finally {
|
|
224
|
+
manager.window = null;
|
|
225
|
+
manager.kill(sessionId);
|
|
226
|
+
}
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
test('spawn() opens a completed Epic whose worktree dir still exists in that worktree dir', () => {
|
|
230
|
+
const mainCwd = fs.mkdtempSync(path.join(tmpHome, 'sm-pty-closed-live-'));
|
|
231
|
+
const worktreeCwd = fs.mkdtempSync(path.join(os.tmpdir(), 'sm-pty-closed-live-wt-'));
|
|
232
|
+
const sessionId = 'epic-session-closed-live';
|
|
233
|
+
writeActiveIndexWithWorktree(mainCwd, { epicId: 'epic-cl', claudeSessionId: sessionId, worktreeDir: worktreeCwd });
|
|
234
|
+
setEpic(mainCwd, 'epic-cl', { status: 'completed' });
|
|
235
|
+
|
|
236
|
+
const cap = captureSpawn();
|
|
237
|
+
manager.spawn({ tabId: sessionId, cwd: mainCwd, cols: 80, rows: 24 });
|
|
238
|
+
expect(cap.count).toBe(1);
|
|
239
|
+
expect(cap.opts.cwd).toBe(worktreeCwd);
|
|
240
|
+
manager.kill(sessionId);
|
|
241
|
+
fs.rmSync(worktreeCwd, { recursive: true, force: true });
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
test('spawn() still refuses session_unreachable without any node-pty spawn', () => {
|
|
245
|
+
const mainCwd = fs.mkdtempSync(path.join(tmpHome, 'sm-pty-unreachable-'));
|
|
246
|
+
const sessionId = 'epic-session-unreachable';
|
|
247
|
+
const deadDir = path.join(os.tmpdir(), 'sm-pty-unreachable-dead-worktree');
|
|
248
|
+
writeActiveIndexWithWorktree(mainCwd, { epicId: 'epic-u', claudeSessionId: sessionId, worktreeDir: deadDir });
|
|
249
|
+
setEpic(mainCwd, 'epic-u', { worktreeStatus: 'merged' });
|
|
250
|
+
// Transcript exists only under the dead worktree's encoded project dir.
|
|
251
|
+
const enc = deadDir.replace(/[^a-zA-Z0-9]/g, '-');
|
|
252
|
+
const tdir = path.join(tmpHome, '.claude', 'projects', enc);
|
|
253
|
+
fs.mkdirSync(tdir, { recursive: true });
|
|
254
|
+
fs.writeFileSync(path.join(tdir, `${sessionId}.jsonl`), '{"type":"user"}\n');
|
|
255
|
+
|
|
256
|
+
const cap = captureSpawn();
|
|
192
257
|
const result = manager.spawn({ tabId: sessionId, cwd: mainCwd, cols: 80, rows: 24 });
|
|
193
|
-
expect(
|
|
258
|
+
expect(cap.count).toBe(0);
|
|
194
259
|
expect(result.pid).toBeNull();
|
|
195
|
-
expect(result.error).toMatch(/
|
|
260
|
+
expect(result.error).toMatch(/cannot be resumed/);
|
|
196
261
|
});
|
package/src/main/build-info.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "0.92.
|
|
3
|
-
"gitSha": "
|
|
4
|
-
"gitShortSha": "
|
|
2
|
+
"version": "0.92.1",
|
|
3
|
+
"gitSha": "cc3a80b2b9933e408e8effca99e550e3409b4d42",
|
|
4
|
+
"gitShortSha": "cc3a80b",
|
|
5
5
|
"gitBranch": "HEAD",
|
|
6
|
-
"builtAt": "2026-09-
|
|
6
|
+
"builtAt": "2026-09-20T22:38:10.510Z",
|
|
7
7
|
"dirty": false
|
|
8
8
|
}
|
package/src/main/pty.cjs
CHANGED
|
@@ -23,6 +23,7 @@ const { checkInsideHome } = require('./lib/insideHome.cjs');
|
|
|
23
23
|
const { sendIfAlive } = require('./lib/sendToRenderer.cjs');
|
|
24
24
|
const opsErrorLog = require('./lib/opsErrorLog.cjs');
|
|
25
25
|
const { planEpicSpawn } = require('./lib/epicSpawnPlan.cjs');
|
|
26
|
+
const { resolveEpicSpawnCwd } = require('./lib/epicSpawnCwd.cjs');
|
|
26
27
|
const telemetryCounters = require('./lib/telemetryCounters.cjs');
|
|
27
28
|
|
|
28
29
|
// Absolute path to the installed package root (src/main/ -> ../../), shown in
|
|
@@ -180,8 +181,22 @@ class PtyManager {
|
|
|
180
181
|
// so this asks the same decision function chatRunner.cjs does (epicSpawnPlan.cjs). `cwd`
|
|
181
182
|
// itself is NEVER repointed; only this actual PTY spawn option is (epicSpawnCwd.cjs's
|
|
182
183
|
// ops-root hazard). A refused plan is surfaced in-tab rather than spawning into a bad cwd.
|
|
184
|
+
// Split: Chat and Terminal share this one verdict function, but the Terminal additionally
|
|
185
|
+
// downgrades `epic_closed` to a plain shell — it opens `$SHELL -il` (no --resume, no
|
|
186
|
+
// transcript write), not a chat turn. That path uses the read-side resolver WITHOUT
|
|
187
|
+
// `deps.restore`, so opening a closed Epic's terminal never re-attaches a checkout.
|
|
183
188
|
const plan = planEpicSpawn({ cwd, claudeSessionId: tabId });
|
|
184
|
-
|
|
189
|
+
let spawnCwd;
|
|
190
|
+
if (plan.ok) {
|
|
191
|
+
spawnCwd = plan.execCwd;
|
|
192
|
+
} else if (plan.code === 'epic_closed') {
|
|
193
|
+
spawnCwd = resolveEpicSpawnCwd({ cwd, claudeSessionId: tabId });
|
|
194
|
+
sendIfAlive(
|
|
195
|
+
this.window,
|
|
196
|
+
`pty:data:${tabId}`,
|
|
197
|
+
`\r\nThis Epic is completed \u2014 opening a read-only shell in ${spawnCwd}. Start a new Epic for new work.\r\n`,
|
|
198
|
+
);
|
|
199
|
+
} else {
|
|
185
200
|
opsErrorLog.appendError({
|
|
186
201
|
cwd,
|
|
187
202
|
scope: 'pty',
|
|
@@ -193,7 +208,6 @@ class PtyManager {
|
|
|
193
208
|
setImmediate(() => sendIfAlive(this.window, `pty:exit:${tabId}`, { exitCode: 1, signal: undefined }));
|
|
194
209
|
return { pid: null, cwd, reattached: false, error: plan.message };
|
|
195
210
|
}
|
|
196
|
-
const spawnCwd = plan.execCwd;
|
|
197
211
|
if (spawnCwd !== cwd) {
|
|
198
212
|
console.log(`[pty] tabId=${tabId} isolated in worktree ${spawnCwd}`);
|
|
199
213
|
}
|