stackgen-plugin
Skill1 starsChanged 8 days agoMITView on GitHub
The stackgen plugin's own shape — the dispatch rule, the pack and
In this repository7 other files it gives its agents.
- CLAUDE.mdCLAUDE.md
- installer/CLAUDE.mdCLAUDE.md
- site/CLAUDE.mdCLAUDE.md
- plugin-authoringSkill
- releaseSkill
- skill-kitSkill
- vwf-pluginSkill
The file
We split this skill into the rules it gives an agent
1 rule. About 0 are common, 0 are distinctive, and 1 is very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈0
Distinctive · estimate
This author's own choices. Worth a look.
1
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 104
No bundle is fetched for a repo's baseline any more.
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
The stackgen Plugin
stackgen is the principles-driven stack materializer and the only stack
plugin left — vwf's one dependency. It implements vwf's stack-adapter contract:
skills/stackgen-stack-menu returns the stack options as a vwf menu payload,
skills/stackgen-stack-template returns one stack as a template payload, and
both are called by vwf rather than by users — the menu by /vwf:architecture
and /vwf:setup, the template by /vwf:setup (whose materialize pass lands a
first pin), /vwf:plan and /vwf:execute (pure conventions reads).
/vwf:architecture decides a pin and materializes nothing.
skills/stackgen-sync is the one user-only skill: the explicit, lockfile-diffed
re-sync. skills/stackgen-reputation is the one skill invocable both ways —
disable-model-invocation: false and no user-invocable line — so the
generator can call it over every concrete third-party name a generated component
emits, and a person can run /stackgen:stackgen-reputation <ecosystem>:<name> …
on a name before typing it anywhere; it returns pass, warn or block per
name from public read APIs over WebFetch, and is the only network stackgen
touches beyond Context7. Since devtools dissolved into it, stackgen also
carried the repo's own gate doctrine as packs, and those packs left in turn:
every universal file a repo runs — the mise config and task library, dprint,
taplo, pre-commit, gitleaks, grype, the house linter, git and the editor
settings — is bootstrap's, the
setup:* and code:* tasks with them, and stackgen:tool-config, which once
rendered them, was removed on 2026-11-03. /vwf:init runs bootstrap's readme
prompt, /vwf:setup's materialize pass applies packs through the adapter, and
/vwf:doctor judges drift through bootstrap's own
mise run setup:configs --dry-run, in full mode and in setup's Step 0 only.
Each asset is authoritative for its own subject. This file is a map; do not restate a count or a rule that an asset below already owns.
| Read | For |
assets/taxonomy.md | the closed component types and categories; capability tokens stay vwf's |
assets/kinds.md | the kind vocabulary — each kind a closed topic bar, one artifact per topic |
assets/pack-format.md | the shape of a pack: <type>/<slug>/pack.yaml + prose + optional skills/agents/config/ |
assets/output-tree.md | where a materialization lands, the lockfile, the two targets outside .claude/ |
assets/ids.md | one slug rule, two independent tokens: a project's id (p:<id>/ and the commit scopes) and a repo's name (REPO_NAME, the checkout folder); flags name member repos |
assets/artifact-doctrine.md | the host rules deciding whether a generated skill, agent or hook is valid at all |
assets/contracts/ | the provider-neutral doctrine per capability or kind that instance packs cite and stay thin; the newest is contracts/workspace.md, the workspace category's, which states how the agent reaches the team's knowledge workspace and what it may read and write, and is the third contract to realize no vwf token at all. Before it, contracts/web-head.md, what any web framework pack — shipped or generated — realizes for a project declaring site, or webapp with seo: the head set, the icon sizes, the manifest, robots and sitemap, and the icon task; and before that contracts/audit.md, the audit category's, realizing vwf's audit-store and deliberately beside observability.md rather than inside it |
stacks/inventory.md | generated — every pack, bundle and kind with counts; mise run p:plugins:inventory |
stacks/readme.md | the narrative — which wave landed what, and why |
agents/ | stackgen-skill-reviewer, the generator's gate |
The user-facing reference is site/src/content/docs/plugins/stackgen.md. The
checker rules, the two mise gates and the authoring traps are the sibling
plugin-authoring skill, which also applies here.
The dispatch rule
A component is the atom (a language, its package manager, a framework, a gate, a datastore instance, a cloud service); a bundle is a recorded composition of component refs — never a directory. Given a bundle a project pins, stackgen resolves its composition and dispatches per component:
- A component a shipped pack covers is copied verbatim from
stacks/. - An uncovered component is generated: resolve the kind and its topic bar →
detect the real stack → one Context7 research pass per topic → instantiate
vwf's principles catalog with citations → at assemble, every concrete
third-party name the component emits (packages, runner-invoked tools, a
user_mcp_servers:orlsp_servers:entry's whole runner chain and every URL or bare host in itsargsandenvvalues, actions, images, SwiftPM packages, Maven artifacts and Gradle plugins, mise tools asmise:<backend>:<path>@<version>) throughstackgen-reputation— nine prefixes,host:the newest, which always blocks since no source vets a remote host, Go, Cargo, Ruby and .NET reached throughmise:— ablockhalting the component with the verdict table until the user names a replacement, never a silent swap → thestackgen-skill-reviewergate, capped at four rounds, after which residuals are reported rather than looped; its tenth check reads the verdict table it is handed — every name has a row, none readsblock— and it stays offline. A shell or inline code in a chain, adata:orfile:value and a code-loading variable each stop generation asUNRESOLVED:. The vetting ends at what it can read from the declared command: the consent gate shows the full command and its env values, and the person at the gate is the control for any form it does not parse. The generator's context7 halt: generation needs a Context7 server from some source — thecontext7addon invwf-tools, a user-level MCP, another plugin — and halts without one, naming the remedy: addcontext7in/vwf:architecture's tools menu, run/vwf:setup, restart the session. Context7 or a reputation source unreachable → halt, never guess. The verdict table is shown whole beside the reviewer's verdict at the dry-run consent gate; a template read-back (/vwf:plan,/vwf:execute) never re-checks.
Mixed compositions are the ordinary case, with one consent and one landing per bundle, so a later re-sync can act on one component alone. Packs are assets, not live skills — installing stackgen floods no session with every stack's doctrine.
No bundle is fetched for a repo's baseline any more. The hygiene kind, its pack,
its bundle and the unconditional: bundle key retired on 2026-09-27: the
hygiene config files — .gitignore, .gitattributes and the issue forms —
are bootstrap's, and the prose files — CONTRIBUTING.md, SECURITY.md and
the licence texts — are /vwf:init's own assets. init runs bootstrap's readme
prompt once, for the base, and the prompt reaches the members, so a member is
shaped on its own evidence; it makes no adapter fetch. A repo is shaped when it
carries .config/bootstrap.toml; one with .config/stackgen.yaml or the old
mise
layout and no such file is shaped on the old layout, and bootstrap's
docs/sync/migrate.md moves it through the same step. What setup does fetch
is every pinned axis — its
materialize pass invokes -stack-template once per (repo, slug), with the
repo: line naming the member — which is the one landing path
/vwf:architecture no longer takes. The repo: line is the whole of the
optional input beside the catalog paths: the answers: and values: maps
retired on 2026-10-23 with the pack keys they fed.
A pack ships no toolchain. The tool pins, gates, gate configs, generic tasks
(setup:deps, code:format, code:lint), pre-commit hooks and secrets setup
of a repo are bootstrap's — a separate repo, selected in
.config/bootstrap.toml and synced by mise run setup:configs. The
materializer reads a fresh shallow clone of bootstrap to choose the tools a
composition needs, from each component's pack.yaml facts and conventions, and
sends add tools and add user plugins instructions through bootstrap's
docs/sync/vwf.md, behind their own consent line. It never edits
.config/bootstrap.toml and never names a bootstrap tool from memory; it
records what it asked for in the lockfile's bootstrap: block, so removal sends
the matching remove and stackgen-sync diffs that block. A pack names its
Claude plugin in a ## Claude plugins section of its conventions.md. A repo
with no .config/bootstrap.toml gets nothing landed — a pack's tasks source
bootstrap's _scripts/helpers — and the remedy named is /vwf:setup reshape. A
value only the machine can answer (XCODE_VERSION, SIMULATOR_*,
COMPILE_SDK, EMULATOR_IMAGE) is found by bootstrap's tasks, or set by the
repo in the [env] of .config/mise/conf.d/zz-repo/mise.<env>.toml. The
procedure is skills/stackgen-stack-template/references/bootstrap-handoff.md.
A bundle's frontmatter may also carry default: true, since 2026-09-15:
stackgen-stack-menu copies it onto every entry whose bundle carries it and
computes none, and vwf's architecture menu preselects the one flagged entry
among those it offers on a round, after filtering by the project's platforms —
highlighted, never assumed, naming no tool. At most one bundle per axis per
platform: two flagged bundles on one axis conflict when either declares no
platforms: list (it is offered on every round of the axis) or their lists
intersect, and p:plugins:check rule 14 refuses the pair, naming the platform
they share. Two are flagged today. On the project axis it is astro-ssg,
platforms: [site] — what a site project's round highlights, and nothing on
any other platform's round; the other four entries on that round, the three
remaining Astro bundles and html (the framework/html pack, a hand-authored
page tree under the document category, since 2026-09-15), carry no flag. None
of the three app-framework bundles carries one either — dart-flutter
(cross-platform-ui), swift-swiftui (the app-framework/swiftui pack, the
first under native-ui, since 2026-09-23) and kotlin-compose (the
app-framework/compose pack, the second under native-ui, since 2026-10-09) —
so an app platform's round preselects nothing. On the design axis it is
design-tool's claude-code — the terminal itself as a design tool, the fourth
of that kind beside claude-design, lovable and stitch, and the first with
a file canvas: a committed docs/design/<project>/ its three import skills
read as files, plus a fourth, user-invocable design-session skill that writes
it — the design system and the logo, and since pack 0.2.0 a flow's screens
(screens <flow>, from the brief /vwf:screens prompt wrote, into
screens/<flow>--<platform>/) and a review round (review <flow>, which serves
the canvas from the repo, waits for Done, then applies every open comment).
The server is the skill's own scripts/serve.mjs, a single-file Node program
with no dependencies: it binds 127.0.0.1 on an ephemeral port, serves only the
canvas, carries no auth and no TLS, and appends each comment to a committed
comments/<flow>--<platform>.yaml. It does not install taste-skill:
design-session halts plainly when the plugin is absent, naming the tools menu,
and the three import skills do not need it.
The addon axis holds the optional tools, each a pack and a bundle of type,
kind and axis addon, no bundle flagged default: true: addon/context7 and
addon/notion declare user_mcp_servers:, and addon/taste-skill names
taste-skill@taste-skill, at user scope, in the ## Claude plugins section of
its conventions.md, which the materializer asks bootstrap to install. The menu
carries them for /vwf:architecture's tools round, with an optional needed_by
note on an entry. .config/vwf.yaml's product-level addons: list records the
pick, and /vwf:setup materializes each slug in the base repo alone, on every
run, and removes a dropped one. addon is the eighth axis and not a tool axis:
"the three tool axes" stays design, cicd and stylesheet.
A project-axis bundle may also carry an os: map, <platform>: [ <os> ],
since 2026-10-10: its keys a subset of the bundle's platforms:, the first OS
on each list the primary. stackgen-stack-template passes it through in the
payload, and /vwf:setup records it as projects.<name>.stack.os, beside
languages and frameworks; vwf alone maps a (platform, OS) pair to a device
frame, so the bundle names no frame. Three carry one — swift-swiftui,
kotlin-compose and dart-flutter; an absent map, or a platform absent from
it, reads as no OS known.
Where it lands, and the consent tiers
Both paths land directly in the repo's committed .claude/ tree — output
closed to skills, agents, hooks and rules — recorded per component in
.claude/stackgen/lock.yaml. Two targets sit outside it, each merging, never
owning, removed only by subtraction of the keys the lockfile recorded:
- a generated local plugin,
vwf-tools, at the fixed path~/.claude/plugins/local/vwf-tools/, which exists becauselspServersis a manifest-only feature no project file can express — the one way to provide a language server is to be a plugin — and which holds every MCP server too, from a pack'suser_mcp_servers:, the only MCP key. The project-scopedmcp_servers:key and the.mcp.jsonwrite path are retired; a machine with the olderstackgen-lspplugin is offered one migration, and a declaration is never copied out of a repo's.mcp.json, only taken from a shipped pack. Aclaims.jsonbeside the manifest maps each key to the repos claiming it, so a removal drops a key only when no repo claims it. User scope is safe for a language server because every generatedlspServersentry must carry anextensionToLanguagemap; an MCP server has no such map, so the gate states its reach across every repo on the machine; - a pack's own
config/tree, mirroring the repo root, for the repo config a component genuinely owns — mode preserved, so a task file lands 755. Since 2026-10-23 the tier is an allowlist, and rule 11 refuses any other path: a test task (.config/mise/tasks/test/<name>— swiftui's and compose'stest/golden, android'stest/e2e), a project task at the.config/mise/tasks/p/_project/marked position (the three Cloudflare deploy packs'deployoverlay,framework/astro'siconsandframework/html's byte-identical copy of it — rule 13 forbids a payload citing a sibling pack), a root file on the landable allowlist (the deploy packs'wrangler.jsonc), andconfig/_<name>/, pack-private and never copied. Everything a pack once shipped beyond that — subtasks, gate configs (.config/swiftlint.yml, …),templates/mise folders, pnpm's.npmrcandnpxalias, fnox'sfnox.tomlandsetup/secrets— is bootstrap's or gone; the.npmrcandcode:format:pnpmhave no replacement. Thep/_project/position is shared on purpose, so a pack adding a file to it names a task no other pack in the same bundle already ships. Note the second underscore rule:config/_<name>/at the top of the tier is pack-private and never copied, but nested deeperp/_project/is a marked position, copied and renamed to the pinned project's id — slugged perassets/ids.md, which owns that rule and the measured reason for it. Still fenced out:package.json, any language manifest or lockfile, a pack's own editor settings, and CI workflows — the last of those refused inside.github/, which is otherwise an allowlisted root directory beside.config/. What lands at the repo root is capped by a fixed allowlist, whose doctrine isassets/output-tree.mdand whose tiers do not all reach the checker: the landable tier isPACK_CONFIG_ROOT_FILESinscripts/src/check.ts, enforced byp:plugins:checkrule 11, and beside it sits a tier of root files vwf writes —CLAUDE.md— which may sit at a shaped root and which no pack may land.readme.mdis on the landable tier only because a shaped repo has one — no pack may ship it. The files bootstrap's sync writes are bootstrap's, and a pack ships none of their lines. The lockfile has norendered:entries and noskipped:list. Its per-filehash:— on every copied entry — is the landing hash, and/vwf:inittouches no file a pack landed, so a differing hash is what/vwf:doctor's Content drift predicate judges.
Three consent tiers: the .claude/ files ride the ordinary dry-run gate;
settings.json, a pack's config/ tree and the bootstrap
instructions are never written or sent without their own separate consent lines;
and the local plugin is two separately declinable items — the manifest write,
and the user-scoped registration, whose two claude plugin commands are
printed and confirmed, never auto-run. CLAUDE.md is vwf's: the materializer
recommends /vwf:setup.
Authoring a pack
- A landed file cites nothing by plugin path. Everything under
skills/,agents/,rules/,hooks/andconfig/, plus a pack'sconventions.mdand a bundle's body, is copied verbatim into a repo where no plugin is installed, sop:plugins:checkrule 13 refuses four forms in them: the literal${CLAUDE_PLUGIN_ROOT}, a bareassets/…path, a../climb leaving the tree the file lands in (only a file underskills/has one — it may still reach a sibling skill of the same pack, which lands beside it; an agent, a rule, aconventions.mdand a bundle body each land as one file, so any climb at all is a break), and a path into another pack. A bare<type>/<slug>— or<type>/<slug>@<version>— is the identifier vocabulary and stays legal. Name the asset by role ("stackgen's secrets contract"), or state the rule it carries inline; a sibling component's conventions are "the<type>/<slug>component's conventions, in this composition's template". Never swap one path for another. - The whole
config/payload tier is checked before it ships.p:plugins:checkrule 11 runs over each pack'sconfig/tier: the exec bit and a known shebang on every task file (mise reports a 644 task as an unknown one rather than a permission error) and on everyhooks/*.sh; the landable tier of the root allowlist over the tier's top level — the vwf-owned tier never reaches the checker, so a pack shippingCLAUDE.mdthere is refused like any unallowlisted path — with a CI workflow refused inside.github/; under.config/, onlytest/andp/_project/tasks (PACK_CONFIG_DOTCONFIG_ALLOWLIST), which names no bootstrap tool; no packtemplates/tier; that the pack'sbinariesandlockfilefacts take the shapes doctor reads — a binary a bare name or{ name, probe }, a lockfile a list of relative paths or globs with no..; and that a retiredtool-config:,machine_env:,values:orconditional:key is refused outright. The rule in full is theplugin-authoringskill'sreferences/checks.md. Rule 15 is retired, with the exclusion lists it compared. Rule 17 fails anymise usethe plugin ships unless the line forbids it: a tool is entered into the owningconf.d/<folder>/file first, then installed withmise install. - Never format a payload file with this repo's dprint config. The tier is excluded from it on purpose, and so are the template trees: the target repo formats these files with the shipped config, which omits settings this repo sets, so formatting one here makes a freshly initialised repo fail its own first hook run. Run the shipped config when a payload file needs formatting.
- A pack carries judgment, never the vendor's syntax — worked YAML comes
from Context7 at use time. Category-level doctrine is written once, in
assets/contracts/; instance packs cite it. - The no-skill-lost rule runs backwards: a pack is the destination that has to exist before a source retires, never a replacement on landing. Every curated stack plugin has now retired, so for every pack here the pack is the only home.
- A generated artifact is lazily hung and never line-capped — a large one is decomposed into a router skill plus on-demand references, never trimmed.
- The
vwf-stack-adapterkeyword inplugin.jsonis load-bearing:p:plugins:checkrequires the menu + template pair on every plugin carrying it, and the keyword on every plugin shipping either skill, so dropping one side cannot silently turn the rule off. - Both adapter skills are skill-invoked:
disable-model-invocation: falseso vwf can reach them by their constructed names, anduser-invocable: falseso neither spends a/menu slot on a skill that answers only a program. Rule 9 asserts both literal lines, and asserts the explicitfalserather than the mere absence oftrue— absence states nothing about the thing vwf depends on.stackgen-reputationis the third shape —disable-model-invocation: falsewith nouser-invocableline — so the generator reaches it and it still takes a/menu slot; rule 9 keys off the two adapter names only and does not read it.stackgen-stack-templatekeeps itsargument-hint; it costs nothing on a hidden skill and documents the one argument the caller passes — still<slug>alone. The target repo is not a second argument: it arrives as one optionalrepo: <path>line beside the principles-catalog paths, the member's path relative to the base repo root, absent meaning the current repo. The materializer writes there and keeps that repo's own.claude/stackgen/lock.yaml, so two members pinning the same slug hold two independent materializations.
A script that is not a plugin hook
One hook script ships here as a pack payload, copied into the target repo by
a materialization rather than discovered from a hooks/hooks.json: the
package-manager/pnpm pack's npm→pnpm/bun normalizer (PreToolUse on Bash,
via updatedInput), which moved here from the retired typescript plugin with
the package manager it rewrites for — a JS/TS rewrite is stack doctrine, and
vwf's workflow doctrine stays language-agnostic; the one TypeScript module vwf
ships is engine glue for the progress pane, not doctrine. The
capability-provider/fnox pack's ciphertext guard was the second, and was
deleted on 2026-10-07 with the encrypted mode it gated.
It is still gated here, as payload rather than as a hook: rule 11 asserts the
script's exec bit and its shebang; nothing lints the body. What no rule reads is
the hooks.yaml beside it — checkHookScripts, the older rule, follows only a
plugin's own hooks/hooks.json, so the event and matcher a payload hook is
wired to are asserted by nothing in this repo.
mise run p:plugins:npm-normalize-test covers the normalizer's behaviour: it
table-tests the script through the system sed for both package managers,
each table in a temp dir seeded with the lockfile that selects pnpm or bun. Hook
scripts must stay portable to macOS BSD sed — no \s, no \b.
Documentation
Any change to stackgen's behaviour must reconcile readme.md, CLAUDE.md and
site/src/content/docs/plugins/stackgen.md in the same commit — the repo's
hard rule. Delegate the sweep to /vwf:docs-sync. A behaviour change also bumps
version in plugin.json (plain X.Y.Z) and regenerates the marketplace with
mise run p:plugins:marketplace. A new pack, bundle or kind regenerates
stacks/inventory.md with mise run p:plugins:inventory — never type a count
into prose; --check in pre-commit and CI fails a stale inventory, and the
generator throws on a kind that assets/kinds.md does not define — and on a
bundle component ref that is not <type>/<slug>@<version>, that names no
stacks/<type>/<slug>/pack.yaml, or that pins a version the pack no longer
carries. Bumping a pack therefore means re-pinning every bundle that names it.
@generated refs name no pack by design and are skipped.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

