Skip to content

Commit 7f2456f

Browse files
add breaking v1 to v2 migration
1 parent a939ea5 commit 7f2456f

1 file changed

Lines changed: 35 additions & 8 deletions

File tree

global-template/docgen/bin/docgen-v2.mjs

Lines changed: 35 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,20 @@
11
#!/usr/bin/env node
22
import fs from 'node:fs';
33
import path from 'node:path';
4-
import { fileURLToPath } from 'node:url';
54
import { buildInventory } from '../lib/inventory.mjs';
65
import { compileContext } from '../lib/context.mjs';
76
import { databaseStats } from '../lib/indexer.mjs';
87
import { all, audit, generate, index, model, plan, publish, status } from '../lib/pipeline.mjs';
98
import { budgetReport, resetTelemetry } from '../lib/provider.mjs';
10-
import { commandExists, engineHome, ensureDir, findProjectRoot, kitVersion, loadConfig, parseArgs, projectPaths, readJson, requireProjectRoot, userHome, writeJson } from '../lib/core.mjs';
9+
import { commandExists, engineHome, ensureDir, kitVersion, loadConfig, parseArgs, projectPaths, readJson, requireProjectRoot, writeJson } from '../lib/core.mjs';
1110
import { runWorkspace } from './workspace.mjs';
1211

1312
function usage() {
1413
console.log(`Command Code DocGen ${kitVersion} — token-efficient semantic-index pipeline
1514
1615
Repository commands:
1716
docgen init [directory]
17+
docgen migrate
1818
docgen doctor
1919
docgen index [--force]
2020
docgen model
@@ -45,20 +45,46 @@ function copyTree(source, target) {
4545
}
4646
}
4747

48+
function defaultConfig() { return readJson(path.join(engineHome, 'project-template', 'config', 'documentation.json')); }
49+
function defaultState() { return readJson(path.join(engineHome, 'project-template', 'state', 'state.json')); }
4850
function init(target = '.') {
4951
const root = path.resolve(target); ensureDir(root); const paths = projectPaths(root); const template = path.join(engineHome, 'project-template');
5052
if (fs.existsSync(template)) copyTree(template, paths.base);
51-
for (const dir of [paths.context, paths.telemetry, path.dirname(paths.budget), paths.model, path.dirname(paths.plan), paths.audit, paths.publish, paths.runs, path.dirname(paths.inventory)]) ensureDir(dir);
53+
for (const dir of [paths.context, paths.telemetry, path.dirname(paths.budget), paths.model, path.dirname(paths.plan), paths.audit, paths.publish, paths.traceability, paths.runs, path.dirname(paths.inventory)]) ensureDir(dir);
5254
const marker = readJson(paths.project, {}); writeJson(paths.project, { ...marker, schemaVersion: '2.0', kitVersion, initializedAt: marker.initializedAt ?? new Date().toISOString(), engineScope: marker.engineScope ?? 'global', projectRoot: root.replaceAll('\\', '/') });
53-
if (!fs.existsSync(paths.config)) writeJson(paths.config, readJson(path.join(engineHome, 'project-template', 'config', 'documentation.json')));
54-
if (!fs.existsSync(paths.state)) writeJson(paths.state, { schemaVersion: '2.0', kitVersion, stages: {}, pages: {} });
55+
if (!fs.existsSync(paths.config)) writeJson(paths.config, defaultConfig());
56+
if (!fs.existsSync(paths.state)) writeJson(paths.state, defaultState());
5557
console.log(`Initialized DocGen ${kitVersion} at ${root}`);
5658
}
5759

60+
function migrate(root) {
61+
const paths = projectPaths(root); const current = readJson(paths.config, {}); const marker = readJson(paths.project, {});
62+
if (current.schemaVersion === '2.0' && marker.kitVersion === kitVersion) { console.log(`DocGen project is already on ${kitVersion}.`); return; }
63+
const timestamp = new Date().toISOString().replace(/[:.]/g, '-'); const backup = path.join(paths.base, 'migration-backup', timestamp); ensureDir(backup);
64+
const moveNames = ['evidence','model','plan','audit','traceability','state','publish','index','context','telemetry','budget','runs','prompts'];
65+
for (const name of moveNames) {
66+
const source = path.join(paths.base, name); if (!fs.existsSync(source)) continue;
67+
const target = path.join(backup, name); ensureDir(path.dirname(target)); fs.renameSync(source, target);
68+
}
69+
const configDir = path.join(paths.base, 'config');
70+
if (fs.existsSync(configDir)) { const target = path.join(backup, 'config'); ensureDir(path.dirname(target)); fs.renameSync(configDir, target); }
71+
const next = defaultConfig();
72+
next.projectName = current.projectName ?? next.projectName;
73+
next.outputRoot = current.outputRoot ?? next.outputRoot;
74+
next.commandCode = { ...next.commandCode, executable: current.commandCode?.executable ?? '', trust: current.commandCode?.trust ?? next.commandCode.trust, skipOnboarding: current.commandCode?.skipOnboarding ?? next.commandCode.skipOnboarding, yolo: current.commandCode?.yolo ?? next.commandCode.yolo, model: current.commandCode?.model ?? '', stageModels: { ...next.commandCode.stageModels, ...(current.commandCode?.stageModels ?? {}) } };
75+
next.ignore = { ...next.ignore, ...(current.ignore ?? {}), binary: { ...next.ignore.binary, ...(current.ignore?.binary ?? {}) } };
76+
writeJson(paths.config, next); writeJson(paths.state, defaultState());
77+
for (const dir of [paths.context, paths.telemetry, path.dirname(paths.budget), paths.model, path.dirname(paths.plan), paths.audit, paths.publish, paths.traceability, paths.runs, path.dirname(paths.inventory)]) ensureDir(dir);
78+
writeJson(paths.project, { ...marker, schemaVersion: '2.0', kitVersion, migratedAt: new Date().toISOString(), migrationBackup: path.relative(root, backup).replaceAll('\\', '/') });
79+
console.log(`Migrated project to DocGen ${kitVersion}.`);
80+
console.log(`Legacy .docgen artifacts were archived at ${path.relative(root, backup).replaceAll('\\', '/')}.`);
81+
console.log('Generated docs and .docgenignore were preserved. Run `docgen index`, then `docgen all`.');
82+
}
83+
5884
function doctor(root) {
5985
const errors = []; const warnings = [];
6086
if (!commandExists('git')) errors.push('git executable not found');
61-
try { const config = loadConfig(root); if (!config || typeof config !== 'object') errors.push('invalid documentation config'); } catch (error) { errors.push(error.message); }
87+
try { const config = loadConfig(root); if (!config || typeof config !== 'object') errors.push('invalid documentation config'); if (config.schemaVersion !== '2.0') errors.push('legacy configuration detected; run `docgen migrate`'); } catch (error) { errors.push(error.message); }
6288
if (!process.versions.node || Number(process.versions.node.split('.')[0]) < 22) errors.push('Node.js 22+ is required for node:sqlite');
6389
if (!commandExists(process.platform === 'win32' ? 'cmdc' : 'cmd') && !loadConfig(root).commandCode?.executable) warnings.push('Command Code executable was not found using default names; configure commandCode.executable if needed.');
6490
try { const inventory = buildInventory(root); if (!inventory.files.length) warnings.push('source inventory is empty'); } catch (error) { errors.push(error.message); }
@@ -69,8 +95,8 @@ function doctor(root) {
6995
function printStatus(root) { console.log(JSON.stringify(status(root), null, 2)); }
7096
function printBudget(root) { console.log(JSON.stringify(budgetReport(root), null, 2)); }
7197
function sourceList(root, filter = '') { const inv = buildInventory(root); for (const file of inv.files) if (!filter || file.path.toLowerCase().includes(filter.toLowerCase())) console.log(file.path); }
72-
function sourceGrep(root, query) { if (!query) throw new Error('Usage: docgen source-grep <text>'); const inv = buildInventory(root); const needle = query.toLowerCase(); for (const item of inv.files) { const text = fs.readFileSync(path.join(root, item.path), 'utf8'); for (const [index, line] of text.split(/\r?\n/).entries()) if (line.toLowerCase().includes(needle)) console.log(`${item.path}:${index + 1}:${line.trim()}`); } }
73-
function ignore(root, target) { const inv = buildInventory(root, { force: true }); if (target) { const found = inv.files.find((x) => x.path === target); const excluded = inv.excluded.find((x) => x.path === target); console.log(JSON.stringify(found ? { included: true, ...found } : { included: false, ...(excluded ?? { path: target, reason: 'not-found' }) }, null, 2)); } else console.log(JSON.stringify(inv.metrics, null, 2)); }
98+
function sourceGrep(root, query) { if (!query) throw new Error('Usage: docgen source-grep <text>'); const inv = buildInventory(root); const needle = query.toLowerCase(); for (const item of inv.files) { const text = fs.readFileSync(path.join(root, item.path), 'utf8'); for (const [lineIndex, line] of text.split(/\r?\n/).entries()) if (line.toLowerCase().includes(needle)) console.log(`${item.path}:${lineIndex + 1}:${line.trim()}`); } }
99+
function ignore(root, target) { const inv = buildInventory(root); if (target) { const found = inv.files.find((item) => item.path === target); const excluded = inv.excluded.find((item) => item.path === target); console.log(JSON.stringify(found ? { included: true, ...found } : { included: false, ...(excluded ?? { path: target, reason: 'not-found' }) }, null, 2)); } else console.log(JSON.stringify(inv.metrics, null, 2)); }
74100

75101
async function main() {
76102
const [command, ...rest] = process.argv.slice(2); const { positional, options } = parseArgs(rest);
@@ -79,6 +105,7 @@ async function main() {
79105
if (command === 'workspace') { await runWorkspace(rest, { kitVersion }); return; }
80106
const root = requireProjectRoot();
81107
switch (command) {
108+
case 'migrate': migrate(root); break;
82109
case 'doctor': doctor(root); break;
83110
case 'index': console.log(JSON.stringify(index(root, { force: Boolean(options.force) }), null, 2)); break;
84111
case 'model': console.log(JSON.stringify(await model(root), null, 2)); break;

0 commit comments

Comments
 (0)