{"id":17557,"plugin_id":"plugins_6a78e83987748191afc0c56e12172fce","kind":"skill","collection_source":null,"comparison_source":null,"observed_at":"2026-09-30T23:14:15.755Z","digest":"15b279e683702d86a076756a86ce41f2f38d63046f6e978300c98d1395254977","against":null,"payload":{"description":"Enforce TypeScript package boundaries, entry points, and cyclic dependency rules with dependency-cruiser. Use when structuring monorepo packages, establishing public API entry points, or preventing circular imports — even if the user says \"fix TS package boundaries\". Do NOT use for non-TypeScript repositories.","included_files":[{"relative_path":"agents/openai.yaml","size_in_bytes":106},{"relative_path":"dependency-cruiser.config.cjs","size_in_bytes":3712}],"name":"setup-ts-deep-modules","skill_md_contents":"---\nname: setup-ts-deep-modules\ndescription: \"Enforce TypeScript package boundaries, entry points, and cyclic dependency rules with dependency-cruiser. Use when structuring monorepo packages, establishing public API entry points, or preventing circular imports — even if the user says \\\"fix TS package boundaries\\\". Do NOT use for non-TypeScript repositories.\"\n---\n\n# Setup TS Deep Modules\n\nConfigure and verify strict deep-module boundaries across TypeScript packages using `dependency-cruiser`, ensuring public interfaces exist exclusively at package root files while private implementations remain sealed inside subdirectories.\n\n---\n\n## Core Invariants\n\n1. **Root Files as Exclusive Entry Points**: External callers and consumers may only import package root files (`index.ts`, `client.ts`, `server.ts`); all subdirectories (`lib/`, `internal/`, `tests/`) are private.\n2. **Four Strict Dependency Rules**: Enforce (1) Entry-point boundary, (2) Intra-package internal freedom, (3) Tests import entry-points only, (4) Zero dependency cycles.\n3. **Barrels Strongly Discouraged**: Expose focused, multiple root entry points instead of giant barrel files that re-export whole subtrees.\n4. **Mandatory Biting Proof**: Before completing setup, deliberately introduce a forbidden deep import to verify that `lint:boundaries` fails fast.\n5. **No Speculative Path Aliases**: Wire boundary checks directly through dependency-cruiser rather than altering `tsconfig.json` paths or building complex build-time layers.\n\n---\n\n## Architecture & Map of Content (MOC)\n\n```\nsrc/packages/\n  <package-name>/\n    index.ts          ◄── Public Entry Point (exported to other packages)\n    client.ts         ◄── Additional Public Entry Point\n    lib/              ◄── Private Implementation (sealed from outsiders)\n    tests/            ◄── Tests (import only root entry points)\n```\n\n| Rule | Enforcement | Target |\n|---|---|---|\n| **Entry Boundary** | `not-to-subfolder` | Outside code $\\rightarrow$ root files only |\n| **Test Boundary** | `tests-through-entrypoints` | `tests/` $\\rightarrow$ root entry points only |\n| **Cycle Prevention** | `no-circular` | Disallow all circular dependencies |\n| **Verification Gate** | `lint:boundaries` script | Automated CI failure on breach |\n\n---\n\n## Step-by-Step Procedure (TWI)\n\n### Step 1: Detect Environment & Package Manager\n- **Action**: Detect the package manager (`bun.lockb` $\\rightarrow$ bun, `pnpm-lock.yaml` $\\rightarrow$ pnpm, `yarn.lock` $\\rightarrow$ yarn, else npm) and locate the packages root (`src/packages` or `packages`).\n- **Key Point**: Check for existing `.dependency-cruiser.*` configuration to merge rules rather than overwriting.\n- **Why**: Preserving existing project package manager standards ensures zero friction with current CI scripts.\n\n### Step 2: Install and Configure Dependency Cruiser\n- **Action**: Install `dependency-cruiser` as a devDependency and copy `dependency-cruiser.config.cjs` to `.dependency-cruiser.cjs` with updated `PACKAGES_ROOT`.\n- **Key Point**: Use `.cjs` extension so CommonJS exports function seamlessly in ESM / `\"type\": \"module\"` projects.\n- **Inline Checklist**:\n  - [ ] Package manager identified correctly\n  - [ ] `dependency-cruiser` added to `devDependencies`\n  - [ ] `.dependency-cruiser.cjs` configured with 4 core rules\n\n### Step 3: Wire Scripts & Scaffold Clean Example\n- **Action**: Add `\"lint:boundaries\": \"depcruise <packages-root>\"` to `package.json` and create an example deep package:\n  - `<packages-root>/example/index.ts` (public entry point delegating to `lib/`)\n  - `<packages-root>/example/lib/impl.ts` (hidden implementation)\n  - `<packages-root>/example/tests/example.test.ts` (imports `../index`)\n- **Why**: A reference package serves as living documentation and a starter template for developers.\n\n### Step 4: Prove the Rules Bite (Verification Gate)\n- **Action**: Execute three validation passes:\n  1. Run `lint:boundaries` $\\rightarrow$ must **PASS** on clean repo.\n  2. Add deep import `import { impl } from \"../lib/impl\"` in test $\\rightarrow$ must **FAIL**.\n  3. Revert deep import and run `lint:boundaries` $\\rightarrow$ must **PASS**.\n- **Key Point**: Never sign off on boundary rules without observing an intentional test failure.\n- **Why**: Unproven linter configs frequently have syntax or glob errors that silently pass all code.\n\n### Step 5: Document Package Conventions & Agent Pointer\n- **Action**: Create `<packages-root>/README.md` explaining layout and rules, and add a single-line context pointer in `AGENTS.md` / `CLAUDE.md`.\n- **Key Point**: Explicitly explain why barrel files are discouraged.\n- **Why**: Agent pointers ensure future autonomous sessions respect package boundaries from their first prompt.\n\n---\n\n## Anti-Rationalization Guardrails\n\n| Tempting Rationalization | Binding Rule | Engineering Rationale |\n|---|---|---|\n| *\"Skip testing whether the rule fails on bad imports.\"* | **Mandatory 3-step proof (Pass $\\rightarrow$ Fail $\\rightarrow$ Pass).** | Linters with invalid glob configurations pass silently without enforcing anything. |\n| *\"Export all submodules through a giant root index.ts barrel.\"* | **Discourage large barrel files.** | Giant barrels destroy tree-shaking and create hidden cyclic import dependencies. |\n| *\"Import directly from lib/ in unit tests for convenience.\"* | **Enforce tests through public entry points only.** | Deep imports in tests tightly couple test suites to volatile internal refactors. |\n\n"},"changes":[],"summary":"First saved snapshot. No earlier version is available for comparison.","summary_kind":"deterministic","summary_metadata":{}}