Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .claude-plugin/marketplace.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@
},
"metadata": {
"description": "keepwright — set up and continuously keep engineering quality and architecture true in any git repo.",
"version": "2.2.0"
"version": "2.3.0"
},
"plugins": [
{
"name": "keepwright",
"description": "Interactive wizard that scaffolds a quality architecture (CLAUDE.md, rules, GitHub Actions with AI review, validators, hooks) and keeps it audited and enforced over time.",
"version": "2.2.0",
"version": "2.3.0",
"author": {
"name": "Leonardo Candiani"
},
Expand Down
2 changes: 1 addition & 1 deletion .claude-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "keepwright",
"version": "2.2.0",
"version": "2.3.0",
"description": "Set up and continuously keep engineering quality and architecture true in any git repo. Interactive wizard, deterministic scaffolding, multi-agent audits, and AI PR review wired to OAuth.",
"author": {
"name": "Leonardo Candiani"
Expand Down
283 changes: 263 additions & 20 deletions .github/workflows/ci.yml

Large diffs are not rendered by default.

278 changes: 278 additions & 0 deletions CHANGELOG.md

Large diffs are not rendered by default.

34 changes: 33 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,14 @@ validators, and git hooks — detecting your stack and adapting. After setup it
keeps maintaining: it audits the repo and uses multi-agent workflows to derive
your design and writing-voice patterns, then turns them into rules and validators.

## Requirements

A git repository, and `bun` (or Node 18+ with `npx tsx`) for the engine and the
validators. The scaffolded hooks and helper scripts are bash, and
`setup-oauth-secret.sh` reads the macOS Keychain, so **macOS and Linux are the
supported hosts**; on Windows, use WSL. The generated GitHub Actions run on
`ubuntu-latest` by default and do not depend on your machine.

## Install

Run each `/plugin` command on its own — don't paste both at once.
Expand Down Expand Up @@ -45,6 +53,7 @@ Loads keepwright's commands, skills, and agents into the current session — no
| `/keepwright:setup` | Interactive wizard. Detects the stack and installs the full architecture. |
| `/keepwright:audit` | Checks integration coverage of an existing repo against the architecture. |
| `/keepwright:review` | Compares repo state against the patterns derived from your code and docs. |
| `/keepwright:tidy` | Non-destructive cleanup of a cluttered repo. Proves what is junk, duplicated, orphaned or misplaced through an import graph and git history, then quarantines it into `.attic/` instead of deleting it. Every operation is reversible from a manifest, and the whole run is documented under `.keepwright/tidy/`. |
| `/keepwright:overhaul` | Full-repo overhaul orchestrator: parallel recon, a grilling interview, architecture by a frontier model, execution delegated to cheaper models, lessons catalyzed into rules. Every phase emits an artifact in `.overhaul/`, so work resumes across sessions and models. Use it to refactor, modernize, or clean up an existing repo end to end. |

## Workflows
Expand All @@ -59,9 +68,32 @@ Multi-agent orchestration the commands run under the hood — each fans out para

## Skills & agents

- **Skills** — `keepwright` (the methodology behind the wizard), `pr-review` (the review procedure the CI calls as `/pr-review #N`), and `overhaul` (the full-repo overhaul orchestrator: recon → grilling → architect specs → delegated execution → catalysis, with artifacts under `.overhaul/`).
- **Skills** — `keepwright` (the methodology behind the wizard), `pr-review` (the review procedure the CI calls as `/pr-review #N`), `tidy` (non-destructive repo cleanup: scan → charter → plan → apply → report → catalysis, with artifacts under `.keepwright/tidy/`), and `overhaul` (the full-repo overhaul orchestrator: recon → grilling → architect specs → delegated execution → catalysis, with artifacts under `.overhaul/`).
- **Agents** — `design-auditor` and `voice-auditor`: read-only auditors that inspect the repo's design and writing-voice dimensions.

## Cleaning without deleting

`/keepwright:tidy` is the answer to a repo that has silently filled up with
backup files, committed build output, byte-identical duplicates, modules nothing
imports any more, and a root directory nobody can read.

It never deletes. The engine knows exactly three operations, and none of them
destroys bytes: `quarantine` moves a file into `.attic/<date>/` with its original
path preserved, `untrack` drops a path from the index while the file stays on
disk, and `move` relocates a file. It refuses to run on the default branch or on
a dirty tree, and it writes a `MANIFEST.json` holding the exact inverse of every
operation, so `--undo <manifest> --apply` puts the repo back byte for byte.

What makes it more than a filename heuristic is the evidence. `tidy-scan.ts`
builds an import graph over the repo's own sources and walks it from the real
entry points (framework routes with or without `src/`, config and test files,
edge functions, anything with a shebang, anything `package.json` or a CI workflow
executes), then combines that with git history and a textual mention sweep. A
file is only called an orphan when no entry point reaches it, nothing imports it,
and no tracked file even names it. Everything else is reported as a question, not
an action. The scanner is deliberately biased toward calling things used: a false
"still in use" costs a line of output, a false "unused" costs someone their code.

## Three layers

- **Wizard** (`/keepwright:setup`) — an interactive command that detects git,
Expand Down
16 changes: 14 additions & 2 deletions commands/setup.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,13 @@ Raw arguments: `$ARGUMENTS`

Treat the JSON above as **defaults**, not the final config.

**Stop here if `isGitRepo` is `false`.** Say so plainly and offer `git init`.
Everything below assumes git: the worker agent is installed with
`isolation: worktree` and cannot spawn without a repository, the hooks have
nothing to attach to, and the workflows have nothing to run on. Installing into
a directory that is not a repo produces a setup that looks complete and works
nowhere.

## Steps

1. **Map (large/existing repos only).** If the repo is non-trivial — lots of
Expand All @@ -41,8 +48,11 @@ Treat the JSON above as **defaults**, not the final config.

3. **Write config.** Write the finalized config to `keepwright.config.json` at the
repo root, conforming to `${CLAUDE_PLUGIN_ROOT}/schema/keepwright.config.schema.json`.
Set `language` from the user's `~/.claude` language so GENERATED artifacts match
their language — the plugin's own text stays English.
Set `language` so GENERATED artifacts match the maintainer's language, while
the plugin's own text stays English. Detection reads `language` from
`~/.claude/settings.json`, a field Claude Code does not populate by default,
so it usually comes back empty: when it does, ASK, rather than silently
defaulting to English in a repo whose docs are written in another language.

4. **Apply (deterministic, creates files + git).** Confirm with the user first
(this writes the constitution, rules, workflows, validators, hooks). Then run:
Expand Down Expand Up @@ -74,6 +84,8 @@ Treat the JSON above as **defaults**, not the final config.

## Rules of engagement

- **git is a precondition, not a detail.** Never run the apply step in a
directory where `isGitRepo` is false.
- **English** for everything keepwright outputs about itself. **Generated
artifacts** (CLAUDE.md prose, rule docs, messages) follow the user's `language`.
- Be **decisive** on technical defaults; only ask the user about genuine choices.
Expand Down
148 changes: 148 additions & 0 deletions commands/tidy.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
---
description: Clean up a cluttered repo without destroying anything — evidence-backed, reversible, and fully documented
argument-hint: '[--scan-only] [--stale-days N] [path scope]'
disable-model-invocation: true
allowed-tools: Read, Glob, Grep, Write, Edit, Bash(bun:*), Bash(git:*), Bash(gh:*), AskUserQuestion
---

# keepwright tidy

Take a repo that has accumulated junk, scratch files, dead code, duplicates and
misplaced folders, and leave it genuinely cleaner, with every change reversible
and every decision written down.

Raw arguments: `$ARGUMENTS`

## The contract you are bound by

1. **Nothing is deleted. Ever.** Not by you, not by the engine. A file that
leaves its place is MOVED into `.attic/<date>/<original path>`; a file that
should not be in git is UNTRACKED and stays on disk. `rm` is never the answer
and is not an operation the engine accepts.
2. **No claim without evidence.** Every operation you propose cites a finding
from `tidy-scan.ts` and the evidence string that finding carries. "Looks
unused" is not evidence. If you believe a file is dead but the scan does not
back you, say so as an open question instead of acting on it.
3. **Every phase writes a file.** A phase whose result lives only in this
conversation did not happen. Artifacts go in `.keepwright/tidy/<date>/`.
4. **The repo must end smarter, not just tidier.** Phase 5 turns whatever made
the mess into a rule, a `.gitignore` line, or a validator. Skipping it means
the same clutter returns next quarter.

## Scan (already run)

!`bun "${CLAUDE_PLUGIN_ROOT}/scripts/tidy-scan.ts" 2>/dev/null || echo '{"_error":"scanner failed — is bun installed, and is this a git repo?"}'`

Read the JSON above before writing anything. `totals.findings` is the size of
the job; `byClass` is its shape; each finding carries `confidence`
(high/medium/low), `action` (quarantine/untrack/review) and `evidence`.

If the scanner returned `_error` or a `degraded` block, say so plainly and stop
before proposing operations. A partial scan cannot justify moving files.

With `--scan-only`, stop after Phase 0: write the inventory, report it, do not
interview and do not plan.

## Phase 0 — Inventory

Write `.keepwright/tidy/<date>/INVENTORY.md` from the scan: totals, the finding
table grouped by class, and a three-sentence verdict on what shape this repo is
in and what the single biggest source of clutter is.

Group the findings; never paste 200 raw rows at the user. High-confidence
findings get named individually, the long tail gets counted.

## Phase 1 — Charter (interview, this is a gate)

Write `.keepwright/tidy/<date>/TIDY-CHARTER.md`. Use **AskUserQuestion** and ask
only what the scan genuinely cannot answer. Four things must end up resolved:

- **Sacred ground** — paths that must not move whatever the evidence says
(vendored code, generated files someone depends on, a folder mid-migration).
- **Proof command** — what proves the repo still works: `npm test`, `bun run
build`, `tsc --noEmit`, a curl against a dev server. If the repo has no proof
at all, say so in the charter; the run then stops at quarantine of provably
inert files (junk, empty, backup artifacts) and proposes nothing about code.
- **Kill list confirmation** — show the high-confidence quarantine candidates
and get an explicit yes. Medium and low confidence stay as proposals.
- **Scope** — the whole repo or one subtree, and roughly how much churn is
welcome this round.

Mark anything still open as `[NEEDS DECISION: <question>]`. **Do not enter Phase
2 while a single `[NEEDS DECISION]` marker remains in the charter.** Ask again,
or narrow the scope so the undecided part falls outside it.

## Phase 2 — Plan

Write two files:

- `.keepwright/tidy/<date>/plan.json` — the machine-checkable plan the engine
runs. Shape: `{ "label", "sacred": [...], "operations": [ { "op":
"quarantine" | "untrack" | "move", "path", "to"?, "reason" } ] }`. The
`reason` is what a reviewer reads in the PR, so make it the evidence, not a
restatement of the action.
- `.keepwright/tidy/<date>/TIDY-PLAN.md` — the same plan for humans, ordered,
grouped by class, with a section listing every finding you deliberately did
**not** act on and why. That section is the honest half of the report.

Ordering rules: provably inert files first (junk, empty, backup artifacts), then
duplicates and misplaced files, then orphaned code last. Never mix a risky
operation into the first batch.

A finding whose `action` is `review` never becomes an operation on your own
authority. It becomes a line in the plan's open-questions section, or a question
to the user.

## Phase 3 — Baseline, apply, prove

1. Run the charter's proof command and record the result in
`.keepwright/tidy/<date>/BASELINE.md`. **A red baseline stops the run**: you
cannot prove your cleanup is harmless against a repo that was already broken.
2. Branch: `git checkout -b tidy/<date>`. The engine refuses to run on `main`
or `master`, and refuses to run on a dirty tree, on purpose.
3. Dry run first: `bun "${CLAUDE_PLUGIN_ROOT}/scripts/tidy-apply.ts"
.keepwright/tidy/<date>/plan.json`. Read what it says it will do. A rejected
plan comes back with a `problems` list; fix the plan, never bypass the check.
4. Apply: same command with `--apply`. It writes
`.keepwright/tidy/<date>/MANIFEST.json`, which holds the exact inverse of
every operation performed.
5. Run the proof command again. **If it goes red, undo immediately**: `bun
"${CLAUDE_PLUGIN_ROOT}/scripts/tidy-apply.ts" --undo
.keepwright/tidy/<date>/MANIFEST.json --apply`, then report which operation
broke it and stop. Do not attempt a repair edit inside a tidy run: fixing
code is a different job with a different review.
6. Commit with the operations summarized in the body, and the proof output.

## Phase 4 — Report

Write `.keepwright/tidy/<date>/REPORT.md`: tracked files and bytes before and
after, a table of what moved where, what was untracked and why, the findings
left untouched with the reason, and the one-line undo command. Then open the PR
with that report as the body.

The PR description must state, in plain words, that nothing was deleted and
where the quarantined files live, so a reviewer can restore any of them with a
single `git mv`.

## Phase 5 — Catalysis (do not skip)

Clutter comes back unless the repo learns. For each recurring class in the scan:

- Build output or machine-local files tracked in git → fix `.gitignore`.
- Backup and scratch files that keep landing in `src/` → a line in `CLAUDE.md`
or a rule under `.claude/rules/` saying where scratch work goes.
- The same dead module type appearing again → a validator, if it is mechanically
checkable.

Where the keepwright structure already exists in the repo, add the rule there and
re-equalize `CLAUDE.md` (every rule needs a pointer). Where it does not, append a
short section to `CLAUDE.md` and offer `/keepwright:setup`.

## Rules of engagement

- English for keepwright's own output; generated artifacts follow the repo's
configured `language`.
- Be decisive about mechanics, never about someone else's code. When the
evidence is thin, the honest move is a question, not a quarantine.
- Report the count you can prove. "12 files quarantined, 41 findings left as
open questions" beats "cleaned up the repo".
62 changes: 41 additions & 21 deletions schema/keepwright.config.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,14 @@
"description": "Declarative configuration produced by detection + the setup wizard and consumed by the deterministic apply engine.",
"type": "object",
"additionalProperties": false,
"required": ["project", "repo", "stack", "deploy", "runner", "auth"],
"required": [
"project",
"repo",
"stack",
"deploy",
"runner",
"auth"
],
"properties": {
"project": {
"type": "string",
Expand All @@ -29,73 +36,86 @@
"default": "English",
"description": "Output language for GENERATED artifacts (CLAUDE.md prose, rule docs, messages). Read from ~/.claude settings 'language'; the plugin itself stays in English. English when absent."
},
"mode": {
"type": "string",
"enum": ["setup", "audit", "maintain"],
"default": "setup",
"description": "setup = greenfield/first install; audit = report integration coverage only; maintain = re-apply deltas + derived rules on an existing repo."
},
"stack": {
"type": "string",
"description": "Detected primary stack, e.g. nextjs-serverless, nextjs, node-cli, python-fastapi, deno, go, rust, monorepo."
},
"layers": {
"type": "array",
"items": { "type": "string" },
"items": {
"type": "string"
},
"description": "Pipeline layers derived from the real structure, e.g. [routes, actions, lib, db, integrations]."
},
"deploy": {
"type": "string",
"enum": ["vercel", "supabase-functions", "docker-ghcr", "npm-publish", "static-pages", "none"],
"enum": [
"vercel",
"supabase-functions",
"docker-ghcr",
"npm-publish",
"static-pages",
"none"
],
"description": "Deploy variant chosen by stack; selects the deploy workflow template."
},
"runner": {
"type": "string",
"enum": ["self-hosted", "github"],
"enum": [
"self-hosted",
"github"
],
"default": "self-hosted",
"description": "GitHub Actions runner target. self-hosted keeps CI minutes at zero."
},
"auth": {
"type": "string",
"enum": ["oauth", "apikey"],
"enum": [
"oauth",
"apikey"
],
"default": "oauth",
"description": "Claude auth for the AI review/mention workflows. oauth = subscription token via /install-github-app (no metered cost); apikey = ANTHROPIC_API_KEY (pay per use)."
},
"criticalFiles": {
"type": "array",
"items": { "type": "string" },
"items": {
"type": "string"
},
"description": "Glob/paths flagged as critical; the heuristic review warns when they change."
},
"customValidators": {
"type": "array",
"items": { "type": "string" },
"description": "Names of project-specific validators to scaffold into scripts/validators/."
},
"derivedPatterns": {
"type": "object",
"additionalProperties": false,
"description": "Patterns derived from the repo by the derive-patterns workflow; feed the generated validators and the AI review.",
"properties": {
"design": {
"type": "array",
"items": { "type": "string" },
"items": {
"type": "string"
},
"description": "Design/architecture conventions found in the repo (naming, layering, error handling, boundaries)."
},
"voice": {
"type": "array",
"items": { "type": "string" },
"items": {
"type": "string"
},
"description": "Writing-voice conventions found in the repo (commit style, UI copy tone, doc register, banned terms)."
}
}
},
"issues": {
"type": "object",
"additionalProperties": false,
"description": "Automatic issue triage. The triage workflow classifies new issues via GitHub Models (free in Actions) and a deterministic job applies only advisory labels — never closes, assigns, or merges.",
"description": "Automatic issue triage. The triage workflow classifies new issues via GitHub Models (free in Actions) and a deterministic job applies only advisory labels \u2014 never closes, assigns, or merges.",
"properties": {
"triage": {
"type": "string",
"enum": ["off", "github-models"],
"enum": [
"off",
"github-models"
],
"default": "github-models",
"description": "github-models runs the classifier free over the GITHUB_TOKEN; off makes the triage workflow a no-op."
},
Expand Down
Loading
Loading