Planner
Functions
planStructure
async function planStructure(elements: APIElement[], topology: TopologyAnalysis, client: LLMClient): Promise<DocStructure>
Use planStructure to let an LLM analyze your scanned API surface and decide how documentation should be organized — which pages to create, what to group together, and how navigation should flow.
Reach for this after you've scanned a codebase and run topology analysis. Rather than dumping every exported symbol into a flat list, planStructure reasons about relationships between elements and produces a coherent information architecture before any docs are written.
It sends a summary of your API surface and its dependency topology to the LLM, which returns a structured plan: pages, groupings, and a navigation tree. That plan then drives the actual doc generation step.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
elements | APIElement[] | Yes | The full list of scanned API symbols (functions, classes, types) extracted from your source. Pass the output of your scanner directly — order doesn't matter. |
topology | TopologyAnalysis | Yes | Dependency and relationship graph between elements, produced by topology analysis. Gives the LLM the context it needs to group related symbols intelligently. |
client | LLMClient | Yes | Configured LLM client that handles the actual AI call. Controls which model and provider are used for planning. |
Returns
Returns a DocStructure containing the planned pages, element groupings, and navigation tree. Pass this directly to your doc generation step — each PagePlan in the structure maps a set of APIElements to an output file, and the NavigationNode tree defines the sidebar hierarchy.
Heads up
- The quality of the structure depends heavily on the topology analysis. If
topologyis sparse or missing relationships, the LLM will fall back to grouping by file — run a full topology pass before calling this. - This makes an LLM call and can be slow for large APIs (500+ elements). Consider caching the returned
DocStructureto disk so you don't re-plan on every generation run.
Example:
// Inline types — do not import from autodocs
type APIElement = {
name: string;
kind: "function" | "class" | "type" | "interface";
filePath: string;
signature: string;
description?: string;
};
type TopologyAnalysis = {
clusters: Array<{ label: string; elementNames: string[] }>;
dependencies: Record<string, string[]>;
};
type PagePlan = {
slug: string;
title: string;
elementNames: string[];
};
type NavigationNode = {
title: string;
href?: string;
children?: NavigationNode[];
};
type DocStructure = {
pages: PagePlan[];
navigation: NavigationNode[];
};
type LLMClient = {
complete: (prompt: string) => Promise<string>;
};
// Mock planStructure — mirrors the real implementation's behavior
async function planStructure(
elements: APIElement[],
topology: TopologyAnalysis,
client: LLMClient
): Promise<DocStructure> {
const summary = elements
.map((el) => `${el.kind} ${el.name}: ${el.signature}`)
.join("\n");
const clusterSummary = topology.clusters
.map((c) => `${c.label}: ${c.elementNames.join(", ")}`)
.join("\n");
const prompt = `
You are planning documentation structure for an API.
API elements:
${summary}
Detected clusters:
${clusterSummary}
Return a JSON object with:
- pages: array of { slug, title, elementNames[] }
- navigation: array of { title, href?, children? }
`;
const raw = await client.complete(prompt);
return JSON.parse(raw) as DocStructure;
}
// Mock LLM client — replace with your real provider
const mockLLMClient: LLMClient = {
complete: async (_prompt: string): Promise<string> => {
const structure: DocStructure = {
pages: [
{
slug: "authentication",
title: "Authentication",
elementNames: ["createToken", "revokeToken", "validateToken"],
},
{
slug: "users",
title: "Users",
elementNames: ["createUser", "getUser", "deleteUser"],
},
],
navigation: [
{
title: "API Reference",
children: [
{ title: "Authentication", href: "/docs/authentication" },
{ title: "Users", href: "/docs/users" },
],
},
],
};
return JSON.stringify(structure);
},
};
const elements: APIElement[] = [
{ name: "createToken", kind: "function", filePath: "src/auth/token.ts", signature: "createToken(userId: string): Promise<string>" },
{ name: "revokeToken", kind: "function", filePath: "src/auth/token.ts", signature: "revokeToken(token: string): Promise<void>" },
{ name: "validateToken", kind: "function", filePath: "src/auth/token.ts", signature: "validateToken(token: string): Promise<boolean>" },
{ name: "createUser", kind: "function", filePath: "src/users/user.ts", signature: "createUser(email: string): Promise<User>" },
{ name: "getUser", kind: "function", filePath: "src/users/user.ts", signature: "getUser(id: string): Promise<User>" },
{ name: "deleteUser", kind: "function", filePath: "src/users/user.ts", signature: "deleteUser(id: string): Promise<void>" },
];
const topology: TopologyAnalysis = {
clusters: [
{ label: "Authentication", elementNames: ["createToken", "revokeToken", "validateToken"] },
{ label: "Users", elementNames: ["createUser", "getUser", "deleteUser"] },
],
dependencies: {
createToken: ["createUser"],
revokeToken: [],
validateToken: [],
createUser: [],
getUser: [],
deleteUser: [],
},
};
async function main() {
try {
const structure = await planStructure(elements, topology, mockLLMClient);
console.log("Planned pages:");
structure.pages.forEach((page) => {
console.log(` /${page.slug} — "${page.title}" (${page.elementNames.length} elements)`);
});
console.log("\nNavigation tree:");
console.log(JSON.stringify(structure.navigation, null, 2));
// Expected output:
// Planned pages:
// /authentication — "Authentication" (3 elements)
// /users — "Users" (3 elements)
//
// Navigation tree:
// [
// {
// "title": "API Reference",
// "children": [
// { "title": "Authentication", "href": "/docs/authentication" },
// { "title": "Users", "href": "/docs/users" }
// ]
// }
// ]
} catch (err) {
console.error("Failed to plan structure:", err);
}
}
main();