fix: prevent getLoadedSkills crash and auto-build workspace packages (#1767)

Add defensive fallback in auto-prompts.ts so a missing getLoadedSkills
export degrades gracefully (empty skill list) instead of crashing every
auto-mode dispatch iteration.

Add ensure-workspace-builds.cjs postinstall script that detects missing
dist/ directories in workspace packages and rebuilds them automatically.
This prevents stale-build issues after fresh clones where dist/ is
gitignored but required at runtime by jiti-loaded extensions.

Closes #1734

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
TÂCHES 2026-03-21 09:19:48 -06:00 committed by GitHub
parent 050d51475b
commit d1b6a8a6b1
3 changed files with 60 additions and 2 deletions

View file

@ -66,7 +66,7 @@
"build:native": "node native/scripts/build.js",
"build:native:dev": "node native/scripts/build.js --dev",
"dev": "node scripts/dev.js",
"postinstall": "node scripts/link-workspace-packages.cjs && node scripts/postinstall.js",
"postinstall": "node scripts/link-workspace-packages.cjs && node scripts/ensure-workspace-builds.cjs && node scripts/postinstall.js",
"pi:install-global": "node scripts/install-pi-global.js",
"pi:uninstall-global": "node scripts/uninstall-pi-global.js",
"sync-pkg-version": "node scripts/sync-pkg-version.cjs",

View file

@ -0,0 +1,58 @@
#!/usr/bin/env node
/**
* ensure-workspace-builds.cjs
*
* Checks whether workspace packages have been compiled (dist/ exists with
* index.js). If any are missing, runs the build for those packages.
*
* Designed for the postinstall hook so that `npm install` in a fresh clone
* produces a working runtime without a manual `npm run build` step.
*
* Skipped in CI (where the full build pipeline handles this) and when
* installing as an end-user dependency (no packages/ directory).
*/
const { existsSync } = require('fs')
const { resolve, join } = require('path')
const { execSync } = require('child_process')
const root = resolve(__dirname, '..')
const packagesDir = join(root, 'packages')
// Skip if packages/ doesn't exist (published tarball / end-user install)
if (!existsSync(packagesDir)) process.exit(0)
// Skip in CI — the pipeline runs `npm run build` explicitly
if (process.env.CI === 'true' || process.env.CI === '1') process.exit(0)
// Workspace packages that need dist/index.js at runtime.
// Order matters: dependencies must build before dependents.
const WORKSPACE_PACKAGES = [
'native',
'pi-tui',
'pi-ai',
'pi-agent-core',
'pi-coding-agent',
]
const missing = []
for (const pkg of WORKSPACE_PACKAGES) {
const distIndex = join(packagesDir, pkg, 'dist', 'index.js')
if (!existsSync(distIndex)) {
missing.push(pkg)
}
}
if (missing.length === 0) process.exit(0)
process.stderr.write(` Building ${missing.length} workspace package(s) missing dist/: ${missing.join(', ')}\n`)
for (const pkg of missing) {
const pkgDir = join(packagesDir, pkg)
try {
execSync('npm run build', { cwd: pkgDir, stdio: 'pipe' })
process.stderr.write(`${pkg}\n`)
} catch (err) {
process.stderr.write(`${pkg} build failed: ${err.message}\n`)
// Non-fatal — the user can run `npm run build` manually
}
}

View file

@ -424,7 +424,7 @@ export function buildSkillActivationBlock(params: {
params.taskPlanContent ?? undefined,
);
const visibleSkills = getLoadedSkills().filter(skill => !skill.disableModelInvocation);
const visibleSkills = (typeof getLoadedSkills === 'function' ? getLoadedSkills() : []).filter(skill => !skill.disableModelInvocation);
const installedNames = new Set(visibleSkills.map(skill => normalizeSkillReference(skill.name)));
const avoided = new Set(resolvePreferenceSkillNames(prefs?.avoid_skills ?? [], params.base));
const matched = new Set<string>();