@@ -2,6 +2,7 @@ import fs from 'node:fs'
22import path from 'node:path'
33import os from 'node:os'
44import { fileURLToPath } from 'node:url'
5+ import { spawn } from 'node:child_process'
56import yaml from 'yaml'
67
78type Catalog = {
@@ -39,6 +40,10 @@ function arg(name: string) {
3940 return idx >= 0 ? process . argv [ idx + 1 ] : undefined
4041}
4142
43+ function hasFlag ( name : string ) {
44+ return process . argv . includes ( `--${ name } ` )
45+ }
46+
4247function readJson < T > ( p : string ) : T {
4348 return JSON . parse ( fs . readFileSync ( p , 'utf8' ) )
4449}
@@ -81,6 +86,25 @@ function readCatalog(repoRoot: string): Catalog {
8186 return yaml . parse ( fs . readFileSync ( p , 'utf8' ) )
8287}
8388
89+ async function runNodeScript ( scriptPath : string , args : string [ ] , cwd : string ) {
90+ const fullArgs = [ scriptPath , ...args ]
91+ const display = `${ process . execPath } ${ fullArgs . join ( ' ' ) } `
92+ console . log ( `Running: ${ display } ` )
93+
94+ await new Promise < void > ( ( resolve , reject ) => {
95+ const child = spawn ( process . execPath , fullArgs , {
96+ cwd,
97+ stdio : 'inherit' ,
98+ } )
99+
100+ child . on ( 'error' , reject )
101+ child . on ( 'close' , ( code ) => {
102+ if ( code === 0 ) return resolve ( )
103+ reject ( new Error ( `Command failed with exit code ${ code } : ${ display } ` ) )
104+ } )
105+ } )
106+ }
107+
84108function getAllDeps ( pkg : any ) : Record < string , string > {
85109 return {
86110 ...( pkg . dependencies ?? { } ) ,
@@ -213,7 +237,7 @@ function resolveExactFromPackageJson(
213237 )
214238}
215239
216- function main ( ) {
240+ async function main ( ) {
217241 const cmd = process . argv [ 2 ]
218242 if ( cmd !== 'install' ) throw new Error ( `Unknown command: ${ cmd } ` )
219243
@@ -234,6 +258,8 @@ function main() {
234258
235259 const repoRoot = getRepoRootFromThisFile ( )
236260
261+ const buildMissing = hasFlag ( 'build-missing' )
262+
237263 const manifestPath = path . join ( repoRoot , 'registry' , 'manifest.json' )
238264 if ( ! fs . existsSync ( manifestPath ) ) {
239265 throw new Error (
@@ -244,7 +270,7 @@ function main() {
244270 }
245271
246272 const catalog = readCatalog ( repoRoot )
247- const manifest = readJson < Manifest > ( manifestPath )
273+ let manifest = readJson < Manifest > ( manifestPath )
248274
249275 // Validate catalog schema minimally (fail fast with clear errors)
250276 for ( const [ id , lib ] of Object . entries < any > ( catalog . libraries ?? { } ) ) {
@@ -269,6 +295,16 @@ function main() {
269295 const prevLock = readJsonIfExists < any > ( outLockPath )
270296 const prevInstalled : Record < string , any > = prevLock ?. installed ?? { }
271297
298+ if (
299+ buildMissing &&
300+ prevLock ?. registry ?. type &&
301+ prevLock . registry . type !== 'local'
302+ ) {
303+ throw new Error (
304+ `--build-missing is only supported for local registries (found: ${ prevLock . registry . type } ).` ,
305+ )
306+ }
307+
272308 // Always install the top-level tanstack agent if present
273309 const tanstackAgent = manifest . agents . find ( ( a ) => a . id === 'tanstack' )
274310 if ( tanstackAgent ) {
@@ -292,14 +328,56 @@ function main() {
292328 )
293329 }
294330
295- const skill = manifest . skills . find (
331+ let skill = manifest . skills . find (
296332 ( s ) => s . lib === libId && s . version === exactVersion ,
297333 )
298334 if ( ! skill ) {
299- throw new Error (
300- `Registry missing ${ libId } @${ exactVersion } .\n` +
301- `Build it in TanStack/agents and rebuild manifest.` ,
335+ if ( ! buildMissing ) {
336+ throw new Error (
337+ `Registry missing ${ libId } @${ exactVersion } .\n` +
338+ `Build it in TanStack/agents and rebuild manifest.` ,
339+ )
340+ }
341+
342+ const skillcCliPath = path . join (
343+ repoRoot ,
344+ 'packages' ,
345+ 'skillc' ,
346+ 'dist' ,
347+ 'cli.js' ,
348+ )
349+
350+ if ( ! fs . existsSync ( skillcCliPath ) ) {
351+ throw new Error (
352+ `skillc CLI not found at: ${ skillcCliPath } \n` +
353+ `Build the repo first (pnpm build:all) or run skillc from source.` ,
354+ )
355+ }
356+
357+ console . log (
358+ `Registry missing ${ libId } @${ exactVersion } . Building with skillc...` ,
359+ )
360+
361+ await runNodeScript (
362+ skillcCliPath ,
363+ [ 'build-lib' , '--lib' , libId , '--version' , exactVersion ] ,
364+ repoRoot ,
302365 )
366+
367+ console . log ( 'Rebuilding registry manifest...' )
368+ await runNodeScript ( skillcCliPath , [ 'build-manifest' ] , repoRoot )
369+
370+ manifest = readJson < Manifest > ( manifestPath )
371+ skill = manifest . skills . find (
372+ ( s ) => s . lib === libId && s . version === exactVersion ,
373+ )
374+
375+ if ( ! skill ) {
376+ throw new Error (
377+ `Registry still missing ${ libId } @${ exactVersion } after build.\n` +
378+ `Check skillc output for details.` ,
379+ )
380+ }
303381 }
304382
305383 const agentFileName = `tanstack-${ libId } .md`
@@ -364,4 +442,7 @@ function main() {
364442 console . log ( `Installed: ${ libs . join ( ', ' ) || '(none)' } ` )
365443}
366444
367- main ( )
445+ main ( ) . catch ( ( error ) => {
446+ console . error ( error instanceof Error ? error . message : error )
447+ process . exit ( 1 )
448+ } )
0 commit comments