claude-code-session-manager 0.38.5 → 0.38.7

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 (29) hide show
  1. package/dist/assets/{TiptapBody-D1ufJtTb.js → TiptapBody-C74mBmMU.js} +1 -1
  2. package/dist/assets/index-BGTU12rl.css +32 -0
  3. package/dist/assets/index-DPSmbyU_.js +3178 -0
  4. package/dist/index.html +2 -2
  5. package/package.json +2 -1
  6. package/plugins/session-manager-dev/skills/blog-for-project-feature/2-storyboard/SKILL.md +35 -9
  7. package/plugins/session-manager-dev/skills/blog-for-project-feature/3-capture/SKILL.md +16 -7
  8. package/plugins/session-manager-dev/skills/blog-for-project-feature/6-compose/SKILL.md +8 -5
  9. package/plugins/session-manager-dev/skills/blog-for-project-feature/7-review/SKILL.md +7 -4
  10. package/plugins/session-manager-dev/skills/blog-for-project-feature/SKILL.md +1 -1
  11. package/plugins/session-manager-dev/skills/blog-for-project-feature/feature.config.yaml +11 -2
  12. package/src/main/__tests__/prdCreate.test.cjs +55 -1
  13. package/src/main/__tests__/queueOps-interactive-ac-lint.test.cjs +65 -0
  14. package/src/main/__tests__/scheduler-sigterm-commit.test.cjs +43 -0
  15. package/src/main/index.cjs +10 -10
  16. package/src/main/ipcSchemas.cjs +17 -16
  17. package/src/main/layoutStore.cjs +58 -0
  18. package/src/main/lib/prdCreate.cjs +66 -43
  19. package/src/main/pty.cjs +0 -5
  20. package/src/main/queueOps.cjs +58 -0
  21. package/src/main/scheduler.cjs +48 -3
  22. package/src/main/transcripts.cjs +2 -15
  23. package/src/preload/api.d.ts +39 -116
  24. package/src/preload/index.cjs +6 -27
  25. package/dist/assets/index-CZrfXQpq.css +0 -32
  26. package/dist/assets/index-DwjviHVo.js +0 -3501
  27. package/src/main/hives.cjs +0 -287
  28. package/src/main/superagent.cjs +0 -212
  29. package/src/main/usageMatrix.cjs +0 -340
@@ -1,287 +0,0 @@
1
- /**
2
- * hives.cjs — subagent recipe templates ("Hives").
3
- *
4
- * A Recipe is a named sequence of steps, each referencing an agent by name
5
- * from `~/.claude/agents/<agentName>.md`. Shape:
6
- * { slug, name, description, brief?, steps: [{ agentName, note? }] }
7
- *
8
- * Legacy hives (old shape: `roles: [{ label, prompt }]`) are migrated on
9
- * first read: each role is materialized as an agent .md and replaced by a
10
- * step referencing it. Migration is idempotent and permanent (re-saved).
11
- *
12
- * Storage: `~/.claude/session-manager/hives/<slug>.json`
13
- * - slug must match SLUG_RE: /^[a-z0-9-_]{1,64}$/
14
- * - up to 32 steps per recipe
15
- * - per-field byte caps mirrored in the inline zod schemas below
16
- *
17
- * IPC namespace (channel names kept for backwards compat):
18
- * - hives:list -> { hives: Recipe[], error: string | null }
19
- * - hives:get -> { hive: Recipe | null, error: string | null }
20
- * - hives:save -> { ok: boolean, error: string | null }
21
- * - hives:delete -> { ok: boolean, error: string | null }
22
- *
23
- * All mutations go through config.cjs::writeJson / writeTextAtomic (atomic
24
- * tmp+rename) and config.cjs::validatePath (allowedRoots = home dir).
25
- * Never raw fs.writeFile.
26
- */
27
-
28
- 'use strict';
29
-
30
- const { ipcMain } = require('electron');
31
- const fsp = require('node:fs/promises');
32
- const path = require('node:path');
33
- const os = require('node:os');
34
- const { z } = require('zod');
35
- const config = require('./config.cjs');
36
- const { kebabCase: sharedKebabCase } = require('./lib/kebabCase.cjs');
37
-
38
- // ──────────────────────────────────────────── caps
39
- const SLUG_RE = /^[a-z0-9-_]{1,64}$/;
40
- const AGENT_NAME_RE = /^[a-z0-9-_]{1,64}$/;
41
- const MAX_NAME_LEN = 128;
42
- const MAX_DESC_LEN = 2048;
43
- const MAX_BRIEF_LEN = 8 * 1024;
44
- const MAX_NOTE_LEN = 2048;
45
- const MAX_STEPS = 32;
46
-
47
- // ──────────────────────────────────────────── inline zod schemas
48
- const recipeStepSchema = z.object({
49
- agentName: z.string().regex(AGENT_NAME_RE),
50
- note: z.string().max(MAX_NOTE_LEN).optional(),
51
- }).strict();
52
-
53
- const recipeSchema = z.object({
54
- slug: z.string().regex(SLUG_RE),
55
- name: z.string().min(1).max(MAX_NAME_LEN),
56
- description: z.string().max(MAX_DESC_LEN).default(''),
57
- brief: z.string().max(MAX_BRIEF_LEN).optional(),
58
- steps: z.array(recipeStepSchema).min(1).max(MAX_STEPS),
59
- }).strict();
60
-
61
- const slugPayload = z.object({
62
- slug: z.string().regex(SLUG_RE),
63
- }).strict();
64
-
65
- // Field name "hive" kept for IPC channel backwards compatibility.
66
- const savePayload = z.object({
67
- slug: z.string().regex(SLUG_RE),
68
- hive: recipeSchema,
69
- }).strict();
70
-
71
- // ──────────────────────────────────────────── paths
72
- function rootDir() {
73
- return path.join(os.homedir(), '.claude', 'session-manager', 'hives');
74
- }
75
-
76
- function agentsDir() {
77
- return path.join(os.homedir(), '.claude', 'agents');
78
- }
79
-
80
- function hivePath(slug) {
81
- if (!SLUG_RE.test(slug)) {
82
- throw new Error(`invalid hive slug (must match ${SLUG_RE.source})`);
83
- }
84
- return path.join(rootDir(), `${slug}.json`);
85
- }
86
-
87
- async function ensureRoot() {
88
- await fsp.mkdir(rootDir(), { recursive: true });
89
- }
90
-
91
- // ──────────────────────────────────────────── helpers
92
- function kebabCase(s) {
93
- return sharedKebabCase(s, { maxLen: 64, fallback: 'agent' });
94
- }
95
-
96
- // ──────────────────────────────────────────── legacy migration
97
- async function migrateLegacyHive(slug, raw) {
98
- const adir = agentsDir();
99
- await fsp.mkdir(adir, { recursive: true });
100
-
101
- const usedNames = new Set();
102
- const steps = [];
103
-
104
- for (const role of (raw.roles ?? [])) {
105
- let base = kebabCase(role.label ?? 'agent');
106
- let agentName = base;
107
- let suffix = 2;
108
- while (usedNames.has(agentName)) {
109
- agentName = `${base.slice(0, 62)}-${suffix++}`;
110
- }
111
- usedNames.add(agentName);
112
-
113
- const agentFilePath = path.join(adir, `${agentName}.md`);
114
- // validatePath ensures we stay within allowedRoots (home dir).
115
- const validPath = config.validatePath(agentFilePath);
116
-
117
- let exists = false;
118
- try { await fsp.stat(validPath); exists = true; } catch { /* ENOENT = not yet created */ }
119
-
120
- if (!exists) {
121
- const descLine = (role.prompt ?? '').replace(/[\r\n]+/g, ' ').slice(0, 200);
122
- const content = `---\nname: ${role.label}\ndescription: ${descLine}\n---\n\n${role.prompt ?? ''}\n`;
123
- await config.writeTextAtomic(validPath, content);
124
- }
125
-
126
- steps.push({ agentName });
127
- }
128
-
129
- const recipe = {
130
- slug,
131
- name: raw.name,
132
- description: raw.description ?? '',
133
- steps,
134
- ...(raw.defaultPlan ? { brief: raw.defaultPlan } : {}),
135
- };
136
-
137
- // Persist migrated shape permanently so subsequent reads skip migration.
138
- await saveHive(slug, recipe);
139
-
140
- return { hive: recipe, error: null };
141
- }
142
-
143
- // ──────────────────────────────────────────── core ops
144
- async function listHives() {
145
- try {
146
- await ensureRoot();
147
- const r = await config.listDir(rootDir(), { filesOnly: true });
148
- if (!r.ok) return { hives: [], error: r.error };
149
- const slugs = r.entries
150
- .filter((e) => e.name.endsWith('.json'))
151
- .map((e) => e.name.replace(/\.json$/, ''))
152
- .filter((s) => SLUG_RE.test(s));
153
- // Load each in parallel. Skip any that fail to parse cleanly.
154
- const hives = await Promise.all(
155
- slugs.map(async (slug) => {
156
- try {
157
- const got = await readHive(slug);
158
- return got.hive;
159
- } catch {
160
- return null;
161
- }
162
- }),
163
- );
164
- return {
165
- hives: hives.filter((h) => h !== null).sort((a, b) => a.slug.localeCompare(b.slug)),
166
- error: null,
167
- };
168
- } catch (e) {
169
- return { hives: [], error: e.message };
170
- }
171
- }
172
-
173
- async function readHive(slug) {
174
- const abs = hivePath(slug);
175
- const r = await config.readJson(abs);
176
- if (!r || !r.exists) return { hive: null, error: null };
177
- if (r.parseError) return { hive: null, error: `parse error: ${r.parseError}` };
178
-
179
- const raw = r.data;
180
-
181
- // Legacy migration: old shape has `roles` array instead of `steps`.
182
- if (raw && Array.isArray(raw.roles) && !Array.isArray(raw.steps)) {
183
- return migrateLegacyHive(slug, raw);
184
- }
185
-
186
- // Re-validate stored shape so a hand-edited file can't smuggle in bad data.
187
- const parsed = recipeSchema.safeParse(raw);
188
- if (!parsed.success) {
189
- return { hive: null, error: `invalid recipe on disk: ${parsed.error.issues[0]?.message ?? 'unknown'}` };
190
- }
191
- // Trust the file's own slug only if it matches the filename; filename wins.
192
- const hive = { ...parsed.data, slug };
193
- return { hive, error: null };
194
- }
195
-
196
- async function getHive(slug) {
197
- try {
198
- return await readHive(slug);
199
- } catch (e) {
200
- return { hive: null, error: e.message };
201
- }
202
- }
203
-
204
- async function saveHive(slug, hive) {
205
- try {
206
- // Body slug must match path slug.
207
- if (hive.slug !== slug) {
208
- return { ok: false, error: `slug mismatch: payload slug "${hive.slug}" != path "${slug}"` };
209
- }
210
- await ensureRoot();
211
- const abs = hivePath(slug);
212
- const out = {
213
- ...hive,
214
- description: hive.description ?? '',
215
- };
216
- if (out.brief === undefined || out.brief === '') delete out.brief;
217
- const result = await config.writeJson(abs, out);
218
- if (!result || !result.ok) {
219
- return { ok: false, error: result?.error ?? 'write failed' };
220
- }
221
- return { ok: true, error: null };
222
- } catch (e) {
223
- return { ok: false, error: e.message };
224
- }
225
- }
226
-
227
- async function deleteHive(slug) {
228
- try {
229
- const abs = hivePath(slug);
230
- let real;
231
- try {
232
- real = config.validatePath(abs);
233
- if (typeof config.validateWrite === 'function') {
234
- config.validateWrite(real);
235
- }
236
- } catch (e) {
237
- return { ok: false, error: e.message };
238
- }
239
- try {
240
- await fsp.unlink(real);
241
- } catch (e) {
242
- if (e.code === 'ENOENT') {
243
- return { ok: true, error: null };
244
- }
245
- return { ok: false, error: e.message };
246
- }
247
- return { ok: true, error: null };
248
- } catch (e) {
249
- return { ok: false, error: e.message };
250
- }
251
- }
252
-
253
- // ──────────────────────────────────────────── IPC
254
- function validated(schema, handler) {
255
- return (_event, payload) => {
256
- const parsed = schema.parse(payload);
257
- return handler(parsed);
258
- };
259
- }
260
-
261
- function registerHiveHandlers() {
262
- ipcMain.handle('hives:list', () => listHives());
263
- ipcMain.handle(
264
- 'hives:get',
265
- validated(slugPayload, ({ slug }) => getHive(slug)),
266
- );
267
- ipcMain.handle(
268
- 'hives:save',
269
- validated(savePayload, ({ slug, hive }) => saveHive(slug, hive)),
270
- );
271
- ipcMain.handle(
272
- 'hives:delete',
273
- validated(slugPayload, ({ slug }) => deleteHive(slug)),
274
- );
275
- }
276
-
277
- module.exports = {
278
- registerHiveHandlers,
279
- // exported for tests
280
- rootDir,
281
- hivePath,
282
- SLUG_RE,
283
- listHives,
284
- getHive,
285
- saveHive,
286
- deleteHive,
287
- };
@@ -1,212 +0,0 @@
1
- /**
2
- * SuperAgent — "boss" runner that dispatches specialist subagents.
3
- *
4
- * Ported (in MVP form) from ClaudeCodeUnleashed's SuperAgent feature.
5
- * Differs significantly from upstream:
6
- * - We don't call Groq/OpenAI. The boss IS Claude — we send a structured
7
- * prompt to the active PTY tab asking Claude to act as the boss, pick
8
- * specialists, and report progress in a parseable format.
9
- * - No autonomous output-parsing / fast-path / WAIT logic. State machine
10
- * is renderer-driven via transcript events (see live.ts pattern).
11
- * - Per-tab run state lives here so the renderer can poll status across
12
- * navigation, and so a future supervisor probe can act on stuck runs.
13
- *
14
- * Channels:
15
- * superagent:start({ tabId, prompt, specialistCount, depth })
16
- * → { ok, error? } and broadcasts the boss prompt to the tab's PTY.
17
- * superagent:status(tabId) → SuperAgentRunState | null
18
- * superagent:stop(tabId) → { ok }
19
- *
20
- * Broadcasts:
21
- * superagent:state-changed → { tabId, state } whenever a run starts/stops.
22
- *
23
- * The PTY write is intentionally done via the existing ptyManager.write
24
- * indirection, NOT a fresh spawn — SuperAgent runs inside the user's current
25
- * Claude session.
26
- */
27
-
28
- const { ipcMain } = require('electron');
29
- const { schemas } = require('./ipcSchemas.cjs');
30
- const { manager: ptyManager } = require('./pty.cjs');
31
- const logs = require('./logs.cjs');
32
-
33
- // Per-tab run state. Single live run per tab — starting a new run on a tab
34
- // that's already running stops the existing one first.
35
- //
36
- // Shape (also exposed on the renderer via api.d.ts SuperAgentRunState):
37
- // { status: 'idle' | 'running' | 'done' | 'error',
38
- // prompt, specialistCount, depth,
39
- // startedAt: number | null, finishedAt: number | null,
40
- // error?: string }
41
- const runs = new Map();
42
-
43
- let mainWindow = null;
44
-
45
- function attachWindow(win) {
46
- mainWindow = win;
47
- }
48
-
49
- function broadcast(tabId) {
50
- if (!mainWindow || mainWindow.isDestroyed()) return;
51
- const state = runs.get(tabId) ?? null;
52
- try {
53
- mainWindow.webContents.send('superagent:state-changed', { tabId, state });
54
- } catch { /* renderer gone */ }
55
- }
56
-
57
- /**
58
- * Build the prompt we send to Claude to act as the boss. Structured so the
59
- * renderer can (in a follow-up) pattern-match progress markers off the
60
- * transcript without us having to parse PTY output ourselves.
61
- *
62
- * O(1) string concat — the prompt is bounded by the zod max(8KiB).
63
- */
64
- function buildBossPrompt({ prompt, specialistCount, depth }) {
65
- const depthLine = depth === 'quick'
66
- ? 'Quick pass — surface-level analysis, single-shot specialist work.'
67
- : depth === 'deep'
68
- ? 'Deep pass — multi-step plans per specialist, recurse on findings.'
69
- : 'Standard pass — moderate detail, one or two iterations per specialist.';
70
-
71
- return [
72
- '## SuperAgent boss mode',
73
- '',
74
- `You are coordinating ${specialistCount} specialist subagent(s) for the user's task.`,
75
- depthLine,
76
- '',
77
- 'Workflow:',
78
- ` 1. Pick ${specialistCount} specialist subagent type(s) that best match the task.`,
79
- ' Report them as: [SUPERAGENT] specialists: <type1>, <type2>, ...',
80
- ' 2. Dispatch them via the Task tool, one at a time or in parallel as appropriate.',
81
- ' 3. After each specialist returns, report: [SUPERAGENT] progress: <specialist> done',
82
- ' 4. When the task is complete, summarize results and report: [SUPERAGENT] complete',
83
- '',
84
- 'User task:',
85
- prompt,
86
- ].join('\n');
87
- }
88
-
89
- function startRun(payload) {
90
- const { tabId, prompt, specialistCount, depth } = payload;
91
-
92
- // Stop any in-flight run on this tab — single run-per-tab invariant.
93
- const existing = runs.get(tabId);
94
- if (existing && existing.status === 'running') {
95
- runs.set(tabId, {
96
- ...existing,
97
- status: 'done',
98
- finishedAt: Date.now(),
99
- });
100
- }
101
-
102
- const fullPrompt = buildBossPrompt({ prompt, specialistCount, depth });
103
-
104
- // PTY write — exactly the pattern docstring'd at the top of the task brief.
105
- // The trailing carriage-return submits the prompt in Claude's TUI.
106
- let writeErr = null;
107
- try {
108
- ptyManager.write({ tabId, data: fullPrompt + '\r' });
109
- } catch (e) {
110
- writeErr = e?.message ?? String(e);
111
- }
112
-
113
- if (writeErr) {
114
- const errState = {
115
- status: 'error',
116
- prompt,
117
- specialistCount,
118
- depth,
119
- startedAt: Date.now(),
120
- finishedAt: Date.now(),
121
- error: writeErr,
122
- };
123
- runs.set(tabId, errState);
124
- broadcast(tabId);
125
- logs.writeLine({
126
- scope: 'superagent',
127
- level: 'error',
128
- message: 'start failed',
129
- meta: { tabId, error: writeErr },
130
- });
131
- return { ok: false, error: writeErr };
132
- }
133
-
134
- const state = {
135
- status: 'running',
136
- prompt,
137
- specialistCount,
138
- depth,
139
- startedAt: Date.now(),
140
- finishedAt: null,
141
- };
142
- runs.set(tabId, state);
143
- broadcast(tabId);
144
-
145
- logs.writeLine({
146
- scope: 'superagent',
147
- level: 'info',
148
- message: 'start',
149
- meta: { tabId, specialistCount, depth, promptBytes: prompt.length },
150
- });
151
-
152
- return { ok: true };
153
- }
154
-
155
- function stopRun(tabId) {
156
- const existing = runs.get(tabId);
157
- if (!existing || existing.status !== 'running') {
158
- return { ok: true };
159
- }
160
- runs.set(tabId, {
161
- ...existing,
162
- status: 'done',
163
- finishedAt: Date.now(),
164
- });
165
- broadcast(tabId);
166
- logs.writeLine({
167
- scope: 'superagent',
168
- level: 'info',
169
- message: 'stop',
170
- meta: { tabId },
171
- });
172
- return { ok: true };
173
- }
174
-
175
- function getStatus(tabId) {
176
- return runs.get(tabId) ?? null;
177
- }
178
-
179
- function dropTab(tabId) {
180
- if (runs.has(tabId)) {
181
- runs.delete(tabId);
182
- if (process.env.SM_DEBUG_LEAKS === '1') {
183
- console.log('[superagent] dropTab', tabId, 'runs.size=', runs.size);
184
- }
185
- }
186
- }
187
-
188
- function registerSuperAgentHandlers() {
189
- ipcMain.handle('superagent:start', (_e, payload) => {
190
- const parsed = schemas.superagentStart.parse(payload);
191
- return startRun(parsed);
192
- });
193
-
194
- ipcMain.handle('superagent:status', (_e, payload) => {
195
- const { tabId } = schemas.superagentTabId.parse(payload);
196
- return getStatus(tabId);
197
- });
198
-
199
- ipcMain.handle('superagent:stop', (_e, payload) => {
200
- const { tabId } = schemas.superagentTabId.parse(payload);
201
- return stopRun(tabId);
202
- });
203
- }
204
-
205
- module.exports = {
206
- attachWindow,
207
- registerSuperAgentHandlers,
208
- dropTab,
209
- // Exposed for tests.
210
- buildBossPrompt,
211
- _runs: runs,
212
- };