| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173 |
- #!/usr/bin/env node
- /**
- * sync-doc-paths — Propagate DocCenter path changes to all workflow JSONs.
- *
- * Usage:
- * node scripts/sync-doc-paths.js # dry-run (show renames only)
- * node scripts/sync-doc-paths.js --apply # actually write changes
- * node scripts/sync-doc-paths.js --rename 6:16 # move path 6 → 16 everywhere
- * node scripts/sync-doc-paths.js --rename 6:16,2:12 # multiple renames
- * node scripts/sync-doc-paths.js --sync-desc # also sync descriptions from registry
- *
- * What it does:
- * 1. Reads src/data/doc-paths.js as the source of truth
- * 2. Scans all workflow JSONs (both .vl-code/workflows/ and seed-workflows/)
- * 3. For each workflow:
- * a. Updates registry.docs descriptions to match doc-paths.js
- * b. If --rename given, remaps old path → new path in registry.docs keys + step docs arrays
- * 4. Updates hardcoded docId references in JS source files
- */
- import fs from 'fs';
- import path from 'path';
- import { fileURLToPath } from 'url';
- const __dirname = path.dirname(fileURLToPath(import.meta.url));
- const ROOT = path.resolve(__dirname, '..');
- // Dynamic import of doc-paths (ES module)
- const { PATH_MAP, DOC_REGISTRY } = await import('../src/data/doc-paths.js');
- const args = process.argv.slice(2);
- const apply = args.includes('--apply');
- const syncDesc = args.includes('--sync-desc');
- const renameIdx = args.indexOf('--rename');
- const renames = new Map(); // oldPath → newPath
- if (renameIdx !== -1 && args[renameIdx + 1]) {
- for (const pair of args[renameIdx + 1].split(',')) {
- const [from, to] = pair.split(':').map(Number);
- if (!isNaN(from) && !isNaN(to)) {
- renames.set(from, to);
- }
- }
- }
- // ─── Workflow JSON directories ──────────────────────────────────
- const WORKFLOW_DIRS = [
- path.join(ROOT, '.vl-code', 'workflows'),
- path.join(ROOT, 'public', 'seed-workflows'),
- ];
- let totalChanges = 0;
- console.log(`\n📄 DocCenter Path Sync ${apply ? '(APPLY MODE)' : '(DRY RUN)'}`);
- console.log(` Registry: ${Object.keys(DOC_REGISTRY).length} entries`);
- if (renames.size > 0) {
- console.log(` Renames: ${[...renames.entries()].map(([f, t]) => `${f}→${t}`).join(', ')}`);
- }
- console.log('');
- // ─── Phase 1: Update workflow JSONs ─────────────────────────────
- for (const dir of WORKFLOW_DIRS) {
- if (!fs.existsSync(dir)) continue;
- const files = fs.readdirSync(dir).filter(f => f.endsWith('.json'));
- for (const file of files) {
- const fp = path.join(dir, file);
- const relPath = path.relative(ROOT, fp);
- let content = fs.readFileSync(fp, 'utf-8');
- let data;
- try { data = JSON.parse(content); } catch { continue; }
- const changes = [];
- const registryDocs = data?.registry?.docs;
- if (registryDocs && typeof registryDocs === 'object') {
- // 1a. Apply renames to registry.docs keys
- if (renames.size > 0) {
- const newDocs = {};
- for (const [pathStr, desc] of Object.entries(registryDocs)) {
- const pathNum = Number(pathStr);
- const newPath = renames.get(pathNum);
- if (newPath !== undefined) {
- newDocs[String(newPath)] = desc;
- changes.push(` registry.docs: key ${pathStr} → ${newPath}`);
- } else {
- newDocs[pathStr] = desc;
- }
- }
- data.registry.docs = newDocs;
- }
- // 1b. Sync descriptions from doc-paths.js (only with --sync-desc flag)
- // NOTE: Some path numbers are reused across workflow families with different meanings.
- // Only use --sync-desc when you've verified the registry descriptions are correct.
- if (syncDesc) {
- for (const [pathStr, desc] of Object.entries(data.registry.docs)) {
- const pathNum = Number(pathStr);
- const canonical = PATH_MAP.get(pathNum);
- if (canonical && canonical.desc !== desc) {
- changes.push(` registry.docs["${pathStr}"]: "${desc}" → "${canonical.desc}"`);
- data.registry.docs[pathStr] = canonical.desc;
- }
- }
- }
- }
- // 1c. Apply renames to step docs arrays
- if (renames.size > 0 && data.steps) {
- const renameInSteps = (steps) => {
- for (const step of steps) {
- if (step.in?.docs && Array.isArray(step.in.docs)) {
- for (let i = 0; i < step.in.docs.length; i++) {
- const pathNum = Number(step.in.docs[i]);
- const newPath = renames.get(pathNum);
- if (newPath !== undefined) {
- changes.push(` ${step.id}.in.docs: "${step.in.docs[i]}" → "${newPath}"`);
- step.in.docs[i] = String(newPath);
- }
- }
- }
- // Recurse into children (for Fork nodes)
- if (step.children && Array.isArray(step.children)) {
- // children are IDs, not nested steps — check steps array
- }
- }
- };
- renameInSteps(data.steps);
- }
- if (changes.length > 0) {
- totalChanges += changes.length;
- console.log(`📝 ${relPath} (${changes.length} changes)`);
- for (const c of changes) console.log(c);
- if (apply) {
- fs.writeFileSync(fp, JSON.stringify(data, null, 2) + '\n', 'utf-8');
- console.log(` ✅ Written`);
- }
- console.log('');
- }
- }
- }
- // ─── Phase 2: Check JS source files for hardcoded docId/path ───
- const JS_FILES_TO_CHECK = [
- 'src/vl/workflow-executor.js',
- 'src/web/server.js',
- 'src/tools/vl-parse.js',
- 'src/cloud/cloud-api.js',
- ];
- console.log('─── JS source file checks ───');
- for (const relFile of JS_FILES_TO_CHECK) {
- const fp = path.join(ROOT, relFile);
- if (!fs.existsSync(fp)) continue;
- const content = fs.readFileSync(fp, 'utf-8');
- // Check for hardcoded docId numbers that should use doc-paths.js
- const docIdMatches = [...content.matchAll(/docId[:\s]*(\d+)/g)];
- for (const m of docIdMatches) {
- const docId = Number(m[1]);
- // Find if this docId is in our registry
- for (const [alias, entry] of Object.entries(DOC_REGISTRY)) {
- if (entry.docId === docId) {
- console.log(` ⚠ ${relFile}: hardcoded docId ${docId} → should use docIdFor('${alias}') from doc-paths.js`);
- totalChanges++;
- }
- }
- }
- }
- // ─── Summary ────────────────────────────────────────────────────
- console.log(`\n${apply ? '✅' : '📋'} Total: ${totalChanges} change(s) ${apply ? 'applied' : 'found (use --apply to write)'}\n`);
|