Loading source
Pulling the file list, source metadata, and syntax-aware rendering for this listing.
Source from repo
One-time setup that gathers your project's design context and saves it to CLAUDE.md for future sessions.
Files
Skill
Size
Entrypoint
Format
Open file
Syntax-highlighted preview of this file as included in the skill package.
scripts/live-session-store.mjs
1import fs from 'node:fs';2import path from 'node:path';3import { getLegacyLiveSessionsDir, getLiveSessionsDir } from './impeccable-paths.mjs';45const COMPLETED_PHASES = new Set(['completed', 'discarded']);67export function createLiveSessionStore({ cwd = process.cwd(), sessionId } = {}) {8const rootDir = getLiveSessionsDir(cwd);9const legacyRootDir = getLegacyLiveSessionsDir(cwd);10fs.mkdirSync(rootDir, { recursive: true });11const snapshotCache = new Map();1213function loadCachedOrRebuild(id) {14const cached = snapshotCache.get(id);15if (cached) return cached;16const journalPath = getReadableJournalPath(id);17const rebuilt = rebuildSnapshotFromJournal(journalPath, id);18snapshotCache.set(id, rebuilt);19return rebuilt;20}2122function getReadableJournalPath(id) {23const primary = getJournalPath(rootDir, id);24if (fs.existsSync(primary)) return primary;25const legacy = getJournalPath(legacyRootDir, id);26if (fs.existsSync(legacy)) return legacy;27return primary;28}2930return {31rootDir,32legacyRootDir,33appendEvent(event) {34const normalized = normalizeEvent(event, sessionId);35const journalPath = getJournalPath(rootDir, normalized.id);36const snapshotPath = getSnapshotPath(rootDir, normalized.id);37const legacyJournalPath = getJournalPath(legacyRootDir, normalized.id);38if (!fs.existsSync(journalPath) && fs.existsSync(legacyJournalPath)) {39fs.copyFileSync(legacyJournalPath, journalPath);40}41const prior = loadCachedOrRebuild(normalized.id);42const seq = prior.nextSeq;43const entry = {44seq,45id: normalized.id,46type: normalized.type,47ts: new Date().toISOString(),48event: normalized,49};50fs.appendFileSync(journalPath, JSON.stringify(entry) + '\n');51const next = applyEvent(prior.snapshot, entry, prior.diagnostics);52snapshotCache.set(normalized.id, { snapshot: next, diagnostics: next.diagnostics || [], nextSeq: seq + 1 });53writeSnapshot(snapshotPath, next);54return next;55},56getSnapshot(id = sessionId, opts = {}) {57if (!id) throw new Error('session id required');58const journalPath = getReadableJournalPath(id);59const snapshotPath = getSnapshotPath(rootDir, id);60const rebuilt = rebuildSnapshotFromJournal(journalPath, id);61snapshotCache.set(id, rebuilt);62writeSnapshot(snapshotPath, rebuilt.snapshot);63if (!opts.includeCompleted && COMPLETED_PHASES.has(rebuilt.snapshot.phase)) return null;64return rebuilt.snapshot;65},66listActiveSessions() {67const ids = new Set();68for (const dir of [legacyRootDir, rootDir]) {69if (!fs.existsSync(dir)) continue;70for (const name of fs.readdirSync(dir)) {71if (name.endsWith('.jsonl')) ids.add(name.slice(0, -'.jsonl'.length));72}73}74return [...ids]75.sort()76.map((id) => this.getSnapshot(id))77.filter(Boolean);78},79};80}8182function normalizeEvent(event, fallbackId) {83if (!event || typeof event !== 'object') throw new Error('event object required');84const id = event.id || fallbackId;85if (!id || typeof id !== 'string') throw new Error('event id required');86if (!event.type || typeof event.type !== 'string') throw new Error('event type required');87return { ...event, id };88}8990function getJournalPath(rootDir, id) {91return path.join(rootDir, safeSessionId(id) + '.jsonl');92}9394function getSnapshotPath(rootDir, id) {95return path.join(rootDir, safeSessionId(id) + '.snapshot.json');96}9798function safeSessionId(id) {99if (!/^[A-Za-z0-9_-]{1,128}$/.test(id)) throw new Error('invalid session id: ' + id);100return id;101}102103function baseSnapshot(id) {104return {105id,106phase: 'new',107pageUrl: null,108sourceFile: null,109expectedVariants: 0,110arrivedVariants: 0,111visibleVariant: null,112paramValues: {},113pendingEventSeq: null,114pendingEvent: null,115deliveryLease: null,116checkpointRevision: 0,117activeOwner: null,118sourceMarkers: {},119fallbackMode: null,120annotationArtifacts: [],121diagnostics: [],122updatedAt: null,123};124}125126function rebuildSnapshotFromJournal(journalPath, id) {127let snapshot = baseSnapshot(id);128const diagnostics = [];129let nextSeq = 1;130if (!fs.existsSync(journalPath)) return { snapshot, diagnostics, nextSeq };131132const lines = fs.readFileSync(journalPath, 'utf-8').split('\n');133for (let i = 0; i < lines.length; i++) {134const line = lines[i];135if (!line.trim()) continue;136try {137const entry = JSON.parse(line);138if (!entry || typeof entry !== 'object') throw new Error('entry is not object');139if (Number.isInteger(entry.seq)) nextSeq = Math.max(nextSeq, entry.seq + 1);140snapshot = applyEvent(snapshot, entry);141} catch (err) {142diagnostics.push({143error: 'journal_parse_failed',144line: i + 1,145message: err.message,146});147}148}149snapshot.diagnostics = [...snapshot.diagnostics, ...diagnostics];150return { snapshot, diagnostics, nextSeq };151}152153function applyEvent(snapshot, entry, inheritedDiagnostics = []) {154const event = entry.event || entry;155const next = {156...snapshot,157paramValues: { ...(snapshot.paramValues || {}) },158sourceMarkers: { ...(snapshot.sourceMarkers || {}) },159annotationArtifacts: [...(snapshot.annotationArtifacts || [])],160diagnostics: [...(snapshot.diagnostics || [])],161updatedAt: entry.ts || new Date().toISOString(),162};163164if (inheritedDiagnostics.length && next.diagnostics.length === 0) {165next.diagnostics = [...inheritedDiagnostics];166}167168switch (event.type) {169case 'generate':170next.phase = 'generate_requested';171next.pageUrl = event.pageUrl ?? next.pageUrl;172next.expectedVariants = event.count ?? next.expectedVariants;173next.pendingEventSeq = entry.seq ?? next.pendingEventSeq;174next.pendingEvent = toPendingEvent(event);175if (event.screenshotPath) upsertArtifact(next.annotationArtifacts, { type: 'screenshot', path: event.screenshotPath });176break;177case 'variants_ready':178case 'agent_done':179next.phase = event.carbonize === true ? 'carbonize_required' : 'variants_ready';180next.sourceFile = event.file ?? next.sourceFile;181next.arrivedVariants = event.arrivedVariants ?? (next.arrivedVariants ?? next.expectedVariants);182next.pendingEventSeq = null;183next.pendingEvent = null;184if (event.carbonize === true) {185next.diagnostics.push({186error: 'carbonize_cleanup_required',187file: event.file || null,188message: 'Accepted variant still has carbonize markers that must be folded into source CSS.',189});190}191break;192case 'checkpoint':193if ((event.revision ?? 0) >= (next.checkpointRevision ?? 0)) {194next.phase = event.phase ?? next.phase;195next.checkpointRevision = event.revision ?? next.checkpointRevision;196next.activeOwner = event.owner ?? next.activeOwner;197next.arrivedVariants = event.arrivedVariants ?? next.arrivedVariants;198next.visibleVariant = event.visibleVariant ?? next.visibleVariant;199if (event.paramValues) next.paramValues = { ...event.paramValues };200} else {201next.diagnostics.push({ error: 'stale_checkpoint_ignored', revision: event.revision });202}203break;204case 'accept':205case 'accept_intent':206next.phase = 'accept_requested';207next.visibleVariant = Number(event.variantId ?? next.visibleVariant);208if (event.paramValues) next.paramValues = { ...event.paramValues };209next.pendingEventSeq = entry.seq ?? next.pendingEventSeq;210next.pendingEvent = toPendingEvent(event);211break;212case 'discard':213next.phase = 'discard_requested';214next.pendingEventSeq = entry.seq ?? next.pendingEventSeq;215next.pendingEvent = toPendingEvent(event);216break;217case 'discarded':218next.phase = 'discarded';219next.pendingEventSeq = null;220next.pendingEvent = null;221break;222case 'complete':223next.phase = 'completed';224next.pendingEventSeq = null;225next.pendingEvent = null;226break;227case 'agent_error':228next.phase = 'agent_error';229next.pendingEventSeq = null;230next.pendingEvent = null;231next.diagnostics.push({ error: 'agent_error', message: event.message || 'unknown agent error' });232break;233default:234next.diagnostics.push({ error: 'unknown_event_type', type: event.type });235break;236}237return next;238}239240function toPendingEvent(event) {241const pending = { ...event };242delete pending.token;243return pending;244}245246function upsertArtifact(artifacts, artifact) {247if (!artifacts.some((existing) => existing.path === artifact.path && existing.type === artifact.type)) {248artifacts.push(artifact);249}250}251252function writeSnapshot(snapshotPath, snapshot) {253fs.writeFileSync(snapshotPath, JSON.stringify(snapshot, null, 2) + '\n');254}255