claude-code-session-manager 0.88.0 → 0.88.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.
Files changed (33) hide show
  1. package/dist/assets/{AgentLibrary-BZ6IY2g1.js → AgentLibrary-kQF8_wAw.js} +1 -1
  2. package/dist/assets/{DataModel-dAoGAODX.js → DataModel-CY_mMnsr.js} +1 -1
  3. package/dist/assets/{History-CF157HyX.js → History-Sji2brb0.js} +1 -1
  4. package/dist/assets/{Hooks-CCPPipBk.js → Hooks-CtqpO-p5.js} +1 -1
  5. package/dist/assets/{HostBilko-Dlmz2m06.js → HostBilko-DN0ydOCr.js} +1 -1
  6. package/dist/assets/{Library-BaXcboWX.js → Library-CoiLZTKh.js} +1 -1
  7. package/dist/assets/{ListDetail-CoUCzG5S.js → ListDetail-CmqiQ_io.js} +1 -1
  8. package/dist/assets/{MarkdownEditor-DTg3JlOT.js → MarkdownEditor-HPoNeKhT.js} +1 -1
  9. package/dist/assets/{McpServers-DdjY1sA3.js → McpServers-Bj1X3_NT.js} +1 -1
  10. package/dist/assets/{Memory-BJ3Jlw0C.js → Memory-1PKjsM3b.js} +1 -1
  11. package/dist/assets/{Panel-ygRSgc41.js → Panel-I1s6ghPo.js} +1 -1
  12. package/dist/assets/{Permissions-m0KAnccF.js → Permissions-BhiiZjHQ.js} +1 -1
  13. package/dist/assets/{Plugins-CJRl6B3Q.js → Plugins-gv3_fkPX.js} +2 -2
  14. package/dist/assets/{ProvenanceBadge-w8cnlVvW.js → ProvenanceBadge-BGcgBd8V.js} +1 -1
  15. package/dist/assets/{SaveBar-BpyPcUIk.js → SaveBar-BGr9eZ4g.js} +1 -1
  16. package/dist/assets/{Scheduler-D2ppxSfy.js → Scheduler-Bdo_awPq.js} +1 -1
  17. package/dist/assets/{ScopeSwitcher-EyuIwt-j.js → ScopeSwitcher-ukl0qWaA.js} +1 -1
  18. package/dist/assets/{Settings-BHSGGFEc.js → Settings-DJkFj22G.js} +1 -1
  19. package/dist/assets/{SkillReferenceGraph-BNaZ2JCF.js → SkillReferenceGraph-0t1Qblp6.js} +1 -1
  20. package/dist/assets/{Skills-Dl3q75QV.js → Skills-CeVdBGE-.js} +1 -1
  21. package/dist/assets/{SystemPrompt-Bg6F-Cyp.js → SystemPrompt-CfKvNHi3.js} +1 -1
  22. package/dist/assets/{TagLibrary-BSutflCy.js → TagLibrary-DAzNaUPd.js} +1 -1
  23. package/dist/assets/{TiptapBody-CXih4xrc.js → TiptapBody--S_rGXoA.js} +1 -1
  24. package/dist/assets/{Toggle-2qtUHhA9.js → Toggle-Dszm0xnp.js} +1 -1
  25. package/dist/assets/{index-B3emy2oI.js → index-Dp4Rc--q.js} +3 -3
  26. package/dist/assets/{settingsSchema-CdzxaqwM.js → settingsSchema-BLbssMYo.js} +1 -1
  27. package/dist/index.html +1 -1
  28. package/package.json +1 -1
  29. package/src/main/__tests__/health-tick-liveness.test.cjs +16 -0
  30. package/src/main/__tests__/scheduler-stall-per-project.test.cjs +14 -0
  31. package/src/main/build-info.json +4 -4
  32. package/src/main/health.cjs +9 -1
  33. package/src/main/scheduler.cjs +9 -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-B3emy2oI.js"></script>
10
+ <script type="module" crossorigin src="./assets/index-Dp4Rc--q.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-BHTX4OTc.css">
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-code-session-manager",
3
- "version": "0.88.0",
3
+ "version": "0.88.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",
@@ -149,3 +149,19 @@ test('readFreshHeartbeat: returns the parsed last line when fresh', () => {
149
149
  fs.rmSync(dir, { recursive: true, force: true });
150
150
  }
151
151
  });
152
+
153
+ test('no false positive: pending rows blocked on an unfinished dependsOn are waiting on a job, not a tick', () => {
154
+ const result = evaluateTickLiveness(
155
+ baseQueue({
156
+ jobs: [
157
+ { slug: '265-head', status: 'running' },
158
+ { slug: '266-next', status: 'pending', dependsOn: ['265-head'] },
159
+ ],
160
+ }),
161
+ { ts: NOW - 30_000, utilization: 0 },
162
+ NOW,
163
+ 1,
164
+ );
165
+ assert.strictEqual(result.stalled, false);
166
+ assert.strictEqual(result.reason, 'no-pending-jobs');
167
+ });
@@ -110,3 +110,17 @@ test('findStaleQuarantinedJobs: fires past threshold, not before', () => {
110
110
  assert.deepStrictEqual(slugs, ['exactly-at-threshold', 'stale']);
111
111
  assert.strictEqual(stale.find((s) => s.slug === 'stale').cwd, '/p1');
112
112
  });
113
+
114
+ test('computeStallSummary: a fully drained project (all completed) is idle, not stalled', () => {
115
+ const state = {
116
+ paused: null,
117
+ jobs: [
118
+ { slug: 'a', cwd: '/p1', status: 'completed' },
119
+ { slug: 'b', cwd: '/p2', status: 'running' },
120
+ ],
121
+ invalidJobs: [],
122
+ };
123
+ const summary = computeStallSummary(state);
124
+ assert.strictEqual(summary.byProject['/p1'].stalled, false);
125
+ assert.strictEqual(computeStallSummary({ paused: null, jobs: [{ slug: 'a', cwd: '/p1', status: 'completed' }], invalidJobs: [] }).stalled, false);
126
+ });
@@ -1,8 +1,8 @@
1
1
  {
2
- "version": "0.88.0",
3
- "gitSha": "8b723b765b5cb9f867b2ae244011b54ab3c81ef0",
4
- "gitShortSha": "8b723b7",
2
+ "version": "0.88.1",
3
+ "gitSha": "03a101e69f411db8f3e5dfb465cb92a77365f32a",
4
+ "gitShortSha": "03a101e",
5
5
  "gitBranch": "HEAD",
6
- "builtAt": "2026-09-19T14:31:26.080Z",
6
+ "builtAt": "2026-09-19T14:48:43.339Z",
7
7
  "dirty": false
8
8
  }
@@ -88,7 +88,15 @@ function readFreshHeartbeat(heartbeatPath) {
88
88
  // now) so it's testable without touching the filesystem.
89
89
  function evaluateTickLiveness(queueState, heartbeat, now, runningCount) {
90
90
  const jobs = queueState.jobs || [];
91
- const pending = jobs.filter((j) => j.status === 'pending');
91
+ // A pending row whose dependsOn names a job that has not completed cannot be
92
+ // dispatched yet — it is waiting on that job, not on a tick. Counting it made
93
+ // a healthy dependsOn chain (one running, the rest waiting) read as a stall.
94
+ const statusBySlug = new Map(jobs.map((j) => [j.slug, j.status]));
95
+ const depsSatisfied = (j) => (j.dependsOn || []).every((d) => {
96
+ const st = statusBySlug.get(d);
97
+ return st === undefined || st === 'completed' || st === 'skipped';
98
+ });
99
+ const pending = jobs.filter((j) => j.status === 'pending' && depsSatisfied(j));
92
100
  const running = runningCount ?? jobs.filter((j) => j.status === 'running').length;
93
101
  const config = queueState.config || {};
94
102
  // The scheduler's private concurrencyCap is retired — the machine-wide
@@ -1865,7 +1865,12 @@ function computeStallSummary(state) {
1865
1865
  byProject[key].invalid = (byProject[key].invalid || 0) + 1;
1866
1866
  }
1867
1867
  const total = jobs.length + invalidJobs.length;
1868
- const stalled = total > 0 && running === 0 && pending === 0 && !state?.paused;
1868
+ // A drained queue (every row completed/skipped) is idle, not stalled — a
1869
+ // stall needs at least one parked problem row (failed/needs_review/
1870
+ // quarantined/invalid) that is waiting on someone.
1871
+ const isProblemStatus = (st) => st !== 'completed' && st !== 'skipped';
1872
+ const stalled = total > 0 && running === 0 && pending === 0 && !state?.paused
1873
+ && (invalidJobs.length > 0 || jobs.some((j) => isProblemStatus(j.status)));
1869
1874
  for (const key of Object.keys(byProject)) {
1870
1875
  const counts = byProject[key];
1871
1876
  const projRunning = counts.running || 0;
@@ -1873,7 +1878,9 @@ function computeStallSummary(state) {
1873
1878
  const projTotal = Object.keys(counts)
1874
1879
  .filter((k) => k !== 'stalled')
1875
1880
  .reduce((sum, k) => sum + counts[k], 0);
1876
- counts.stalled = projTotal > 0 && projRunning === 0 && projPending === 0 && !state?.paused;
1881
+ const projProblem = Object.keys(counts)
1882
+ .some((k) => k !== 'stalled' && k !== 'completed' && k !== 'skipped' && counts[k] > 0);
1883
+ counts.stalled = projTotal > 0 && projRunning === 0 && projPending === 0 && !state?.paused && projProblem;
1877
1884
  }
1878
1885
  return { stalled, total, running, pending, byProject };
1879
1886
  }