setup-ts-deep-modules: Preventing Circular Dependencies in TypeScript
機械翻訳 / Machine-translated
機械翻訳 / Machine-translated
Original article: TypeScriptの循環依存を防ぐ setup-ts-deep-modules (cccskills)
Skill page: setup-ts-deep-modules
Key takeaways from this article
setup-ts-deep-modules is a skill that restricts the public entry points of a TypeScript package to only the files directly at its root. Once installed, it handles everything from configuration and validation to documentation in one go. This article walks through the four rules it introduces, how to verify those rules are working, how to install it for Claude Code or Codex, example prompts, and prerequisites to confirm before getting started.
When you invoke setup-ts-deep-modules, the agent adds boundary checks to your repository and verifies that the rules actually catch violations. The targets are direct references into package internals and circular dependencies. It is distributed from the GitHub repository mattpocock/skills (297,465 stars) under the MIT license.
Please use setup-ts-deep-modules to add boundary checks to the packages in this repository.
The agent starts by inspecting the lockfile to determine the package manager. Packages are placed under src/packages if a src/ directory exists, or packages otherwise. It then adds dependency-cruiser as a dev dependency and places a .dependency-cruiser.cjs configuration file at the repository root.
dependency-cruiser is a tool that inspects imports between files and reports rule violations. Once the work is complete, each package is organized as follows:
src/packages/
<name>/
index.ts ← public file (safe to import from outside)
client.ts ← multiple public files are allowed
lib/ ← implementation; not accessible from outside
tests/ ← tests; private because they are in a subfolder
A lint:boundaries script is also added and wired into your existing aggregate command (such as check or ci) that runs the type check. tscconfig and path aliases are left untouched, so your existing build setup remains unchanged. A sample example package and a README.md documenting the conventions are also created. The work is complete once a single line pointing to that README is added to CLAUDE.md or AGENTS.md.
The core of this skill is declaring that a package's public surface is "only the files directly at its root" and making any reference outside that surface an error. Anything inside a subdirectory is treated as private regardless of its name. You do not need to update the configuration when you add new folders.
Four rules are introduced, and violating any of them is an error.
tests/ may only reference the package's public files and fixtures inside their own tests/ directory. They may not reference their own package's lib/.Because of rule 3, tests verify behavior through the same entry points as consumers. Cross-package integration tests can be written, but tests that read internals directly cannot.
Boundary checking is added as lint:boundaries and wired into your aggregate command—such as check or ci—that already runs the type check. tscconfig and path aliases are not touched, so your existing build configuration does not change. For repositories without an aggregate command, lint:boundaries is added on its own, and the user is told to include it in CI.
The example package serves as a template to copy when creating a new package. The function exported by index.ts delegates its work to lib/impl.ts, and tests import only ../index. It is created as a starting point that can be deleted once it is no longer needed.
After creating the template, the agent runs three checks to confirm the rules are working. The first run passes with no violations. The second run temporarily adds a direct import of lib/impl in a test and expects a failure. The third run restores the original state and passes again.
If the second run does not fail, the configuration is considered incorrect and is fixed before the work is finalized.
The README.md placed in the packages folder covers the structure, the four rules, and how to run lint:boundaries. It explicitly states that barrel files—a single index.ts that re-exports everything underneath—should be avoided in favor of multiple small public files.
A line pointing to this README is also added to CLAUDE.md (or AGENTS.md if CLAUDE.md is absent; if neither exists, AGENTS.md is created). This gives any agent working on the project later a way to discover the boundary conventions from that one line.
There are two ways to install: via a command, or by asking an agent. With the command, you can choose which agent to target—Claude Code, Codex, and others—at the time of execution.
npx skills add https://github.com/mattpocock/skills --skill setup-ts-deep-modules
To ask an agent directly, send the following message as-is:
Please install the following Agent Skill.
https://github.com/mattpocock/skills/tree/main/skills/in-progress/setup-ts-deep-modules
First review the contents of SKILL.md and any accompanying files to confirm there is no suspicious behavior (such as sending data externally or reading credentials), then place it at ~/.claude/skills/setup-ts-deep-modules/ for Claude Code, or ~/.agents/skills/setup-ts-deep-modules/ for Codex.
The installation location depends on whether you want to use the skill across all projects or only in a specific project.
| Agent | All projects | Specific project only |
|---|---|---|
| Claude Code | ~/.claude/skills/ | .claude/skills/ in the project |
| Codex | ~/.agents/skills/ | .agents/skills/ in the project |
The skill folder contains SKILL.md along with dependency-cruiser.config.cjs and agents/openai.yaml, which serve as the basis for the configuration. When installing manually, copy the entire folder.
This skill is designed to be invoked explicitly by the user. Including the skill name in your prompt and noting anything you want to emphasize helps the agent understand your intent.
Please use setup-ts-deep-modules to add boundary checks to the existing repository.
There is already a dependency-cruiser configuration, so please integrate rather than overwrite it.
Even if a .dependency-cruiser.* file already exists, the agent will not overwrite it. It will integrate the four rules and the configuration, then report what was added. Running lint:boundaries after the integration will surface any existing direct internal references or circular dependencies as errors.
Please use setup-ts-deep-modules to create a copyable example package and confirm that violations are detected.
Three files are created under example/: index.ts, lib/impl.ts, and tests/example.test.ts. The results of the three runs—pass, fail, pass—are then shown, so you can confirm the rules are working on the spot.
Following the setup-ts-deep-modules process, please document the package conventions in a README and make it reachable from CLAUDE.md.
The README.md in the packages folder will contain a sample structure and the four rules, each explained in a short paragraph. The line added to CLAUDE.md will convey the message: "Read this README before adding or importing a package."
The first thing to check is the impact on existing code. All four rules are treated as errors and are wired into the same command as your type check. If existing code contains direct internal references or circular dependencies, the aggregate command will start failing as soon as the skill is installed, so budget time to fix those issues.
There are also prerequisites around the structure you are targeting. Packages are expected to be laid out in a single flat layer directly under their parent directory; nested packages (a package inside another package) are not supported.
Restricting which packages are allowed to depend on which others—dependency direction—is also out of scope. That concern is left as commented-out draft entries in the configuration file.
On the environment side, dependency-cruiser is added as a dev dependency using npm, pnpm, yarn, or bun. The configuration file uses the .cjs format, so it works fine in repositories with "type": "module".
The skill's documentation is written assuming the codebase-design skill from the same repository has been used to align terminology. Installing both ensures that the vocabulary used across descriptions stays consistent.
The license is MIT. Before installing, read SKILL.md and the accompanying files to confirm there is no suspicious behavior such as sending data externally or reading credentials.
codebase-design is a skill that helps you keep the interface callers need to learn small and consolidate complex logic inside the module. Because setup-ts-deep-modules uses the terminology from this skill (deep module, interface, etc.), design discussions and boundary enforcement can proceed with a shared vocabulary.
code-review is a skill that reviews changes based on commits or branches, distinguishing between adherence to development conventions and fulfillment of specifications. It is useful after boundary conventions are established, for checking that day-to-day changes remain compliant.
No. The configuration file included with the skill is copied to the repository root as .dependency-cruiser.cjs, with only the package location adjusted. Rules are evaluated based on folder depth, so no adjustments for file extensions are needed.
No, it is not limited to one. Any file directly at the package root is treated as a public file, so you can have multiple files such as index.ts, client.ts, and server.ts. Barrel files that re-export everything underneath are explicitly discouraged.
That is outside the scope of this skill. Dependency direction is treated as a separate concern and is left only as commented-out draft entries in the configuration file. If you need it, you can add it yourself to suit your repository.
setup-ts-deep-modules is a skill that restricts a package's entry points to the public files directly at its root and makes direct references to internals and circular dependencies into errors. The work it covers spans introducing dependency-cruiser, integrating it with the type check, creating a template package, verifying behavior with three runs, and documenting the conventions.
It is suited to teams that want module boundaries enforced by tooling rather than relying on careful review. Read SKILL.md before installing, and set aside time to address any violations in existing code before giving it a try.
This article is a repost of the cccskills article "TypeScriptの循環依存を防ぐ setup-ts-deep-modules."
For full details on the skill and installation instructions, visit the cccskills setup-ts-deep-modules page.