/** * Shared context builders for session startup. * Used by LoadContext.ts (Claude Code) and the opencode plugin. */ import { existsSync, readFileSync } from "node:fs"; import { resolve } from "node:path"; import type { AgentName } from "./agent-registry"; import { resolveAnchor } from "./anchor"; import { dueNudgeSections } from "./daily-nudge"; import { loadHandoffContext } from "./handoff-context"; import { loadOpinionContext } from "./opinions"; import { paths, toPath } from "./paths"; import { loadActiveProjectsContext } from "./projects"; import { loadRecentNotes } from "./relationship"; import { loadFailurePatterns, loadSteeringRules } from "./semi-static"; import { sessionDir } from "./session-dir"; import * as settings from "./settings"; import { readFramePrinciples } from "./wisdom"; import { lastSessionTitle } from "./work-tracking"; /** Load and concatenate loadAtStartup files */ function loadStartupFiles(): string { const files = settings.startupFiles(); if (files.length === 0) return ""; const sections: string[] = []; for (const file of files) { const resolved = toPath(file); if (!existsSync(resolved)) continue; try { const content = readFileSync(resolved, "utf-8").trim(); if (content) sections.push(content); } catch { /* skip unreadable files */ } } return sections.join("\n\n---\n\n"); } /** Load high-confidence wisdom principles for injection into system-reminder */ export function loadWisdomContext(): string { try { const principles = readFramePrinciples(); if (principles.length === 0) return ""; return ["## Crystallized Principles", ...principles.map((p) => `- ${p}`)].join("\n"); } catch { return ""; } } /** Load self-model for session context injection */ function loadSelfModel(): string { try { const p = resolve(paths.memory(), "self-model", "current.md"); if (!existsSync(p)) return ""; const content = readFileSync(p, "utf-8").trim(); if (!content) return ""; return content; } catch { return ""; } } function stampedPath(stamp: string): string { const resolved = resolveAnchor(stamp); return resolved.state === "unresolvable" ? stamp : resolved.path; } /** * Filter raw relationship note lines: * - O entries: stripped (loaded natively via digest) * - HTML comments: stripped, but cwd is extracted from session comments * - Session entries: kept only if block cwd matches current project (or legacy with no cwd) * - W entries and structural lines: always kept */ function filterRelationshipNotes(notes: string, cwd: string): string { const lines = notes.split("\n"); const out: string[] = []; let blockCwd: string | null = null; for (const line of lines) { if (/^## \d{2}:\d{2}/.test(line)) { blockCwd = null; out.push(line); continue; } const cwdMatch = new RegExp(/