import * as fs from "node:fs"; import * as path from "node:path"; const MAX_META_FILE_CHARS = 12_000; const MAX_META_TOTAL_CHARS = 32_000; export const META_FILE_ORDER = ["GOAL.md", "LEDGER.md", "STATUS.md", "ROADMAP.md"]; const META_FILE_TEMPLATES: Record = { "GOAL.md": "# Goal\n\nRecord the user's durable goal, constraints, and completion evidence here.\n", "ROADMAP.md": "# Roadmap\n\nTrack milestones, ordering, dependencies, and evidence for completing the goal.\n", "LEDGER.md": "# Ledger\n\nUse this for durable facts, decisions, evidence, failed approaches, blockers, and useful pointers.\n", "STATUS.md": "# Status\n\nKeep a brief human-readable summary of material results, remaining work or blockers, and evidence.\n", }; export interface MetaBootstrap { created: string[]; existing: string[]; } function orderedMarkdownFiles(metaDir: string): string[] { const files = fs .readdirSync(metaDir, { withFileTypes: true }) .filter((entry) => entry.isFile() && entry.name.toLowerCase().endsWith(".md")) .map((entry) => entry.name); const priority = new Map(META_FILE_ORDER.map((name, index) => [name, index])); return files.sort((left, right) => { const leftPriority = priority.get(left) ?? META_FILE_ORDER.length; const rightPriority = priority.get(right) ?? META_FILE_ORDER.length; return leftPriority - rightPriority || left.localeCompare(right); }); } export function metaDirFor(cwd: string): string { return path.join(cwd, ".pi", "meta"); } export function readMetaFiles(cwd: string): Array<{ name: string; content: string }> { const metaDir = metaDirFor(cwd); try { if (!fs.statSync(metaDir).isDirectory()) return []; } catch { return []; } const result: Array<{ name: string; content: string }> = []; let remaining = MAX_META_TOTAL_CHARS; for (const name of orderedMarkdownFiles(metaDir)) { if (remaining <= 0) break; try { const content = fs.readFileSync(path.join(metaDir, name), "utf8"); const included = content.slice(0, Math.min(MAX_META_FILE_CHARS, remaining)); result.push({ name, content: included }); remaining -= included.length; } catch { // A concurrently changed meta file should not block an agent turn. } } return result; } export function bootstrapMetaFiles(cwd: string): MetaBootstrap { const metaDir = metaDirFor(cwd); fs.mkdirSync(metaDir, { recursive: true }); const result: MetaBootstrap = { created: [], existing: [] }; for (const name of META_FILE_ORDER) { const filePath = path.join(metaDir, name); try { fs.writeFileSync(filePath, META_FILE_TEMPLATES[name] ?? "", { encoding: "utf8", flag: "wx" }); result.created.push(name); } catch (error) { if ((error as NodeJS.ErrnoException).code !== "EEXIST") throw error; result.existing.push(name); } } return result; } export function buildOrchestrationPrompt(cwd: string): string { const files = readMetaFiles(cwd); const metaDir = metaDirFor(cwd); const guidance = [ "## Autonomous execution", "", "Work head down and autonomously until the user's entire goal is achieved and supported by concrete evidence.", `- The fixed durable-memory directory for this session is \`${metaDir}\`. Keep it there even when the implementation or reference repositories live in nested directories.`, "- Use the standard files supplied there—`GOAL.md`, `LEDGER.md`, `STATUS.md`, and `ROADMAP.md`—as durable memory when useful.", "- `GOAL.md`: the durable target, constraints, and completion evidence. Update it when the user changes the goal or evidence corrects an earlier assumption.", "- `LEDGER.md`: durable working memory for yourself: important facts, technical details, decisions, evidence, failed approaches, blockers, and useful pointers.", "- `STATUS.md`: a brief plain-language snapshot for the human: material results, what remains or is blocked, and supporting evidence. Summarize rather than copy the ledger.", "- `ROADMAP.md`: the strategic milestone map: phases, ordering, dependencies, coverage, and evidence showing which milestones are complete.", `- These are standard files, not a limit. Create additional clearly named Markdown files in \`${metaDir}\` when useful for durable note-taking.`, "- Until the goal is verifiably achieved, keep taking substantive actions. A summary of progress, a list of remaining work, or the size of the remaining work is not a reason to stop.", "- Do not end after a small batch of changes. Continue through the complete remaining queue of known tasks, gaps, unchecked roadmap items, TODOs, placeholders, stubs, failing checks, and unverified behavior.", "- Only end when no known work remains. Do not mark an item complete merely to shorten the remaining list; completion requires evidence.", "- Do not ask the user for guidance, prioritization, confirmation, or permission when a reasonable autonomous choice is available. Ask only when progress truly requires unavailable user-only information or authority.", "- Do not pause for progress reports between actions. Keep using tools and completing work.", "- Do not end a work pass to seek assessment, approval, or more direction. Perform all work and verification available to you first.", "- Do not silently narrow, downgrade, or relabel the user's goal to match partial progress.", "- Maintaining meta files is not progress and must not replace substantive work.", "- Updating durable memory or committing a batch is not a stopping point while any known work remains; return immediately to substantive work.", "- For broad goals with multiple independent workstreams, actively use subagents early. Give them concrete deliverables and non-overlapping ownership, run independent work in parallel when safe, and continue your own work while they run. Integrate and verify their results; delegation does not transfer responsibility for the full goal.", "- Create specialized agents when that materially improves a workstream. Keep `.pi/meta/` out of delegated prompts unless directly needed.", "- If repeated approaches stall, reassess the strategy and try a materially different approach.", "- Before claiming completion, compare the result with `GOAL.md` and verify it with concrete evidence.", "- Before ending your work, synchronize `GOAL.md`, `LEDGER.md`, `STATUS.md`, and `ROADMAP.md` with the actual result, concrete evidence, and any remaining limitations.", ]; if (files.length === 0) return guidance.join("\n"); const memory = files.flatMap(({ name, content }) => ["", `### .pi/meta/${name}`, "", content]); return [...guidance, "", "Current durable memory:", ...memory].join("\n"); }