paperclipai/paperclip · error · Error

TypeScript CLI not found at

Error message

TypeScript CLI not found at ${tscCliPath}

What it means

This bootstrap script builds @paperclipai/shared and @paperclipai/plugin-sdk from source before plugins can load. It shells out to the TypeScript compiler located at node_modules/typescript/bin/tsc. If that file does not exist, dependencies were not installed (or TypeScript was removed/hoisted elsewhere), so it throws immediately before doing any work.

Solutions

  1. Run `pnpm install` at the repo root so node_modules/typescript/bin/tsc exists.
  2. If install ran, check that typescript is a (dev)Dependency of the root workspace and re-add it if a lockfile update removed it.
  3. If node_modules is corrupted or pruned, delete node_modules and reinstall (`rm -rf node_modules && pnpm install`).
  4. As a last resort, verify the path expectation in scripts/ensure-plugin-build-deps.mjs:12 matches your package manager's layout (e.g. pnpm's virtual store) and adjust tscCliPath.

Example fix

// before
$ node scripts/ensure-plugin-build-deps.mjs
Error: TypeScript CLI not found at /repo/node_modules/typescript/bin/tsc
// after
$ pnpm install
$ node scripts/ensure-plugin-build-deps.mjs
[paperclip] plugin build deps ready
Defensive patterns

Strategy: validation

Validate before calling

import fs from "node:fs";
const tscCliPath = "node_modules/typescript/bin/tsc";
if (!fs.existsSync(tscCliPath)) {
  console.error("Run `pnpm install` first: TypeScript CLI missing at " + tscCliPath);
  process.exit(1);
}

Type guard

const hasTsc = (p) => fs.existsSync(p) && fs.statSync(p).isFile();

Prevention

When it happens

Trigger: Running scripts/ensure-plugin-build-deps.mjs (directly or via the plugin build path) when node_modules/typescript/bin/tsc is absent — e.g. `pnpm install` was never run, node_modules was partially deleted, TypeScript is not in the root dependency tree, or a pnpm version/hoisting change moved the binary.

Common situations: Fresh clone without `pnpm install`; CI checkout missing the install step; stale node_modules after a lockfile change that dropped typescript; running the script from a pruned/production install (`--prod`) where devDependencies like typescript are omitted.

Understand the failure class

Background: "File not found" and ENOENT errors: why libraries can't find a file that should exist — this error's family across 50 libraries.

Related errors


AI-assisted analysis of paperclipai/paperclip@3f1d897a7c (2026-09-18). Data as JSON: /api/errors/661090a7c041018f. Report an issue: GitHub.

Appendix: source

Thrown at scripts/ensure-plugin-build-deps.mjs:37

    name: "@paperclipai/shared",
    output: path.join(rootDir, "packages/shared/dist/index.js"),
    completion: path.join(rootDir, "packages/shared/dist/.paperclip-build-complete"),
    sourceDir: path.join(rootDir, "packages/shared/src"),
    tsconfig: path.join(rootDir, "packages/shared/tsconfig.json"),
    dependencies: [],
  },
  {
    name: "@paperclipai/plugin-sdk",
    output: path.join(rootDir, "packages/plugins/sdk/dist/index.js"),
    completion: path.join(rootDir, "packages/plugins/sdk/dist/.paperclip-build-complete"),
    sourceDir: path.join(rootDir, "packages/plugins/sdk/src"),
    tsconfig: path.join(rootDir, "packages/plugins/sdk/tsconfig.json"),
    dependencies: [0],
  },
];

if (!fs.existsSync(tscCliPath)) {
  throw new Error(`TypeScript CLI not found at ${tscCliPath}`);
}

function directoryFingerprint(directory, exclude) {
  const hash = createHash("sha256");
  function visit(dir) {
    for (const entry of fs.readdirSync(dir, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name))) {
      const entryPath = path.join(dir, entry.name);
      if (entryPath === exclude) continue;
      if (entry.isDirectory()) {
        visit(entryPath);
      } else if (entry.isFile()) {
        const content = fs.readFileSync(entryPath);
        hash.update(JSON.stringify([path.relative(directory, entryPath), content.length]));
        hash.update(content);
      }
    }
  }
  visit(directory);

View on GitHub (pinned to 3f1d897a7c)