Skip to content

Commit 7eab687

Browse files
committed
add support for --build-missing flag and enhance registry handling
1 parent 799f4cf commit 7eab687

File tree

5 files changed

+88
-17568
lines changed

5 files changed

+88
-17568
lines changed

packages/installer/src/cli.ts

Lines changed: 88 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import fs from 'node:fs'
22
import path from 'node:path'
33
import os from 'node:os'
44
import { fileURLToPath } from 'node:url'
5+
import { spawn } from 'node:child_process'
56
import yaml from 'yaml'
67

78
type 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+
4247
function 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+
84108
function 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

Comments
 (0)