Index of every page on this site: llms.txt
All skills
harlan-zw avatar

/improve-ts-pkg-architecture

@2a530d0
Run it yourself

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

  • 6 files
  • 48.5 KB
  • MIT
  • Updated last month
  • GitHub
Use this Skill: https://skilld.dev/gh/harlan-zw/harlan-agent-kit/improve-ts-pkg-architecture

Nothing lands on disk. Nothing to clean up.

Fork this Skill

Edit a local copy. It keeps the author and licence.

SKILL.md

≈47 tokens for metadata: the name and description. ≈3.3k when used: this file. ≈9k more on demand in 5 files.

Description uses 2.4% of example budget

Before choosing a Skill, your Agent reads its name and description. All available Skills share that space.

  • A shorter description leaves more room for other Skills. This entry exceeds our 1% size suggestion.

In our Claude Code example, all Skill names and descriptions share 8,000 characters. This Skill uses ≈189 characters, or 2.4%.

The 1% threshold is a size suggestion. Longer descriptions can still fit.

Your model, settings, and other Skills decide how much text your Agent can read.

Example settings and source

The example uses a 200k-token context and default Claude Code settings. The count includes the name, description, separators, and when_to_use when present. Codex also counts local file paths.

Skit's source and limits: Codex 0.160.1, Claude Code 2.1.292.

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 exactly. This skill is informed by the project's domain model (GLOSSARY.md, docs/arch/, docs/adr/).

Worktree isolation

Before any edit, read and follow the worktree isolation contract. Keep mutation in a task-owned wt worktree with a live claim. Keep the primary checkout read only.

Companion files

  • LANGUAGE.md — full vocabulary and principles.
  • DEEPENING.md — dependency categories, seam ladder (file → subpath export → packages/*), testing strategy.
  • TS-PKG-SEAMS.md — catalogue of TS-package-native seams (exports, conditional exports, packages/*, catalogs, bin, hookable, unplugin, citty).
  • PKG-CONVENTIONS.md — package conventions (factory shape, options + hooks, error modes, CLI shape, treeshake invariants, fixture layout, runtime portability).
  • INTERFACE-DESIGN.md — design-it-twice with parallel sub-agents for chosen candidates.
  • code-comments.md — the comment contract for any code this skill writes.

Process

1. Explore

Read the published surface and the requested boundary first. Use scoped inspection to resolve a specific uncertainty. Read the installed ripast Skill before using RipIDE. Prefer a supplied launcher and preserve its version. Use RipIDE for supported mechanical operations. Design new contracts with direct edits. Before a cross-package move, check destination dependencies and exported entry points.

Support architectural claims with the relevant exports, imports, call sites, or semantic tool evidence. Text matches and classified scan occurrences do not establish unique semantic consumers. State the evidence's scope and limits. Do not run every discovery command merely to establish tool usage.

In examples, ripide means the supplied or prepared launcher. Pass --tsconfig only to commands that accept it. scan, tree, and unused do not accept it. Scope discovery with a relevant --glob, such as 'packages/core/src/**'. Expand only when needed.

Choose an inspection only when it can change the next decision:

Command What it surfaces
ripide unused --glob 'packages/core/src/**' --exports local Top-level declarations with zero project references → deletion-test slam-dunks
ripide tree --glob 'packages/core/src/**' --exports exported Public surface per file → shallow modules + leaks (anything exported that isn't in the exports map is a confessed private leak)
ripide tree --glob 'packages/core/src/**' --exports local Internals per file → locality opportunities

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

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

Cite supported numbers when presenting. Otherwise describe the verified relationship without an invented count. Save full output as an artifact. Read relevant names, locations, and short context into the model. Batch independent reads. Keep dependent mutations and checks sequential.

Read package.json exports, bin, peerDependencies, sideEffects, and engines. The exports map is the contract. Then read GLOSSARY.md, docs/arch/, and docs/adr/ when 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; 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 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 pnpm dlx publint && pnpm dlx @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 for the architecture, TS-PKG-SEAMS.md for the framework seam, and the relevant convention section from 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 consumer relationships from resolved imports and call sites; no guesswork. Use scan <symbol> --kind identifier-reference,import-specifier to locate candidates, then inspect their identity.

  • 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 §"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 installed Brundlefly glossary Skill 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. 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? ripide scan <symbol> (counts) or ripide 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 ripide rename <from> <to> --tsconfig tsconfig.json --apply (add --scope <file> if multi-declared)
    Move an exported declaration ripide 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) ripide rename-file <old> <new> --tsconfig tsconfig.json --apply

    Use the installed ripast Skill's operation and verification guidance. Keep verification enabled. Recover complete diagnostics before classifying an apparent false positive. Design new APIs and behavior with direct edits. Use supported semantic commands for their mechanical changes. If public types or exports change, rebuild affected declarations before downstream typechecks. Repair a failed check, then run the focused proving check before broad verification. Keep meaningful failing-first tests. Do not repeat unchanged failing lint commands. Repeat passed checks only after relevant edits or new evidence. Run required final checks on the submitted tree.

  • 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

skilld matched fixed text patterns in SKILL.md and file names. Patterns miss obfuscated code.

skilld run checks every file with the same patterns. It asks for approval before it loads a Skill with a behavior marked Needs approval.

No third-party reports yet.

Signed by skilld at 2a530d0. This ties the file your Agent reads to that commit on GitHub. It does not review the instructions.

Last checked against GitHub 2 days ago.

Activeupdated last month
effort
high

README badge

README badge for harlan-zw/harlan-agent-kit/improve-ts-pkg-architecture