Problem
packages/cli/script/postinstall.mjs:52 resolves the platform binary package under an unscoped name:
const packageName = `aictrl-${platform}-${arch}`
// ...
const packageJsonPath = require.resolve(`${packageName}/package.json`)
But every platform binary we publish is scoped: @aictrl/cli-linux-x64, @aictrl/cli-darwin-arm64, etc. The unscoped name aictrl-linux-x64 has never existed on npm (confirmed: npm view aictrl-linux-x64 → 404).
require.resolve('aictrl-linux-x64/package.json') always throws MODULE_NOT_FOUND, which is caught by the try/catch and propagated, and then the top-level main() catches it, logs "Failed to setup aictrl binary:", and calls process.exit(0) (line 128-131) — so it silently no-ops on every install.
Why it hasn't broken anything user-visible
The real binary resolution happens in packages/cli/bin/aictrl, which uses the correct scoped name ("@aictrl/cli-" + platform + "-" + arch) and walks the node_modules tree to find it. postinstall.mjs was apparently meant as a performance optimization (symlink/hardlink the platform binary to bin/.aictrl at install time so bin/aictrl can short-circuit) but it has been no-opping for at least as long as the scoped package names have existed.
Evidence it's dead
Options
Option 1: Fix the scoped name
-const packageName = `aictrl-${platform}-${arch}`
+const packageName = `@aictrl/cli-${platform}-${arch}`
This makes postinstall.mjs actually do something: hardlink/symlink node_modules/@aictrl/cli-linux-x64/bin/aictrl into packages/cli/bin/.aictrl so bin/aictrl finds it immediately via its cached check (line 29-32) instead of walking node_modules.
Risk: postinstall.mjs becomes load-bearing after being silent forever. If the fix-up writes to an unexpected location, or if the script has latent bugs that only surface when it actually runs, we could break npm install @aictrl/cli on a fresh machine for the first time. The linking logic also tries multiple strategies (hardlink fallback to copy) so there are multiple paths to verify.
Option 2: Delete postinstall.mjs entirely
bin/aictrl already does full platform detection (including avx2, baseline, musl variants — see lines 34-149) and walks node_modules looking for any matching platform binary. It works today without any postinstall hook. The only thing we lose is the startup-path short-circuit via the .aictrl cache file, which is probably negligible.
Both options remove the same confusion. Option 2 is the minimum-risk path — delete dead code, don't resurrect it. Option 1 is only worth it if someone has evidence the startup short-circuit measurably matters (e.g. profiling showing the node_modules walk is slow on large trees).
Recommend Option 2 unless someone has benchmark data.
Related observations
- The workflow publishes
postinstall.mjs inside the @aictrl/cli tarball (via scripts.postinstall written by script/publish.ts:31), so if we delete the file we also need to remove the "postinstall" script entry from the published package.json
- Note:
script/publish.ts itself appears to be dead code (writes a package named @aictrl/cli-ai, not invoked by any workflow). See the separate issue tracking that.
Problem
packages/cli/script/postinstall.mjs:52resolves the platform binary package under an unscoped name:But every platform binary we publish is scoped:
@aictrl/cli-linux-x64,@aictrl/cli-darwin-arm64, etc. The unscoped nameaictrl-linux-x64has never existed on npm (confirmed:npm view aictrl-linux-x64→ 404).require.resolve('aictrl-linux-x64/package.json')always throwsMODULE_NOT_FOUND, which is caught by the try/catch and propagated, and then the top-levelmain()catches it, logs "Failed to setup aictrl binary:", and callsprocess.exit(0)(line 128-131) — so it silently no-ops on every install.Why it hasn't broken anything user-visible
The real binary resolution happens in
packages/cli/bin/aictrl, which uses the correct scoped name ("@aictrl/cli-" + platform + "-" + arch) and walks thenode_modulestree to find it.postinstall.mjswas apparently meant as a performance optimization (symlink/hardlink the platform binary tobin/.aictrlat install time sobin/aictrlcan short-circuit) but it has been no-opping for at least as long as the scoped package names have existed.Evidence it's dead
packageName = "aictrl-linux-x64"is an unscoped package that has never been published (npm view aictrl-linux-x64→ 404)@aictrl/cli-linux-x64etc. (post-fix: refresh platform binary optionalDependencies on every release #46 / fix: pin publish workflow npm to 11.10.1 (pre promise-retry removal) #49, all 11 shapes now live on npm at v0.3.2)postinstall.mjsOptions
Option 1: Fix the scoped name
This makes
postinstall.mjsactually do something: hardlink/symlinknode_modules/@aictrl/cli-linux-x64/bin/aictrlintopackages/cli/bin/.aictrlsobin/aictrlfinds it immediately via itscachedcheck (line 29-32) instead of walkingnode_modules.Risk:
postinstall.mjsbecomes load-bearing after being silent forever. If the fix-up writes to an unexpected location, or if the script has latent bugs that only surface when it actually runs, we could breaknpm install @aictrl/clion a fresh machine for the first time. The linking logic also tries multiple strategies (hardlink fallback to copy) so there are multiple paths to verify.Option 2: Delete postinstall.mjs entirely
bin/aictrlalready does full platform detection (including avx2, baseline, musl variants — see lines 34-149) and walksnode_moduleslooking for any matching platform binary. It works today without any postinstall hook. The only thing we lose is the startup-path short-circuit via the.aictrlcache file, which is probably negligible.Both options remove the same confusion. Option 2 is the minimum-risk path — delete dead code, don't resurrect it. Option 1 is only worth it if someone has evidence the startup short-circuit measurably matters (e.g. profiling showing the
node_moduleswalk is slow on large trees).Recommend Option 2 unless someone has benchmark data.
Related observations
postinstall.mjsinside the@aictrl/clitarball (viascripts.postinstallwritten byscript/publish.ts:31), so if we delete the file we also need to remove the"postinstall"script entry from the published package.jsonscript/publish.tsitself appears to be dead code (writes a package named@aictrl/cli-ai, not invoked by any workflow). See the separate issue tracking that.