Imported from jjdubski/svelte-plus (
AGENTS.md). Install upstream withnpx skills add jjdubski/svelte-plus. Copyright stays with the author.
svelte-plus — Agent Guide
This is an interactive CLI scaffolding tool for Svelte 5 +
SvelteKit projects. It wraps npx sv create, adds opinionated
QoL features, and can also retrofit an existing Svelte project.
Quick start
npx svelte-plus my-app # create new project
npx svelte-plus . # retrofit existing Svelte project
Repo structure
| Path | Role |
|---|---|
bin/cli.js |
CLI entrypoint (shebang, arg parsing, errors) |
src/main.js |
Core logic — createNew(), addToExisting(), |
| all helper functions (~2921 lines) | |
src/prompts.js |
Interactive prompt flow via @clack/prompts |
tests/main.test.js |
Unit + integration tests (~1757 lines) |
tests/options-combinations.test.js |
Exhaustive parameter tests (~991 lines) |
tests/fixtures/ |
existing-svelte/ and not-svelte/ |
for isSvelteProject() detection |
|
bin/actionlint, bin/reviewdog, bin/shellcheck |
Committed binaries for local reviewdog runs |
vitest.config.js |
Vitest config for this repo's tests |
eslint.config.js |
Flat config, @eslint/js + prettier + globals |
Exact developer commands
| Command | What it does |
|---|---|
npm test |
vitest run (tests in tests/**/*.test.js) |
npm run test:watch |
vitest (watch mode) |
npm run lint |
eslint . (flat config, @eslint/js + prettier) |
npm run lint:fix |
eslint . --fix |
npm run format |
prettier --write . |
npm run format:check |
prettier --check . |
npm run prepare |
husky (husky install) |
npm run reviewdog |
Run all reviewdog runners locally (filter-mode=nofilter) |
npm run reviewdog:diff |
Run all runners with filter-mode=added (PR-like) |
npm run reviewdog:eslint |
Run only ESLint reviewdog runner |
npm run reviewdog:actionlint |
Run only actionlint reviewdog runner |
npm run reviewdog:markdownlint |
Run only markdownlint reviewdog runner |
npm run reviewdog:shellcheck |
Run only ShellCheck reviewdog runner |
npx fallow |
Static analysis / code health check |
Prettier config: useTabs: true, tabWidth: 4, singleQuote: true, trailingComma: "none", printWidth: 100.
Markdown files override: tabWidth: 2, printWidth: 500, embeddedLanguageFormatting: "off".
ESLint flat config enforces no-unused-vars with _ prefix
convention (argsIgnorePattern: '^_', varsIgnorePattern: '^_').
Test files get globals.vitest for vitest globals.
Node: engines >=18, CI uses 22.
How the CLI works
-
main({ appName })routes tocreateNew()oraddToExisting():- Existing Svelte project →
addToExisting() - Non-existent dir →
createNew() - Existing non-Svelte dir → cancels with exit 1
- No
appName→ prompts for project directory, then same logic (also re-checks for existing Svelte project)
- Existing Svelte project →
-
Prompt flow in
getOptions():- svelte-plus setup mode: Default / Minimal / Custom (select subset of features)
- Template (new projects): Minimal / Demo / Library
- Type checking: TypeScript / JSDoc / None
- Add-ons (multi-select): eslint, prettier, vitest, playwright, tailwindcss, sveltekit-adapter, drizzle, better-auth, mdsvex, paraglide, storybook, mcp
- Addon sub-prompts:
vitest: unit testing, component testing (multi)tailwindcss: typography, forms plugins (multi); then asks about shadcn-svelte setupsveltekit-adapter: auto/node/static/vercel/cloudflare/ netlify; cloudflare gets Workers vs Pagesdrizzle: database (postgresql/mysql/sqlite/d1) + client per database + optional docker-composebetter-auth: email/password, GitHub OAuth (multi)paraglide: language tags (text), include demo (confirm)mcp: IDE selection (claude-code, cursor, gemini, opencode, vscode, other) + setup (local/remote). When onlyopencodeis selected, the setup prompt is skipped via theconditioninsv add.
- Package manager: none / npm / yarn / pnpm / bun / deno
-
createNew()flow:npx sv create --template X --types Y --add '...' --no-install <dir>- Anything with option-specific args (vitest, tailwindcss,
sveltekit-adapter, drizzle, better-auth, paraglide, mcp)
uses formatters in
ADDON_FORMATTERS. - Add-ons with no special formatter are passed as-is.
- Storybook is deferred — excluded from
sv create(via--no-add-onsfallback), runs assv add storybookinside the project directory after creation. - Creates standalone
vitest.config.*if vitest selected (sv create embeds vitest in vite.config.*; this gives users a discoverable config file). - Updates
package.jsonwith svelte-plus features (husky, lint-staged, lucide-svelte, fallow dep, stylelint, prettier-plugin-tailwindcss, scripts, etc.) - Writes config files (
.prettierrc,.prettierignore,.env.example,src/app.css, CI workflows) - Post-setup: hooks,
npm install,npx husky init, shadcn, playwright browsers, format
-
addToExisting()flow:- Runs
npx sv add <addon> --no-git-checkper addon - Falls back to npm install if
sv addfails (eslint + eslint-plugin-svelte, prettier + plugins, vitest — each tracked inpendingDeps) - Same config file writing and post-setup as
createNew()
- Runs
Available add-ons (12)
| Addon | sv CLI support | Sub-prompts |
|---|---|---|
| eslint | full | none |
| prettier | full | none |
| vitest | full | usages: unit, component |
| playwright | full | none |
| tailwindcss | full | plugins: typography, forms; shadcn-svelte prompt |
| sveltekit-adapter | full | adapter, cfTarget (for cloudflare) |
| drizzle | full | database, client, docker |
| better-auth | full | demos: password, github |
| paraglide | full | languages, demo |
| mdsvex | full | none |
| storybook | deferred | none (runs via sv add after creation) |
| mcp | full | ides, setup (skipped when only opencode) |
CI workflows (5)
Committed in .github/workflows/ and also generated for new
projects. Generated by writeTestWorkflow(),
writeFallowWorkflow(), writeReviewdogWorkflow(),
writeTriggerWorkflow(), and writeBuildWorkflow().
test.yml—npm ci+npm test. Package-manager aware (uses correct cache key and frozen install). Supportsworkflow_dispatch, PR, push triggers. Includes pending check run management for dispatch-triggered runs.build.yml—npm ci+npm run build. Posts build failure as a PR comment and creates check run annotations fromvite-plugin-svelteerror output.fallow.yml—fallow-rs/fallow@v2SARIF analysis with audit mode on PRs, full scan on push. Includes check run lifecycle and PR comment on findings.reviewdog.yml— 6 jobs: eslint, actionlint, markdownlint, shellcheck, stylelint, svelte-check + a fullcheckjob for push events. PR review on added lines; full check on push. Each job manages pending check runs for dispatch-triggered runs.trigger.yml—/workflowcomment handler. Clears existing check runs on the PR, then dispatches all 4 workflows (test, fallow, reviewdog, build) in parallel viagh workflow run.
All 5 workflows share the same trigger pattern:
workflow_dispatch, PR opened/ready_for_review, push to main.
Draft PRs do not trigger CI — they need /workflow comment
or ready_for_review event.
svelte-plus feature flags
In DEFAULT_SVELTE_PLUS_FEATURES:
config-files— .prettierrc, .prettierignore, .env.example, app.css, test/build/trigger CI workflowshusky— git hooks (pre-commit, pre-push)lint-staged— run linters on staged fileslucide-svelte— icon library depformat-lint-scripts— add format/lint/lint:fix scripts to package.jsoninstall-deps— run package manager installfallow— static analysis dep + CI workflowreviewdog— reviewdog CI workflow + config + stylelint
Three setup modes: default (all features),
minimal (config-files only), custom (pick subset).
Architecture notes
buildAddonArgs()converts addon options tosv create --addarg strings. Each addon has a formatter inADDON_FORMATTERS. When adding new addons, add a formatter and test it.runStep()/runCommandStep()— shared wrappers for spinner + logging. Heavy steps (listed inHEAVY_STEP_IDS) get the spinner immediately; others get a 2-second delay before showing it.writeHook()writes to.husky/<name>, chmod +x. Pre-commit runslint:fix+npx lint-staged(lint-staged only if feature enabled); pre-push runs<pm> test --if-present.- Storybook is deferred — not passed to
sv create --add, runs vianpx sv add storybook --no-git-checkafter project creation. - Package manager agnostic — maps
PKG_CMDSfor npm/yarn/pnpm/bun/deno for dev/test commands, andPKG_MANAGER_CMDSfor install commands. - MCP addon
onlyOpencodecondition: When onlyopencodeis selected as the IDE, the setup (local/remote) prompt is skipped, matching thesv addcondition behavior. setupShadcn()— runsshadcn-svelte@latest initwith--skip-preflightfirst; falls back to init without the flag. Skips ifpkgManagerisnone(prevents hanging). If tailwindcss wasn't already added, adds it viasv addfirst with a fallback tonpm install -D tailwindcss @tailwindcss/vite.applyFeatureDeps()— shared function that adds husky, lint-staged, lucide-svelte, fallow, stylelint, prettier-plugin-tailwindcss, and format/lint scripts to the project's package.json. Used by bothcreateNew()andaddToExisting().- Generated
.prettierrchasprintWidth: 120(the repo's own is 100),tailwindStylesheet: './src/app.css', includesprettier-plugin-svelte+prettier-plugin-tailwindcss, and has overrides for*.md(tabWidth: 2, printWidth: 500) and*.svelte(parser: svelte). - Vitest config generation: When vitest is selected, a
standalone
vitest.config.*is created alongside the embedded config invite.config.*for discoverability. Uses.tsor.jsextension based on types setting. buildGithubWorkflow()/buildBuildWorkflow(): Generate workflow YAML adapted to the chosen package manager (cache key, frozen install command, dev/test scripts).
Testing quirks
- All child processes are mocked. Tests mock
child_process(execSync) and@clack/promptsentirely.spawnis explicitly set toundefinedin test mocks. No real binaries execute. - Test helpers:
tmpDir()creates temp dirs,fixture(name)resolves fixture paths,defaultOpts()/existingOpts()build option objects. - Integration tests pre-create the project directory
before calling
createNew()(most tests) but verify it gets created for the "waits for project directory" test. - Run
npm testbefore opening a PR. Tests are fast (~2s) since they're fully mocked. vitest.config.jsincludes['tests/**/*.test.js']. All unit and integration tests live intests/.setupShadcn()tests mock both the primary init and the fallback init, verifying error output on double failure.
Husky hooks (this repo)
Current repo hooks:
pre-commit:npm run lint:fix --if-present+npx lint-stagedpre-push:npm test --if-present+npm run reviewdog --if-present
The generated hooks (in new projects) differ slightly:
pre-push only runs <pm> test --if-present.
Gotchas
bin/binaries are committed but gitignored. The.gitignorepatternsactionlint,reviewdog,shellcheck(no leading/) match the committed files inbin/and also block new untracked copies elsewhere. Git-tracked files aren't affected by gitignore, so the binaries stay functional in the working tree.AGENTS.mdis excluded from markdownlint (in both.reviewdog.ymland CI workflow). Any content here bypasses markdown linting, but this file also won't catch lint failures that other.mdfiles would..husky/.gitignorecontains only_(husky's internal shim directory). Don't let this look incomplete.- ESLint
_prefix convention: unused function args and variables must be prefixed with_to suppress lint errors. eslint.config.jsignores:node_modules/,.husky/,new-project/,docs/— this is important when running eslint so it doesn't choke on test artifacts.