Full-stack development plugin for Spring Boot Kotlin + React TypeScript monorepos.
Nothing to clone. The wellforge repo root is a plugin marketplace
(.claude-plugin/marketplace.json), and its entry points at a git-subdir source pinned to
a plugin-v* tag — so what you install is a named release, not whatever main happened to
be that morning.
claude plugin marketplace add matteocodogno/wellforge
claude plugin install wellforge@wellforge --scope user(or interactively: /plugin → wellforge → install → scope: user)
Later, to move to a newer release:
claude plugin marketplace update wellforge # re-read the manifest (it names the new tag)
claude plugin update wellforge@wellforgeWhat "update" resolves to is not documented. The manifest pins a tag and Claude Code records the installed
version+gitCommitShain~/.claude/plugins/installed_plugins.json, but whetherplugin updatefollows the tag in the refreshed manifest, the default branch, or theversionfield is not stated in the plugin-marketplace docs. Treat the pair of commands above as the reliable sequence, and check the recorded version afterwards:jq -r '.plugins["wellforge@wellforge"][0].version' ~/.claude/plugins/installed_plugins.json./wellforge:doctorreports it for you, alongside where the plugin came from.
Marketplace installs deliberately fetch the tagged release from git, so a local clone registered as a marketplace still gives you the published plugin, not your edits. To run the code you are editing, bypass the marketplace:
claude --plugin-dir <wellforge-checkout>/wellforge-pluginThat is per-launch and affects nothing else — which is what you want while iterating, since an in-place edit to an installed plugin is cached by version and would not take effect anyway until the version is bumped.
Inside Claude Code:
/plugin → wellforge listed under Installed
/mcp → sequential-thinking, playwright, github, context-hub connected
/hooks → 7 hooks listed
/wellforge:doctor → full health report, install source, and the command index
| Path | What |
|---|---|
.mcp.json |
sequential-thinking, playwright, github, context-hub MCP servers |
commands/spec.md |
/wellforge:spec — interview (--grill for one question at a time) → feature spec (step 1 of 3) |
commands/plan.md |
/wellforge:plan — approved spec → technical plan (step 2 of 3) |
commands/design.md |
/wellforge:design — UX flows, screens & states, component reuse, a11y (UI features) |
commands/tasks.md |
/wellforge:tasks — approved plan → dependency-aware task list (step 3 of 3) |
commands/implement.md |
/wellforge:implement — implement chosen tasks (IDs/range/next/all), parallel by DAG, QE-verified |
commands/orchestrate.md |
/wellforge:orchestrate — full team pipeline: classify → spec → plan → tasks → parallel devs → QE verdict, 2 human gates |
commands/eval.md |
/wellforge:eval — LM-judge scores the feature against the central rubric (gate into done) |
commands/done.md |
/wellforge:done — verify the tier-aware done gate, then record status: done (or retire as superseded) |
commands/doctor.md |
/wellforge:doctor — health check (tools, MCP, hooks, guards, --tests) + the command index |
commands/status.md |
/wellforge:status — recap every feature's phase + the next command to run (read-only) |
commands/triage.md |
/wellforge:triage — spec-health heartbeat: stale in-progress, unresolved drift, passed-QE-never-eval'd (read-only digest) |
commands/new.md |
/wellforge:new — interview → stack recommendation → Copier scaffold → build verify → connections |
commands/upgrade.md |
/wellforge:upgrade — copier update to a newer template version + AI conflict resolution + gates |
commands/adopt.md |
/wellforge:adopt — brownfield onboarding: AI-readiness, spec workflow, gates with measured baseline |
commands/extract-template.md |
/wellforge:extract-template — profile a project's stack, gap-check it, optionally extract an org-internal Copier template |
commands/spike.md |
/wellforge:spike — main-loop build from a one-paragraph brief, advisory gates, no agents (rigor: spike) |
commands/promote.md |
/wellforge:promote — graduate a feature (or the project) up a rigor tier, paying the deferred debt |
commands/release.md |
/wellforge:release — version bump + CHANGELOG from Conventional Commits, tag, GitHub release |
commands/grill-me.md |
/wellforge:grill-me — one-question-at-a-time interview of an idea, spec or plan; ends in a decision ledger file that spec --ledger and orchestrate --ledger read. --docs also maintains the glossary and offers ADRs |
commands/improve-architecture.md |
/wellforge:improve-architecture — scan for shallow modules: at most five evidenced refactor candidates, design the picked one twice, route it to the refactor pipeline. Changes no code |
commands/retro.md |
/wellforge:retro — look back at a session or feature; at most five ranked, evidence-backed fixes to the environment, applied only on a yes |
commands/terse.md |
/wellforge:terse — toggle terse conversational output for this run |
commands/terse-compress.md |
/wellforge:terse-compress — one-way compression of a WellForge-owned file, behind a fact-preservation gate |
agents/product-owner.md |
PO — spec.md: problem, user stories, ACs, non-goals |
agents/architect.md |
Architect — plan.md: architecture, contracts, AC→test mapping; also the architecture review behind /wellforge:improve-architecture |
agents/designer.md |
Designer — design.md: flows, screens, component reuse, a11y |
agents/frontend-dev.md |
FE dev — implements tasks per react-ts-vite conventions |
agents/backend-dev.md |
BE dev — implements tasks per stack skill conventions |
agents/devops.md |
DevOps — CI/CD, infra, tool connections (verified, not assumed) |
agents/quality-engineer.md |
QE — AC verification, gates run, evidence-based verdict (deterministic half) |
agents/evaluator.md |
Evaluator — LM-judge, rubric-scored verdict (non-deterministic half) |
agents/owasp-reviewer.md |
Specialist: OWASP Top 10 security review |
agents/adr-writer.md |
Specialist: Architecture Decision Record writer |
hooks/hooks.json |
7 lifecycle hooks |
hooks/scripts/session-start.sh |
Injects git state + domain glossary at session start |
hooks/scripts/pre-bash-guard.sh |
Blocks recursive deletion from root/home, SQL nukes, pipe-to-shell, force push / reset --hard / force branch delete, and commands naming a secret file |
hooks/scripts/pre-file-guard.sh |
The same protected files for the Read/Write/Edit/Grep tools — it reads the path parameter, so no text guessing |
hooks/scripts/post-lint.sh |
ts/tsx → Prettier+ESLint · kt/kts → ktlintFormat |
hooks/scripts/post-spec-guard.sh |
The lifecycle rules, mechanically. On any edit to specs/*/spec.md|brief.md: status: done only when forge-state.py's done_gate.passes, rigor: never downward, no reopening a closed feature by edit, no status outside the enum. PostToolUse, so it detects and demands a revert rather than preventing — see the note below |
hooks/scripts/notify.sh |
macOS notification + Telegram DM |
hooks/scripts/stop-verify.sh |
Blocks on spec drift + type/compile errors before Claude stops — over the branch's whole change set (merge base ∪ working tree), not just unstaged files. Surprise to know about: a cosmetic spec.md edit committed earlier on the branch blocks every Stop until /wellforge:tasks re-syncs (which stamps synced: even when nothing else changes). That is the drift rule working; it does not feel like it. |
hooks/scripts/pre-compact-backup.sh |
Snapshots session state before compaction |
hooks/scripts/trace-subagent.sh |
SubagentStop → best-effort token events to .forge/runs/.events.jsonl (observability) |
scripts/forge-state.py |
Feature lifecycle state, deterministically — walks specs/, validates frontmatter against the schema, counts tasks, computes drift from git order, joins QE/eval verdicts, resolves the tier, evaluates the done gate. --json emits forge-state/v1; status, triage, done and promote all read it instead of re-deriving |
config/spec-frontmatter.schema.json |
Machine-readable mirror of the spec-driven skill's frontmatter; check-docs.py fails if its enums drift from the skill |
scripts/run-report.py |
Summarizes .forge/runs/ — agents, verdicts, drift, estimated cost; --budget (spend vs tier ceiling, top consumer) and --rework (QE/security fail rounds per feature and agent — the re-tiering evidence) |
scripts/check-routing.py |
Verifies agent frontmatter models match the routing policy (drift guard) |
scripts/check-budget.py |
Measures what the plugin injects into every session (all skill/command/agent descriptions) and fails when it exceeds the ratchet in config/budget.yml |
config/budget.yml |
The session-injection ceiling, and why it moves only by decision |
config/security-triggers.yml |
Path globs + substrings whose presence in a batch dispatches the owasp-reviewer; always_at_tier: [production] |
config/rigor-budgets.yml |
Advisory per-tier cost/wall-clock ceilings + the rework thresholds — surfaced by triage, never blocking |
scripts/security-triggers.py |
Evaluates those triggers against touch: ∪ git diff — what implement/orchestrate call before QE |
docs/PLUGIN-MIGRATIONS.md (repo) |
Project-side changes per plugin minor — read by /wellforge:upgrade, reported by /wellforge:doctor |
config/model-pricing.yml |
Per-model price table for run-report cost estimates |
Two hooks protect secrets and destructive operations, and they work differently on purpose:
pre-file-guard.shreads the tool's path parameter (Read/Write/Edit/MultiEdit/ NotebookEdit, and Grep — which sendspathand, in content mode, prints matching lines). It is exact about the file it is given: a path is protected or it isn't.- The two guards must agree. They protect the same files by different means, so a file
one blocks and the other waves through is a hole, not a nuance —
Readof.mise.local.tomlwas blocked whilecatsailed past until 2026-09-20. The asymmetry that IS deliberate:.mise.local.tomlis read-denied and write-allowed in both, because it is the sanctioned secret store the setup flow creates. Metadata-only commands (ls,stat,test,git check-ignore) may name a protected file — they reveal nothing, and refusing them is what taught people to route around the guard. That carve-out applies only to a single simple command: a separator (&&,;,|,$(…)) disqualifies it, becausels . && cat <secret>starts withls. - Enumerate the small set, not the unbounded one. The
.mise.local.tomlrule is inverted for this reason: it denies any command naming the file except a handful of sanctioned write shapes (>/>>into it,tee,touch,mise set). The first version allow-listed readers —cat,head,less— andgrep,awk,sed,cp,python3 -c,curl -d @fileandgit diffall walked straight through. There is no finite list of ways to read a file; there is a finite list of ways you are meant to write one. pre-bash-guard.shcan only match the text of a command, because that is all a shell invocation gives it. Two consequences worth knowing before you file a bug:- False positives. A command that merely mentions a protected name is blocked even
if it opens nothing —
grep -rn ".env" docs/, a loop containing the string, this very README's examples. That is the accepted cost of a text-only rule;.env.example,*.jinjaand--force-with-leaseare scrubbed because they came up constantly, and more exceptions get added the same way. Work around it by not naming the file, or run the command yourself. - A directory-wide content grep is not coverable.
Grepat a protected file is blocked;Grepat a directory whose pattern happens to match a line inside one is not, because no path check can see that. Same forcat dir/*. If a secret must never reach a transcript, the file being unreadable is the guarantee — the guard is not. - It is a seatbelt, not a sandbox. Text matching is evadable by anyone trying — variable indirection, base64, an unusual spelling. It is there to stop an accident, not an adversary. Real enforcement lives where it cannot be talked around: the gitleaks pre-commit hook, the security-floor CI gate, and branch protection.
- False positives. A command that merely mentions a protected name is blocked even
if it opens nothing —
Both have regression matrices (hooks/scripts/tests/, run by CI). Every case in them is
something a guard once got wrong — add yours there rather than only widening a regex.
| config/model-routing.yml | Tool-neutral: agent → tier (frontier/mid/cheap) — the portable routing policy |
| config/model-tiers.yml | Per-tool: tier → concrete model (claude aliases, opencode provider/model) |
| skills/connections/ | Standardized tool-connection checklists (GitHub, MCP, environments) — each ends with a verification command |
| skills/domain-modeling/ | the project glossary — one word per concept, the edge-case stress test, who may write it |
| skills/frontend-design/ | visual direction for NEW product surfaces — the surface-class gate and two-pass token system |
| skills/git-policy/ | Linear history + Conventional Commits — the format, the four enforcement layers, what to do when a gate rejects you |
| skills/quality-gates/ | What CI enforces, where thresholds live, and the rule that they change only by PR to gates/ |
| skills/template-contract/ | The binding contract every Copier template satisfies — one root copier.yml, the shared questions, the manifest, the two version series |
| skills/grilling/ | the one-at-a-time interview — read first, parents before children, a recommendation per question, the decision ledger |
| skills/heartbeat/ | Scheduled-automation conventions — surface-never-ship, dedup, deterministic-vs-agentic, cost bound |
| skills/hono-ts-backend/ | Hono + TypeScript + Drizzle + Effect — best practices and scaffolding |
| skills/kotlin-springboot/ | Spring Boot + Kotlin + jOOQ + Liquibase + Modulith |
| skills/mise/ | dev-tool version manager — tools, tasks, and the monorepo addressing rule |
| skills/observability/ | Run-trace (.forge/runs/) format conventions — producers and consumers |
| skills/pulumi-gcp-ts/ | Pulumi IaC in TypeScript on GCP — stacks, ComponentResources, CrossGuard, mock tests |
| skills/react-ts-vite/ | React + TypeScript + Vite + Mantine + TanStack |
| skills/rigor-tiers/ | spike / mvp / production — how much pipeline runs, the security floor, the effort cue |
| skills/self-critique/ | the one bounded pass over your own artifact before the gate that follows |
| skills/spec-driven/ | Spec-driven workflow conventions (format, status lifecycle, drift rule) |
| skills/springboot-scaffold/ | JVM routing + module conventions — which generator applies, and how to add a service to an existing monorepo by hand |
| skills/systematic-debugging/ | root cause before the fix, the 3-attempt architecture stop, never silence a symptom |
| skills/tdd/ | test-first for the dev agents — one behavior per cycle, the right-red rule, what is exempt |
| skills/template-extraction/ | stack profile, preset gap-check, org-internal template extraction with an IP/secret scrub |
| skills/terse/ | token-efficient conversational output — byte-identical invariant, artifact exemption |
| skills/visual-companion/ | browser tool the designer uses to show mockups instead of describing them (opt-in, interactive only) |
| skills/worktree-isolation/ | what a worktree does and does NOT isolate; the shared-state preflight and integration protocol |
| Server | Transport | Auth | Pinned version |
|---|---|---|---|
sequential-thinking |
stdio | none | @modelcontextprotocol/server-sequential-thinking@2026.8.31 |
playwright |
stdio | none | @playwright/mcp@0.0.82 |
github |
HTTP | OAuth via /mcp on first use |
n/a (hosted) |
context-hub |
stdio | none | @aisuite/chub@0.1.4 |
Versions checked against the npm registry on 2026-09-23 (npm view <pkg> version). This
table is the record because .mcp.json is JSON and cannot carry a comment.
Every stdio server is launched with npx, and every one of them names an exact version.
@latest — which playwright and sequential-thinking used to carry — resolves on the
developer's machine, at launch, to whatever the registry serves that minute: an unreviewed
dependency running inside the session, where a bad release reaches every teammate the moment
it is published and nothing records what they actually ran. context-hub was already pinned,
so the pattern was established and the other two were simply missed; check-docs.py now
fails if any npx server carries a moving tag or no version at all. See SECURITY.md.
Bumping a pin is a plugin PATCH release (plugin-vX.Y.Z), not a silent edit — the
version people run is part of what the plugin is.
telegram is managed by telegram@claude-plugins-official — install separately via /plugin.
Guided wizard (creates the bot with you, detects your chat id, sends a test message):
wellforge telegramConfig lands in ~/.config/wellforge/telegram.env (chmod 600, sourced from ~/.zshrc;
the notify hook also reads it directly). Manual alternative: export
TELEGRAM_BOT_TOKEN and TELEGRAM_CHAT_ID yourself — never commit them.
See settings-snippet.jsonc — copy into ~/.claude/settings.json:
{
"enabledPlugins": {
"pyright-lsp@claude-plugins-official": true,
"telegram@claude-plugins-official": true
},
"skipDangerousModePermissionPrompt": true,
"attribution": { "commit": "", "pr": "" }
}
skipDangerousModePermissionPrompt— personal machines only, never commit to shared repos.
Create .claude/context/glossary.md in your project — injected into every session:
# Domain glossary
- **<term>**: <one-line definition the AI should know for this project>
- **<acronym>**: <what it expands to and means in your domain>You do not have to write it by hand. /wellforge:grill-me --docs builds and sharpens it
during an interview, and /wellforge:spec proposes the terms a new spec introduces. The
domain-modeling skill defines the format (the flat list above stays valid; _Avoid:_
synonyms, ## Relationships and ## Open ambiguities are optional) and the rules: one word
per concept, a definition that survives its edge cases, and agents that use the terms but
never edit the file.