Vendored nuxt-extensions core Knowledge Base
Gotchas
- Upstream tags have NO
vprefix —@lenne.tech/nuxt-extensionspublishes tags like7.19.0, notv7.19.0. Usingv7.19.0in git refs or changelog links produces 404s. This differs from@lenne.tech/nest-serverwhich also drops the prefix but some tooling adds it back automatically. nuxt.config.tsmodule registration is NOT auto-handled on sync — When a new composable or module file is added upstream, the sync agent copies the file but does NOT register it innuxt.config.ts. You must manually add the module reference after a sync, otherwise Nuxt does not expose the new functionality.- Auto-imports work for composables + components but NOT for types — Files in
app/core/are auto-imported by Nuxt for composables and components, but explicitly imported types (e.g.import type { Foo } from '@lenne.tech/nuxt-extensions') need their paths rewritten to relative references after vendoring. This rewrite is not fully automated. - Local patches in
app/core/are invisible to future syncs — Same gotcha as nest-server-core-vendoring: the updater can't infer your intent. Document intentional local deviations inapp/core/LOCAL-PATCHES.md. - 1.7.0 sync brings the AI module — The 1.7.0 baseline adds
src/runtime/composables/use-lt-ai*.ts,src/runtime/lib/ai.ts, andsrc/runtime/types/ai.ts(~700 LoC). After sync, projects MUST add anltExtensions.ai: { enabled, basePath }section tonuxt.config.ts— auto-imports do not work until the option is registered. Default option in upstream isenabled: true, so projects that never want AI should setenabled: falseafter the sync. - 1.7.0 pre-release-only breaking renames — Only relevant for projects that vendored a 1.7.0 PRE-release into
app/core/. Rename map (apply via project-wide replace after sync):useLtAiSnippets→useLtAiPrompts,LtAiPromptSnippet→LtAiPrompt,LtAiPromptSnippetInput→LtAiPromptInput,LtAiPromptTemplate→LtAiSlot,LtAiPromptTemplateInput→LtAiSlotInput,UseLtAiSnippetsReturn→UseLtAiPromptsReturn. Additionally: the EXECUTION payload type foruseLtAi.prompt()was renamedLtAiPromptInput→LtAiPromptRunInput(CRUD input keeps the conventionalLtAiPromptInputname). Projects that synced a 1.6.x or earlier baseline are unaffected.
This skill provides knowledge and resources for lenne.tech projects that have vendored the @lenne.tech/nuxt-extensions module into their source tree. For automated execution, use the matching agents:
lt-dev:nuxt-extensions-core-updatervia/lt-dev:frontend:update-nuxt-extensions-core-- pulls upstream changes into the vendored core, with AI-driven curation against local patcheslt-dev:nuxt-extensions-core-contributorvia/lt-dev:frontend:contribute-nuxt-extensions-core-- identifies substantial local changes to the vendored core and prepares them as Upstream-Pull-Requests to the nuxt-extensions repository
When This Skill Activates
- Discussing the vendored nuxt-extensions pattern
- Asking how to sync frontend core from upstream into a vendored project
- Asking how to port a local frontend fix back to the upstream repository
- Troubleshooting nuxt.config.ts module registration after vendoring
- Planning an upstream-sync with conflict resolution for the frontend core
- Explaining the difference to the classic npm-based update flow for nuxt-extensions
Skill Boundaries
| User Intent | Correct Skill |
|---|---|
| "Sync vendored frontend core from upstream 1.5.3" | THIS SKILL |
| "Port this composable fix back to nuxt-extensions" | THIS SKILL |
| "Update nuxt-extensions via npm" | developing-lt-frontend |
| "Create a new Nuxt composable" | developing-lt-frontend |
| "Fix a CVE via npm audit" | maintaining-npm-packages |
| "Sync vendored backend core from upstream" | nest-server-core-vendoring |
| "Modify @lenne.tech/nuxt-extensions itself and test via pnpm link" | contributing-to-lt-framework |
Detecting a Vendored Frontend Project
A project is considered vendored if all of the following are true:
app/core/VENDOR.mdexists (in the frontend subproject root, e.g.projects/app/app/core/VENDOR.md)package.jsondoes not list@lenne.tech/nuxt-extensionsindependenciesordevDependenciesapp/core/contains the nuxt-extensions source (composables, components, plugins, etc.)
A project is npm-based (classic) if:
@lenne.tech/nuxt-extensionsis a regular dependency inpackage.json- There is no
VENDOR.mdunderapp/core/
The fullstack-updater (classic agent) detects this automatically and delegates
to nuxt-extensions-core-updater for vendored projects.
Modification Policy (when to touch app/core/)
Vendoring copies the nuxt-extensions source into the project tree so Claude Code can read it directly -- this is a comprehension aid, not an invitation to fork. The policy:
- FIRST check whether upstream already has the fix — prefer updating over
hand-patching. Before changing anything in
app/core/, confirm the vendored baseline is current: compare the version/commit recorded inapp/core/VENDOR.mdagainst the latest@lenne.tech/nuxt-extensionsrelease and the upstream default branch, and read the upstream version of the exact file you intend to change. If a newer release — or upstream HEAD — already contains the fix, optimization, or enhancement, adopt it via/lt-dev:frontend:update-nuxt-extensions-coreinstead of writing a local patch. A hand-patch that duplicates or diverges from an upstream fix creates a needless merge conflict on the next sync and often reimplements the change worse than the maintainers already did. Only hand-patch when the fix genuinely does not exist upstream yet — and then still contribute it (step 4). Runpnpm run check:vendor-freshness(or readVENDOR.md) if unsure whether the baseline is stale. - Change
app/core/ONLY when the change is generally useful to all nuxt-extensions consumers. Valid reasons:- Bugfixes that apply to every consumer
- Framework enhancements with broad applicability (new composables, better defaults, SSR fixes)
- Closing security vulnerabilities
- Type/config compatibility fixes that every consumer would hit
- Every other change belongs in project code (outside
app/core/), via project-level composables, components, middleware, or plugin overrides. Project-specific business rules, customer branding, or proprietary integrations must never live in the vendored core. - Generally-useful changes MUST be submitted as an upstream PR to
github.com/lenneTech/nuxt-extensions. Use/lt-dev:frontend:contribute-nuxt-extensions-coreto prepare the PR. Do not let useful fixes rot in a single project's vendor tree -- they belong upstream so every consumer benefits and the local patch disappears on the next sync. - When in doubt, ask before editing
app/core/. The contributor agent exists precisely to keep the vendor tree close to upstream.
The nuxt-extensions-core-contributor agent enforces this distinction by
categorizing every local commit as upstream-candidate (generic) vs.
project-specific (stays local) vs. unclear (asks the human).
The Frontend Vendor Model (one-way curation)
The vendored frontend core lives as first-class project code. Flow is:
Upstream (github.com/lenneTech/nuxt-extensions)
|
| /lt-dev:frontend:update-nuxt-extensions-core (curated, one-way)
v
Project vendor (app/core/)
|
| /lt-dev:frontend:contribute-nuxt-extensions-core (manual review, cherry-pick)
v
Upstream PR (via normal GitHub review process)
Local patches are expected. Projects legitimately modify the vendored core when business rules, integration adapters, or bugfixes are needed before upstream ships them. Those patches persist through syncs.
Changes are never auto-pushed. The contributor agent prepares PR drafts -- a human reviews and submits them through normal GitHub workflow. No git-subtree push, no automatic upstream replay.
No Flatten-Fix Needed
Unlike the backend vendor model (nest-server), the nuxt-extensions source structure
is already flat. The upstream repository organizes its code directly in src/
without a nested core/ subdirectory, so when vendored into app/core/, no
import-path rewriting is needed for internal files.
This is an explicit simplification compared to the backend vendoring pattern:
- Backend (nest-server): Requires flatten-fix on
index.ts,core.module.ts,test/test.helper.ts, andcore-persistence-model.interface.tsafter every sync - Frontend (nuxt-extensions): No flatten-fix needed -- direct copy works
The only import changes required are in the consumer project, not in the vendored code itself:
nuxt.config.ts-- remove the@lenne.tech/nuxt-extensionsmodule entry, replace with local module registration pointing toapp/core/- Up to 4 explicit type/testing imports that reference the npm package name need rewriting to relative paths
Vendor Directory Structure
After vendoring, the frontend subproject looks like:
projects/app/
├── app/
│ ├── core/
│ │ ├── VENDOR.md <- NEW (marker + baseline metadata)
│ │ ├── LICENSE <- copied for provenance
│ │ ├── composables/ <- copied from upstream src/runtime/composables/
│ │ ├── components/ <- copied from upstream src/runtime/components/
│ │ ├── plugins/ <- copied from upstream src/runtime/plugins/
│ │ ├── middleware/ <- copied from upstream src/runtime/middleware/
│ │ ├── utils/ <- copied from upstream src/runtime/utils/
│ │ ├── types/ <- copied from upstream src/runtime/types/
│ │ └── ... <- other runtime directories
│ ├── components/ <- project components
│ ├── composables/ <- project composables
│ └── ...
├── nuxt.config.ts <- module entry rewritten
├── package.json <- @lenne.tech/nuxt-extensions removed
└── ...
Nuxt's auto-import mechanism handles composables and components from app/core/
automatically -- no consumer-import codemod is needed for auto-imported items.
nuxt.config.ts Rewrite Pattern
When converting from npm to vendor mode, the nuxt.config.ts module registration
changes:
Before (npm mode):
export default defineNuxtConfig({
modules: [
'@lenne.tech/nuxt-extensions',
// other modules...
],
})
After (vendor mode):
export default defineNuxtConfig({
modules: [
// '@lenne.tech/nuxt-extensions' removed -- vendored into app/core/
// other modules...
],
})
The lt frontend convert-mode --to vendor CLI command handles this rewrite
automatically.
Upstream Sync Workflow (curated)
When a new upstream version is available, the nuxt-extensions-core-updater agent:
- Reads
VENDOR.mdbaseline version and baseline commit SHA - Clones upstream baseline and upstream target into
/tmp/ - Computes three diffs:
upstream-delta.patch: upstream baseline -> upstream targetlocal-changes.patch: upstream baseline -> current project vendorconflicts.json: file-level intersection of both diffs
- Categorizes each upstream hunk:
- Clean pick -- no line overlap with any local change
- Conflict -- touches lines we also modified locally
- Not applicable -- touches code that no consumer file imports
- Shows the curation proposal to the human for review
- Applies approved clean-picks
- Interactive 3-way merge for conflicts
- Raises the project's npm dependencies to at least the upstream target's
versions (semver-max, never downgrade), adds any new runtime deps, then
(unless
--no-maintain) refreshes the rest via/lt-dev:maintenance:maintain. A vendored project must never ship dependencies older than the upstream it mirrors. - Runs
nuxt build+pnpm run lint-- commits if green - Syncs the upstream
CLAUDE.mdinto the frontend project'sCLAUDE.md-- section-level merge preserving project-specific content - Updates
VENDOR.mdwith new baseline + sync history entry
IMPORTANT -- Tag format: nuxt-extensions tags have no v prefix. Use
--branch 1.5.3, not --branch v1.5.3.
Upstream PR Workflow (contribution)
When a local change in the vendored core looks generally useful, the
nuxt-extensions-core-contributor agent:
- Reads
VENDOR.mdbaseline version - Runs
git log --oneline app/core/since baseline - Filters out cosmetic commits:
- Commit messages matching
chore: format,style:,oxfmt,lint:fix,prettier - Commits whose normalized diff (whitespace, quotes, trailing commas removed) is empty
- Commit messages matching
- Categorizes substantial commits:
- Upstream-candidate -- generic bugfix, framework enhancement, framework test addition, type correction
- Project-specific -- business rules, customer-specific enums, project-name references, proprietary integrations
- Unclear -- asks the human
- For each candidate:
- Cherry-picks the commit onto a fresh branch in the local upstream clone (1:1 path mapping, no reverse flatten-fix needed)
- Writes a PR-body draft explaining why the change is generally useful and what project motivated it
- Presents a summary + link list to the human for review
- Human pushes and opens the PR via normal GitHub flow
- After merge, the next
update-nuxt-extensions-corerun will pick up the change as upstream-delivered and can remove the local patch fromVENDOR.md's local-changes log
The ceiling is an open DRAFT PR into main — never a merge. Never merge the
PR, never tag/release/publish. Always gh pr create --draft; never gh pr ready.
Taking a PR out of draft is how the maintainer says they have decided — that signal
is theirs to give. This holds even when an earlier instruction sounded like blanket
approval ("merge everything").
Upstream branch model + PR shape (lenneTech/nuxt-extensions)
This repo has no develop branch — base every contribution on main.
The contribution shape (derived from merged PR #4 and the 1.8.2/1.8.3/1.8.4 release commits; it is documented nowhere else):
- One single commit per PR, subject =
<target-version>: <what + why>(e.g.1.8.5: accept the nest-server roles: string[] shape in isAdmin …). - The commit bumps
package.jsonitself (patch for a bugfix). - The commit carries src + tests only — NO
CHANGELOG.mdentry. The maintainer adds it separately asdocs: add <version> changelog entryonmainafter the merge. Adding one in the PR duplicates that and causes review churn. - A pre-commit hook (
.githooks) auto-runsoxfmt --write src/+oxlint src/. - Full gate before committing:
format:check,lint,dev:prepare,test:types,test,build(pnpm run check:rawchains them pluspnpm audit).
Flag the version bump to the user: if two contribution PRs are open at once, the second one's bump collides and needs a rebase.
Upstream vitest needs pnpm run dev:prepare first (it generates
.nuxt/tsconfig.json) — without it every test file fails at transform, which looks
like a broken port but is not.
No Reverse Flatten-Fix Needed
Unlike backend contributions (where ./common/ must become ./core/common/
before submitting upstream), nuxt-extensions uses a 1:1 path mapping between
the vendored tree and upstream. Files can be cherry-picked directly without
any import-path transformation.
Cosmetic-vs-Substantial: Why Filter
Running pnpm run format and pnpm run lint on a freshly vendored upstream
often produces a formatting-only commit if the project's formatter config
differs from upstream's. These commits have real line changes but zero
semantic content. Without filtering, the contributor agent would suggest
them as upstream-PRs -- wasting reviewer time on upstream and creating
unwanted churn.
The filter lives in the agent, not in .oxlintignore or .prettierignore.
The vendored code should always stay format-compliant with project standards.
Rollback
If the vendor pilot fails or needs to be reverted:
# In the consumer project frontend:
cd projects/app
git log --oneline | grep -E "vendor|framework" | head -20
# Identify the vendor commit range
git revert <first-vendor-commit>..<last-commit> --no-edit
pnpm install
pnpm run build
This restores @lenne.tech/nuxt-extensions as an npm dependency and removes the
app/core/ vendor tree. No data loss, no upstream impact.
References
- nuxt-extensions upstream: https://github.com/lenneTech/nuxt-extensions
- Changelog: https://github.com/lenneTech/nuxt-extensions/blob/main/CHANGELOG.md
- Releases: https://github.com/lenneTech/nuxt-extensions/releases
- Backend vendor skill (for comparison):
nest-server-core-vendoring
Related Skills & Commands
| Element | Relationship |
|---|---|
Agent: nuxt-extensions-core-updater |
Executes the upstream-to-project sync |
Agent: nuxt-extensions-core-contributor |
Prepares project-to-upstream PRs |
/lt-dev:frontend:update-nuxt-extensions-core |
User-facing entry point for the sync |
/lt-dev:frontend:contribute-nuxt-extensions-core |
User-facing entry point for the PR prep |
developing-lt-frontend skill |
npm-mode consumption and general Nuxt work |
contributing-to-lt-framework skill |
Editing the framework repo itself via pnpm link |
nest-server-core-vendoring skill |
The backend mirror of this pattern |