import type { ExtensionAPI } from "@earendil-works/pi-coding-agent"; import { Text } from "@earendil-works/pi-tui"; import { Type } from "typebox"; import type { ReadDocParams, DocgraphToolDetails } from "../types.js"; import { readFileSafe, parseMetadata, isInitialized, contentText } from "../utils.js"; export function registerDocgraphRead(pi: ExtensionAPI): void { pi.registerTool({ name: "docgraph_read", label: "Docgraph Read", description: "Read a managed documentation file, returning its metadata block and body. Provides context for AI agents to determine relevance before reading the full file.", parameters: Type.Object({ path: Type.String({ description: "Relative path to the document (e.g. 'AGENTS.md', 'docs/SPEC.md')", }), }), async execute(_toolCallId, params, _signal, _onUpdate, ctx) { const { path } = params as ReadDocParams; if (!isInitialized(ctx)) { return { content: [ { type: "text", text: "Documentation not initialized. Run `docgraph_init` first.", }, ], details: { action: "read", error: "not_initialized", state: { initialized: false, schemaVersion: 1 }, } as DocgraphToolDetails, }; } const raw = readFileSafe(path, ctx.cwd); if (raw === null) { return { content: [ { type: "text", text: `Document not found: ${path}`, }, ], details: { action: "read", error: "not_found", path, state: { initialized: true, schemaVersion: 1 }, } as DocgraphToolDetails & { path: string }, }; } const metadata = parseMetadata(raw, path); // Separate metadata block from body const lines = raw.split("\n"); let bodyStart = 0; let inMeta = false; for (let i = 0; i < lines.length; i++) { if (lines[i]!.startsWith("> **")) inMeta = true; if (inMeta && !lines[i]!.startsWith(">")) { bodyStart = i; break; } } const body = lines.slice(bodyStart).join("\n").trim(); return { content: [ { type: "text", text: [ `## Metadata for \`${path}\``, `- **Purpose:** ${metadata.purpose}`, `- **Audience:** ${metadata.audience}`, `- **Last Updated:** ${metadata.lastUpdated}`, `- **Depends On:** ${metadata.dependsOn.join(", ") || "None"}`, `- **Referenced By:** ${metadata.referencedBy.join(", ") || "Unknown"}`, "", "## Body", body.slice(0, 4000), // Truncate very large docs body.length > 4000 ? "\n\n... (truncated)" : "", ].join("\n"), }, ], details: { action: "read", path, metadata, bodyLength: body.length, state: { initialized: true, schemaVersion: 1 }, } as DocgraphToolDetails & { path: string; metadata: typeof metadata; bodyLength: number }, }; }, renderCall(args, theme) { return new Text( theme.fg("toolTitle", theme.bold("docgraph-read ")) + theme.fg("muted", args.path), 0, 0, ); }, renderResult(result, _opts, theme) { const text = contentText(result.content?.[0]); const firstLine = text.split("\n")[0] ?? ""; return new Text( theme.fg("muted", firstLine), 0, 0, ); }, }); }