Release v0.3.3
This commit is contained in:
@@ -0,0 +1,164 @@
|
||||
#!/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();
|
||||
Reference in New Issue
Block a user