Skip to content

npx tsc build checks fetch their compiler per the workspace's own .npmrc #83

Description

The stop-time build checks run npx tsc --noEmit with cwd = the project being checked. On a project without a local TypeScript install, npm fetches the compiler on demand — and it resolves the package and the registry from that same project's files, including a workspace-root .npmrc that redirects the registry. The project under check effectively chooses where the plugin's build-check compiler is downloaded from, and the fetched binary's exit code decides whether the check passes.

Call sites (current main)

  • plugins/agent365/hooks/stop/validate-make-ai-teammate.js:317 — runBuild('npx tsc --noEmit', 15000)
  • plugins/agent365/hooks/stop/validate-add-workiq-tools.js:412
  • plugins/agent365/hooks/stop/validate-instrument-observability.js:339

runBuild is execSync(cmd, { cwd, timeout, stdio: 'pipe' }) with cwd set to the workspace.

What happens on a project without a local typescript

npx tsc finds no local tsc binary, so npm exec auto-installs the npm package named tsc — non-interactively in this stdio shape; the only announcement is a warning line:

npm warn exec The following package was not found and will be installed: tsc@2.0.4

Two distinct problems:

  1. The check stops checking with TypeScript. The npm package tsc is not TypeScript — TypeScript ships as the typescript package, which provides the tsc bin. On a clean project, the "TypeScript compilation failed" gate is actually decided by an unrelated third-party package's exit code.
  2. The registry comes from the project being checked. npm resolves per-project configuration from the current directory: a .npmrc at the workspace root (registry=...) selects where the package is fetched from. The whole point of a stop-time check is to run carefully against project content; letting the project's own config file pick the download source for the checking tool inverts that.

Observed shape

On a clean machine: an empty project containing only a .npmrc pointing registry at a local test registry (loopback), a package.json, and a .ts file; running node validate-make-ai-teammate.js in that directory — npm fetched the package named tsc from the loopback registry and executed its bin with --noEmit, cwd = the project. The fetched binary's exit code fed the check result (no "TypeScript compilation failed" issue was raised when it exited 0). With the ambient registry instead of the redirected one, the same run fetched tsc@2.0.4 from the public registry with the warning line above — same mechanism, different source.

Suggested fix

Resolve the compiler from the plugin's own pinned toolchain instead of ambient npx resolution, for example:

  • declare typescript as a dependency of the plugin and invoke node <plugin root>/node_modules/typescript/bin/tsc --noEmit directly (argv, no shell), or
  • at minimum, npx --yes typescript@<pinned version> with npm_config_registry set explicitly by the plugin so workspace config cannot redirect it.

The dotnet build legs of the same scripts inherit cwd too; dotnet is a fixed-name SDK binary rather than a package fetch, but the explicit-resolution pattern applies there as well.

Related

#81 covers the frontmatter event-key casing that currently keeps these stop hooks from registering; the toolchain issue above applies to any execution path of these check scripts, and to the checks themselves once they register.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions