import { lstat, realpath } from "node:fs/promises"; import { isAbsolute, relative, resolve, sep } from "node:path"; import { isEditToolResult, isReadToolResult, isToolCallEventType, isWriteToolResult, type ExtensionAPI, } from "@earendil-works/pi-coding-agent"; import { Type } from "typebox"; import { buildExplorationFacts } from "../src/exploration.ts"; import { DEFAULT_CONFIG, isAutomaticMaintenanceEnabled, loadAgentsMdConfig, } from "../src/config.ts"; import { formatInstructionStatus, InstructionSession, } from "../src/instructions.ts"; import { auditManagedScopes, ManagedPlanStore } from "../src/managed.ts"; import { deriveAffectedScopes, fingerprintSourceMutation, formatCue, isInstructionPath, MAINTENANCE_LEDGER_TYPE, MaintenanceLedger, type MaintenanceLedgerEntry, } from "../src/maintenance.ts"; import { collectRepositoryFacts, planScopes, type FactPack, validateDraft, validateFactPack, } from "../src/repository.ts"; import type { AgentsMdConfig } from "../src/types.ts"; const scanChangedSchema = Type.Object({}); const stageManagedSchema = Type.Object({ planId: Type.String({ description: "Staged manual initialization plan ID" }), scope: Type.String({ description: "Scope from the supplied fact pack" }), markdown: Type.String({ description: "Concise Markdown guidance for the selected repository scope", }), trace: Type.Optional( Type.Array( Type.Object({ line: Type.Integer({ minimum: 1 }), factIds: Type.Array(Type.String()), }), ), ), }); const applyInitializedSchema = Type.Object({ planId: Type.String({ description: "Manual initialization plan ID" }), }); const finalizeInitializationSchema = Type.Object({ findings: Type.Array( Type.Object({ scope: Type.String({ description: "Containing repository scope" }), kind: Type.Union([ Type.Literal("layout"), Type.Literal("constraint"), Type.Literal("reference"), ]), value: Type.String({ description: "Concise, source-backed finding" }), sourcePath: Type.String({ description: "Repository-relative file read this turn", }), evidence: Type.String({ description: "Exact excerpt from the recorded read", }), }), { minItems: 1, maxItems: 64 }, ), }); interface InitializationState { readonly root: string; readonly maxDepth: number; readonly maxFiles: number; readonly reads: Map; phase: "discovering" | "drafting"; planId?: string; } const applyManagedSchema = Type.Object({ scope: Type.String({ description: "Affected managed scope from agents_scan_changed", }), markdown: Type.String({ description: "Concise Markdown guidance for the affected repository scope", }), trace: Type.Optional( Type.Array( Type.Object({ line: Type.Integer({ minimum: 1 }), factIds: Type.Array(Type.String()), }), ), ), }); export default function registerAgentsMd(pi: ExtensionAPI): void { const instructions = new InstructionSession(); const managedPlans = new ManagedPlanStore(); const maintenance = new MaintenanceLedger(); let config: AgentsMdConfig = DEFAULT_CONFIG; let scanToolRegistered = false; let stageToolRegistered = false; let initApplyToolRegistered = false; let finalizeInitializationToolRegistered = false; let applyToolRegistered = false; let initialization: InitializationState | undefined; let currentCwd = process.cwd(); const appendLedger = (entry: MaintenanceLedgerEntry): void => { pi.appendEntry(MAINTENANCE_LEDGER_TYPE, entry); }; const recordInitializationRead = async ( cwd: string, targetInput: string, content: string, ): Promise => { const active = initialization; if (!active || content.length === 0 || isInstructionPath(targetInput)) return; try { const lexical = resolve(cwd, targetInput); const info = await lstat(lexical); if (info.isSymbolicLink() || !info.isFile()) return; const target = await realpath(lexical); if (!isContained(active.root, target)) return; const path = toPortablePath(relative(active.root, target)); if (path.length === 0) return; active.reads.set(path, content); } catch { // A failed or changing read is not usable as initialization evidence. } }; const stagePlanForEntry = async ( ctx: { cwd: string }, entry: MaintenanceLedgerEntry, ) => { const scan = await collectRepositoryFacts(ctx.cwd); const scopePlan = planScopes(scan, { maxGeneratedBytes: config.maintenance.maxGeneratedBytes, }); const plan = managedPlans.create(scan, scopePlan); const packs = entry.scopes .map((scope) => plan.factPacks.get(scope)) .filter((pack): pack is FactPack => pack !== undefined) .filter((pack) => validateFactPack(pack).valid); return { plan, packs }; }; const ensureApplyTool = (): void => { if (applyToolRegistered) return; applyToolRegistered = true; pi.registerTool({ name: "agents_apply_managed", label: "Apply Managed AGENTS.md", description: "Apply concise AI-generated guidance to the current maintenance scope.", parameters: applyManagedSchema, async execute(_toolCallId, params, _signal, _onUpdate, _ctx) { const active = maintenance.getActive(); if (!active || active.status !== "scanned" || !active.planId) { return { content: [ { type: "text", text: "No scanned maintenance cue is active." }, ], details: {}, }; } if (!active.scopes.includes(params.scope)) { return { content: [ { type: "text", text: "Scope is outside the active maintenance cue.", }, ], details: {}, }; } const plan = managedPlans.get(active.planId); const pack = plan?.factPacks.get(params.scope); if (!pack) { return { content: [ { type: "text", text: "Active maintenance plan is unavailable; scan the changed scope again.", }, ], details: {}, }; } const draft = { markdown: params.markdown, trace: params.trace ?? [] }; const validation = validateDraft(pack, draft, active.retryCount); if (!validation.valid) { const reason = validation.issues .filter((issue) => issue.severity === "error") .map((issue) => issue.message) .join(" "); if (validation.retryAllowed && active.retryCount === 0) { const updated = maintenance.update(active.fingerprint, "scanned", { planId: active.planId, retryCount: 1, reason, }); if (updated) appendLedger(updated); return { content: [ { type: "text", text: `Structural draft error. One retry remains: ${reason}`, }, ], details: {}, }; } const updated = maintenance.update(active.fingerprint, "blocked", { planId: active.planId, reason, }); managedPlans.delete(active.planId); if (updated) appendLedger(updated); return { content: [{ type: "text", text: reason }], details: {} }; } try { const preview = await managedPlans.stageDraft( active.planId, params.scope, draft, ); const applied = await managedPlans.apply(active.planId); managedPlans.delete(active.planId); const updated = maintenance.update(active.fingerprint, "applied", { planId: active.planId, }); if (updated) appendLedger(updated); return { content: [ { type: "text", text: `Applied ${applied.appliedTargets.join(", ")} from validated preview ${preview.id}.`, }, ], details: { planId: active.planId, previewId: preview.id }, }; } catch (error) { managedPlans.delete(active.planId); const updated = maintenance.update(active.fingerprint, "blocked", { planId: active.planId, reason: commandError(error), }); if (updated) appendLedger(updated); return { content: [{ type: "text", text: commandError(error) }], details: {}, }; } }, }); }; const ensureStageTool = (): void => { if (stageToolRegistered) return; stageToolRegistered = true; pi.registerTool({ name: "agents_stage_managed", label: "Stage Managed AGENTS.md", description: "Stage one AI-generated AGENTS.md draft from the active initialization plan.", parameters: stageManagedSchema, async execute(_toolCallId, params, _signal, _onUpdate, _ctx) { if ( !initialization || initialization.phase !== "drafting" || initialization.planId !== params.planId ) { return { content: [ { type: "text", text: "No active initialization plan is awaiting drafts.", }, ], details: {}, }; } const plan = managedPlans.get(params.planId); if (!plan) { return { content: [ { type: "text", text: "Initialization plan is unavailable." }, ], details: {}, }; } const pack = plan.factPacks.get(params.scope); if (!pack) { return { content: [ { type: "text", text: "Scope is not selected in this plan." }, ], details: {}, }; } const draft = { markdown: params.markdown, trace: params.trace ?? [] }; const validation = validateDraft(pack, draft); if (!validation.valid) { const reason = validation.issues .filter((issue) => issue.severity === "error") .map((issue) => issue.message) .join(" "); return { content: [{ type: "text", text: reason }], details: {} }; } try { const preview = await managedPlans.stageDraft( params.planId, params.scope, draft, ); return { content: [ { type: "text", text: `Staged validated preview ${preview.id} for ${preview.targetPath}. Stage every remaining scope, then call agents_apply_initialized for ${params.planId}.`, }, ], details: { planId: params.planId, previewId: preview.id, targetPath: preview.targetPath, before: preview.before, after: preview.after, }, }; } catch (error) { return { content: [{ type: "text", text: commandError(error) }], details: {}, }; } }, }); }; const ensureInitApplyTool = (): void => { if (initApplyToolRegistered) return; initApplyToolRegistered = true; pi.registerTool({ name: "agents_apply_initialized", label: "Write Initialized AGENTS.md", description: "Apply every validated preview in a completed manual initialization plan, rolling back earlier writes when a later write fails.", parameters: applyInitializedSchema, async execute(_toolCallId, params, _signal, _onUpdate, _ctx) { if ( !initialization || initialization.phase !== "drafting" || initialization.planId !== params.planId ) { return { content: [ { type: "text", text: "No completed initialization plan is active.", }, ], details: {}, }; } const plan = managedPlans.get(params.planId); if (!plan) { return { content: [ { type: "text", text: "Initialization plan is unavailable." }, ], details: {}, }; } if (plan.previews.size !== plan.factPacks.size) { return { content: [ { type: "text", text: "Every selected scope must have a validated preview before applying.", }, ], details: {}, }; } try { const applied = await managedPlans.apply(params.planId); managedPlans.delete(params.planId); initialization = undefined; return { content: [ { type: "text", text: `Applied ${applied.appliedTargets.join(", ")} from initialization plan ${params.planId}.`, }, ], details: { planId: params.planId, targets: applied.appliedTargets }, }; } catch (error) { return { content: [{ type: "text", text: commandError(error) }], details: {}, }; } }, }); }; const ensureFinalizeInitializationTool = (): void => { if (finalizeInitializationToolRegistered) return; finalizeInitializationToolRegistered = true; pi.registerTool({ name: "agents_finalize_init_discovery", label: "Finalize Repository Discovery", description: "Validate source-backed repository findings and return fact packs for AGENTS.md generation.", parameters: finalizeInitializationSchema, async execute(_toolCallId, params, _signal, _onUpdate, ctx) { const active = initialization; if (!active || active.phase !== "discovering") { return { content: [ { type: "text", text: "No active repository discovery is awaiting findings.", }, ], details: {}, }; } try { const scan = await collectRepositoryFacts(ctx.cwd, { maxDepth: active.maxDepth, }); const discovered = buildExplorationFacts( scan, active.reads, params.findings, ); if (discovered.issues.length > 0 || discovered.facts.length === 0) { const reason = discovered.issues.length > 0 ? discovered.issues.join(" ") : "No distinct source-backed findings were provided."; return { content: [{ type: "text", text: reason }], details: {} }; } const enriched = Object.freeze({ ...scan, facts: Object.freeze([...scan.facts, ...discovered.facts]), }); const scopePlan = planScopes(enriched, { maxDepth: active.maxDepth, maxFiles: active.maxFiles, }); const plan = managedPlans.create(enriched, scopePlan); const packs = [...plan.factPacks.values()]; if ( packs.length === 0 || packs.some((pack) => !validateFactPack(pack).valid) ) { return { content: [ { type: "text", text: "Discovery did not produce a valid AGENTS.md fact pack.", }, ], details: {}, }; } active.phase = "drafting"; active.planId = plan.id; ensureStageTool(); ensureInitApplyTool(); return { content: [ { type: "text", text: JSON.stringify({ planId: plan.id, factPacks: packs }), }, ], details: { planId: plan.id, scopes: packs.map((pack) => pack.scope), }, }; } catch (error) { return { content: [{ type: "text", text: commandError(error) }], details: {}, }; } }, }); }; const ensureScanTool = (): void => { if (scanToolRegistered) return; scanToolRegistered = true; pi.registerTool({ name: "agents_scan_changed", label: "Scan Changed Scope", description: "Return repository context for the current maintenance cue.", parameters: scanChangedSchema, async execute(_toolCallId, _params, _signal, _onUpdate, ctx) { const active = maintenance.getActive(); if (!active) { return { content: [ { type: "text", text: "No active AGENTS.md maintenance cue." }, ], details: {}, }; } try { const { plan, packs } = await stagePlanForEntry(ctx, active); if (packs.length === 0) { managedPlans.delete(plan.id); const updated = maintenance.update(active.fingerprint, "blocked", { reason: "No affected scope has validated local facts.", }); if (updated) appendLedger(updated); return { content: [ { type: "text", text: "No managed update is required." }, ], details: {}, }; } const updated = maintenance.update(active.fingerprint, "scanned", { planId: plan.id, }); if (updated) appendLedger(updated); ensureApplyTool(); return { content: [ { type: "text", text: JSON.stringify({ planId: plan.id, factPacks: packs, }), }, ], details: { planId: plan.id, scopes: packs.map((pack) => pack.scope), }, }; } catch (error) { const updated = maintenance.update(active.fingerprint, "blocked", { reason: commandError(error), }); if (updated) appendLedger(updated); return { content: [{ type: "text", text: commandError(error) }], details: {}, }; } }, }); }; pi.on("session_start", async (_event, ctx) => { currentCwd = ctx.cwd; const loaded = await loadAgentsMdConfig({ cwd: ctx.cwd, projectTrusted: ctx.isProjectTrusted(), }); config = loaded.config; maintenance.restore(ctx.sessionManager.getEntries()); if ( isAutomaticMaintenanceEnabled(config, ctx.mode) && maintenance.getActive() ) { ensureScanTool(); } }); pi.on("session_tree", async (_event, ctx) => { currentCwd = ctx.cwd; maintenance.restore(ctx.sessionManager.getEntries()); managedPlans.clear(); initialization = undefined; }); pi.on("agent_start", async () => { maintenance.beginRun(); }); pi.on("session_compact", async () => { instructions.clearReadCache(); }); pi.on("tool_call", async (event) => { const maintenanceActive = maintenance.getActive() !== undefined; if ( !isToolCallEventType("edit", event) && !isToolCallEventType("write", event) ) return; if (initialization) { return { block: true, reason: "AGENTS.md initialization is read-only until its package-owned apply completes. Run /agents-cancel to stop it.", }; } if (!maintenanceActive) return; const target = event.input.path; if ( typeof target === "string" && (isInstructionPath(target) || (await resolvesToInstructionTarget(currentCwd, target))) ) { return { block: true, reason: "Active AGENTS.md maintenance permits package-owned apply writes only.", }; } }); pi.on("tool_result", async (event, ctx) => { if ( !isReadToolResult(event) || event.isError || event.content.length === 0 || event.content.some((block) => block.type !== "text") ) { return; } const target = event.input.path; if (typeof target !== "string") return; await recordInitializationRead( ctx.cwd, target, event.content .map((block) => (block.type === "text" ? block.text : "")) .join("\n"), ); const injection = await instructions.injectForRead(ctx.cwd, target); if (!injection) return; return { content: [...event.content, ...injection.appendedContent] }; }); pi.on("tool_result", async (event, ctx) => { if ( event.isError || (!isEditToolResult(event) && !isWriteToolResult(event)) ) return; if (!isAutomaticMaintenanceEnabled(config, ctx.mode)) return; const maintenanceMode = config.maintenance.mode; if (maintenanceMode === "off" || config.maintenance.maxAffectedFiles <= 0) return; const target = event.input.path; if (typeof target !== "string") return; const mutation = await fingerprintSourceMutation(ctx.cwd, target); if (!mutation) return; try { const scan = await collectRepositoryFacts(ctx.cwd); const scopePlan = planScopes(scan, { maxGeneratedBytes: config.maintenance.maxGeneratedBytes, }); const selectedScopes = scopePlan.decisions .filter((decision) => decision.selected) .map((decision) => decision.scope); const scopes = deriveAffectedScopes(mutation.path, selectedScopes); const registration = maintenance.registerCue( mutation, scopes, maintenanceMode, config.maintenance.maxCuesPerAgentRun, config.maintenance.maxAffectedFiles, ); if (!registration) return; if (registration.entry.mode === "review") { appendLedger(registration.entry); ctx.ui.notify( `AGENTS.md review notification for ${registration.entry.scopes.join(", ")}; no draft was generated.`, "info", ); return; } let plan; try { plan = managedPlans.create(scan, scopePlan); } catch (error) { const updated = maintenance.update( registration.entry.fingerprint, "blocked", { reason: commandError(error), }, ); if (updated) appendLedger(updated); throw error; } const entry = maintenance.update( registration.entry.fingerprint, registration.entry.status, { planId: plan.id, }, ); if (!entry) { managedPlans.delete(plan.id); return; } appendLedger(entry); if (entry.mode === "settled") return; ensureScanTool(); return { content: [...event.content, { type: "text", text: registration.cue }], }; } catch (error) { ctx.ui.notify(commandError(error), "warning"); } }); pi.on("agent_settled", async () => { if (config.maintenance.mode !== "settled") return; const pending = maintenance.getPendingSettled(); if (!pending) return; const entry = maintenance.update(pending.fingerprint, "prompted"); if (!entry) return; pi.appendEntry(MAINTENANCE_LEDGER_TYPE, entry); ensureScanTool(); pi.sendMessage( { customType: "pi-agents-md:maintenance-cue", content: formatCue(entry.scopes), display: false, details: { fingerprint: entry.fingerprint }, }, { triggerTurn: true, deliverAs: "followUp", }, ); }); pi.registerCommand("agents-init", { description: "Explore the repository, then generate AGENTS.md guidance", handler: async (args, ctx) => { try { const options = parseInitOptions(args); const root = await realpath(ctx.cwd); if (!(await lstat(root)).isDirectory()) { throw new Error("Repository root must be a directory."); } initialization = { root, maxDepth: options.depth, maxFiles: options.maxFiles, reads: new Map(), phase: "discovering", }; ensureFinalizeInitializationTool(); pi.sendMessage( { customType: "pi-agents-md:init-discovery", content: formatInitializationDiscoveryRequest(), display: false, details: { maxDepth: options.depth, maxFiles: options.maxFiles }, }, { triggerTurn: true, deliverAs: "followUp" }, ); ctx.ui.notify( "Exploring the repository and generating AGENTS.md guidance in this session.", "info", ); } catch (error) { ctx.ui.notify(commandError(error), "error"); } }, }); pi.registerCommand("agents-cancel", { description: "Cancel an active AGENTS.md initialization", handler: async (_args, ctx) => { if (!initialization) { ctx.ui.notify("No AGENTS.md initialization is active.", "info"); return; } if (initialization.planId) managedPlans.delete(initialization.planId); initialization = undefined; ctx.ui.notify( "Cancelled AGENTS.md initialization; ordinary writes are unblocked.", "info", ); }, }); pi.registerCommand("agents-audit", { description: "Audit managed AGENTS.md blocks without writing", handler: async (args, ctx) => { try { const requestedScope = parseOptionalScope(args); const scan = await collectRepositoryFacts(ctx.cwd); const scopePlan = planScopes(scan); const scopes = requestedScope === undefined ? scopePlan.decisions .filter((decision) => decision.selected) .map((decision) => decision.scope) : [requestedScope]; const findings = await auditManagedScopes(scan.root, scopes); const errors = findings.filter( (finding) => finding.severity === "error", ); ctx.ui.notify( `Audited ${findings.length} scope(s): ${errors.length} error(s). ${findings.map((finding) => `${finding.targetPath}: ${finding.message}`).join(" ")}`, errors.length > 0 ? "warning" : "info", ); } catch (error) { ctx.ui.notify(commandError(error), "error"); } }, }); pi.registerCommand("agents-apply", { description: "Apply staged, validated package-owned AGENTS.md blocks", handler: async (args, ctx) => { const planId = args.trim(); if (planId.length === 0) { ctx.ui.notify("Usage: /agents-apply ", "error"); return; } if (initialization?.planId === planId) { ctx.ui.notify( "Active initialization plans must use agents_apply_initialized after every selected scope is staged.", "error", ); return; } try { const result = await managedPlans.apply(planId); managedPlans.delete(planId); ctx.ui.notify( `Applied ${result.appliedTargets.length} managed AGENTS.md block(s) from ${result.planId}.`, "info", ); } catch (error) { ctx.ui.notify(commandError(error), "error"); } }, }); pi.registerCommand("agents-refresh", { description: "Clear the nested AGENTS.md read cache", handler: async (args, ctx) => { if (args.trim().length > 0) { ctx.ui.notify("Usage: /agents-refresh", "error"); return; } instructions.clearReadCache(); ctx.ui.notify("Cleared nested AGENTS.md read cache.", "info"); }, }); pi.registerCommand("agents-status", { description: "Show nested AGENTS.md injection decisions and budgets", handler: async (_args, ctx) => { const active = maintenance.getActive(); const maintenanceStatus = active ? ` maintenance=${active.status}:${active.scopes.join(",")}` : " maintenance=idle"; const initializationStatus = initialization ? ` initialization=${initialization.phase}${initialization.planId ? `:${initialization.planId}` : ""} reads=${initialization.reads.size}` : " initialization=idle"; ctx.ui.notify( `${formatInstructionStatus(instructions.getStatus())}${maintenanceStatus}${initializationStatus}`, "info", ); }, }); } function parseInitOptions(args: string): { depth: number; maxFiles: number } { let depth = 6; let maxFiles = 64; const tokens = args.trim().length === 0 ? [] : args.trim().split(/\s+/); for (let index = 0; index < tokens.length; index += 1) { const option = tokens[index]; const value = tokens[index + 1]; if ( (option !== "--depth" && option !== "--max-files") || value === undefined ) { throw new Error("Usage: /agents-init [--depth N] [--max-files N]"); } if (!/^\d+$/.test(value) || Number(value) < 1) { throw new Error("Depth and max-files must be positive integers."); } if (option === "--depth") depth = Number(value); else maxFiles = Number(value); index += 1; } return { depth, maxFiles }; } function parseOptionalScope(args: string): string | undefined { const raw = args.trim(); if (raw.length === 0) return undefined; const scope = raw.replaceAll("\\", "/").replace(/^\.\//, "").replace(/\/+$/, "") || "."; if ( scope !== "." && (scope.startsWith("/") || scope .split("/") .some((part) => part.length === 0 || part === "." || part === "..")) ) { throw new Error("Audit path must be a contained relative directory."); } return scope; } export function formatInitializationDiscoveryRequest(): string { return [ "Initialize repository AGENTS.md guidance in two phases: understand first, then generate.", "Phase 1 is autonomous exploration. Inspect the project tree, manifests, build and test configuration, CI, existing instruction files, entry points, core modules, representative tests, and non-obvious subsystem boundaries. Use read, search, code navigation, project analysis, and shell inspection commands when useful. Do not use edit or write tools; the package applies the final files.", "After understanding the repository, call agents_finalize_init_discovery with concise natural-language findings. Each finding must cite a file read in this turn and include a short excerpt so the extension can place it in the correct scope. Use a root scope for cross-repository guidance and child scopes only for real local differences.", "Phase 2 begins after the discovery tool returns fact packs. Render useful, concise Markdown with headings and wording that fit the repository; trace metadata is optional. Call agents_stage_managed for every pack, then agents_apply_initialized. Do not write AGENTS.md directly.", ].join("\n\n"); } function toPortablePath(path: string): string { return path.split(sep).join("/"); } function isContained(root: string, target: string): boolean { const child = relative(root, target); return ( child === "" || (!isAbsolute(child) && child !== ".." && !child.startsWith(`..${sep}`)) ); } async function resolvesToInstructionTarget( cwd: string, targetInput: string, ): Promise { try { const target = await realpath(resolve(cwd, targetInput)); return isInstructionPath(target); } catch { return false; } } function commandError(error: unknown): string { return error instanceof Error ? error.message : "AGENTS.md operation failed."; }