---
title: "improve-ts-pkg-architecture by harlan-zw · skilld"
canonical_url: "https://skilld.dev/gh/harlan-zw/harlan-agent-kit/improve-ts-pkg-architecture/-/LANGUAGE"
meta:
  description: "Review architecture for a TypeScript package without nuxt.config.ts. Use for package-native refactors, exports, workspaces, module boundaries, and testability. From harlan-zw/harlan-agent-kit."
  "og:description": "Review architecture for a TypeScript package without nuxt.config.ts. Use for package-native refactors, exports, workspaces, module boundaries, and testability. From harlan-zw/harlan-agent-kit."
  "og:title": "improve-ts-pkg-architecture by harlan-zw"
  "twitter:description": "Review architecture for a TypeScript package without nuxt.config.ts. Use for package-native refactors, exports, workspaces, module boundaries, and testability. From harlan-zw/harlan-agent-kit."
  "twitter:title": "improve-ts-pkg-architecture by harlan-zw"
---

`

[All skills](https://skilld.dev/skills)

[![harlan-zw avatar](https://skilld.dev/_img/avatar?url=https%3A%2F%2Fgithub.com%2Fharlan-zw.png%3Fsize%3D96)](https://skilld.dev/gh/harlan-zw)

# **/improve-ts-pkg-architecture**

by [Harlan Wilton](https://skilld.dev/gh/harlan-zw)· [harlan-zw](https://skilld.dev/gh/harlan-zw)/ [harlan-agent-kit](https://skilld.dev/gh/harlan-zw/harlan-agent-kit)·10 stars

 1

Review architecture for a TypeScript package without nuxt.config.ts. Use for package-native refactors, exports, workspaces, module boundaries, and testability.

[GitHub](https://github.com/harlan-zw/harlan-agent-kit/blob/491d032ad2b040c879097c10fe563c1a4655be6d/harlan-agent-kit/skills/improve-ts-pkg-architecture/SKILL.md "View SKILL.md on GitHub") Updated 2 hours ago

## Skill content

[Raw](https://skilld.dev/api/skills-raw/harlan-zw/harlan-agent-kit/improve-ts-pkg-architecture)

## Improve TS Package Architecture

Surface architectural friction in a TypeScript package (library, CLI, or pnpm monorepo) and propose **deepening opportunities** — refactors that turn shallow modules into deep ones, using the package's own publishable surface as the seam. The aim is testability and AI-navigability.

Vocabulary, principles, and forbidden patterns live in their canonical files; this file references them. Use the terms in [LANGUAGE.md](https://skilld.dev/gh/harlan-zw/harlan-agent-kit/improve-ts-pkg-architecture/-/LANGUAGE.md) exactly. This skill is *informed* by the project's domain model (`GLOSSARY.md`, `docs/arch/`, `docs/adr/`).

### Worktree isolation

Before any edit, follow the [worktree isolation contract](https://github.com/harlan-zw/harlan-agent-kit/blob/main/harlan-agent-kit/references/worktree-isolation.md). It provides the atomic live-agent claim used below.

An existing worktree alone does not prove another agent is active.

`wt` is the only worktree tool. Never run `git worktree add`, and never use a harness worktree option such as `EnterWorktree` or `isolation: "worktree"`. Those write to `.claude/worktrees/`, which is banned. `wt` places every worktree at `<parent>/<repo>.<branch-slug>`.

Keep the primary checkout read only. Before mutation, run `wt list --format=json`. Reuse the task's worktree with `wt switch <branch>`, or create one with `wt switch --create <branch> --base <base>`. Read its absolute `path` from the JSON, then pass that path as `workdir` to every later command. Never share a mutation worktree between tasks.

### Companion files

- [LANGUAGE.md](https://skilld.dev/gh/harlan-zw/harlan-agent-kit/improve-ts-pkg-architecture/-/LANGUAGE.md) — full vocabulary and principles.
- [DEEPENING.md](https://skilld.dev/gh/harlan-zw/harlan-agent-kit/improve-ts-pkg-architecture/-/DEEPENING.md) — dependency categories, seam ladder (file → subpath export → `packages/*`), testing strategy.
- [TS-PKG-SEAMS.md](https://skilld.dev/gh/harlan-zw/harlan-agent-kit/improve-ts-pkg-architecture/-/TS-PKG-SEAMS.md) — catalogue of TS-package-native seams ( `exports`, conditional exports, `packages/*`, catalogs, `bin`, hookable, unplugin, citty).
- [PKG-CONVENTIONS.md](https://skilld.dev/gh/harlan-zw/harlan-agent-kit/improve-ts-pkg-architecture/-/PKG-CONVENTIONS.md) — package conventions (factory shape, options + hooks, error modes, CLI shape, treeshake invariants, fixture layout, runtime portability).
- [INTERFACE-DESIGN.md](https://skilld.dev/gh/harlan-zw/harlan-agent-kit/improve-ts-pkg-architecture/-/INTERFACE-DESIGN.md) — design-it-twice with parallel sub-agents for chosen candidates.
- [code-comments.md](https://github.com/harlan-zw/harlan-agent-kit/blob/main/harlan-agent-kit/references/code-comments.md) — the comment contract for any code this skill writes.

### Process

#### 1. Explore

**Always run ripast first.** Every claim about depth, caller counts, locality, or cross-package leaks must be backed by a ripast invocation; text search misses shadowed identifiers, type-only imports, and re-exports through a subpath. If you cannot cite ripast output, drop the claim.

Pass `--tsconfig tsconfig.json` (root, or per-package in a monorepo) so subpath aliases and workspace refs resolve. For `scan` (rg-driven) and noisy `tree` output, scope with `--glob 'src/**,packages/*/src/**,test/**'`; trim per project (`bin/**`, `scripts/**`, `playground/**` as needed).

Opening pass — run before forming any candidate:

| Command | What it surfaces |
| --- | --- |
| `npx -y @ripast/cli unused --tsconfig tsconfig.json --exports local` | Top-level declarations with zero project references → deletion-test slam-dunks |
| `npx -y @ripast/cli tree --exports exported --tsconfig tsconfig.json` | Public surface per file → shallow modules + leaks (anything exported that isn't in the `exports` map is a confessed private leak) |
| `npx -y @ripast/cli tree --exports local --tsconfig tsconfig.json` | Internals per file → locality opportunities |

Per-candidate, before listing (`scan` is rg-driven — use `--glob` here):

| Command | What it surfaces |
| --- | --- |
| `npx -y @ripast/cli scan <symbol> --glob ...` | Caller count + kind classification → drives deletion test + §2 thresholds |
| `npx -y @ripast/cli scan <symbol> --kind identifier-reference,import-specifier --glob ...` | Same, minus string-literal noise |
| `npx -y @ripast/cli scan <symbol> --graph mermaid --glob ...` | Importer graph → cross-package leaks (graph spanning `packages/a/src/` + `packages/b/src/` via deep import) |

Cite numbers when presenting.

**Read the package's published surface first**: `package.json` `exports`, `bin`, `peerDependencies`, `sideEffects`, `engines`. The `exports` map is the contract. Then read `GLOSSARY.md`, `docs/arch/`, and `docs/adr/` if present.

Orient on the package shape:

- Is this a **single-repo library**, a **CLI** ( `bin` field), a **plugin** (peer-dep on rollup/vite/eslint/etc.), or a **pnpm monorepo** with `packages/*`?
- Which TS-package-native seams are already used: subpath exports, conditional exports ( `types`/ `import`/ `node`/ `browser`/ `workerd`), `packages/*`, `catalog:` versions, `hookable` hooks, `unplugin`/factory shape, `citty` subcommands, `c12` config loading?
- Where does the package hand-roll its own seam (a singleton in a top-level file, a global `EventEmitter`, a custom plugin registry, ad-hoc env reads) instead of using one the ecosystem provides?

Then use the Agent tool with `subagent_type=Explore` to walk the codebase. Note friction signals:

- **Shallow modules** — files re-exporting one function, factories that just `new` a class, utils that wrap one-liners. Spot via `tree --exports exported`.
- **Bouncing** — understanding one feature requires five files with no central seam. `scan <symbol> --graph mermaid` spanning 10+ directories.
- **Pure-function extraction without locality** — bugs hide in how the pieces compose (orchestration order, error recovery, cache invalidation).
- **Cross-cutting leak** — logging/config/error-mapping/telemetry scattered across 10+ files instead of one factory or hook bus.
- **Hard-to-test interface** — modules reading `process.env` deep inside, singletons at import, side effects on import.
- Walk [PKG-CONVENTIONS.md](https://skilld.dev/gh/harlan-zw/harlan-agent-kit/improve-ts-pkg-architecture/-/PKG-CONVENTIONS.md); each section's "Gap" entry is a friction signal — if present in the codebase, that's a candidate.
- **Cross-package leaks** (monorepo) — relative or deep imports across workspace packages bypass the `exports` map. Detect with `scan <symbol> --graph mermaid` straddling two `packages/*`. See [TS-PKG-SEAMS.md](https://skilld.dev/gh/harlan-zw/harlan-agent-kit/improve-ts-pkg-architecture/-/TS-PKG-SEAMS.md) `Workspace packages`.
- **`exports` map leaks** — deep paths ( `pkg/dist/internal/foo`) instead of declared subpaths. Either expose a subpath or stop the leak.
- **Cross-module coupling that should flow through a hook bus** — two modules importing each other to coordinate, or fan-in to a coordinator module. `hookable` is the answer. Detect via two-way edges in `scan --graph mermaid`.
- **Treeshake / bundle-size signals** — heavy SDK statically imported at module top when only one path uses it. Move the import inside the function, or behind a conditional export. Run `npx -y publint && npx -y @arethetypeswrong/cli --pack .` plus `du -sh dist/`.
- **Repeated import-time work** (regex/schema/table compilation) running whether the module is used or not. Move into a memoised getter inside the factory.

#### 2. Present candidates

Present a numbered list of deepening opportunities. For each candidate:

- **Files** — which files/modules are involved (include flavour paths: `src/index.ts`, `src/cli.ts`, `packages/core/src/foo.ts`, `package.json` `exports`, `pnpm-workspace.yaml`)
- **Problem** — why the current architecture is causing friction
- **Solution** — plain English description of what would change, named in TS-pkg terms (e.g. *"collapse these five files into a single `createPipeline(opts)` factory exported from a new `./pipeline` subpath; the existing files become private implementation under `src/pipeline/`, and the factory exposes a `hookable` hook bus for the three current extension points"*)
- **Benefits** — explained in terms of locality and leverage, and also in how tests would improve (e.g. *"the factory can be tested directly with vitest; today the three hooks fire from three different files and the only way to observe them is to import each privately"*)

**Use GLOSSARY.md for the domain, [LANGUAGE.md](https://skilld.dev/gh/harlan-zw/harlan-agent-kit/improve-ts-pkg-architecture/-/LANGUAGE.md) for the architecture, [TS-PKG-SEAMS.md](https://skilld.dev/gh/harlan-zw/harlan-agent-kit/improve-ts-pkg-architecture/-/TS-PKG-SEAMS.md) for the framework seam, and the relevant convention section from [PKG-CONVENTIONS.md](https://skilld.dev/gh/harlan-zw/harlan-agent-kit/improve-ts-pkg-architecture/-/PKG-CONVENTIONS.md) when the candidate is a convention gap.** Example phrasings: *"the Order intake module exposed as a `./intake` subpath"*, *"establish PKG-CONVENTIONS.md §Hook bus for the build pipeline"* — not *"the FooBarHandler"*, not *"the Order service"*.

**Reject before listing.** Validate caller-count thresholds with `scan <symbol> --kind identifier-reference,import-specifier`; no guesswork.

- **Deletion test fails.** Thresholds: subpath export ≥3 consumers or a distinct public concept; workspace `packages/*` ≥2 independent consumers AND zero coupling back to the host; factory ≥2 callers OR real branching/options; pure value/type mapping ≥4 callers.
- **No locality win.** Pure-function extraction with no consolidation, speculative seams, defensive re-validation of invariants the caller's types already enforce.
- **Renames dressed up as architecture.** Note separately or skip.
- **Already conventional** — matches a PKG-CONVENTIONS.md section.
- **Subpath read in exactly one place.** Confirm with `scan <import-specifier> --kind import-specifier`.
- **Config threaded through ≥3 layers unchanged** — consume at the creation layer or place an adapter at the deepest meaningful seam.
- **Contradicts a current ADR without a load-bearing reason.** See ADR conflicts below.

**Forbidden patterns** (always flag, never propose) — full list in [PKG-CONVENTIONS.md](https://skilld.dev/gh/harlan-zw/harlan-agent-kit/improve-ts-pkg-architecture/-/PKG-CONVENTIONS.md) §"Forbidden patterns".

**ADR conflicts**: if a candidate contradicts an existing ADR, only surface it when the friction is real enough to warrant revisiting the ADR. Mark it clearly (e.g. *"contradicts ADR-0007 — but worth reopening because…"*). Don't list every theoretical refactor an ADR forbids.

Do NOT propose interfaces yet. Ask the user: "Which of these would you like to explore?"

#### 3. Grilling loop

Once the user picks a candidate, drop into a grilling conversation. Walk the design tree with them — constraints, dependencies, the shape of the deepened module, what sits behind the seam, what tests survive. For TS-pkg candidates, also walk: which runtimes it must work in (node / browser / workerd / edge / bun), whether it appears in the `exports` map (and at which subpath / conditional), whether it lives in `src/` or a workspace `packages/*`, what the treeshake / `sideEffects` story is.

Side effects happen inline as decisions crystallize:

- **Naming a deepened module after a concept not in `GLOSSARY.md`?** Read the [glossary skill](https://github.com/harlan-zw/harlan-agent-kit/blob/main/harlan-agent-kit/skills/glossary/SKILL.md) and add the term there: the term, one sentence of meaning in this codebase, and where it lives.
- **Sharpening a fuzzy term during the conversation?** Update `GLOSSARY.md` right there.
- **User rejects the candidate with a load-bearing reason?** Offer an ADR, framed as: *"Want me to record this as an ADR so future architecture reviews don't re-suggest it?"* Only offer when the reason would actually be needed by a future explorer to avoid re-suggesting the same thing — skip ephemeral reasons ("not worth it right now") and self-evident ones. Write it to `docs/adr/NNNN-slug.md` with context, decision, and consequences.
- **Want to explore alternative interfaces for the deepened module?** See [INTERFACE-DESIGN.md](https://skilld.dev/gh/harlan-zw/harlan-agent-kit/improve-ts-pkg-architecture/-/INTERFACE-DESIGN.md). Sub-agents are pre-seeded with TS-pkg-native shapes (single factory, factory + hook bus, subpath-exposed surface, ports & adapters) so the design space is grounded in what the ecosystem already offers.
- **Need to know the true blast radius of a rename/move before committing?** `npx -y @ripast/cli scan <symbol>` (counts) or `npx -y @ripast/cli scan <symbol> --graph mermaid` (importer graph). Quote numbers before promising scope.
- **Decision crystallized into a concrete refactor?** Execute through ripast, not Edit. Pick the primitive:

  Pass `--tsconfig tsconfig.json` (or the per-package one) on `rename`, `move`, and `rename-file` so all callers are rewritten.

  | Refactor | Command |
  | --- | --- |
  | Rename a symbol across files | `npx -y @ripast/cli rename <from> <to> --tsconfig tsconfig.json --apply` (add `--scope <file>` if multi-declared) |
  | Move an exported declaration | `npx -y @ripast/cli move <symbol> --from <a> --to <b> --tsconfig tsconfig.json --apply` |
  | Move a file (e.g. `src/utils/foo.ts` → `src/pipeline/foo.ts` to close a leak) | `npx -y @ripast/cli rename-file <old> <new> --tsconfig tsconfig.json --apply` |

  All mutating commands default to dry-run — preview the diff, then `--apply`. `--verify` (default on for `rename`/ `move`) blocks the apply on new type diagnostics; fix them, never `--no-verify` past them. Edit is only correct for single-file or <5-match changes. ripast carries out the move; it does not justify the deepening.
- **The refactor changes the `exports` map?** Update `package.json` `exports` in the same pass. If a subpath is added or removed, the change is a SemVer-visible event — note it for the next release.

Source: [SKILL.md on GitHub](https://github.com/harlan-zw/harlan-agent-kit/blob/491d032ad2b040c879097c10fe563c1a4655be6d/harlan-agent-kit/skills/improve-ts-pkg-architecture/SKILL.md)

## Related skills

-
-
-
-
-
-