agentleFS
virajpvirajp/claude-plugins/.claude/skills/stackgen-plugin/SKILL.md

stackgen-plugin

Skill1 starsChanged 8 days agoMITView on GitHub

The stackgen plugin's own shape — the dispatch rule, the pack and

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
  1. Very specific:

    No bundle is fetched for a repo's baseline any more.

    line 104

≈ “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.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

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.

ReadFor
assets/taxonomy.mdthe closed component types and categories; capability tokens stay vwf's
assets/kinds.mdthe kind vocabulary — each kind a closed topic bar, one artifact per topic
assets/pack-format.mdthe shape of a pack: <type>/<slug>/pack.yaml + prose + optional skills/agents/config/
assets/output-tree.mdwhere a materialization lands, the lockfile, the two targets outside .claude/
assets/ids.mdone 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.mdthe 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.mdgenerated — every pack, bundle and kind with counts; mise run p:plugins:inventory
stacks/readme.mdthe 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:

  1. A component a shipped pack covers is copied verbatim from stacks/.
  2. 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: or lsp_servers: entry's whole runner chain and every URL or bare host in its args and env values, actions, images, SwiftPM packages, Maven artifacts and Gradle plugins, mise tools as mise:<backend>:<path>@<version>) through stackgen-reputation — nine prefixes, host: the newest, which always blocks since no source vets a remote host, Go, Cargo, Ruby and .NET reached through mise: — a block halting the component with the verdict table until the user names a replacement, never a silent swap → the stackgen-skill-reviewer gate, 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 reads block — and it stays offline. A shell or inline code in a chain, a data: or file: value and a code-loading variable each stop generation as UNRESOLVED:. 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 — the context7 addon in vwf-tools, a user-level MCP, another plugin — and halts without one, naming the remedy: add context7 in /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 because lspServers is 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's user_mcp_servers:, the only MCP key. The project-scoped mcp_servers: key and the .mcp.json write path are retired; a machine with the older stackgen-lsp plugin is offered one migration, and a declaration is never copied out of a repo's .mcp.json, only taken from a shipped pack. A claims.json beside 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 generated lspServers entry must carry an extensionToLanguage map; 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's test/golden, android's test/e2e), a project task at the .config/mise/tasks/p/_project/ marked position (the three Cloudflare deploy packs' deploy overlay, framework/astro's icons and framework/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), and config/_<name>/, pack-private and never copied. Everything a pack once shipped beyond that — subtasks, gate configs (.config/swiftlint.yml, …), templates/ mise folders, pnpm's .npmrc and npx alias, fnox's fnox.toml and setup/secrets — is bootstrap's or gone; the .npmrc and code:format:pnpm have no replacement. The p/_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 deeper p/_project/ is a marked position, copied and renamed to the pinned project's id — slugged per assets/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 is assets/output-tree.md and whose tiers do not all reach the checker: the landable tier is PACK_CONFIG_ROOT_FILES in scripts/src/check.ts, enforced by p:plugins:check rule 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.md is 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 no rendered: entries and no skipped: list. Its per-file hash: — on every copied entry — is the landing hash, and /vwf:init touches 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/ and config/, plus a pack's conventions.md and a bundle's body, is copied verbatim into a repo where no plugin is installed, so p:plugins:check rule 13 refuses four forms in them: the literal ${CLAUDE_PLUGIN_ROOT}, a bare assets/… path, a ../ climb leaving the tree the file lands in (only a file under skills/ has one — it may still reach a sibling skill of the same pack, which lands beside it; an agent, a rule, a conventions.md and 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:check rule 11 runs over each pack's config/ 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 every hooks/*.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 shipping CLAUDE.md there is refused like any unallowlisted path — with a CI workflow refused inside .github/; under .config/, only test/ and p/_project/ tasks (PACK_CONFIG_DOTCONFIG_ALLOWLIST), which names no bootstrap tool; no pack templates/ tier; that the pack's binaries and lockfile facts 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 retired tool-config:, machine_env:, values: or conditional: key is refused outright. The rule in full is the plugin-authoring skill's references/checks.md. Rule 15 is retired, with the exclusion lists it compared. Rule 17 fails any mise use the plugin ships unless the line forbids it: a tool is entered into the owning conf.d/<folder>/ file first, then installed with mise 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-adapter keyword in plugin.json is load-bearing: p:plugins:check requires 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: false so vwf can reach them by their constructed names, and user-invocable: false so neither spends a / menu slot on a skill that answers only a program. Rule 9 asserts both literal lines, and asserts the explicit false rather than the mere absence of true — absence states nothing about the thing vwf depends on. stackgen-reputation is the third shape — disable-model-invocation: false with no user-invocable line — 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-template keeps its argument-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 optional repo: <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.

Download now (ZIP)

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
What did you use it for? e.g. Reviewing pull requests in a Rails app

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.