11"use strict" ;
2- var __importDefault = ( this && this . __importDefault ) || function ( mod ) {
3- return ( mod && mod . __esModule ) ? mod : { "default" : mod } ;
4- } ;
52Object . defineProperty ( exports , "__esModule" , { value : true } ) ;
63exports . createIndexBuilder = exports . IndexBuilder = void 0 ;
74const fs_1 = require ( "fs" ) ;
8- const path_1 = __importDefault ( require ( "path" ) ) ;
95const constants_1 = require ( "../core/constants" ) ;
10- const ProjectLayout_1 = require ( "../utils/ProjectLayout" ) ;
11- const SKIP_DIRS = new Set ( [ 'node_modules' , 'dist' , '.git' , 'changes' , 'for-ai' ] ) ;
6+ // Single source of truth for the index algorithm. `build-index.ts` is also compiled to the
7+ // standalone `.ospec/tools/build-index-auto.cjs` used by the git hook; requiring it here means
8+ // the writer and the hook share one implementation (no parser/layout divergence). The tool
9+ // only runs its CLI `main()` under `require.main === module`, so importing it is side-effect free.
10+ const indexTool = require ( '../tools/build-index' ) ;
1211async function pathExists ( targetPath ) {
1312 try {
1413 await fs_1 . promises . access ( targetPath , fs_1 . constants . F_OK ) ;
@@ -19,109 +18,27 @@ async function pathExists(targetPath) {
1918 }
2019}
2120async function readJson ( filePath ) {
22- return JSON . parse ( ( await fs_1 . promises . readFile ( filePath , 'utf8' ) ) . replace ( / ^ \uFEFF / , '' ) ) ;
21+ return JSON . parse ( ( await fs_1 . promises . readFile ( filePath , 'utf8' ) ) . replace ( / ^ / , '' ) ) ;
2322}
2423class IndexBuilder {
25- constructor ( skillParser ) {
26- this . skillParser = skillParser ;
27- }
24+ // skillParser is retained for backward-compatible construction; the index algorithm now
25+ // lives in the shared build-index tool, so parsing is no longer done here.
26+ constructor ( _skillParser ) { }
2827 async build ( rootDir ) {
29- const config = await this . readProjectConfig ( rootDir ) ;
30- const projectLayout = ( 0 , ProjectLayout_1 . getProjectLayout ) ( config ) ;
31- const managedRoot = ( 0 , ProjectLayout_1 . getProjectManagedRoot ) ( rootDir , projectLayout ) ;
32- const modules = { } ;
33- const tagIndex = { } ;
34- let totalFiles = 0 ;
35- let totalSections = 0 ;
36- const visit = async ( currentDir ) => {
37- const entries = ( await fs_1 . promises . readdir ( currentDir , { withFileTypes : true } ) ) . sort ( ( left , right ) => left . name . localeCompare ( right . name ) ) ;
38- for ( const entry of entries ) {
39- const fullPath = path_1 . default . join ( currentDir , entry . name ) ;
40- if ( entry . isDirectory ( ) ) {
41- if ( ! SKIP_DIRS . has ( entry . name ) ) {
42- await visit ( fullPath ) ;
43- }
44- continue ;
45- }
46- if ( entry . name !== constants_1 . FILE_NAMES . SKILL_MD ) {
47- continue ;
48- }
49- totalFiles ++ ;
50- const relativePath = path_1 . default . relative ( rootDir , fullPath ) . replace ( / \\ / g, '/' ) ;
51- const content = await fs_1 . promises . readFile ( fullPath , 'utf-8' ) ;
52- const parsed = this . skillParser . parseSkillFile ( content ) ;
53- const moduleName = parsed . frontmatter . name || relativePath ;
54- const title = parsed . frontmatter . title || parsed . frontmatter . name || relativePath ;
55- const tags = parsed . frontmatter . tags || [ ] ;
56- const sections = parsed . sections ;
57- totalSections += Object . keys ( sections ) . length ;
58- modules [ moduleName ] = {
59- file : relativePath ,
60- title,
61- tags,
62- sections,
63- } ;
64- for ( const tag of tags ) {
65- if ( ! tagIndex [ tag ] ) {
66- tagIndex [ tag ] = [ ] ;
67- }
68- tagIndex [ tag ] . push ( moduleName ) ;
69- }
70- }
71- } ;
72- if ( await pathExists ( managedRoot ) ) {
73- await visit ( managedRoot ) ;
74- }
75- const activeChangesDir = ( 0 , ProjectLayout_1 . resolveManagedPath ) ( rootDir , 'changes/active' , projectLayout ) ;
76- const activeChanges = ( await pathExists ( activeChangesDir ) )
77- ? ( await fs_1 . promises . readdir ( activeChangesDir ) ) . sort ( ( left , right ) => left . localeCompare ( right ) )
78- : [ ] ;
79- for ( const tag of Object . keys ( tagIndex ) ) {
80- tagIndex [ tag ] = tagIndex [ tag ] . sort ( ( left , right ) => left . localeCompare ( right ) ) ;
81- }
82- return {
83- version : '1.0' ,
84- generated : new Date ( ) . toISOString ( ) ,
85- git_commit : null ,
86- active_changes : activeChanges ,
87- stats : {
88- totalFiles,
89- totalModules : Object . keys ( modules ) . length ,
90- totalSections,
91- } ,
92- modules,
93- tagIndex,
94- } ;
28+ return indexTool . buildIndex ( rootDir ) ;
9529 }
9630 async write ( rootDir ) {
97- const config = await this . readProjectConfig ( rootDir ) ;
98- const indexPath = ( 0 , ProjectLayout_1 . resolveManagedPath ) ( rootDir , constants_1 . FILE_NAMES . SKILL_INDEX , config ) ;
99- const previous = ( await pathExists ( indexPath ) )
100- ? ( await readJson ( indexPath ) )
101- : null ;
102- const index = await this . build ( rootDir ) ;
103- const previousComparable = previous ? this . stripVolatileFields ( previous ) : null ;
104- const nextComparable = this . stripVolatileFields ( index ) ;
105- if ( previous && JSON . stringify ( previousComparable ) === JSON . stringify ( nextComparable ) ) {
106- return previous ;
107- }
108- const output = {
109- ...index ,
110- generated : new Date ( ) . toISOString ( ) ,
111- } ;
112- await fs_1 . promises . writeFile ( indexPath , JSON . stringify ( output , null , 2 ) , 'utf-8' ) ;
113- return output ;
31+ const { index } = await indexTool . writeIndex ( rootDir , { silent : true } ) ;
32+ return index ;
11433 }
11534 async createEmpty ( rootDir ) {
116- const config = await this . readProjectConfig ( rootDir ) ;
117- const indexPath = ( 0 , ProjectLayout_1 . resolveManagedPath ) ( rootDir , constants_1 . FILE_NAMES . SKILL_INDEX , config ) ;
118- const previous = ( await pathExists ( indexPath ) )
119- ? ( await readJson ( indexPath ) )
120- : null ;
35+ const layout = await indexTool . getProjectLayout ( rootDir ) ;
36+ const indexPath = indexTool . resolveManagedPath ( rootDir , constants_1 . FILE_NAMES . SKILL_INDEX , layout ) ;
37+ const previous = ( await pathExists ( indexPath ) ) ? await readJson ( indexPath ) : null ;
12138 const index = {
12239 version : '1.0' ,
12340 generated : new Date ( ) . toISOString ( ) ,
124- git_commit : null ,
41+ git_commit : indexTool . resolveGitCommit ( rootDir ) ,
12542 active_changes : [ ] ,
12643 stats : {
12744 totalFiles : 0 ,
@@ -131,29 +48,17 @@ class IndexBuilder {
13148 modules : { } ,
13249 tagIndex : { } ,
13350 } ;
134- if ( previous && JSON . stringify ( this . stripVolatileFields ( previous ) ) === JSON . stringify ( this . stripVolatileFields ( index ) ) ) {
51+ if ( previous && JSON . stringify ( stripVolatileFields ( previous ) ) === JSON . stringify ( stripVolatileFields ( index ) ) ) {
13552 return previous ;
13653 }
13754 await fs_1 . promises . writeFile ( indexPath , JSON . stringify ( index , null , 2 ) , 'utf-8' ) ;
13855 return index ;
13956 }
140- stripVolatileFields ( index ) {
141- const { generated : _generated , ...stable } = index ;
142- return stable ;
143- }
144- async readProjectConfig ( rootDir ) {
145- const configPath = path_1 . default . join ( rootDir , constants_1 . FILE_NAMES . SKILLRC ) ;
146- if ( ! ( await pathExists ( configPath ) ) ) {
147- return null ;
148- }
149- try {
150- return await readJson ( configPath ) ;
151- }
152- catch {
153- return null ;
154- }
155- }
15657}
15758exports . IndexBuilder = IndexBuilder ;
59+ function stripVolatileFields ( index ) {
60+ const { generated : _generated , git_commit : _gitCommit , ...stable } = index ;
61+ return stable ;
62+ }
15863const createIndexBuilder = ( skillParser ) => new IndexBuilder ( skillParser ) ;
15964exports . createIndexBuilder = createIndexBuilder ;
0 commit comments