claude-code-session-manager 0.35.3 → 0.35.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/assets/{TiptapBody-DmCekkUQ.js → TiptapBody-CUO_twO8.js} +1 -1
- package/dist/assets/index-BHyeZfve.css +32 -0
- package/dist/assets/index-DIVOtBGO.js +3558 -0
- package/dist/index.html +2 -2
- package/package.json +1 -1
- package/plugins/session-manager-dev/skills/develop/SKILL.md +14 -5
- package/plugins/session-manager-dev/skills/develop/standards.md +1 -0
- package/plugins/session-manager-dev/skills/explain-to-me/SKILL.md +48 -35
- package/plugins/session-manager-dev/skills/my-feedback/SKILL.md +20 -16
- package/plugins/session-manager-dev/skills/optimize-kpi/SKILL.md +4 -4
- package/plugins/session-manager-dev/skills/process-feedback/SKILL.md +32 -16
- package/src/main/__tests__/chat-mcp-consent-notice.test.cjs +137 -0
- package/src/main/__tests__/mcpStatus.test.cjs +61 -0
- package/src/main/__tests__/scheduler-autofix-select.test.cjs +50 -2
- package/src/main/__tests__/scheduler-autopromote.test.cjs +19 -1
- package/src/main/__tests__/scheduler-investigation-prompt.test.cjs +51 -0
- package/src/main/browserView.cjs +2 -2
- package/src/main/chatRunner.cjs +218 -21
- package/src/main/index.cjs +19 -0
- package/src/main/ipcSchemas.cjs +7 -0
- package/src/main/mcpStatus.cjs +93 -0
- package/src/main/scheduler.cjs +221 -73
- package/src/preload/api.d.ts +34 -0
- package/src/preload/index.cjs +14 -0
- package/dist/assets/index-CHKMzzCM.js +0 -3558
- package/dist/assets/index-DVqmrWP3.css +0 -32
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-DIVOtBGO.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-BHyeZfve.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.5",
|
|
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",
|
|
@@ -38,10 +38,19 @@ the only step that runs on the cheaper executor.
|
|
|
38
38
|
## Standards (single source of truth)
|
|
39
39
|
|
|
40
40
|
The engineering standards (Performance, Debugging, API reuse / single source of truth, TDD,
|
|
41
|
-
and the executor-facing Execution discipline) live in **`standards.md`** beside this file
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
41
|
+
and the executor-facing Execution discipline) live in **`standards.md`** beside this file, in
|
|
42
|
+
the same skill directory (`.../skills/develop/standards.md` — NOT `~/.claude/skills/develop/`,
|
|
43
|
+
which is a different, non-existent path; resolve it relative to wherever this SKILL.md itself
|
|
44
|
+
was loaded from). **Re-read that file fresh with the Read tool immediately before pasting it
|
|
45
|
+
into each PRD (Phase 1 step 4) — never reuse a copy cached earlier in the same conversation.**
|
|
46
|
+
A long authoring session can span an edit to `standards.md` (including one autonomously applied
|
|
47
|
+
by a prior incident's fix-plan) without the model noticing; pasting a stale in-context copy
|
|
48
|
+
silently ships PRDs missing the latest execution-discipline rules. This is exactly the
|
|
49
|
+
single-source-of-truth violation the standards themselves warn against — don't let it happen to
|
|
50
|
+
the standards block itself. (Incident: PRDs 467/468 authored late in a long session carried a
|
|
51
|
+
standards.md snapshot from before the "You ARE the executor" guard was added earlier that same
|
|
52
|
+
session, so PRD 467's headless run repeated the exact anti-pattern the guard exists to prevent.)
|
|
53
|
+
Never restate or fork its content — one concept, one implementation.
|
|
45
54
|
|
|
46
55
|
For interactive dev work, also apply the `test-driven-development` and `systematic-debugging`
|
|
47
56
|
skills; the headless PRDs get the distilled core from `standards.md` instead, since they
|
|
@@ -195,7 +204,7 @@ single definition of "tracked to done" for both entry paths.
|
|
|
195
204
|
## References (reuse, don't duplicate)
|
|
196
205
|
|
|
197
206
|
- `~/.claude/session-manager/scheduled-plans/PRD_AUTHORING.md` — the §1–§10 safety rules.
|
|
198
|
-
-
|
|
207
|
+
- `standards.md` beside this file — the engineering + execution-discipline rules inlined into every PRD. Re-read fresh each time (see "Standards" above) — don't reuse a cached copy.
|
|
199
208
|
- `test-driven-development`, `systematic-debugging` — interactive dev sessions.
|
|
200
209
|
- `requesting-code-review` — the Phase-2 review gate.
|
|
201
210
|
|
|
@@ -58,6 +58,7 @@ Data-driven from 400+ scheduler runs: long hangs (not bad code) are the dominant
|
|
|
58
58
|
- **Verify before done.** Run the acceptance test command once before declaring success. If it's red, fix it or `exit 1` with the failure — never end the run on a failing test (that trips the verifier's `transcript_errors` downgrade).
|
|
59
59
|
- **Fail loud, fail fast.** On any step failure, print one diagnostic line and `exit 1`; don't swallow with `|| true` or spin in a silent retry. A `rateLimited` exit-1 is the scheduler's benign auto-pause (auto-resumes next window) — not a failure to engineer around.
|
|
60
60
|
- **Stay in the AC.** Do not add work past the acceptance checklist ("while we're here" generators/fixtures are the post-AC-overrun incident). Body must be clean UTF-8 — no NUL/control bytes.
|
|
61
|
+
- **You ARE the executor — never re-queue or self-schedule.** A headless PRD run must perform its own acceptance criteria directly. Do NOT invoke `/develop`, `/process-feedback`, or any queue-authoring skill from inside a run — those are interactive main-loop skills that author a *new* PRD and return, so the run exits 0 having done nothing (no commit, no sentinel → `needs_review` with `no_verdict_sentinel`). Do NOT call `ScheduleWakeup`/set a tracking loop either — the process exits when the run ends and nothing re-invokes it. If the PRD's work looks large, decompose and execute it inline within this run; never delegate it back to the queue. (Incident: PRD 460 invoked `/develop`, spawned a duplicate PRD 461, and exited 0 with no work.)
|
|
61
62
|
- **Negative-assertion checks must exit 0 when clean.** A check that verifies the *absence* of something (a `grep` that should find nothing, "no leftover X", `diff` expecting no change) must return exit 0 on the clean case. A bare `grep` exits **1 on no-match** — so the *success* path surfaces as `is_error=true` and the verifier downgrades a perfect run to `needs_review`. Always invert: `if <detector>; then echo "HALT: <what was found>"; exit 1; fi; echo clean`. Never let the no-match/empty path carry the non-zero exit.
|
|
62
63
|
- **Recover or annotate every error — don't strand a Traceback in the transcript.** The verifier downgrades an otherwise-perfect run to `needs_review` when a `Traceback`/`Error` appears with *no visible recovery within ~10 lines* (the `transcript_errors` heuristic — the single most common false-positive on green deliverables). Two executor habits cause it: (1) **throwaway probes that error** — an inline `python -c` with a quoting/f-string slip, a wrong kwarg, a bad path. When a probe errors, immediately re-run the corrected version *or* print one line `# expected/handled: <why>` right after, so recovery is adjacent. Don't move on leaving a bare error as the last thing in that step. Prefer a small temp `.py` file over a fragile multi-quote `python -c` one-liner (inline f-string errors are the top source of stranded tracebacks). (2) See the timeout rule below.
|
|
63
64
|
- **An *expected* bounded-timeout (exit 124) must be annotated, not bare.** `timeout`-capping a genuinely long task you expect to hit the cap (a full-universe ingest, a long scan) is correct — but a bare `Exit code 124` reads as a failure to the verifier. Wrap it so the cap is a success-with-note: `timeout 120 <cmd> || { rc=$?; [ $rc -eq 124 ] && echo "hit time cap — idempotent/partial, rows persist incrementally; OK" || { echo "HALT: <cmd> failed rc=$rc"; exit 1; }; }`. (Distinguish 124 = expected cap from a real non-zero.) For work that legitimately needs longer than a safe cap, run it in the background and poll a bounded number of times rather than capping the foreground command.
|
|
@@ -1,28 +1,31 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: explain-to-me
|
|
3
3
|
description: >-
|
|
4
|
-
Build and maintain HUMAN_LEARN/ — a human-readable,
|
|
5
|
-
knowledge base at the repo root that explains how this
|
|
6
|
-
Deep-probes the codebase for a given topic/component
|
|
7
|
-
real constants, real on-disk state — never invented),
|
|
8
|
-
clean, self-contained HTML — one combined component
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
human-
|
|
15
|
-
|
|
4
|
+
Build and maintain session-manager-operations/HUMAN_LEARN/ — a human-readable,
|
|
5
|
+
visually-rich HTML knowledge base at the repo root that explains how this
|
|
6
|
+
project actually works. Deep-probes the codebase for a given topic/component
|
|
7
|
+
(real file:line refs, real constants, real on-disk state — never invented),
|
|
8
|
+
then writes or updates a clean, self-contained HTML — one combined component
|
|
9
|
+
page (session-manager-operations/HUMAN_LEARN/index.html) plus a separate
|
|
10
|
+
skill-chain map (session-manager-operations/HUMAN_LEARN/SKILL_MAP.html) for
|
|
11
|
+
the local-dev workflow — with advanced CSS, flow diagrams, sticky nav,
|
|
12
|
+
collapsible tables. Use whenever the user says "/explain-to-me X", "explain X
|
|
13
|
+
to me", "explain how X works", "document X for me", "add X to HUMAN_LEARN",
|
|
14
|
+
"make me a human-readable page for X", or "update the skill map". Keywords:
|
|
15
|
+
explain, explain-to-me, human-learn, HUMAN_LEARN, skill map, document,
|
|
16
|
+
one-pager, how does X work, visual explainer, knowledge base.
|
|
16
17
|
model: opus
|
|
17
18
|
---
|
|
18
19
|
|
|
19
20
|
# explain-to-me
|
|
20
21
|
|
|
21
22
|
Turn a question about this codebase ("explain how the scheduler works") into a
|
|
22
|
-
**clean, visually-rich HTML one-pager** in `HUMAN_LEARN/`
|
|
23
|
-
keep that folder's index coherent as it grows. The output
|
|
24
|
-
catching up on the project — not for the model. It is the
|
|
25
|
-
to the Memory tab (which is Claude's own terse recall
|
|
23
|
+
**clean, visually-rich HTML one-pager** in `session-manager-operations/HUMAN_LEARN/`
|
|
24
|
+
at the repo root, and keep that folder's index coherent as it grows. The output
|
|
25
|
+
is for a *human* catching up on the project — not for the model. It is the
|
|
26
|
+
human-facing complement to the Memory tab (which is Claude's own terse recall
|
|
27
|
+
store). All session-manager per-project operations live under
|
|
28
|
+
`session-manager-operations/`.
|
|
26
29
|
|
|
27
30
|
The bar: **clean, explanatory, visually rich, self-contained** (opens directly in
|
|
28
31
|
a browser — no build, no external network deps). Every claim grounded in the
|
|
@@ -53,26 +56,35 @@ If you can't state it in the present tense without a date or a PRD, cut it.
|
|
|
53
56
|
## Where things live
|
|
54
57
|
|
|
55
58
|
- Repo root: `git rev-parse --show-toplevel` (fall back to cwd if not a git repo).
|
|
56
|
-
- Knowledge base: `<root>/HUMAN_LEARN/`. Create it
|
|
57
|
-
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
59
|
+
- Knowledge base: `<root>/session-manager-operations/HUMAN_LEARN/`. Create it
|
|
60
|
+
(and `session-manager-operations/` if needed) if missing.
|
|
61
|
+
- **One combined page** — `session-manager-operations/HUMAN_LEARN/index.html`.
|
|
62
|
+
Every component explainer is a `<section>` in this single file, under a sticky
|
|
63
|
+
section-nav, ordered so the page reads top-to-bottom as one narrative. A new
|
|
64
|
+
topic becomes a **new `<section>`**, never a new file. One navigable page that
|
|
65
|
+
logically follows itself beats a folder of thin pages; do not re-introduce
|
|
66
|
+
per-topic `.html` files.
|
|
67
|
+
- **The Skill Map** — `session-manager-operations/HUMAN_LEARN/SKILL_MAP.html`, a
|
|
68
|
+
**separate, dedicated** page (NOT a section of index.html) that visualizes the
|
|
69
|
+
*local-development skill chain*: the two intakes (interactive human prompt;
|
|
70
|
+
agent feedback via `/process-feedback`) converging on `/develop`, which owns
|
|
71
|
+
PRD authoring + reads `standards.md`, queues onto the scheduler, and gates with
|
|
72
|
+
review/verify — plus `/my-feedback` outbound. It is the "how I build on this
|
|
73
|
+
project" companion to index.html's "how this project works." index.html links
|
|
74
|
+
to it from the nav; it links back.
|
|
69
75
|
|
|
70
76
|
## Workflow
|
|
71
77
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
78
|
+
0. **Self-migrate a legacy root-level folder first.** If `HUMAN_LEARN/` exists
|
|
79
|
+
at the repo root **and** `session-manager-operations/HUMAN_LEARN/` does not,
|
|
80
|
+
relocate it now — `mkdir -p session-manager-operations` (if needed), then
|
|
81
|
+
`git mv HUMAN_LEARN session-manager-operations/HUMAN_LEARN` when the repo is
|
|
82
|
+
git-tracked, else a plain `mv`. This converges any project this skill runs
|
|
83
|
+
in to the new layout even if its bulk-relocation PRD hasn't landed yet.
|
|
84
|
+
1. **Locate & survey.** Find the repo root + `session-manager-operations/HUMAN_LEARN/`.
|
|
85
|
+
If `index.html` exists, read it to see what's already documented — you are
|
|
86
|
+
*maintaining* a knowledge base, not starting fresh. If the topic already has
|
|
87
|
+
a page/section, you are updating it, not duplicating it.
|
|
76
88
|
2. **Deep-probe the topic.** Read the real code. Gather exact `file:line`
|
|
77
89
|
references, real constant names + values, and real on-disk state where it
|
|
78
90
|
matters (run `ls`/`cat`/`jq` to ground claims in what actually exists). For a
|
|
@@ -98,8 +110,9 @@ If you can't state it in the present tense without a date or a PRD, cut it.
|
|
|
98
110
|
in `~/.claude/skills/`, a changed Role/Never contract, a re-routed step in the
|
|
99
111
|
chain — refresh `SKILL_MAP.html` (its chain diagram + the role-contract table +
|
|
100
112
|
the two walkthroughs) so it stays a faithful map of the actual files on disk.
|
|
101
|
-
7. **Tell the user the path** to open (`HUMAN_LEARN/index.html`
|
|
102
|
-
`HUMAN_LEARN/SKILL_MAP.html`, optionally with a
|
|
113
|
+
7. **Tell the user the path** to open (`session-manager-operations/HUMAN_LEARN/index.html`
|
|
114
|
+
or `session-manager-operations/HUMAN_LEARN/SKILL_MAP.html`, optionally with a
|
|
115
|
+
`#anchor`). Do not commit unless asked.
|
|
103
116
|
|
|
104
117
|
## Grounding rules (non-negotiable)
|
|
105
118
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: my-feedback
|
|
3
3
|
description: >-
|
|
4
|
-
File a feedback
|
|
4
|
+
File a feedback or enhancement request FROM the current project INTO another
|
|
5
5
|
project's inbound feedback folder, following that project's own README
|
|
6
6
|
convention. The cross-project complement of /process-feedback (which works the
|
|
7
7
|
*current* project's inbox). Use whenever the user says "/my-feedback to X",
|
|
@@ -38,21 +38,25 @@ The invocation is `/my-feedback to <<project>>` (or "send feedback to
|
|
|
38
38
|
<project>`."* Then list candidate sibling projects that have an intake folder:
|
|
39
39
|
```bash
|
|
40
40
|
for d in ~/Projects/*/; do
|
|
41
|
-
[ -d "$d/feedback" ] && echo " - $(basename "$d") (feedback/)"
|
|
41
|
+
[ -d "$d/session-manager-operations/feedback" ] && echo " - $(basename "$d") (session-manager-operations/feedback/)"
|
|
42
42
|
done
|
|
43
43
|
```
|
|
44
44
|
and stop.
|
|
45
|
-
- Resolve the path: `~/Projects/<project>/feedback/`.
|
|
46
|
-
canonical folder name — do not accept or invent variants
|
|
47
|
-
`feedback-inbox/`,
|
|
48
|
-
differently-named folder is drift,
|
|
49
|
-
`external-feedback/` existed for ~2.5 weeks
|
|
50
|
-
being merged back into `feedback
|
|
51
|
-
or anywhere else). Fuzzy-match a
|
|
52
|
-
|
|
53
|
-
-
|
|
54
|
-
|
|
55
|
-
|
|
45
|
+
- Resolve the path: `~/Projects/<project>/session-manager-operations/feedback/`.
|
|
46
|
+
This is the **one** canonical folder name — do not accept or invent variants
|
|
47
|
+
(`external-feedback/`, `feedback-inbox/`, a root-level `feedback` folder, etc.) even
|
|
48
|
+
if a near-miss directory exists; a stray differently-named folder is drift,
|
|
49
|
+
not a valid convention (burrow's `external-feedback/` existed for ~2.5 weeks
|
|
50
|
+
from exactly this mistake before being merged back into `feedback` on
|
|
51
|
+
2026-07-10 — don't recreate it, in burrow or anywhere else). Fuzzy-match a
|
|
52
|
+
near miss only to confirm it's actually named
|
|
53
|
+
`session-manager-operations/feedback/`, not to accept a differently-named
|
|
54
|
+
folder as equivalent. All session-manager per-project operations live under
|
|
55
|
+
`session-manager-operations/`.
|
|
56
|
+
- **If the target has no `session-manager-operations/feedback/` folder, STOP.**
|
|
57
|
+
Don't invent one under any name — say the project doesn't accept feedback
|
|
58
|
+
this way and ask how to proceed (it may take requests via issues, a
|
|
59
|
+
different folder, or not at all).
|
|
56
60
|
|
|
57
61
|
## 1. Read the target's README FIRST — it is the authority
|
|
58
62
|
|
|
@@ -61,9 +65,9 @@ unique per project** (file-naming scheme, required sections, the status-log
|
|
|
61
65
|
table, the closing ritual). Read it before writing anything:
|
|
62
66
|
|
|
63
67
|
```bash
|
|
64
|
-
cat ~/Projects/<project>/feedback/README.md
|
|
65
|
-
ls ~/Projects/<project>/feedback/ # open items + numbering
|
|
66
|
-
ls ~/Projects/<project>/feedback/processed/ 2>/dev/null # closed examples to match
|
|
68
|
+
cat ~/Projects/<project>/session-manager-operations/feedback/README.md
|
|
69
|
+
ls ~/Projects/<project>/session-manager-operations/feedback/ # open items + numbering
|
|
70
|
+
ls ~/Projects/<project>/session-manager-operations/feedback/processed/ 2>/dev/null # closed examples to match
|
|
67
71
|
```
|
|
68
72
|
|
|
69
73
|
Read one **processed** example end-to-end to copy the house format exactly — the
|
|
@@ -62,9 +62,9 @@ files it points to:
|
|
|
62
62
|
all, that itself is a finding — recommend adding it.
|
|
63
63
|
- **The levers** — the files the section names as the knobs (tiers/config,
|
|
64
64
|
selection, throughput/cadence/registry, etc.).
|
|
65
|
-
- **The feedback intake** — `feedback/` at the repo
|
|
66
|
-
name — do not treat a differently-named folder as
|
|
67
|
-
`README.md` convention.
|
|
65
|
+
- **The feedback intake** — `session-manager-operations/feedback/` at the repo
|
|
66
|
+
root (the one canonical name — do not treat a differently-named folder as
|
|
67
|
+
equivalent), and its `README.md` convention.
|
|
68
68
|
|
|
69
69
|
**If CLAUDE.md declares no North-Star KPI section, STOP** and tell the user this
|
|
70
70
|
project hasn't declared one — the KPI belongs in CLAUDE.md's mission statement
|
|
@@ -228,7 +228,7 @@ Give it the 3 temp files **and** the Step-2 in-flight/cooldown set. It must:
|
|
|
228
228
|
failure mode this rule exists to prevent.
|
|
229
229
|
- **Write ONE consolidated feedback item** (for the this-project-owned half) into
|
|
230
230
|
this project's intake folder, named by the folder README's convention
|
|
231
|
-
(`feedback/<YYYY-MM-DD>-NN-kpi-optimization.md`, next free `NN`), with: title,
|
|
231
|
+
(`session-manager-operations/feedback/<YYYY-MM-DD>-NN-kpi-optimization.md`, next free `NN`), with: title,
|
|
232
232
|
**From:** `optimize-kpi loop`, date (PT), priority + why, **TL;DR**, **Evidence**
|
|
233
233
|
(cite the Step-1 scorecard numbers, the **Step-1b usage+log findings** the root
|
|
234
234
|
cause rests on, *and* the Step-2 prior-lever grade), **Why it matters**
|
|
@@ -8,8 +8,9 @@ description: >-
|
|
|
8
8
|
execution from there), and fold lessons back into the folder's README so
|
|
9
9
|
future feedback (written by other agents/projects) gets better. Use whenever the user says "/process-feedback",
|
|
10
10
|
"review the feedback folder", "work through the feedback", "any open
|
|
11
|
-
feedback?", or drops new files into feedback/.
|
|
12
|
-
cross-project requests, process feedback, triage
|
|
11
|
+
feedback?", or drops new files into session-manager-operations/feedback/.
|
|
12
|
+
Keywords: feedback, intake, cross-project requests, process feedback, triage
|
|
13
|
+
feedback.
|
|
13
14
|
model: opus
|
|
14
15
|
---
|
|
15
16
|
|
|
@@ -21,15 +22,18 @@ an interactive human prompt. It evaluates inbound feedback, dispatches the codea
|
|
|
21
22
|
RESOLUTION, archive). It does **not** implement, and it does **not** re-specify how PRDs are
|
|
22
23
|
tracked — that lives once, in `/develop` Phase 2.
|
|
23
24
|
|
|
24
|
-
Work the project's intra-project feedback intake
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
25
|
+
Work the project's intra-project feedback intake
|
|
26
|
+
(`session-manager-operations/feedback/` at the repo root — this is the **one**
|
|
27
|
+
canonical name; do not create or treat a differently-named folder like
|
|
28
|
+
`external-feedback/` as equivalent, even for cross-service requests — that
|
|
29
|
+
split caused ~2.5 weeks of drifted duplicate tracking in burrow before being
|
|
30
|
+
merged back on 2026-07-10). Each file is a request from an upstream/downstream
|
|
31
|
+
service in the same stack (e.g. Burrow ⇄ signal-builder ⇄ social-signals-trader)
|
|
32
|
+
— cross-service origin does not mean a different folder, it's still just
|
|
33
|
+
`session-manager-operations/feedback/`. The folder's own `README.md` is the
|
|
34
|
+
authority on file conventions — read it first; the steps below are the
|
|
35
|
+
process. All session-manager per-project operations live under
|
|
36
|
+
`session-manager-operations/`.
|
|
33
37
|
|
|
34
38
|
**Core principle:** this skill *triages and dispatches*; it does not implement.
|
|
35
39
|
Anything that requires writing code for this project is decomposed and queued as
|
|
@@ -55,15 +59,27 @@ prevent.
|
|
|
55
59
|
|
|
56
60
|
## Steps
|
|
57
61
|
|
|
62
|
+
### 0a. Self-migrate a legacy root-level folder first
|
|
63
|
+
|
|
64
|
+
Before anything else, check for the pre-migration layout: if a legacy
|
|
65
|
+
`feedback` folder exists at the repo root **and**
|
|
66
|
+
`session-manager-operations/feedback/` does not, relocate it now —
|
|
67
|
+
`mkdir -p session-manager-operations` (if needed), then
|
|
68
|
+
`git mv feedback session-manager-operations/feedback` when the repo is
|
|
69
|
+
git-tracked, else a plain `mv`. This converges any project this skill runs in
|
|
70
|
+
to the new layout even if its bulk-relocation PRD hasn't landed yet. Only then
|
|
71
|
+
continue with step 0.
|
|
72
|
+
|
|
58
73
|
### 0. Quick-exit — bail in milliseconds if there's nothing to do
|
|
59
74
|
|
|
60
75
|
**Do this first, cheaply, before reading any code or spawning anything.** This
|
|
61
76
|
skill is run on a schedule across many projects (the scheduler's feedback sweep),
|
|
62
77
|
so the empty case must cost almost nothing:
|
|
63
78
|
|
|
64
|
-
- If `feedback/` doesn't exist
|
|
79
|
+
- If `session-manager-operations/feedback/` doesn't exist (after the step-0a
|
|
80
|
+
self-migration check) → report "no feedback intake in `<project>`" and
|
|
65
81
|
**EXIT**. (Don't check for or fall back to an `external-feedback/`-style
|
|
66
|
-
variant — `feedback/` is the only canonical name.)
|
|
82
|
+
variant — `session-manager-operations/feedback/` is the only canonical name.)
|
|
67
83
|
- If the folder exists but holds **no open item** (every file is in `processed/`
|
|
68
84
|
or marked ✅/archived; nothing open/🆕) → report "no open feedback in
|
|
69
85
|
`<project>`" and **EXIT immediately**. Do NOT read source, evaluate, or start
|
|
@@ -74,7 +90,7 @@ so the empty case must cost almost nothing:
|
|
|
74
90
|
|
|
75
91
|
### 1. Read the intake
|
|
76
92
|
|
|
77
|
-
- Read `feedback/README.md` (conventions + the status log), then every open/🆕
|
|
93
|
+
- Read `session-manager-operations/feedback/README.md` (conventions + the status log), then every open/🆕
|
|
78
94
|
file still in the inbox. Under this contract a **🛠 (queued) item has already
|
|
79
95
|
been archived to `processed/`** at disposition time — so anything still sitting
|
|
80
96
|
in the inbox is genuinely un-dispositioned and needs a pass. You will not find
|
|
@@ -95,7 +111,7 @@ drifted. Then classify each ask:
|
|
|
95
111
|
RESOLUTION with the reason; never silently skip. Resolved now (no code) →
|
|
96
112
|
close immediately in step 4.
|
|
97
113
|
- **Theirs, forward** — the root cause lives in another service. Do NOT reach
|
|
98
|
-
across the boundary to hack around it; file a feedback
|
|
114
|
+
across the boundary to hack around it; file a feedback or PRD item in *that*
|
|
99
115
|
project's intake folder and reference it (use `/my-feedback to <project>`).
|
|
100
116
|
Resolved now (no code in *this* repo) → close immediately in step 4.
|
|
101
117
|
|
|
@@ -127,7 +143,7 @@ Record the emitted PRD filenames/ids — you need them for steps 4–6.
|
|
|
127
143
|
### 4. Disposition + archive every item now — this is the definition of done
|
|
128
144
|
|
|
129
145
|
Every open item gets a disposition, a `## RESOLUTION`, and a `git mv` to
|
|
130
|
-
`feedback/processed/` **in this pass** — archival is at disposition time, not
|
|
146
|
+
`session-manager-operations/feedback/processed/` **in this pass** — archival is at disposition time, not
|
|
131
147
|
delivery time. Do NOT leave a queued item in the inbox "until its PRD lands":
|
|
132
148
|
that is the drift this contract forbids. By disposition:
|
|
133
149
|
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* chat-mcp-consent-notice.test.cjs — regression for the headless MCP-consent
|
|
3
|
+
* hang: a chat run with stdin closed can never answer an MCP server's own
|
|
4
|
+
* interactive consent flow (e.g. `/design consent`). This drives the real
|
|
5
|
+
* stdout parser with a synthetic stream-json `tool_result` denial line and
|
|
6
|
+
* asserts a `chat:run:notice` event is broadcast — informational, not a
|
|
7
|
+
* terminal event.
|
|
8
|
+
*
|
|
9
|
+
* Run: timeout 120 node --test src/main/__tests__/chat-mcp-consent-notice.test.cjs
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
'use strict';
|
|
13
|
+
|
|
14
|
+
delete process.env.SM_CHAT_CONCURRENCY;
|
|
15
|
+
|
|
16
|
+
const { test } = require('node:test');
|
|
17
|
+
const assert = require('node:assert/strict');
|
|
18
|
+
const fs = require('node:fs');
|
|
19
|
+
const os = require('node:os');
|
|
20
|
+
const path = require('node:path');
|
|
21
|
+
|
|
22
|
+
function writeStub(lines) {
|
|
23
|
+
const stubPath = path.join(os.tmpdir(), `sm-claude-stub-${process.pid}-${Math.floor(Math.random() * 1e9)}.cjs`);
|
|
24
|
+
const body = lines.map((l) => `process.stdout.write(${JSON.stringify(JSON.stringify(l))} + "\\n");`).join('\n');
|
|
25
|
+
fs.writeFileSync(stubPath, `#!${process.execPath}\n${body}\nprocess.exit(0);\n`, { mode: 0o755 });
|
|
26
|
+
return stubPath;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function isTerminal(channel) {
|
|
30
|
+
return (
|
|
31
|
+
channel === 'chat:run:complete' ||
|
|
32
|
+
channel === 'chat:run:needs-input' ||
|
|
33
|
+
channel === 'chat:run:error'
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
test('MCP consent denial in a tool_result surfaces a chat:run:notice event', async () => {
|
|
38
|
+
const stubPath = writeStub([
|
|
39
|
+
{
|
|
40
|
+
type: 'user',
|
|
41
|
+
message: {
|
|
42
|
+
content: [
|
|
43
|
+
{
|
|
44
|
+
type: 'tool_result',
|
|
45
|
+
tool_use_id: 'toolu_1',
|
|
46
|
+
is_error: true,
|
|
47
|
+
content: [
|
|
48
|
+
{
|
|
49
|
+
type: 'text',
|
|
50
|
+
text:
|
|
51
|
+
"Claude Design: write_files The user hasn't granted this — run /design consent " +
|
|
52
|
+
"to grant it (it can't be approved automatically in this permission mode).",
|
|
53
|
+
},
|
|
54
|
+
],
|
|
55
|
+
},
|
|
56
|
+
],
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
{ type: 'result', subtype: 'success', result: 'done anyway' },
|
|
60
|
+
]);
|
|
61
|
+
process.env.SM_CLAUDE_BIN = stubPath;
|
|
62
|
+
delete require.cache[require.resolve('../chatRunner.cjs')];
|
|
63
|
+
const cr = require('../chatRunner.cjs');
|
|
64
|
+
|
|
65
|
+
const events = [];
|
|
66
|
+
cr.attachWindow({
|
|
67
|
+
isDestroyed: () => false,
|
|
68
|
+
webContents: {
|
|
69
|
+
isDestroyed: () => false,
|
|
70
|
+
send: (channel, payload) => events.push({ channel, payload }),
|
|
71
|
+
},
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
cr.run({ tabId: 'T-notice', sessionId: 'S-notice', prompt: 'do a design thing', cwd: process.cwd(), resume: false });
|
|
75
|
+
|
|
76
|
+
for (let i = 0; i < 60 && !events.some((e) => isTerminal(e.channel)); i++) {
|
|
77
|
+
await new Promise((r) => setTimeout(r, 25));
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const notice = events.find((e) => e.channel === 'chat:run:notice');
|
|
81
|
+
assert.ok(notice, 'chat:run:notice should be broadcast');
|
|
82
|
+
assert.match(notice.payload.message, /consent/i, 'message mentions consent');
|
|
83
|
+
assert.match(notice.payload.message, /terminal/i, 'message mentions the raw terminal session');
|
|
84
|
+
|
|
85
|
+
// Notice is informational, not terminal — the normal result still fires.
|
|
86
|
+
const terminal = events.filter((e) => isTerminal(e.channel));
|
|
87
|
+
assert.equal(terminal.length, 1, 'exactly one terminal event still fires');
|
|
88
|
+
assert.equal(terminal[0].channel, 'chat:run:complete');
|
|
89
|
+
|
|
90
|
+
try { fs.unlinkSync(stubPath); } catch { /* already gone */ }
|
|
91
|
+
delete process.env.SM_CLAUDE_BIN;
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
test('a normal tool_result with no consent marker does not fire chat:run:notice', async () => {
|
|
95
|
+
const stubPath = writeStub([
|
|
96
|
+
{
|
|
97
|
+
type: 'user',
|
|
98
|
+
message: {
|
|
99
|
+
content: [
|
|
100
|
+
{
|
|
101
|
+
type: 'tool_result',
|
|
102
|
+
tool_use_id: 'toolu_2',
|
|
103
|
+
is_error: false,
|
|
104
|
+
content: [{ type: 'text', text: 'Wrote 3 files successfully.' }],
|
|
105
|
+
},
|
|
106
|
+
],
|
|
107
|
+
},
|
|
108
|
+
},
|
|
109
|
+
{ type: 'result', subtype: 'success', result: 'all good' },
|
|
110
|
+
]);
|
|
111
|
+
process.env.SM_CLAUDE_BIN = stubPath;
|
|
112
|
+
delete require.cache[require.resolve('../chatRunner.cjs')];
|
|
113
|
+
const cr = require('../chatRunner.cjs');
|
|
114
|
+
|
|
115
|
+
const events = [];
|
|
116
|
+
cr.attachWindow({
|
|
117
|
+
isDestroyed: () => false,
|
|
118
|
+
webContents: {
|
|
119
|
+
isDestroyed: () => false,
|
|
120
|
+
send: (channel, payload) => events.push({ channel, payload }),
|
|
121
|
+
},
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
cr.run({ tabId: 'T-clean', sessionId: 'S-clean', prompt: 'do a normal thing', cwd: process.cwd(), resume: false });
|
|
125
|
+
|
|
126
|
+
for (let i = 0; i < 60 && !events.some((e) => isTerminal(e.channel)); i++) {
|
|
127
|
+
await new Promise((r) => setTimeout(r, 25));
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
assert.ok(
|
|
131
|
+
!events.some((e) => e.channel === 'chat:run:notice'),
|
|
132
|
+
'no chat:run:notice should fire for a clean tool_result (no false positive)',
|
|
133
|
+
);
|
|
134
|
+
|
|
135
|
+
try { fs.unlinkSync(stubPath); } catch { /* already gone */ }
|
|
136
|
+
delete process.env.SM_CLAUDE_BIN;
|
|
137
|
+
});
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* mcpStatus.test.cjs — unit tests for the `claude mcp list` output parser.
|
|
3
|
+
*
|
|
4
|
+
* Run: timeout 120 node --test src/main/__tests__/mcpStatus.test.cjs
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
'use strict';
|
|
8
|
+
|
|
9
|
+
const { test } = require('node:test');
|
|
10
|
+
const assert = require('node:assert/strict');
|
|
11
|
+
const { parseMcpList } = require('../mcpStatus.cjs');
|
|
12
|
+
|
|
13
|
+
const SAMPLE = `Checking MCP server health…
|
|
14
|
+
|
|
15
|
+
claude.ai Gmail: https://gmailmcp.googleapis.com/mcp/v1 - ✔ Connected
|
|
16
|
+
fetch: uvx mcp-server-fetch - ✔ Connected
|
|
17
|
+
n8n: bash /home/bilko/.config/n8n-mcp/run.sh - ✘ Failed to connect
|
|
18
|
+
session-manager-scheduler: node scripts/scheduler-mcp-server.cjs - ⏸ Pending approval (run \`claude\` to approve)
|
|
19
|
+
claude_design: https://api.anthropic.com/v1/design/mcp (HTTP) - ✔ Connected
|
|
20
|
+
claude.ai Google Drive: https://drivemcp.googleapis.com/mcp/v1 - ! Needs authentication
|
|
21
|
+
`;
|
|
22
|
+
|
|
23
|
+
test('parses the captured claude mcp list sample', () => {
|
|
24
|
+
const servers = parseMcpList(SAMPLE);
|
|
25
|
+
assert.ok(servers.length >= 6, `expected >=6 servers, got ${servers.length}`);
|
|
26
|
+
|
|
27
|
+
const byName = Object.fromEntries(servers.map((s) => [s.name, s]));
|
|
28
|
+
|
|
29
|
+
assert.equal(byName['claude.ai Gmail'].status, 'connected');
|
|
30
|
+
assert.equal(byName['fetch'].status, 'connected');
|
|
31
|
+
assert.equal(byName['n8n'].status, 'failed');
|
|
32
|
+
assert.equal(byName['session-manager-scheduler'].status, 'pending');
|
|
33
|
+
assert.equal(byName['claude_design'].status, 'connected');
|
|
34
|
+
assert.equal(byName['claude_design'].transport, 'http');
|
|
35
|
+
assert.equal(byName['claude.ai Google Drive'].status, 'needs-auth');
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
test('ignores the header and blank lines', () => {
|
|
39
|
+
const servers = parseMcpList(SAMPLE);
|
|
40
|
+
assert.ok(!servers.some((s) => /Checking MCP server health/i.test(s.name)));
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
test('defaults transport to stdio for non-HTTP targets', () => {
|
|
44
|
+
const servers = parseMcpList(SAMPLE);
|
|
45
|
+
const byName = Object.fromEntries(servers.map((s) => [s.name, s]));
|
|
46
|
+
assert.equal(byName['n8n'].transport, 'stdio');
|
|
47
|
+
assert.equal(byName['fetch'].transport, 'stdio');
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
test('returns [] (not a throw) on empty input', () => {
|
|
51
|
+
assert.deepEqual(parseMcpList(''), []);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
test('returns [] (not a throw) on garbage input', () => {
|
|
55
|
+
assert.deepEqual(parseMcpList('this is not\na valid mcp list output\n12345'), []);
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
test('returns [] on non-string input without throwing', () => {
|
|
59
|
+
assert.deepEqual(parseMcpList(null), []);
|
|
60
|
+
assert.deepEqual(parseMcpList(undefined), []);
|
|
61
|
+
});
|