@autobe/agent
Version:
AI backend server code generator
195 lines • 8.04 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.fillTocDeterministic = fillTocDeterministic;
const uuid_1 = require("uuid");
const FixedAnalyzeTemplate_1 = require("./structures/FixedAnalyzeTemplate");
// ─── Helpers ───
function slugify(text) {
return text
.toLowerCase()
.trim()
.replace(/[^\p{L}\p{N} -]/gu, "")
.replace(/\s+/g, "-")
.replace(/-+/g, "-")
.replace(/^-|-$/g, "");
}
// ─── Zero-cost metric/token stubs ───
const ZERO_TOKEN_USAGE = {
total: 0,
input: { total: 0, cached: 0 },
output: {
total: 0,
reasoning: 0,
accepted_prediction: 0,
rejected_prediction: 0,
},
};
const ZERO_METRIC = {
attempt: 0,
success: 0,
consent: 0,
validationFailure: 0,
invalidJson: 0,
};
const EMPTY_ACQUISITION = { previousAnalysisSections: [] };
// ─── Main ───
/**
* Fill the TOC file (00-toc.md) deterministically — no LLM calls.
*
* Must be called AFTER all other files (01-05) have completed Stage 2/3 so that
* their module/unit/section titles are available for navigation.
*
* Returns the final markdown content directly (no module/unit hierarchy). Also
* sets minimal `unitResults` and `sectionResults` so that
* `convertToSectionEntries()` for preloading still works.
*/
function fillTocDeterministic(ctx, props) {
var _a, _b;
const { scenario, tocFileState, otherFileStates, expandedTemplate } = props;
const step = ((_b = (_a = ctx.state().analyze) === null || _a === void 0 ? void 0 : _a.step) !== null && _b !== void 0 ? _b : -1) + 1;
// Build flat markdown content — no # / ## / ### hierarchy
const content = buildTocContent(scenario, expandedTemplate, otherFileStates);
// Minimal unit/section events for convertToSectionEntries() preloading
const unitEvent = {
type: "analyzeWriteUnit",
id: (0, uuid_1.v7)(),
moduleIndex: 0,
unitSections: [
{
title: "Table of Contents",
purpose: "Project summary, document navigation, glossary, and assumptions.",
content: "Project summary, document navigation, glossary, and assumptions.",
keywords: ["project-summary", "document-map", "glossary", "navigation"],
},
],
step,
retry: 0,
total: 1,
completed: 1,
tokenUsage: Object.assign({}, ZERO_TOKEN_USAGE),
metric: Object.assign({}, ZERO_METRIC),
acquisition: Object.assign({}, EMPTY_ACQUISITION),
created_at: new Date().toISOString(),
};
const sectionEvent = {
type: "analyzeWriteSection",
id: (0, uuid_1.v7)(),
moduleIndex: 0,
unitIndex: 0,
sectionSections: [{ title: "Table of Contents", content }],
step,
retry: 0,
total: 1,
completed: 1,
tokenUsage: Object.assign({}, ZERO_TOKEN_USAGE),
metric: Object.assign({}, ZERO_METRIC),
acquisition: Object.assign({}, EMPTY_ACQUISITION),
created_at: new Date().toISOString(),
};
tocFileState.moduleResult = Object.assign(Object.assign({}, tocFileState.moduleResult), { moduleSections: [
{
title: "Table of Contents",
purpose: "Project summary, document navigation, glossary, and assumptions.",
content: "",
},
] });
tocFileState.unitResults = [unitEvent];
tocFileState.sectionResults = [[sectionEvent]];
return content;
}
// ─── Content builder ───
/**
* Build the entire TOC content as a single markdown string.
*
* Combines project vision, scope, document map, canonical sources, glossary,
* and assumptions into one flat section.
*/
function buildTocContent(scenario, expandedTemplate, otherFileStates) {
const lines = [];
lines.push("### Table of Contents", "");
// ── Project Vision ──
const actors = scenario.actors.map((a) => a.name).join(", ");
const entities = scenario.entities.map((e) => e.name).join(", ");
lines.push(`**${scenario.prefix}** is a backend service with the following actors and domain entities.`, "", `**Actors**: ${actors}`, `**Entities**: ${entities}`);
// ── Scope ──
lines.push("", "---", "", "**Scope**", "");
for (const e of scenario.entities) {
const rels = e.relationships && e.relationships.length > 0
? ` — ${e.relationships.join(", ")}`
: "";
lines.push(`- **${e.name}**${rels}`);
}
lines.push("");
for (const a of scenario.actors) {
lines.push(`- **${a.name}** (${a.kind})`);
}
// ── Document Map ──
lines.push("", "---", "", "**Document Map**", "");
lines.push((0, FixedAnalyzeTemplate_1.buildFixedAnalyzeDocumentMapContent)(expandedTemplate));
// ── Section Navigation ──
lines.push("", "**Section Navigation**");
lines.push("", '<!-- Load sections by ID: `process({ request: { type: "getAnalysisSections", sectionIds: [ID, ...] } })` -->');
// TOC itself occupies section ID 0, so remaining files start from ID 1
let sectionId = 1;
for (const state of otherFileStates) {
if (!state.moduleResult || !state.unitResults)
continue;
const filename = state.file.filename;
lines.push("", `**[${filename}](./${filename})**`);
// Per-file anchor counter for GFM duplicate heading resolution
const anchorCounts = new Map();
const resolveAnchor = (title) => {
var _a;
const base = slugify(title);
const count = (_a = anchorCounts.get(base)) !== null && _a !== void 0 ? _a : 0;
anchorCounts.set(base, count + 1);
return count === 0 ? base : `${base}-${count}`;
};
// Same traversal order as assembleModule / convertToSectionEntries:
// moduleIndex ascending, then unitIndex ascending
for (let moduleIndex = 0; moduleIndex < state.moduleResult.moduleSections.length; moduleIndex++) {
const moduleSection = state.moduleResult.moduleSections[moduleIndex];
const unitEvent = state.unitResults[moduleIndex];
if (!moduleSection || !unitEvent)
continue;
lines.push(`- [${moduleSection.title}](./${filename}#${resolveAnchor(moduleSection.title)})`);
for (const unitSection of unitEvent.unitSections) {
const purpose = unitSection.purpose ? ` — ${unitSection.purpose}` : "";
lines.push(` - [${sectionId}] [${unitSection.title}](./${filename}#${resolveAnchor(unitSection.title)})${purpose}`);
sectionId++;
}
}
}
// ── Canonical Sources ──
lines.push("", "---", "", "**Canonical Sources**", "");
lines.push((0, FixedAnalyzeTemplate_1.buildFixedAnalyzeCanonicalSourceContent)());
// ── Glossary ──
lines.push("", "---", "", "**Glossary**", "");
for (const e of scenario.entities) {
const rels = e.relationships && e.relationships.length > 0
? ` — ${e.relationships.join(", ")}`
: "";
lines.push(`- **${e.name}**${rels}`);
}
// ── Constraints & Features ──
const constraintLines = [];
for (const file of scenario.files) {
if (file.constraints && file.constraints.length > 0) {
for (const c of file.constraints) {
constraintLines.push(`- ${c}`);
}
}
}
if (constraintLines.length > 0) {
lines.push("", "---", "", "**Constraints**", "");
lines.push(...constraintLines);
}
const features = scenario.features.length > 0
? scenario.features.map((f) => `- ${f.id}`).join("\n")
: null;
if (features) {
lines.push("", "**Active Features**", "", features);
}
return lines.join("\n");
}
//# sourceMappingURL=fillTocDeterministic.js.map