11#!/usr/bin/env node
22import fs from 'node:fs' ;
33import path from 'node:path' ;
4- import { fileURLToPath } from 'node:url' ;
54import { buildInventory } from '../lib/inventory.mjs' ;
65import { compileContext } from '../lib/context.mjs' ;
76import { databaseStats } from '../lib/indexer.mjs' ;
87import { all , audit , generate , index , model , plan , publish , status } from '../lib/pipeline.mjs' ;
98import { 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' ;
1110import { runWorkspace } from './workspace.mjs' ;
1211
1312function usage ( ) {
1413 console . log ( `Command Code DocGen ${ kitVersion } — token-efficient semantic-index pipeline
1514
1615Repository 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' ) ) ; }
4850function 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+
5884function 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) {
6995function printStatus ( root ) { console . log ( JSON . stringify ( status ( root ) , null , 2 ) ) ; }
7096function printBudget ( root ) { console . log ( JSON . stringify ( budgetReport ( root ) , null , 2 ) ) ; }
7197function 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
75101async 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