63 lines
2.4 KiB
JavaScript
63 lines
2.4 KiB
JavaScript
// Appends unregistered docs/superpowers/**/*.md entries to manifest.yaml.
|
|
// Uses yaml.parseDocument for comment-preserving round-trip.
|
|
// Idempotent: re-running adds 0 entries when all docs are already registered.
|
|
// Usage: node scripts/scaffold-manifest.mjs
|
|
import { readFileSync, writeFileSync, readdirSync, existsSync } from 'node:fs';
|
|
import { dirname, resolve, join } from 'node:path';
|
|
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
import { createRequire } from 'node:module';
|
|
import { scaffoldEntryObject, findUnregistered } from './lib/design-index.mjs';
|
|
|
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
const SP = resolve(__dirname, '../docs/superpowers');
|
|
|
|
// `yaml` lives in ui/node_modules — same resolution pattern as validate-design-docs.mjs
|
|
const require = createRequire(pathToFileURL(resolve(__dirname, '../ui/package.json')));
|
|
const YAML = await import(pathToFileURL(require.resolve('yaml')).href);
|
|
|
|
const SUBDIRS = ['specs', 'plans', 'archived/specs', 'archived/plans'];
|
|
|
|
function listDocs() {
|
|
const out = [];
|
|
for (const d of SUBDIRS) {
|
|
const abs = join(SP, d);
|
|
if (!existsSync(abs)) continue;
|
|
for (const f of readdirSync(abs)) {
|
|
if (f.endsWith('.md')) out.push(`${d}/${f}`);
|
|
}
|
|
}
|
|
return out.sort();
|
|
}
|
|
|
|
const mfPath = join(SP, 'manifest.yaml');
|
|
const rawYaml = existsSync(mfPath) ? readFileSync(mfPath, 'utf-8') : 'docs:\n';
|
|
const doc = YAML.parseDocument(rawYaml);
|
|
|
|
// Ensure `docs` key exists as a sequence (handle null scalar from 'docs:\n' seed)
|
|
const existingSeq = doc.get('docs', true);
|
|
if (!(existingSeq instanceof YAML.YAMLSeq)) {
|
|
doc.set('docs', new YAML.YAMLSeq());
|
|
}
|
|
const seq = doc.get('docs', true); // true = return the node itself, not the JS value
|
|
seq.flow = false; // force block style for human curability
|
|
|
|
// Build the set of already-registered paths
|
|
const registered = new Set(
|
|
(seq.items ?? []).map((n) => n.get('path'))
|
|
);
|
|
|
|
const unregistered = findUnregistered(listDocs(), registered);
|
|
|
|
for (const rel of unregistered) {
|
|
const md = readFileSync(join(SP, rel), 'utf-8');
|
|
const entry = scaffoldEntryObject(rel, md);
|
|
const node = doc.createNode(entry);
|
|
// Attach # TODO: verify topic comment to the topic scalar node
|
|
const topicItem = node.get('topic', true);
|
|
if (topicItem) topicItem.comment = ' TODO: verify topic';
|
|
seq.add(node);
|
|
}
|
|
|
|
writeFileSync(mfPath, String(doc), 'utf-8');
|
|
console.log(`scaffold: added ${unregistered.length} entries (total ${seq.items.length})`);
|