SLOPSHOPPER

zensu

Product Lifecycle Manager — Features as First-Class Citizens

v0.31.0FSL-1.1-Apache-2.0updated 2026-10-26MKITConsulting/zensu-claude-code
A shopper browsing a rack in a slop shop
README

<a href="https://zensu.dev"><img src="assets/zensu-logo.svg" alt="Zensu" width="120"></a>

Zensu Plugin for Claude Code

License: FSL-1.1-Apache-2.0 Version

Turn an idea into a reviewed pull request — without babysitting the agent.

Zensu plans the feature with you, implements it, and then puts the result through a review panel that has to cite real file and line numbers for every finding. What ships is tracked as a feature, not as a pile of commits, so your product people can see progress without opening a terminal.

Any language, any stack. The implementation workflow and the review chain need no configuration and no account; tracking features in Zensu needs the zensu CLI and a Zensu account (which skills need what).

What you get

  • A plan you approve first. Claude Code plans; Zensu asks which delivery route to take and remembers a Zensu-workflow answer for the rest of the session — or never, when the project sets hooks.defaultDeliveryRoute. You stay in the loop once, not every turn.
  • A review the turn waits for. Specialist reviewers run in parallel and an independent judge checks their blind spots. Once the implementation is marked complete, a Stop hook keeps the turn from ending until the review chain closes. The hold is bounded; why it let go is recorded in the session transcript, not shown in the terminal.
  • Findings you can trust. Every finding is graded against the real source before anyone acts on it. What does not hold up is marked, not silently fixed.
  • Guardrails around the agent. Gates stop it from committing a secret or mutating tracked product data outside a workflow, and an opt-in gate keeps it from writing into a sibling checkout. Those gates have a documented escape hatch; the gates that protect Zensu's own state and the browser consent gate have none (Configuration).
  • Features, not commits. Security profiles, user journeys, tiers, and release readiness live in a dashboard instead of in your head.
  • Work capture without duplicate entry. With personal CLI OAuth and a compatible backend/CLI, the agent records the agreed goal and reports new committed results automatically. You accept the exact version through its web review link. This also works during direct implementation; it never treats stopping, merging or releasing as acceptance. See automatic work capture.

What Zensu changes in every session

Zensu acts in every Claude Code session where the plugin is enabled, not only when you run one of its skills. Know these before you add it to a setup you already rely on:

  • Subagents keep their tools, except what reaches Zensu's own state or runs commands outside Bash. Every subagent that is not one of Zensu's own — the built-in agents such as Explore, general-purpose and Plan, your project's agents, and agents from other plugins — may call every tool its definition and your permissions grant, MCP tools, Skill and Workflow included. Zensu's capability gate denies it what reaches Zensu's own state: Session Control and workflow paths, .zensu/, writes to the installed plugin or its private data, Zensu configuration layers and Zensu MCP mutations. A tool whose inputs Zensu does not know, such as a browser MCP tool, may not name that state anywhere in its input. PowerShell, the shell aliases other than Bash, Monitor commands and tools whose name marks them as running commands stay denied, because Zensu's command gates see only Bash and PowerShell commands. A subagent that reports its type, other than a zensu-review-* review persona, may run Bash behind an accident guard; the guard prevents accidents and is not a sandbox. Subagents may not read .zensu/, so Grep and Glob must name a subdirectory: a search from the project root is refused. hooks.subagentShell: false withdraws the shell grant, and the subagents: row of /zensu:doctor shows the boundary. Details: Session control.
  • Context is added to every session. Measured from Zensu 0.29.0 session transcripts on 2026-10-24: about 7,500 to 7,800 characters at most session starts (the evidence-discipline and best-solution-first rules, an orientation primer and the banner), about 9,100 characters on the first prompt of a session and on the first prompt after /compact or /clear (about 11,200 when the prompt carries a planning keyword), and about 1,800 to 5,300 characters on every later prompt. Automatic work capture, added after 0.29.0, adds about 760 characters to every session start. The skill listing carries Zensu's skill descriptions on top. Architecture says how this was measured and breaks it down per hook.
  • zen-mode is on by default. Replies open with a one-line recap, put the result first, stay near eight lines and end with one next step. Say "zen off" or run /zensu:zen-mode --off to leave it for the session; set hooks.zenModeDefault: false to make it opt-in again.
  • Claude asks which delivery route to take. After you approve a plan, Zensu has Claude ask whether to use Autopilot, the Zensu workflow or Pilot, or to implement directly; on a direct code request it asks whether to use the Zensu workflow. /zensu:delivery-route or hooks.defaultDeliveryRoute answers it ahead of time; hooks.autoTdd: false and hooks.tddReminder: false switch the two questions off.
  • Work capture can report your commits to Zensu. At every session start Zensu tells Claude to record an agreed implementation goal in Zensu, with or without the Zensu workflow, through the ai-work commands of the zensu CLI and your personal zensu auth login. Once a goal is begun, a Stop hook reports each new clean commit for it and returns a review link where you accept that exact version; nothing accepts it for you. It sends the goal's title (at most 160 characters), its key, a criteria version or digest and the commit SHA, never transcripts, prompts, diffs or file lists. Neither hook has a key. Without a CLI that provides ai-work and a personal login, capture is reported as unavailable and the work continues. Automatic work capture.
  • The Stop hook can hold the turn. While a Zensu workflow chain is open and its implementation is marked complete, Claude cannot end its turn until the review chain closes. That hold is bounded (hooks.autoFixMaxRounds plus three blocked stops) and is lifted by hooks.chainEnforcer: false or by ZENSU_CHAIN=off in the environment Claude Code starts with. A durable /zensu:autopilot run holds the turn until the run ends or blocks; hooks.autopilotEnforcer: false or ZENSU_AUTOPILOT=off lifts that hold.
  • A chain closes only on proof. By default (evidence.fullSuiteGate and evidence.acceptanceGate both required) a Zensu workflow chain closes only after a full test-suite run that the plugin executed itself passed on the current tree, and after every acceptance criterion of the plan was verified live. Set either key to advisory to report the verdict without blocking, or set ZENSU_FULL_SUITE_GATE=off or ZENSU_ACCEPTANCE_GATE=off for one closing step; those two escapes are recorded in the chain's bypass ledger. /zensu:full-suite moves the full suite to a verified CI pipeline instead.
  • node must be on PATH. The capability gate runs on every tool call and needs node. Without it every tool call is refused with only zensu: reviewer capability gate unavailable, which does not name node.

Install

Requires Claude Code 2.1.296 or newer.

claude plugin marketplace add MKITConsulting/zensu-claude-code
claude plugin install zensu --scope project

If the install fails with Host key verification failed or Permission denied (publickey), Claude Code cloned the plugin over SSH. That is its default for GitHub plugin sources, and unlike marketplace add the install step has no HTTPS fallback. Force HTTPS:

CLAUDE_CODE_PLUGIN_PREFER_HTTPS=1 claude plugin install zensu --scope project

Update later with:

claude plugin marketplace update zensu
claude plugin update zensu@zensu

Optional, for feature tracking: install zensu-cli 0.10.0 or later with brew install --cask mkitconsulting/tap/zensu (upgrade with brew upgrade --cask zensu), curl -fsSL https://zensu.dev/install.sh | sh, or on Windows irm https://zensu.dev/install.ps1 | iex, then run zensu auth login.

Run /zensu:setup to configure Zensu step by step, or /zensu:doctor if something is not firing.

Try it

The first two need no Zensu account.

Just this change — describe what you want and approve the plan. Zensu asks which delivery route to take: Autopilot to a reviewed pull request, the Zensu workflow with its review chain, Pilot for a feature already tracked in Zensu, or implementing it directly. Answer the route question with the Zensu workflow once: the session remembers that answer (a direct, Autopilot or Pilot answer decides only that plan or request), and /zensu:delivery-route or hooks.defaultDeliveryRoute fixes either route without asking.

One feature, hands off — /zensu:autopilot takes a plain-language idea to a validated pull request: one planning gate, then plan → build → review → fix → verify, unattended. It stops at a ready PR and never merges. It needs gh or glab, logged in.

Existing codebase — /zensu:ghost-scan discovers what you already shipped and imports it as tracked features. Needs a Zensu account.

New product — /zensu:bootstrap reads your vision doc and creates the features, journeys, and tiers. Then /zensu:implement KEY-1 builds the first one. Needs a Zensu account.

What's included

Skills (31)

The count is the workflow skills in these tables. The read-only diagnostics skill is documented separately in Diagnostics below and is intentionally kept out of these tables (32 skills are registered in plugin.json).

The Needs column says what a skill needs beyond Claude Code: "Zensu account" means the zensu CLI, logged in; gh or glab means the forge CLI of your GitHub or GitLab repository, logged in; "—" means nothing else. Nineteen skills make no zensu call at all.

Track features in Zensu
SkillWhat it doesNeeds
/zensu:bootstrapTurn a vision document into tracked features, journeys, security profiles, and tiersZensu account
/zensu:ghost-scanScan an existing repo, discover undocumented features, and import themZensu account
/zensu:docsWrite code-grounded feature documentation that honestly clears the docs release gateZensu account
/zensu:security-reviewA tracked feature's security review: classification, OWASP analysis, STRIDE threat model, release-gate checkZensu account
/zensu:pulseDeveloper journal — privacy-first tracking of your coding sessionsZensu account
Implement
SkillWhat it doesNeeds
/zensu:tddThe Zensu workflow: implement an approved plan in the main thread, verify every acceptance criterion live, then the mandatory review chain and auto-fix loop. Despite its name it runs without RED→GREEN by default; /zensu:tdd-mode or hooks.tddImplementation switches strict TDD on—
/zensu:autopilotIdea → validated pull request, unattended after one planning gate. Never merges or deploysgh or glab
/zensu:implementImplement a tracked feature through the Zensu workflow, then link its tests, sources and docs and create a revisionZensu account
/zensu:pilotThe guided route: probes a tracked feature's real state and offers the next step, one confirmed step at a timeZensu account and a tracked feature; gh or glab for its pull request
/zensu:coverBackfill durable tests at the right level (unit → integration → E2E) for existing code— (a Zensu account only when the target is a feature id)
/zensu:convergeAudit the code against the plan and flow real changes back into the spec—
/zensu:wargameMap a hard task move by move so a cheaper model can execute it blind— (reads a Zensu feature when the brief names one)
/zensu:gauntlet-loopImprove an artifact against a frozen bar under two fresh critics, arbitrating disagreement—
Review
SkillWhat it doesNeeds
/zensu:plan-reviewHave a tailored reviewer team revalidate a plan before any code is written—
/zensu:pr-team-reviewMulti-agent review of an existing GitHub or GitLab PR, published as one consolidated reviewgh or glab
/zensu:pr-fix-findingsWork through every unresolved review thread on a PR and resolve itgh or glab; its prerequisites also ask for a logged-in zensu CLI, though it runs no other zensu command
Verify
SkillWhat it doesNeeds
/zensu:verify-featureExercise the running feature and report what actually happened, for a web UI, an API, a CLI, a library, a worker, infrastructure code, an iOS or Android app, or a desktop app. Report-only. Inside a Zensu workflow chain its --chain mode verifies every acceptance criterion before the chain can close. How to run it standaloneplaywright-cli for a web UI
/zensu:full-suiteRun full test suites in the verified CI pull-request pipeline instead of locally, for this repository or this session; affected tests stay localA GitHub.com repository and gh
Session switches
SkillWhat it doesNeeds
/zensu:tdd-modeSwitch this session between strict RED→GREEN TDD and vanilla, without editing config—
/zensu:delivery-routeFix this session's delivery route (Zensu workflow or direct) so the route question is not asked again this session—
/zensu:zen-modeLow-noise responses for working at reduced capacity. On by default; --off leaves it for the session—
Setup, help and other sessions
SkillWhat it doesNeeds
/zensu:setupInteractive first-run configuration— (a Zensu account for its optional login step)
/zensu:zensu-helpAsk how Zensu or the plugin works. Read-only Q&A—
/zensu:session-trailSee what your other Claude Code instances are doing, take a session over and then release the old one so it can be archived, and trace where a session was continued across windows and accounts—
Recovery
SkillWhat it doesNeeds
/zensu:recover-chainRepair the one review-chain state no other command can leave—
/zensu:reset-review-limitGrant the current review chain another auto-fix budget—
/zensu:adopt-sessionReport a refused automatic adoption after a plugin update landed mid-run and retry it by hand, re-create a vanished recorded project root, or move the session's anchor to a sibling worktree of the same repository (--reanchor)—
/zensu:autopilot-adoptTake over a durable Autopilot run whose owning session is gone—
/zensu:autopilot-releaseFree a working tree an abandoned Autopilot run is still holding—
Internal stages

You do not start these yourself: the review chain starts the first, and a work-order launcher starts the second.

SkillWhat it doesNeeds
/zensu:self-reviewThe terminal self-reflection stage that closes the review chain—
/zensu:workRun one Zensu work order in a headless worker session, reporting every stage until ready_for_merge or a blockA Zensu work order and the session token its launcher passes

Diagnostics — /zensu:doctor

A read-only health check for when something is not firing: CLI and auth, plugin integrity, config validity (including the quoted-boolean trap, where "true" as a string is silently ignored), and session state. It never writes and always exits 0 — a red mark is a finding in the report, not a failed command. It needs no account; with the zensu CLI installed it also reports the login and the CLI version.

Zensu next to Claude Code built-ins

Built-inZensuUse the built-in forUse Zensu for
/security-review/zensu:security-reviewA security pass over the pending changes on your branchA tracked feature's security profile: classification, threat model, security tests and the release gate, recorded in Zensu
/code-reviewthe review chain, /zensu:pr-team-reviewA review of the current diff or a PR that you start yourselfThe review chain runs by itself at the end of every Zensu workflow chain, grades each finding against the source and fixes the routed ones; /zensu:pr-team-review publishes one consolidated multi-agent review on an open PR
/doctor/zensu:doctorProblems with the Claude Code installation itselfA hook or gate of this plugin that is not firing, its config, its CLI login and the session's workflow state
/simplify—Cleanups that reuse, simplify and tidy changed codeZensu has no counterpart; the review chain reports maintainability problems but applies no cleanups on its own
/goal/zensu:wargameKeeping Claude working until a stated condition holdsWriting the move-by-move plan, and optionally the goal contract, before such a run; it plans and executes nothing

Configuration

Defaults are the supported configuration; you do not have to set anything. Flags live in ~/.zensu/config.json (global) and .zensu/config.json (per project, merged over the global file key by key):

{
  "hooks": {
    "pulseSession": false
  }
}

Not every hook can be turned off:

| | Hooks | How | |-|-------|-----| | Switched off by a config key | The banner and primer, the pulse banner, the intent router, zen-mode, both delivery-route questions, the best-solution-first reminder (main thread only), the context nudge, the worktree-keep hooks, the Session Hub receipt, the reviewer-spawn grant, the auto-fix loop, the workflow-launch recorder, the Stop backstops, the Zensu CLI write-gate and the secret scan | A hooks.* key, or context.compactionNudge, set to false | | Off unless you opt in | The source-write gate's checks of Bash writes | hooks.bashWriteGate: true | | Bypassed only through the environment | The TDD phase gate | ZENSU_TDD_GATE=off in the environment Claude Code starts with | | Always on while the plugin is enabled | Session Control and the capability gate that confines subagents (hooks.subagentShell: false only withdraws their Bash), the Session Control check inside the source-write gate, evidence discipline, the plugin-data guard, the browser consent gate and its recorder, the artifact path redactor, the prompt-ledger reset, Autopilot resume context, the review-evidence hooks, the work-order hooks (inert outside a work-order session), the background-review hand-back and both automatic work-capture hooks | Disable the plugin (Turn Zensu off or remove it) |

The full flag reference, the merge order, and the hook table are in docs/configuration.md. config.example.json shows the flags with their default values; copying it pins every default, so your copy stops following later default changes.

Files Zensu writes in your repository

Nothing writes an ignore rule for you, except the .worktree-keep line below.

PathWritten byCommit it?
.zensu/config.jsonYou or /zensu:setupYes, to give every clone the same settings; it overrides each developer's global file key by key
.zensu/plans/, .zensu/logs/*_tdd-*.logThe Zensu workflow, one plan and one progress log per chainOptional. Committed, they are an audit trail; absolute paths in them are redacted when they are written
.zensu/state/Zensu's hooks, per sessionNever. Add .zensu/state/ to .gitignore
.zensu/verify-feature-runs//zensu:verify-feature, one directory per run, removed after a clean teardownNever
.zensu/logs/witness-*.logEarlier releases onlyNever
.zensu/runtime.yaml (older name .zensu/autopilot.yaml)/zensu:verify-feature --setup, after you confirmYes: it tells /zensu:verify-feature and /zensu:autopilot how to start your app
.zensu/overlays/, .zensu/templates/YouYes: team guidance for the review skills, and your pull request body template
.worktree-keep in a Claude Desktop worktreeThe worktree-keep hooksNo; Zensu lists it in the repository's .git/info/exclude

Turn Zensu off or remove it

Zensu's own keys cannot stop the always-on hooks. To stop Zensu in one repository, disable the plugin there: claude plugin disable zensu@zensu --scope local for yourself only, or --scope project for everyone who uses the project's shared Claude Code settings. claude plugin uninstall zensu@zensu --scope project removes a project install (the command's default scope is user); Claude Code also deletes the plugin's data directory under ~/.claude/plugins/data/ unless you pass --keep-data.

What stays behind:

  • ~/.zensu/: your global config.json and, from older releases, the retired plugin-root locator. Delete that locator only once no Claude Code session from an older Zensu installation is still running.
  • .zensu/ in every project you used it in, as listed above.
  • .worktree-keep files in the root of Claude Desktop worktrees under .claude/worktrees/, and the .worktree-keep line in the repository's .git/info/exclude.
  • ~/.claude/zensu/ (under CLAUDE_CONFIG_DIR when that is set): the session lineage ledger of /zensu:session-trail and the Session Hub receipts.
  • The zensu CLI and its stored login, which are a separate install. Run zensu auth logout before you remove the CLI.

Zensu account and CLI

The implementation workflow and the review chain need no CLI, no login, and no Zensu account: /zensu:tdd, the review-aspect panel with its auto-fix loop, and the progress log under .zensu/logs/ run locally, and so does every skill marked "—" in the skill tables. The zensu CLI and an account add the tracking layer on top — feature status, revisions, artifact links, security findings, and release-gate validation — through the skills marked "Zensu account".

zensu auth login signs the CLI in through your browser (OAuth with PKCE) and stores the credentials for every later command. When a zensu call in a session fails with an authentication error, Claude runs zensu auth login and retries; the browser step approves itself while you are signed in to Zensu on the web.

For headless environments, authenticate the CLI with an API key instead — pipe it to the token form, since a bare zensu auth login opens a browser and never reads the env var:

# ZENSU_API_KEY from your CI secrets
echo "$ZENSU_API_KEY" | zensu auth login --with-token -

Verify with zensu auth status, clear with zensu auth logout. A headless /zensu:work session uses the session token its launcher passes in ZENSU_SESSION_TOKEN and ignores any stored login.

Data & privacy

The zensu CLI sends data over HTTPS to https://api.zensu.dev, o

Source 1 files
hooks/usage-feed.ts 67 lines
1import type { EngineInterface, Register, SessionRateLimit, UsageUnit } from 'claude-code'
2
3type RateLimitReading = {
4  kind: string
5  percentUsed: number
6  resetsAt: string | null
7}
8
9const SCHEMA_VERSION = 1
10const USAGE_DIRECTORY = 'zensu/session-hub/v1/usage'
11const MAX_RATE_LIMITS = 8
12const FRESH_READING: readonly UsageUnit[] = ['rateLimits', 'cost']
13const SESSION_ID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/
14const KIND = /^[a-z][a-z0-9_]{0,31}$/
15const RESETS_AT = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{1,9})?(?:Z|[+-]\d{2}:\d{2})$/
16
17function readingsOf(limits: readonly SessionRateLimit[]): RateLimitReading[] {
18  const readings: RateLimitReading[] = []
19  if (!Array.isArray(limits)) return readings
20  for (const limit of limits) {
21    if (readings.length === MAX_RATE_LIMITS) break
22    if (typeof limit?.kind !== 'string' || !KIND.test(limit.kind)) continue
23    if (readings.some(reading => reading.kind === limit.kind)) continue
24    if (typeof limit.percentUsed !== 'number' || !Number.isFinite(limit.percentUsed) || limit.percentUsed < 0) continue
25    const resetsAt =
26      typeof limit.resetsAt === 'string' && RESETS_AT.test(limit.resetsAt) && Number.isFinite(Date.parse(limit.resetsAt))
27        ? limit.resetsAt
28        : null
29    readings.push({ kind: limit.kind, percentUsed: limit.percentUsed, resetsAt })
30  }
31  return readings
32}
33
34async function configRoot($: EngineInterface): Promise<string | undefined> {
35  const configured = (await $.env.get('CLAUDE_CONFIG_DIR'))?.trim()
36  if (configured) return configured.startsWith('/') ? configured.replace(/\/+$/, '') : undefined
37  const home = await $.env.get('HOME')
38  return home?.startsWith('/') ? `${home.replace(/\/+$/, '')}/.claude` : undefined
39}
40
41async function writeReading($: EngineInterface, limits: readonly SessionRateLimit[]): Promise<void> {
42  const rateLimits = readingsOf(limits)
43  if (rateLimits.length === 0) return
44  const sessionId = await $.session.id()
45  if (typeof sessionId !== 'string' || !SESSION_ID.test(sessionId)) return
46  const root = await configRoot($)
47  if (root === undefined) return
48  const directory = `${root}/${USAGE_DIRECTORY}`
49  if (!(await $.fs.exists(directory))) return
50  const stat = await $.fs.stat(directory)
51  if (stat.kind !== 'dir' || stat.isLink) return
52  const observedAt = await $.clock.now()
53  if (!Number.isSafeInteger(observedAt) || observedAt < 0) return
54  const record = { schemaVersion: SCHEMA_VERSION, sessionId, observedAt, rateLimits }
55  await $.fs.write(`${directory}/${sessionId}.json`, `${JSON.stringify(record)}\n`)
56}
57
58export const register: Register = on => {
59  on('session.measure', async ($, e, next) => {
60    const measured = await next(e)
61    try {
62      if (e.changed.some(unit => FRESH_READING.includes(unit))) await writeReading($, e.rateLimits)
63    } catch {}
64    return measured
65  })
66}
67