165 lines
4.7 KiB
JavaScript
165 lines
4.7 KiB
JavaScript
#!/usr/bin/env node
|
|
'use strict';
|
|
|
|
const fs = require('fs');
|
|
const path = require('path');
|
|
const { createToolRegistry } = require('../lib/tool-registry');
|
|
|
|
const ROOT = path.resolve(__dirname, '..');
|
|
const OUTPUT_PATH = path.join(ROOT, 'docs', 'TOOLS.md');
|
|
|
|
function createRegistry(profile) {
|
|
return createToolRegistry({
|
|
getRuntimeContext: () => ({
|
|
config: { toolProfile: profile },
|
|
projectPath: '/tmp/funplay-cocos-docs-project',
|
|
version: '0.0.0-docs',
|
|
}),
|
|
interactionLog: { add() {} },
|
|
runtimeLog: { add() {}, list: () => [], clear: () => 0 },
|
|
sceneBridge: { call: async () => ({ ok: true }) },
|
|
editorExecutor: async () => ({ ok: true }),
|
|
});
|
|
}
|
|
|
|
function buildToolModel() {
|
|
const fullRegistry = createRegistry('full');
|
|
const coreNames = new Set(createRegistry('core').listTools().map((tool) => tool.name));
|
|
const fullNames = new Set(fullRegistry.listTools().map((tool) => tool.name));
|
|
const catalog = fullRegistry.listToolCatalog()
|
|
.map((tool) => ({
|
|
name: tool.name,
|
|
category: tool.category || 'other',
|
|
profile: tool.profile || 'full',
|
|
enabledInCore: coreNames.has(tool.name),
|
|
enabledInFull: fullNames.has(tool.name),
|
|
readOnly: Boolean(tool.annotations && tool.annotations.readOnlyHint),
|
|
destructive: Boolean(tool.annotations && tool.annotations.destructiveHint),
|
|
description: normalizeDescription(tool.description),
|
|
}))
|
|
.sort((a, b) => a.category.localeCompare(b.category) || a.name.localeCompare(b.name));
|
|
|
|
return {
|
|
coreCount: coreNames.size,
|
|
fullCount: fullNames.size,
|
|
catalog,
|
|
};
|
|
}
|
|
|
|
function normalizeDescription(description) {
|
|
return String(description || '')
|
|
.replace(/\s+/g, ' ')
|
|
.trim();
|
|
}
|
|
|
|
function buildMarkdown(model) {
|
|
const categories = groupBy(model.catalog, (tool) => tool.category);
|
|
const lines = [
|
|
'# Tool Reference',
|
|
'',
|
|
'<!-- This file is generated by `npm run docs:generate`. Do not edit by hand. -->',
|
|
'',
|
|
`Generated from \`lib/tool-registry.js\`. The default \`core\` profile exposes ${model.coreCount} tools; the \`full\` profile exposes ${model.fullCount} tools.`,
|
|
'',
|
|
'## Profile Summary',
|
|
'',
|
|
'| Profile | Tool Count | Purpose |',
|
|
'|---|---:|---|',
|
|
`| \`core\` | ${model.coreCount} | Focused default surface for common editor automation. |`,
|
|
`| \`full\` | ${model.fullCount} | All built-in tools, including destructive and low-level helpers. |`,
|
|
'',
|
|
'## Core Tools',
|
|
'',
|
|
model.catalog
|
|
.filter((tool) => tool.enabledInCore)
|
|
.sort((a, b) => a.name.localeCompare(b.name))
|
|
.map((tool) => `\`${tool.name}\``)
|
|
.join(', '),
|
|
'',
|
|
'## Tools By Category',
|
|
'',
|
|
];
|
|
|
|
for (const category of Object.keys(categories).sort()) {
|
|
const tools = categories[category];
|
|
lines.push(`### ${titleCase(category)}`);
|
|
lines.push('');
|
|
lines.push('| Tool | Profiles | Access | Description |');
|
|
lines.push('|---|---|---|---|');
|
|
for (const tool of tools) {
|
|
lines.push(`| \`${tool.name}\` | ${profileLabel(tool)} | ${accessLabel(tool)} | ${escapeTableCell(tool.description)} |`);
|
|
}
|
|
lines.push('');
|
|
}
|
|
|
|
return `${lines.join('\n').replace(/\n{3,}/g, '\n\n')}\n`;
|
|
}
|
|
|
|
function profileLabel(tool) {
|
|
return tool.enabledInCore ? '`core`, `full`' : '`full`';
|
|
}
|
|
|
|
function accessLabel(tool) {
|
|
if (tool.readOnly) {
|
|
return 'read-only';
|
|
}
|
|
if (tool.destructive) {
|
|
return 'mutating';
|
|
}
|
|
return 'stateful';
|
|
}
|
|
|
|
function titleCase(value) {
|
|
return String(value)
|
|
.split(/[-_\s]+/)
|
|
.filter(Boolean)
|
|
.map((part) => `${part.charAt(0).toUpperCase()}${part.slice(1)}`)
|
|
.join(' ');
|
|
}
|
|
|
|
function escapeTableCell(value) {
|
|
return String(value || '')
|
|
.replace(/\|/g, '\\|')
|
|
.replace(/\n/g, '<br>');
|
|
}
|
|
|
|
function groupBy(values, getKey) {
|
|
return values.reduce((groups, value) => {
|
|
const key = getKey(value);
|
|
if (!groups[key]) {
|
|
groups[key] = [];
|
|
}
|
|
groups[key].push(value);
|
|
return groups;
|
|
}, {});
|
|
}
|
|
|
|
function writeDocs(markdown) {
|
|
const directory = path.dirname(OUTPUT_PATH);
|
|
if (!fs.existsSync(directory)) {
|
|
fs.mkdirSync(directory, { recursive: true });
|
|
}
|
|
fs.writeFileSync(OUTPUT_PATH, markdown, 'utf8');
|
|
}
|
|
|
|
function checkDocs(markdown) {
|
|
const existing = fs.existsSync(OUTPUT_PATH) ? fs.readFileSync(OUTPUT_PATH, 'utf8') : '';
|
|
if (existing !== markdown) {
|
|
console.error('docs/TOOLS.md is out of date. Run `npm run docs:generate`.');
|
|
process.exitCode = 1;
|
|
}
|
|
}
|
|
|
|
function main() {
|
|
const args = process.argv.slice(2);
|
|
const markdown = buildMarkdown(buildToolModel());
|
|
if (args.includes('--check')) {
|
|
checkDocs(markdown);
|
|
return;
|
|
}
|
|
writeDocs(markdown);
|
|
console.log(`Wrote ${path.relative(ROOT, OUTPUT_PATH)}`);
|
|
}
|
|
|
|
main();
|