claude-code-session-manager 0.55.1 → 0.57.0
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-g5zDcgLc.js → TiptapBody-B1llI4h-.js} +1 -1
- package/dist/assets/index-DdVGnaCd.js +3227 -0
- package/dist/assets/{index-CMQ3X1Y6.css → index-WUc_GHMf.css} +1 -1
- package/dist/index.html +2 -2
- package/package.json +1 -1
- package/plugins/session-manager-dev/skills/ops-sweep/SKILL.md +136 -0
- package/src/main/__tests__/bilkoHost-deriveSlug.test.cjs +26 -0
- package/src/main/__tests__/bilkoHost-integration.test.cjs +105 -0
- package/src/main/__tests__/bilkoHostCore.test.cjs +72 -0
- package/src/main/__tests__/scheduler-reconcile-history-backfill.test.cjs +105 -0
- package/src/main/agentLibrary.cjs +5 -2
- package/src/main/bilkoHost.cjs +305 -0
- package/src/main/bilkoHostCore.cjs +89 -0
- package/src/main/config.cjs +8 -8
- package/src/main/index.cjs +2 -0
- package/src/main/ipcSchemas.cjs +29 -0
- package/src/main/lib/epicMint.cjs +6 -0
- package/src/main/lib/opsOwnership.cjs +5 -2
- package/src/main/projectPages.cjs +3 -2
- package/src/main/scheduler.cjs +33 -2
- package/src/preload/api.d.ts +87 -2
- package/src/preload/index.cjs +7 -0
- package/dist/assets/index-tW8C4h-7.js +0 -3221
package/src/main/ipcSchemas.cjs
CHANGED
|
@@ -486,6 +486,31 @@ const projectBriefCwd = z.object({
|
|
|
486
486
|
const projectPagesCwd = z.object({
|
|
487
487
|
cwd: z.string().min(1).max(4096),
|
|
488
488
|
}).strict();
|
|
489
|
+
|
|
490
|
+
// ──────────────────────────────────────────── Host on Bilko.run
|
|
491
|
+
// Same validation split: real path validation is config.cjs's validatePath
|
|
492
|
+
// at first fs access in bilkoHost.cjs — these schemas only bound wire shape.
|
|
493
|
+
const bilkoHostCwd = z.object({
|
|
494
|
+
cwd: z.string().min(1).max(4096),
|
|
495
|
+
}).strict();
|
|
496
|
+
const bilkoHostPrepareBundle = z.object({
|
|
497
|
+
cwd: z.string().min(1).max(4096),
|
|
498
|
+
slug: z.string().min(1).max(64).regex(/^[a-z0-9-]+$/, 'slug must be lowercase kebab-case'),
|
|
499
|
+
}).strict();
|
|
500
|
+
const BILKO_HOST_DOCUMENT_SOURCE = z.union([
|
|
501
|
+
z.object({ kind: z.literal('project-page-lens'), lens: z.enum(['home', 'marketing', 'feature', 'architecture']) }).strict(),
|
|
502
|
+
z.object({ kind: z.literal('file'), path: z.string().min(1).max(4096) }).strict(),
|
|
503
|
+
]);
|
|
504
|
+
const bilkoHostAddDocument = z.object({
|
|
505
|
+
cwd: z.string().min(1).max(4096),
|
|
506
|
+
subpath: z.string().min(1).max(200),
|
|
507
|
+
title: z.string().min(1).max(200),
|
|
508
|
+
source: BILKO_HOST_DOCUMENT_SOURCE,
|
|
509
|
+
}).strict();
|
|
510
|
+
const bilkoHostRemoveDocument = z.object({
|
|
511
|
+
cwd: z.string().min(1).max(4096),
|
|
512
|
+
id: z.string().min(1).max(200),
|
|
513
|
+
}).strict();
|
|
489
514
|
const projectBriefSetPin = z.object({
|
|
490
515
|
cwd: z.string().min(1).max(4096),
|
|
491
516
|
block: PROJECT_BRIEF_BLOCK,
|
|
@@ -937,6 +962,10 @@ module.exports = {
|
|
|
937
962
|
memoryStale,
|
|
938
963
|
projectBriefCwd,
|
|
939
964
|
projectPagesCwd,
|
|
965
|
+
bilkoHostCwd,
|
|
966
|
+
bilkoHostPrepareBundle,
|
|
967
|
+
bilkoHostAddDocument,
|
|
968
|
+
bilkoHostRemoveDocument,
|
|
940
969
|
projectBriefSetPin,
|
|
941
970
|
projectBriefUpdate,
|
|
942
971
|
promptSessionTranscriptAppend,
|
|
@@ -215,6 +215,12 @@ function withPathLock(lockPath, task) {
|
|
|
215
215
|
*/
|
|
216
216
|
function ensureEpic(cwd, { goalText, tag, reuseByGoal = false, epicId: explicitEpicId, status = 'proposed', openingPrompt = null, mintIfMissing = true, source = null, forceNewEpic = false } = {}) {
|
|
217
217
|
if (!cwd || typeof cwd !== 'string') throw new Error('ensureEpic: cwd is required');
|
|
218
|
+
// A relative cwd (e.g. a caller passing '.') would otherwise get stored
|
|
219
|
+
// verbatim on the minted Epic's `cwd` field — the renderer's EpicsWorkspace
|
|
220
|
+
// matches Epics to a tab by exact `s.cwd === effectiveCwd` string equality
|
|
221
|
+
// against the tab's absolute path, so a relative value silently produces an
|
|
222
|
+
// Epic that exists on disk but never renders in any project's queue.
|
|
223
|
+
cwd = path.resolve(cwd);
|
|
218
224
|
return withPathLock(activeIndexPath(cwd), () => {
|
|
219
225
|
const index = readActiveIndex(cwd);
|
|
220
226
|
|
|
@@ -48,12 +48,15 @@ const OWNERS = Object.freeze({
|
|
|
48
48
|
'scheduler': 'scheduler',
|
|
49
49
|
// Project Home owns the synthesized Brief (generate + hand-edit).
|
|
50
50
|
'project-brief': 'project-home',
|
|
51
|
-
// The feedback intake folder — written by the RCA hook and feedback filing.
|
|
52
|
-
'feedback': 'feedback',
|
|
53
51
|
// Browser tab scratch saves (DOM captures, screenshots, recorded flows).
|
|
54
52
|
'browser': 'browser',
|
|
55
53
|
// Structured per-tab error log lines (JSONL), tagged for tracing/analysis.
|
|
56
54
|
'logs': 'logs',
|
|
55
|
+
// Host on Bilko.run tab's deterministic bundle prep (dist/index.html +
|
|
56
|
+
// dist/manifest.json only — publish-state.json and anything the
|
|
57
|
+
// bilko-host-publisher Epic authors beyond dist/ is agent-Write-tool
|
|
58
|
+
// output, same unenforceable-by-construction class as project-pages/output.
|
|
59
|
+
'bilko-host': 'bilko-host',
|
|
57
60
|
});
|
|
58
61
|
|
|
59
62
|
/**
|
|
@@ -21,7 +21,7 @@ function outputDir(cwd) {
|
|
|
21
21
|
return path.join(cwd, 'session-manager-operations', 'project-pages', 'output');
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
const LENSES = ['marketing', 'feature', 'architecture'];
|
|
24
|
+
const LENSES = ['home', 'marketing', 'feature', 'architecture'];
|
|
25
25
|
|
|
26
26
|
async function get({ cwd }) {
|
|
27
27
|
const realCwd = config.validatePath(cwd);
|
|
@@ -38,7 +38,7 @@ async function get({ cwd }) {
|
|
|
38
38
|
return { output: null };
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
-
const [marketing, feature, architecture] = htmlResults;
|
|
41
|
+
const [home, marketing, feature, architecture] = htmlResults;
|
|
42
42
|
const generatedAt = typeof manifestResult.data.generatedAt === 'string' ? manifestResult.data.generatedAt : null;
|
|
43
43
|
if (!generatedAt) {
|
|
44
44
|
return { output: null };
|
|
@@ -46,6 +46,7 @@ async function get({ cwd }) {
|
|
|
46
46
|
|
|
47
47
|
return {
|
|
48
48
|
output: {
|
|
49
|
+
home: home.text,
|
|
49
50
|
marketing: marketing.text,
|
|
50
51
|
feature: feature.text,
|
|
51
52
|
architecture: architecture.text,
|
package/src/main/scheduler.cjs
CHANGED
|
@@ -1138,11 +1138,26 @@ async function reconcile(state) {
|
|
|
1138
1138
|
|
|
1139
1139
|
const next = [];
|
|
1140
1140
|
const seen = new Set();
|
|
1141
|
+
// Terminal (completed/failed) jobs whose .md vanished from disk BEFORE
|
|
1142
|
+
// their row ever crossed HISTORY_RETENTION_MS in partitionJobs — i.e. the
|
|
1143
|
+
// common case, since archiveCompletedPrd() (called right when a job
|
|
1144
|
+
// finishes, scheduler.cjs's `newlyCompletedPrds` handling) moves the PRD
|
|
1145
|
+
// file immediately, while partitionJobs' appendHistory only fires after 7
|
|
1146
|
+
// days. Bug found 2026-08-02: the old code below dropped these rows on the
|
|
1147
|
+
// very next reconcile() pass with NO history write at all — the file-gone
|
|
1148
|
+
// check couldn't tell "already recorded to history.jsonl on a prior pass"
|
|
1149
|
+
// apart from "never recorded anywhere", so a job that completed and had
|
|
1150
|
+
// its file auto-archived within minutes lost its history entirely. Both
|
|
1151
|
+
// Queue and History tabs then read as empty even for projects with real,
|
|
1152
|
+
// successfully-completed work. Collected here; reconciled against
|
|
1153
|
+
// historyTerminalBySlug() below and backfilled before being dropped.
|
|
1154
|
+
const terminalDroppedNeedingHistoryCheck = [];
|
|
1141
1155
|
for (const job of state.jobs) {
|
|
1142
1156
|
const p = onDisk.get(job.slug);
|
|
1143
1157
|
if (!p) {
|
|
1144
1158
|
// A terminal job whose .md is gone was archived on purpose — dropping
|
|
1145
|
-
// its row is the intended end of the auto-archive flow
|
|
1159
|
+
// its row is the intended end of the auto-archive flow, PROVIDED it's
|
|
1160
|
+
// already durably recorded in history.jsonl (checked below).
|
|
1146
1161
|
//
|
|
1147
1162
|
// A PENDING or RUNNING job whose .md is merely not VISIBLE is a
|
|
1148
1163
|
// different thing entirely, and dropping it destroys queued work: the
|
|
@@ -1163,6 +1178,8 @@ async function reconcile(state) {
|
|
|
1163
1178
|
seen.add(job.slug);
|
|
1164
1179
|
next.push({ ...job });
|
|
1165
1180
|
console.warn(`[scheduler] reconcile: keeping ${job.status} job ${job.slug} — PRD source not visible in any candidate dir`);
|
|
1181
|
+
} else if (job.status === 'completed' || job.status === 'failed') {
|
|
1182
|
+
terminalDroppedNeedingHistoryCheck.push(job);
|
|
1166
1183
|
}
|
|
1167
1184
|
continue;
|
|
1168
1185
|
}
|
|
@@ -1198,10 +1215,24 @@ async function reconcile(state) {
|
|
|
1198
1215
|
for (const [slug] of onDisk) {
|
|
1199
1216
|
if (!seen.has(slug)) unmatchedSlugs.push(slug);
|
|
1200
1217
|
}
|
|
1201
|
-
const historyBySlug = unmatchedSlugs.length > 0
|
|
1218
|
+
const historyBySlug = (unmatchedSlugs.length > 0 || terminalDroppedNeedingHistoryCheck.length > 0)
|
|
1202
1219
|
? await queueHistory.historyTerminalBySlug()
|
|
1203
1220
|
: new Map();
|
|
1204
1221
|
|
|
1222
|
+
// Backfill: any terminal job dropped above whose slug isn't already in
|
|
1223
|
+
// history.jsonl gets written now, before its row is gone for good. This is
|
|
1224
|
+
// the fix for the bug described where the loop collects
|
|
1225
|
+
// terminalDroppedNeedingHistoryCheck — without it, a job whose PRD file was
|
|
1226
|
+
// auto-archived immediately on completion (the normal case) is lost from
|
|
1227
|
+
// both jobs[] and history.jsonl in the same reconcile pass.
|
|
1228
|
+
if (terminalDroppedNeedingHistoryCheck.length > 0) {
|
|
1229
|
+
const missing = terminalDroppedNeedingHistoryCheck.filter((j) => !historyBySlug.has(j.slug));
|
|
1230
|
+
if (missing.length > 0) {
|
|
1231
|
+
await queueHistory.appendHistory(missing);
|
|
1232
|
+
console.log(`[scheduler] reconcile: backfilled ${missing.length} completed/failed job(s) into history.jsonl (PRD file already archived, row about to drop)`);
|
|
1233
|
+
}
|
|
1234
|
+
}
|
|
1235
|
+
|
|
1205
1236
|
// Terminal-in-history slugs whose .md file is still on disk: fed into the
|
|
1206
1237
|
// auto-archive selection pass below (as synthetic completed entries) so
|
|
1207
1238
|
// their file can still be swept, without ever creating a live job row
|
package/src/preload/api.d.ts
CHANGED
|
@@ -133,7 +133,7 @@ export interface ImportRef {
|
|
|
133
133
|
/** Declared owner ids for the single-writer law over a project's
|
|
134
134
|
* session-manager-operations/ root. Mirrors OWNERS in
|
|
135
135
|
* src/main/lib/opsOwnership.cjs — one writer per namespace; everyone reads. */
|
|
136
|
-
export type OpsWriter = 'epics' | 'scheduler' | 'project-home' | '
|
|
136
|
+
export type OpsWriter = 'epics' | 'scheduler' | 'project-home' | 'browser';
|
|
137
137
|
|
|
138
138
|
export interface WriteResult {
|
|
139
139
|
ok: boolean;
|
|
@@ -267,6 +267,9 @@ export interface AgentPersona {
|
|
|
267
267
|
color: string | null;
|
|
268
268
|
/** Epic intent tags (tagLibrary.ts's TAG_LIBRARY) this persona is associated with. */
|
|
269
269
|
tags: string[];
|
|
270
|
+
/** "<Department> — <Human title>" (e.g. "Engineering — Architect"), free text — drives
|
|
271
|
+
* department grouping in the New Epic Agent picker. Optional/null when not set. */
|
|
272
|
+
title?: string | null;
|
|
270
273
|
path: string;
|
|
271
274
|
body: string;
|
|
272
275
|
/** Project names (basename of cwd) whose currently-open tab overlays this agent. */
|
|
@@ -283,6 +286,7 @@ export interface AgentPersonaSaveInput {
|
|
|
283
286
|
model: string;
|
|
284
287
|
color: string;
|
|
285
288
|
tags: string[];
|
|
289
|
+
title?: string;
|
|
286
290
|
body: string;
|
|
287
291
|
}
|
|
288
292
|
|
|
@@ -987,6 +991,7 @@ export type ProjectBriefUpdateResult =
|
|
|
987
991
|
|
|
988
992
|
// ────────────────────────────────────────────── Project Pages (PRD 929-932)
|
|
989
993
|
export interface ProjectPagesOutput {
|
|
994
|
+
home: string;
|
|
990
995
|
marketing: string;
|
|
991
996
|
feature: string;
|
|
992
997
|
architecture: string;
|
|
@@ -998,6 +1003,76 @@ export interface ProjectPagesGetResult {
|
|
|
998
1003
|
output: ProjectPagesOutput | null;
|
|
999
1004
|
}
|
|
1000
1005
|
|
|
1006
|
+
// ────────────────────────────────────────────── Host on Bilko.run
|
|
1007
|
+
export interface BilkoHostBundleManifest {
|
|
1008
|
+
schemaVersion: 1;
|
|
1009
|
+
slug: string;
|
|
1010
|
+
version: string;
|
|
1011
|
+
builtAt: string;
|
|
1012
|
+
gitSha: string;
|
|
1013
|
+
gitBranch: string;
|
|
1014
|
+
hostKit: { version: string };
|
|
1015
|
+
golden: { path: string; expect: string };
|
|
1016
|
+
health: Record<string, unknown>;
|
|
1017
|
+
bundle: { sizeBytesGz: number; fileCount: number };
|
|
1018
|
+
/** Not part of the host-contract schema itself — this app's own staleness bookkeeping (see BilkoHostGetResult.bundleStale). */
|
|
1019
|
+
documentCount: number;
|
|
1020
|
+
documents: Array<{ subpath: string; title: string }>;
|
|
1021
|
+
}
|
|
1022
|
+
|
|
1023
|
+
export type BilkoHostPublishStatus =
|
|
1024
|
+
| 'not-published'
|
|
1025
|
+
| 'bundle-ready'
|
|
1026
|
+
| 'publishing'
|
|
1027
|
+
| 'published'
|
|
1028
|
+
| 'publish-failed';
|
|
1029
|
+
|
|
1030
|
+
export interface BilkoHostPublishState {
|
|
1031
|
+
status: BilkoHostPublishStatus;
|
|
1032
|
+
slug: string;
|
|
1033
|
+
url?: string;
|
|
1034
|
+
lastAttemptAt?: string;
|
|
1035
|
+
lastError?: string;
|
|
1036
|
+
}
|
|
1037
|
+
|
|
1038
|
+
export type BilkoHostDocumentSource =
|
|
1039
|
+
| { kind: 'project-page-lens'; lens: 'home' | 'marketing' | 'feature' | 'architecture' }
|
|
1040
|
+
| { kind: 'file'; path: string };
|
|
1041
|
+
|
|
1042
|
+
export interface BilkoHostDocument {
|
|
1043
|
+
id: string;
|
|
1044
|
+
/** '' for the root document (dist/index.html); otherwise a '/'-joined lowercase-kebab path, e.g. 'special-doc/01'. */
|
|
1045
|
+
subpath: string;
|
|
1046
|
+
title: string;
|
|
1047
|
+
source: BilkoHostDocumentSource;
|
|
1048
|
+
addedAt: string;
|
|
1049
|
+
/** Where this document resolves once published, given the project's current/derived slug. */
|
|
1050
|
+
url: string;
|
|
1051
|
+
}
|
|
1052
|
+
|
|
1053
|
+
export interface BilkoHostGetResult {
|
|
1054
|
+
hasMarketingPage: boolean;
|
|
1055
|
+
projectName: string;
|
|
1056
|
+
packagePrivate: boolean;
|
|
1057
|
+
packageHomepage: string | null;
|
|
1058
|
+
packageVersion: string;
|
|
1059
|
+
defaultSlug: string;
|
|
1060
|
+
documents: BilkoHostDocument[];
|
|
1061
|
+
/** True when the on-disk dist/ bundle doesn't reflect the current document list — Prepare Bundle (then Publish) is owed. */
|
|
1062
|
+
bundleStale: boolean;
|
|
1063
|
+
bundleManifest: BilkoHostBundleManifest | null;
|
|
1064
|
+
publishState: BilkoHostPublishState | null;
|
|
1065
|
+
}
|
|
1066
|
+
|
|
1067
|
+
export interface BilkoHostPrepareBundleResult {
|
|
1068
|
+
distPath: string;
|
|
1069
|
+
manifest: BilkoHostBundleManifest;
|
|
1070
|
+
}
|
|
1071
|
+
|
|
1072
|
+
export interface BilkoHostDocumentListResult {
|
|
1073
|
+
documents: Array<Omit<BilkoHostDocument, 'url'>>;
|
|
1074
|
+
}
|
|
1075
|
+
|
|
1001
1076
|
// ────────────────────────────────────────────── Per-subagent memory
|
|
1002
1077
|
// Stored at ~/.claude/session-manager/agent-memory/<agentId>.json. Keyed by
|
|
1003
1078
|
// agent name (the .md filename in ~/.claude/agents/), not by workspace cwd.
|
|
@@ -1148,7 +1223,7 @@ export interface ChatCreatePrdPayload {
|
|
|
1148
1223
|
/** Originating tab id — used at job completion to route a status prompt back into the tab. */
|
|
1149
1224
|
sourceTabId?: string;
|
|
1150
1225
|
/** User-selected Feature/Bug tag (PRD 774) carried from the originating PromptTicket. */
|
|
1151
|
-
tag?: 'feature' | 'bug' | 'discussion' | 'build' | 'project-home-builder';
|
|
1226
|
+
tag?: 'feature' | 'bug' | 'discussion' | 'build' | 'project-home-builder' | 'bilko-host-publisher';
|
|
1152
1227
|
}
|
|
1153
1228
|
|
|
1154
1229
|
export type ChatCreatePrdResult =
|
|
@@ -1568,6 +1643,16 @@ export interface SessionManagerAPI {
|
|
|
1568
1643
|
/** Read output/*.html + manifest.json (or `{output: null}` if none exist yet). Never fires an LLM call. */
|
|
1569
1644
|
get: (cwd: string) => Promise<ProjectPagesGetResult>;
|
|
1570
1645
|
};
|
|
1646
|
+
bilkoHost: {
|
|
1647
|
+
/** Read compatibility-gate inputs + any existing bundle/publish state. Never fires an LLM call. */
|
|
1648
|
+
get: (cwd: string) => Promise<BilkoHostGetResult>;
|
|
1649
|
+
/** Stage A: rebuild the whole dist/ tree + dist/manifest.json from the current document list. Pure, no cost, idempotent. */
|
|
1650
|
+
prepareBundle: (cwd: string, slug: string) => Promise<BilkoHostPrepareBundleResult>;
|
|
1651
|
+
/** Add a hosted sub-path document (never the root, which always exists). Does not touch dist/ — Prepare Bundle picks it up. */
|
|
1652
|
+
addDocument: (cwd: string, subpath: string, title: string, source: BilkoHostDocumentSource) => Promise<BilkoHostDocumentListResult>;
|
|
1653
|
+
/** Remove a hosted document (never the root). Only updates documents.json — the file stays live until the next Prepare Bundle + Publish. */
|
|
1654
|
+
removeDocument: (cwd: string, id: string) => Promise<BilkoHostDocumentListResult>;
|
|
1655
|
+
};
|
|
1571
1656
|
promptSessionTranscript: {
|
|
1572
1657
|
/** Append one full-text turn to an Epic's durable JSONL transcript. Best-effort — resolves `{ok:false}` rather than throwing on failure. */
|
|
1573
1658
|
append: (
|
package/src/preload/index.cjs
CHANGED
|
@@ -343,6 +343,13 @@ contextBridge.exposeInMainWorld('api', {
|
|
|
343
343
|
projectPages: {
|
|
344
344
|
get: (cwd) => ipcRenderer.invoke('project-pages:get', { cwd }),
|
|
345
345
|
},
|
|
346
|
+
bilkoHost: {
|
|
347
|
+
get: (cwd) => ipcRenderer.invoke('bilko-host:get', { cwd }),
|
|
348
|
+
prepareBundle: (cwd, slug) => ipcRenderer.invoke('bilko-host:prepare-bundle', { cwd, slug }),
|
|
349
|
+
addDocument: (cwd, subpath, title, source) =>
|
|
350
|
+
ipcRenderer.invoke('bilko-host:add-document', { cwd, subpath, title, source }),
|
|
351
|
+
removeDocument: (cwd, id) => ipcRenderer.invoke('bilko-host:remove-document', { cwd, id }),
|
|
352
|
+
},
|
|
346
353
|
promptSessionTranscript: {
|
|
347
354
|
append: (cwd, epicId, turn) =>
|
|
348
355
|
ipcRenderer.invoke('promptSessionTranscript:append', { cwd, epicId, ...turn }),
|