claude-code-session-manager 0.35.14 → 0.35.16
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/{TiptapBody-o1hQcwtq.js → TiptapBody-BD2BTBY-.js} +1 -1
- package/dist/assets/{index-B39Qiq0n.css → index-BwFHVuSk.css} +1 -1
- package/dist/assets/{index-DqWSSMkp.js → index-C6ep3yfh.js} +180 -180
- package/dist/index.html +2 -2
- package/package.json +2 -1
- package/plugins/session-manager-dev/skills/process-feedback/SKILL.md +52 -6
- package/src/main/__tests__/scheduler-force-tick-outcome.test.cjs +46 -0
- package/src/main/scheduler.cjs +21 -5
package/dist/index.html
CHANGED
|
@@ -7,10 +7,10 @@
|
|
|
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-C6ep3yfh.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
|
-
<link rel="stylesheet" crossorigin href="./assets/index-
|
|
13
|
+
<link rel="stylesheet" crossorigin href="./assets/index-BwFHVuSk.css">
|
|
14
14
|
</head>
|
|
15
15
|
<body class="bg-bg text-fg font-sans antialiased">
|
|
16
16
|
<div id="root"></div>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claude-code-session-manager",
|
|
3
|
-
"version": "0.35.
|
|
3
|
+
"version": "0.35.16",
|
|
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",
|
|
@@ -120,6 +120,7 @@
|
|
|
120
120
|
"@vitejs/plugin-react": "^4.3.3",
|
|
121
121
|
"autoprefixer": "^10.4.20",
|
|
122
122
|
"concurrently": "^9.1.0",
|
|
123
|
+
"jsdom": "^29.1.1",
|
|
123
124
|
"postcss": "^8.4.49",
|
|
124
125
|
"react": "^18.3.1",
|
|
125
126
|
"react-dom": "^18.3.1",
|
|
@@ -1,16 +1,20 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: process-feedback
|
|
3
3
|
description: >-
|
|
4
|
-
Process the current project's inbound feedback folder end-to-end:
|
|
5
|
-
open
|
|
4
|
+
Process the current project's inbound feedback folder end-to-end: first sync
|
|
5
|
+
any open GitHub issues on the project's repo into the intake as new feedback
|
|
6
|
+
files (deduped against already-tracked issues), then read every open item,
|
|
7
|
+
evaluate it against real code, and for work that belongs to this
|
|
6
8
|
project queue it as scheduled PRDs via /develop (never implement inline),
|
|
7
9
|
archive the item as processed **the moment it is queued** (the scheduler owns
|
|
8
10
|
execution from there), and fold lessons back into the folder's README so
|
|
9
|
-
future feedback (written by other agents/projects
|
|
11
|
+
future feedback (written by other agents/projects, or GitHub issue filers)
|
|
12
|
+
gets better. Use whenever the user says "/process-feedback",
|
|
10
13
|
"review the feedback folder", "work through the feedback", "any open
|
|
11
|
-
feedback?",
|
|
14
|
+
feedback?", "check the GitHub issues", "triage the GitHub repo", or drops
|
|
15
|
+
new files into session-manager-operations/feedback/.
|
|
12
16
|
Keywords: feedback, intake, cross-project requests, process feedback, triage
|
|
13
|
-
feedback.
|
|
17
|
+
feedback, GitHub issues.
|
|
14
18
|
model: opus
|
|
15
19
|
---
|
|
16
20
|
|
|
@@ -68,7 +72,49 @@ Before anything else, check for the pre-migration layout: if a legacy
|
|
|
68
72
|
`git mv feedback session-manager-operations/feedback` when the repo is
|
|
69
73
|
git-tracked, else a plain `mv`. This converges any project this skill runs in
|
|
70
74
|
to the new layout even if its bulk-relocation PRD hasn't landed yet. Only then
|
|
71
|
-
continue with step
|
|
75
|
+
continue with step 0b.
|
|
76
|
+
|
|
77
|
+
### 0b. Sync open GitHub issues into the intake (source sync)
|
|
78
|
+
|
|
79
|
+
For a project with a GitHub remote, pull open issues and materialize any not already tracked as a
|
|
80
|
+
new feedback file — **before** the quick-exit check below evaluates what's open, since this step
|
|
81
|
+
can create new open items that check needs to see. This lets a repo's GitHub issue tracker feed
|
|
82
|
+
the same triage → queue pipeline without a human manually copying issues into the feedback folder.
|
|
83
|
+
|
|
84
|
+
- Skip silently (no error, no report) if `gh repo view --json nameWithOwner -q .nameWithOwner`
|
|
85
|
+
fails — no GitHub remote or `gh` unauthenticated. This step is opportunistic, not required.
|
|
86
|
+
- Fetch open issues via `gh api repos/<owner>/<repo>/issues --paginate --jq '...'` (per-issue
|
|
87
|
+
`number,title,body,labels`) rather than `gh issue list`/`gh issue view` — the latter two can hit
|
|
88
|
+
a known `gh` CLI bug on repos with legacy GitHub Projects (classic) boards
|
|
89
|
+
(`GraphQL: Projects (classic) is being deprecated ... (repository.issue.projectCards)`,
|
|
90
|
+
documented in `standards.md`'s Execution discipline section); `gh api` sidesteps it outright by
|
|
91
|
+
never touching the deprecated field. Note `gh api .../issues` also returns pull requests (they
|
|
92
|
+
have an `issue.pull_request` key) — filter those out, this step is issues only.
|
|
93
|
+
- **Dedup before creating anything.** An issue already tracked has a feedback file (open OR
|
|
94
|
+
already in `processed/`) containing the token `gh-issue-<N>` — `grep -rl "gh-issue-<N>"
|
|
95
|
+
session-manager-operations/feedback/` (recursive, covers both the inbox and `processed/`) before
|
|
96
|
+
materializing issue `N`. Skip any issue that already has a match.
|
|
97
|
+
- For each untracked open issue, create
|
|
98
|
+
`session-manager-operations/feedback/<yyyy-mm-dd>-gh-issue-<N>-<slug>.md` following the folder's
|
|
99
|
+
normal frontmatter convention (see `README.md`):
|
|
100
|
+
- `title`: the issue title, verbatim.
|
|
101
|
+
- `source`: `GitHub issue gh-issue-<N> (<issue URL>)` — the `gh-issue-<N>` token here is what
|
|
102
|
+
the dedup grep matches on next run; don't reword it out.
|
|
103
|
+
- `type`: infer from labels (`bug` label → `bug`; `enhancement`/no matching label → `enhancement`;
|
|
104
|
+
`security` → `security`; `performance` → `performance`).
|
|
105
|
+
- `severity`: `normal` unless the issue's own labels/title clearly indicate `blocker`/`high`
|
|
106
|
+
(data loss, crash, security hole, or an unusable feature with no workaround — same bar as the
|
|
107
|
+
README's own severity guidance; don't inflate).
|
|
108
|
+
- Body: `# What happens / what's missing` = the issue body (verbatim, or a faithful trim if very
|
|
109
|
+
long — don't paraphrase away specifics); `# Evidence` = the issue URL + number, plus any
|
|
110
|
+
screenshot/repro references already in the issue body; `# Suggested direction (optional)` =
|
|
111
|
+
any proposed-solution section already present in the issue body.
|
|
112
|
+
- Synced files then flow through steps 1–6 below exactly like any other feedback file — no
|
|
113
|
+
special-casing after creation, same evaluate → queue → disposition → archive pipeline.
|
|
114
|
+
- When step 4 dispositions a synced issue by queuing a PRD, the `## RESOLUTION` must also state
|
|
115
|
+
the originating issue number/URL. This skill does **not** auto-close or auto-comment on the
|
|
116
|
+
GitHub issue — that stays a human (or a later, deliberate step) decision once the fix actually
|
|
117
|
+
ships, not something this sync step does on queue.
|
|
72
118
|
|
|
73
119
|
### 0. Quick-exit — bail in milliseconds if there's nothing to do
|
|
74
120
|
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* scheduler-force-tick-outcome.test.cjs — unit tests for forceTickOutcome().
|
|
3
|
+
*
|
|
4
|
+
* Covers the "No pending jobs" vs "Already running" ambiguity: when
|
|
5
|
+
* pickNextBatch() returns an empty batch with no hold reason, that's only
|
|
6
|
+
* genuinely "no pending jobs" if nothing is running either — if a job
|
|
7
|
+
* already transitioned to running (e.g. the when-available poll won a race
|
|
8
|
+
* against a manual force-tick click), the message must say so instead of
|
|
9
|
+
* flatly claiming nothing is pending.
|
|
10
|
+
*
|
|
11
|
+
* Run: timeout 120 node --test src/main/__tests__/scheduler-force-tick-outcome.test.cjs
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
'use strict';
|
|
15
|
+
|
|
16
|
+
const { test } = require('node:test');
|
|
17
|
+
const assert = require('node:assert/strict');
|
|
18
|
+
const { forceTickOutcome } = require('../scheduler.cjs');
|
|
19
|
+
|
|
20
|
+
test('forceTickOutcome: null result → No pending jobs', () => {
|
|
21
|
+
const outcome = forceTickOutcome(null);
|
|
22
|
+
assert.strictEqual(outcome.message, 'No pending jobs');
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
test('forceTickOutcome: fired → Fired N job(s)', () => {
|
|
26
|
+
const outcome = forceTickOutcome({ fired: true, count: 2, group: 0 });
|
|
27
|
+
assert.strictEqual(outcome.message, 'Fired 2 job(s) (group g0)');
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
test('forceTickOutcome: reason=drained (genuinely nothing pending or running) → No pending jobs', () => {
|
|
31
|
+
const outcome = forceTickOutcome({ fired: false, reason: 'drained' });
|
|
32
|
+
assert.strictEqual(outcome.message, 'No pending jobs');
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
test('forceTickOutcome: reason=already-running (race: poll fired it first) → Already running message, not "No pending jobs"', () => {
|
|
36
|
+
const outcome = forceTickOutcome({ fired: false, reason: 'already-running', runningCount: 1 });
|
|
37
|
+
assert.strictEqual(outcome.message, 'Already running — 1 job(s) in flight');
|
|
38
|
+
assert.notStrictEqual(outcome.message, 'No pending jobs');
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
test('forceTickOutcome: reason=held → detail message surfaces', () => {
|
|
42
|
+
const outcome = forceTickOutcome({ fired: false, reason: 'held', detail: '[scheduler] concurrency [/a]: cap reached, no slots' });
|
|
43
|
+
assert.strictEqual(outcome.message, 'cap reached, no slots');
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
console.log('scheduler-force-tick-outcome tests: PASS');
|
package/src/main/scheduler.cjs
CHANGED
|
@@ -1697,6 +1697,20 @@ function tickQueue() {
|
|
|
1697
1697
|
console.log(`[scheduler] dod-drain: ${err?.message ?? String(err)}`);
|
|
1698
1698
|
});
|
|
1699
1699
|
if (holdReason) return { fired: false, reason: 'held', detail: holdReason };
|
|
1700
|
+
// Distinguish "genuinely nothing to do" from "the batch was already
|
|
1701
|
+
// fired by a concurrent tickQueue() call" (e.g. the periodic
|
|
1702
|
+
// when-available poll winning a race against a manual force-tick —
|
|
1703
|
+
// pickNextBatch correctly returns reason:null in both cases since
|
|
1704
|
+
// neither is a hold; the caller-facing message needs to tell them
|
|
1705
|
+
// apart so "No pending jobs" doesn't read as wrong next to a job
|
|
1706
|
+
// that is visibly running).
|
|
1707
|
+
const runningCount = Math.max(
|
|
1708
|
+
runningSet.size,
|
|
1709
|
+
state.jobs.filter((j) => j.status === 'running').length,
|
|
1710
|
+
);
|
|
1711
|
+
if (runningCount > 0) {
|
|
1712
|
+
return { fired: false, reason: 'already-running', runningCount };
|
|
1713
|
+
}
|
|
1700
1714
|
return { fired: false, reason: 'drained' };
|
|
1701
1715
|
}
|
|
1702
1716
|
|
|
@@ -1748,6 +1762,8 @@ function forceTickOutcome(result) {
|
|
|
1748
1762
|
switch (result.reason) {
|
|
1749
1763
|
case 'drained':
|
|
1750
1764
|
return { ok: true, kind: 'info', message: 'No pending jobs' };
|
|
1765
|
+
case 'already-running':
|
|
1766
|
+
return { ok: true, kind: 'info', message: `Already running — ${result.runningCount} job(s) in flight` };
|
|
1751
1767
|
case 'paused':
|
|
1752
1768
|
return { ok: true, kind: 'warn', message: 'Scheduler is paused' };
|
|
1753
1769
|
case 'cancelled':
|
|
@@ -2403,10 +2419,10 @@ function registerScheduleHandlers() {
|
|
|
2403
2419
|
});
|
|
2404
2420
|
await broadcast();
|
|
2405
2421
|
let message;
|
|
2406
|
-
if (added === 0 && removed === 0) message = 'Rescanned — no
|
|
2407
|
-
else if (added > 0 && removed === 0) message = `Rescanned — ${added} new PRD(s) picked up`;
|
|
2408
|
-
else if (added === 0 && removed > 0) message = `Rescanned — ${removed} PRD(s) removed from disk`;
|
|
2409
|
-
else message = `Rescanned — ${added} new PRD(s) picked up, ${removed} removed from disk`;
|
|
2422
|
+
if (added === 0 && removed === 0) message = 'Rescanned — no new/removed PRD files';
|
|
2423
|
+
else if (added > 0 && removed === 0) message = `Rescanned — ${added} new PRD file(s) picked up`;
|
|
2424
|
+
else if (added === 0 && removed > 0) message = `Rescanned — ${removed} PRD file(s) removed from disk`;
|
|
2425
|
+
else message = `Rescanned — ${added} new PRD file(s) picked up, ${removed} removed from disk`;
|
|
2410
2426
|
return { ok: true, kind: 'info', message };
|
|
2411
2427
|
});
|
|
2412
2428
|
|
|
@@ -2825,4 +2841,4 @@ const remote = {
|
|
|
2825
2841
|
},
|
|
2826
2842
|
};
|
|
2827
2843
|
|
|
2828
|
-
module.exports = { registerScheduleHandlers, attachWindow, init, ROOT, PRDS_DIR, selectHistoryJobs, parsePorcelain, FINISH_PROTOCOL, remote, pickNextBatch, pickForProject, reapDeadRunningJobs, pollRecoveryClearSource, memoryLimitedBatchSize, availableForJobs, reverifyNeedsReview, isRescanCandidate, isPromotableOriginal, selectAutoFixTargets, isEligibleForImmediateAutoFix, resolveRunId, isUnresolvableNeedsReview, healTargetForFix, buildInvestigationPrompt, committedInWindow, isFixPlanSlug, isFixPlanBeyondDepthCap, MAX_INVESTIGATION_DEPTH };
|
|
2844
|
+
module.exports = { registerScheduleHandlers, attachWindow, init, ROOT, PRDS_DIR, selectHistoryJobs, parsePorcelain, FINISH_PROTOCOL, remote, pickNextBatch, pickForProject, reapDeadRunningJobs, pollRecoveryClearSource, memoryLimitedBatchSize, availableForJobs, reverifyNeedsReview, isRescanCandidate, isPromotableOriginal, selectAutoFixTargets, isEligibleForImmediateAutoFix, resolveRunId, isUnresolvableNeedsReview, healTargetForFix, buildInvestigationPrompt, committedInWindow, isFixPlanSlug, isFixPlanBeyondDepthCap, MAX_INVESTIGATION_DEPTH, forceTickOutcome };
|