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

/nuxt-improve-codebase-architecture

@2a530d0
Run it yourself

Review architecture for a project with nuxt.config.ts. Use for Nuxt-native refactors across modules, layers, plugins, composables, server routes, Nitro, or runtime config.

  • 8 files
  • 94.7 KB
  • MIT
  • Updated last month
  • GitHub
Use this Skill: https://skilld.dev/gh/harlan-zw/harlan-agent-kit/nuxt-improve-codebase-architecture

Nothing lands on disk. Nothing to clean up.

Fork this Skill

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

SKILL.md

≈52 tokens for metadata: the name and description. ≈3.6k when used: this file. ≈21k more on demand in 7 files.

Description uses 2.6% 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 ≈208 characters, or 2.6%.

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 Nuxt Codebase Architecture

Surface architectural friction in a Nuxt codebase and propose deepening opportunities — refactors that turn shallow modules into deep ones, using Nuxt's own extension points as seams. The aim is testability and AI-navigability.

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.

Vocabulary and principles

Use the terms in LANGUAGE.md exactly — module, interface, implementation, depth, seam, adapter, leverage, locality. Don't drift into "component," "service," "API," or "boundary."

Load-bearing principles (full list in LANGUAGE.md):

  • Deletion test: if deleting the module just inlines 1-2 lines into each caller, it was a pass-through.
  • The interface is the test surface.
  • One adapter = hypothetical seam. Two = real.
  • No import-time side effects — critical for shared/, composables, server utils.
  • Prefer Nuxt-native seams over hand-rolled equivalents.
  • Default feature-local, promote on evidence. New composables/useFoo.ts or components/Foo.vue is a claim of app-wide ownership; the rename blast radius and naming-collision risk make global auto-import the most expensive seam decision. Colocate + _-prefix until ≥2 unrelated features consume it.

This skill is informed by the project's domain model (GLOSSARY.md, docs/arch/, docs/adr/): the domain language names good seams; ADRs record decisions not to re-litigate.

Companion files

  • LANGUAGE.md — full vocabulary and principles.
  • DEEPENING.md — dependency categories, seam ladder (shared/ → packages/* → Nuxt module/layer), testing strategy.
  • NUXT-SEAMS.md — catalogue of Nuxt's framework-native seams and the scope boundaries between nitro / nuxt server / nuxt client.
  • NITRO-CONVENTIONS.md — server-side conventions Nitro doesn't ship (validators, policies, presenters, error handling, service providers, event/listener registry, tasks, job dispatch, per-layer schema + migrations).
  • VUE-CONVENTIONS.md — framework-agnostic Vue patterns (service composables as factory + provide + use, component thinness).
  • NUXT-APP-CONVENTIONS.md — Nuxt-specific app layer ($fetch as port, async resource composables, useState for SSR-safe app-wide state, client policy mirror).
  • 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

Always run ripast first. Every claim about depth, caller counts, locality, or cross-scope leaks must cite a ripast invocation. Text search misses shadowed identifiers, type-only imports, Vue template refs, and Nuxt auto-imported callers.

Pass --tsconfig .nuxt/tsconfig.json on tree/rename/move/rename-file so auto-imports resolve. Run nuxi prepare first if .nuxt/ is stale. For scan (rg-driven) and when trimming tree, scope with --glob:

--glob 'app/**,server/**,composables/**,plugins/**,modules/**,layers/**,shared/**,nuxt.config.ts'

Adjust per project: drop app/** pre-Nuxt-4 (use pages/**,components/**), drop layers/** if none, add packages/** or a module's src/**.

Opening pass — run before forming any candidate:

Command What it surfaces
pnpm dlx @ripast/cli unused --tsconfig .nuxt/tsconfig.json --exports local Top-level declarations with zero project references → deletion-test slam-dunks
pnpm dlx @ripast/cli tree --exports exported --tsconfig .nuxt/tsconfig.json Public surface per file → shallow modules (tiny interface + tiny impl)
pnpm dlx @ripast/cli tree --exports local --tsconfig .nuxt/tsconfig.json Internals per file → locality opportunities
pnpm dlx @ripast/cli css-class-scan --glob 'app/**,layers/**,components/**,pages/**' Class-token inventory → design-token consolidation candidates

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

Command What it surfaces
pnpm dlx @ripast/cli scan <symbol> --glob ... Caller count + kind classification → drives deletion test + §2 thresholds
pnpm dlx @ripast/cli scan <symbol> --kind identifier-reference,import-specifier --glob ... Same, minus string-literal noise
pnpm dlx @ripast/cli scan <symbol> --graph mermaid --glob ... Importer graph → cross-scope leaks (graph spanning server/ + composables/ + plugins/)

Cite numbers when presenting ("scan returns 2 callers, both in server/api/ — single-scope, deletion test fails").

Read the project's domain glossary and any ADRs in the area first. Then orient on the Nuxt shape of the project:

  • Is this a Nuxt app, a Nuxt module, or a workspace with layers?
  • Which Nuxt extension points are already in use: modules/, layers/, plugins/, composables/, server/api, server/middleware, nitro plugins, runtimeConfig, app:*/nitro:*/build:* hooks, addImports, addServerHandler, addComponent, addPlugin?
  • Where does the project hand-roll its own seam (a singleton, a custom plugin pattern, a global registry) instead of using one Nuxt provides?

Then use subagent_type=Explore to walk the codebase. Friction signals:

  • Feature requires bouncing across composables/, plugins/, server/api, runtimeConfig — scan --graph mermaid spanning ≥3 Nuxt scopes = no central seam.
  • Shallow modules: composable wrapping a single useState, plugin providing one untyped value, server util that's a one-line $fetch passthrough. Spot via tree --exports exported.
  • Pure functions extracted for testability while real bugs hide in plugin order / hook timing / SSR vs client divergence (no locality).
  • Cross-cutting concerns (auth, logging, telemetry) smeared across app/, server/, nitro instead of sitting behind one hook/module.
  • A cluster of plugins + composables + server handlers that wants to be a Nuxt module or layer with a small options + hooks interface.
  • Walk the convention files for missing seams: each "Gap" entry is a candidate when present in the codebase.
  • Cross-scope leaks. types//utils//constants/ imported from both server/ and composables/ — move to shared/. Detect with scan --graph mermaid: importer set straddling both = leak.
  • Import-time side effects in shared/, composables, or server utils — fire in every consumer, break SSR/treeshaking/tests.

2. Present candidates

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

  • Files — which files/modules are involved (include the Nuxt-flavoured paths: modules/foo.ts, layers/billing/, server/api/x.post.ts, plugins/auth.client.ts, composables/useBar.ts, nuxt.config.ts)
  • Problem — why the current architecture is causing friction
  • Solution — plain English description of what would change, named in Nuxt terms (e.g. "collapse these three plugins and two composables into a single Nuxt module that registers addImports for the public composables and an app:created hook for the singleton wiring")
  • Benefits — explained in terms of locality and leverage, and also in how tests would improve (e.g. "the module can be tested with @nuxt/test-utils against a fixture; today the plugin's behaviour can only be observed through a full app boot")

Use GLOSSARY.md for the domain, LANGUAGE.md for the architecture, NUXT-SEAMS.md for the framework seam, and the relevant convention file + section number when the candidate is a convention gap. Example phrasings: "the Order intake module exposed as a Nuxt layer", "establish NITRO-CONVENTIONS.md §1 for the Order create handler" — not "the FooBarHandler", not "the Order service".

Reject before listing. Bias toward fewer, sharper candidates. Validate caller counts with scan --kind identifier-reference,import-specifier scoped to the whole project (no --glob narrowing to the candidate's own layer/dir), and confirm the shape they consume — a uniform interface for the wrapper is a precondition for collapsing it. Cross-layer callers with a different state shape break the candidate. No guesswork.

  • Deletion test fails. Thresholds: single caller = adapter, not a seam; Nuxt layer/module needs ≥3 plugins/composables/handlers + shared config or hooks; schema/policy/presenter/validator/async-resource composable earns its keep at ≥2 callers OR real branching/transform; pure value/type mapping (just destructure/rename) needs ≥4 callers.
  • No locality win. "Testable in isolation" / "future-proof" without bugs/changes concentrating is relocation, not depth. Includes defensive re-validation of invariants a zod schema, typed event, or DB column already enforces.
  • Renames or file reshuffles dressed up as architecture. Note separately or skip.
  • Smuggles import-time side effects.
  • Already matches a NITRO/VUE/NUXT-APP convention.
  • Meta-handler with no work. defineApiHandler earns its keep only when ≥2 of {validation, auth, presenter, domain-error mapping} fire per call.
  • Render-factory composable. Mostly h() calls / column defs / template output with little reactive state. Acceptable at ≥4 callers; otherwise inline or slot-scope.
  • Promotes to global auto-import without cross-feature consumers. Reject if scan --tsconfig .nuxt/tsconfig.json shows callers in a single feature dir — keep colocated + _-prefixed. Promote only at ≥2 unrelated feature consumers. See NUXT-SEAMS.md §"Internal vs global scope".
  • Bundled-concern composable fusing ≥3 concerns (fetch + cache + optimistic + rollback + retry) with no override seams.
  • Wrapper-collapse without project-wide caller audit. A "delete this pass-through wrapper, inline into N callers" candidate is invalid until scan <Wrapper> --kind identifier-reference,import-specifier (no --glob) has been run and every caller's consumed-shape verified. Wrappers in most cases have cross-layer callers (admin pages, ad-hoc usages) that don't share the "obvious" caller's state shape; collapsing on a partial census produces a narrower interface that breaks the outliers at typecheck.
  • Config threaded through ≥3 layers unchanged = missing seam. Consume at creation layer or place adapter at deepest meaningful seam.
  • Single-reader runtimeConfig key. Confirm with scan --kind property,member-access; one hit = fails unless genuinely env-overridable at deploy.
  • Hides event behind a global accessor. Server abstractions thread H3Event through — reject module-level singletons, ambient useRuntimeConfig() without event, implicit getRequestEvent().
  • Contradicts a current ADR without a load-bearing reason.

Forbidden patterns — always flag, never propose. Full lists in NITRO-CONVENTIONS.md and NUXT-APP-CONVENTIONS.md "Forbidden patterns". Headline: never wrap an authenticated route in defineCachedEventHandler — the cache wrapper strips request headers, breaking session/auth. Hoist auth to an outer defineEventHandler and cache the data fetch via defineCachedFunction keyed on the resource.

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 Nuxt candidates, also walk: which Nuxt hook fires when, SSR vs client behaviour, build-time vs runtime, where runtimeConfig ends and component state begins.

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 Nuxt-native shapes (module + hooks, layer, plugin + composable, nitro plugin) so the design space is grounded in what Nuxt already offers.

  • Need to know the true blast radius of a rename/move before committing? pnpm dlx @ripast/cli scan <symbol> (counts) or pnpm dlx @ripast/cli scan <symbol> --graph mermaid (importer graph). Quote numbers before promising scope.

  • Decision crystallized into a concrete refactor? Execute through ripast (--tsconfig .nuxt/tsconfig.json rewrites auto-imported callers too):

    Refactor Command
    Rename symbol pnpm dlx @ripast/cli rename <from> <to> --tsconfig .nuxt/tsconfig.json --apply (--scope <file> if multi-declared)
    Move exported declaration pnpm dlx @ripast/cli move <symbol> --from <a> --to <b> --tsconfig .nuxt/tsconfig.json --apply
    Move a file pnpm dlx @ripast/cli rename-file <old> <new> --tsconfig .nuxt/tsconfig.json --apply
    Design-token / class migration pnpm dlx @ripast/cli css-class-rename --map tokens.json --apply (seed via css-class-scan)

    All mutating commands default to dry-run; preview, then --apply. --verify blocks on new type diagnostics — fix them, never --no-verify. Edit is only correct for single-file or <5-match changes.

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/nuxt-improve-codebase-architecture