Kwizmo
Plynth

Drive Claude Code across your local repositories, with a change history that sits underneath git rather than fighting it. This page covers what you need, how to start it, and how to use each part.

Prerequisites

Three programs, all of which need to be on your PATH. Plynth itself installs nothing — no pip, no npm, no build step.

WhatWhyCheck it
Python 3.8+
python.org
Runs Plynth. Tick Add Python to PATH during install. python --version
git
Git for Windows
The program must be installed. Your folders do not have to be repositories — see below. git --version
Claude Code Does the actual work. Needs a paid Claude plan or API credits. claude --version

Plynth checks for both at startup and shows their status at the bottom of the left rail. Click that status to set where they are.

When you cannot change PATH

Locked-down machines, service accounts and portable installs often leave you without a usable PATH, and "add it to PATH" is not always advice you are allowed to take. Each tool is resolved in this order, first answer wins:

  1. A path you set in Plynth Click the git/claude status in the left rail and paste the full path to each program. Stored in Plynth's database, so it survives restarts.
  2. An environment variable KWIZMO_GIT and KWIZMO_CLAUDE. Useful in a launcher script when you would rather not store anything.
  3. PATH If it happens to work.
  4. The usual install locations C:\Program Files\Git\cmd\git.exe, %APPDATA%\npm\claude.cmd and the other common ones.

To see what was found without starting the server:

python kwizmo.py --check

It prints the resolved path for each tool, how it was found, and the version string it got back from actually running it — so "the file exists" and "the program works" are not confused with each other.

Whichever way a tool is found, the folder it lives in is added to PATH for the programs Plynth starts. Claude Code is a Node program and git shells out to its own helpers, so a tool located by full path can still fail if its siblings are unreachable.

Plynth stores no credentials. Claude Code holds its own login and Plynth simply invokes it. The database contains prompts, snapshot hashes and settings — nothing secret.


Install and start

Unzip the folder anywhere you like. There is nothing to build and nothing to configure first.

  1. Put the folder somewhere sensible Anywhere you have write access. It does not need to live near your repos.
  2. Start it Double-click Kwizmo.bat, or from a terminal:
    cd path\to\kwizmo
    python kwizmo.py
    A console window stays open while it runs. Closing it stops Plynth.
  3. It opens your browser Plynth serves on http://127.0.0.1:8765 and opens it for you. Start it with --no-open to skip that.
  4. Stop it Press Ctrl+C in the console, or close the window.
  5. Updating later Unzip the new version over the old folder, then restart Plynth and reload the page. Your database and settings live outside the folder and are untouched. If you forget to restart, Plynth tells you on load rather than failing oddly.

It binds to 127.0.0.1 only, so nothing is reachable from the network. The database lives at %APPDATA%\Kwizmo\kwizmo.db — delete it to start clean.


Your first five minutes

Point it at one repository, set the checks, then send a small prompt so you can watch the whole loop before trusting it with real work.

  1. Add a folder Press Browse… at the bottom of the left rail and pick it, or paste the full path if you would rather type. The picker marks which folders are already git repositories and which you have added before. A git repository is used as it is; anything else, Plynth offers to track for you — see folders that are not repositories.
  2. Commit anything outstanding Not required, but it makes the first timeline much easier to read if the repo starts clean.
  3. Open Settings and set the checks For a PHP project the defaults are already right: php -l {file} over *.php. Add your test command if you have one. Leave undo on failure off until you trust it.
  4. List your protected paths One glob per line — config files, schema, anything holding credentials. Plynth flags any prompt that touches them.
  5. Send a small prompt Something you can verify by eye. Watch it snapshot, run, check, and land on the timeline. Then open the node and read the diff.
  6. Undo it Press Undo on that prompt and confirm the file went back. Now you know the safety net works before you rely on it.

Everyday use

The rhythm is: several prompts, read the timeline, commit once.

Sending a prompt

Type into the composer at the bottom and press Send to Claude Code, or Ctrl+Enter. Three controls sit beside it:

Keep the thread
On by default. Continues the same Claude Code session, so it remembers the previous prompt. Turn it off to start fresh — useful when switching to an unrelated change.
Permission mode
Edit files freely is the sensible default: Claude edits files but does not run shell commands unprompted. Plan only changes nothing and is worth using first on a large change. Allow commands too removes the guard rails entirely — only point it at a repo you are willing to see broken.
Run as variants
Runs the same prompt two to four ways at once. See below.

One prompt at a time

A repository runs one prompt at a time and Plynth refuses the second. Two prompts against one tree would mean the second one's "before" snapshot captures the first one's half-finished edits, and every diff, blame and undo after that is quietly wrong. There is no sensible way to merge that, so it is refused rather than repaired.

Stop on the working banner ends a run. Whatever it already wrote to disk stays — the run is snapshotted and lands on the timeline marked stopped, so you can read it and undo it like any other.

Edits you make yourself

Plynth snapshots the whole tree, so anything you change between prompts — in Plynth's own editor, in your IDE, or with a text editor — would otherwise be swept into the next prompt's diff and blamed on Claude. Plynth compares against its last checkpoint and records your changes as their own entry, tagged you.

That keeps blame honest, but the more important half is what happens next.

Claude is told about them, every time

An agent works from what it believes the files contain. Edit a file by hand and that belief is now wrong — and the usual result is that the next prompt rewrites your change without ever mentioning it.

So every prompt starts by checking for hand edits, and any it finds are described before your request, naming the file, quoting the lines you added and saying plainly:

These edits were made deliberately, by a person, after you last wrote to this repository. What is on disk is correct and is the only accurate version — anything you remember writing to these files is out of date. Read each of these files before you touch it. Do not restore an earlier version, do not undo these edits while doing something else nearby, and do not rewrite a file wholesale when a smaller change would do.

This runs before ordinary prompts, dry runs, variants and replays alike, so a hand edit cannot reach Claude unannounced. As always, the amber bar above the composer shows what will be sent and lets you read or drop it first.

Watching it work

Tool calls stream into the banner as they happen — which files it read, what it searched for, what it edited. A three-minute silence tells you nothing; a list of files it is reading tells you whether it understood the request. Every run keeps its tool calls, foldable in the detail panel.

Reading the timeline

Every prompt becomes a numbered node, coloured by what happened:

A prompt whose diff contains something shaped like a credential — a private key, a database URL with a password, a hard-coded API token — is tagged possible secret with the match named and masked. Scanning happens on added lines only, before you commit.

Click a node to expand it: the model's summary, the check output, the file list, and a line-numbered diff of exactly what that one prompt changed.

Reviewing before it lands

Tick Review before applying in the composer and the prompt runs in a scratch copy of the repository instead. None of your files change. You get the diff split into individual hunks, tick the ones you want, and only those are applied.

Use it when a prompt is likely to be mostly right — a refactor that will also leave debug logging behind, or a change you want without the reformatting that comes with it.

Dropping part of a prompt that already landed

Drop part… on any prompt does the same thing in reverse: tick the changes you want gone, and everything unticked stays exactly as it is, including work from other prompts.

Each hunk is applied as its own single-hunk patch, bottom-up within each file, so removing one change out of the middle does not shift the line numbers of the others. If a hunk overlaps something else and cannot be applied on its own, Plynth names it and applies the rest.

Claude is told what you dropped

Whenever you drop hunks, decline part of a reviewed change, or undo a prompt outright, the files stop matching what the model believes it wrote. Left unsaid, the next prompt reasons about a version of the code that does not exist — and the usual result is that it quietly puts the rejected change back.

So Plynth holds every rejection and prepends a short account of it to your next message: which file, what was taken out, and the reason you gave. Each dialog has an optional reason box, and it is worth filling in — "we log through the framework, never inline" becomes a constraint the model works within rather than a mistake it repeats.

Each dialog also offers to make your reason a standing rule. Ticking it appends the reason to CLAUDE.md under a heading Plynth owns, so it constrains every future prompt rather than just the next one. Duplicates are refused, and you can edit or delete the list by hand at any time.

An amber bar sits above the composer whenever something is queued. Show it displays the exact text that will be added; Do not send drops it. Nothing goes out on your behalf without appearing there first.

Your prompt is stored on the timeline exactly as you typed it. The added note is archived separately on the run, foldable in the detail panel, so you can always see what was actually sent.

Undoing one prompt

Press Undo on any prompt. Plynth reverse-applies just that prompt's patch, leaving everything before and after it alone.

If a later prompt edited the same lines, git cannot separate the two. Plynth says so and puts the tree back exactly as it was rather than leaving conflict markers in your files. Undo the later prompt first, then retry.

When the checks fail

Ask Claude to fix appears on any prompt whose checks went red. It loads the failure — the files touched and the checker's own output — into the composer as the next prompt. You can edit it before sending.

Committing

The banner at the top of Changes shows how many prompts are waiting. Combined diff shows everything since your last commit in one view. Commit… opens the dialog, where you can untick anything that should not go in.

Two buttons write the message for you. Write it from my prompts builds it locally and instantly out of what you asked for — you described the change before making it, and that description beats anything reconstructed from a diff. Ask Claude to write it spends a call on a better one; it runs in plan mode so it cannot touch your files.

Plynth rebuilds the commit from just the prompts you keep; whatever you left out stays as uncommitted changes in your working tree. If a prompt you kept depends on one you dropped, it says so and names it rather than producing a broken commit.


The five tools

Tabs across the top of the work area.

Changes

The timeline, the composer, and the commit flow. Where you will spend nearly all your time.

Editor

A file list and an editor with syntax highlighting, so a two-line fix does not need a prompt or a trip to your IDE. It highlights around sixty file extensions — C, C++, C#, Java, JavaScript, TypeScript, PHP, Python, Go, Rust, Ruby, Kotlin, Swift, Dart, Scala, Perl, Lua, R, SQL, shell, PowerShell, YAML, JSON, HTML, CSS, Markdown and more — and an unrecognised extension still gets its comments, strings and numbers coloured.

Ctrl+S saves. It is not trying to replace your IDE; it exists so that a hand edit is something Plynth knows about rather than something it discovers later.

Blame

Pick a file and see which prompt wrote each line — including work you have not committed yet. Ordinary git blame cannot do this, because the commits do not exist yet; Plynth blames against the snapshot chain instead, so lines map back to "prompt 4: add Slovenia to the country dropdown". Lines predating Plynth show as committed history.

Three weeks later, "why does this function exist" has an answer.

Bisect

Something worked ten prompts ago and does not now. Bisect checks out each snapshot into a scratch worktree, runs your checks against it, and halves the range each time — twelve prompts resolved in about four checks.

Your working tree is never touched. This needs a check command that actually fails on the bug, so it is only as good as what you configured in Settings.

Search

Find any prompt by what you asked for, or by which file it touched — across one repository or all of them, committed or not. "Which prompt last changed includes/countries.php" is a question git can only answer for work that already has a commit, and only one repo at a time.

The panel above the results totals what you have spent, how long you have waited, and which files the agent keeps returning to. A file near the top of that list every week is usually telling you something about the codebase.

Recipes

Prompts you run again with one word changed. Placeholders in {braces} become fields when you use the recipe. Three ship with the app; Save what is in the composer turns anything you have written into a new one.

Recipes can be global or scoped to one repository.

Settings

Per-repository: display name, the per-file check command and its glob, the test command, whether to undo automatically on failure, and the protected path list. Also where you remove a repository — which forgets its history here but deletes nothing from disk.

Replay

The button beside Combined diff in the pending banner. Pick some prompts, pick a target repository, optionally name a branch, and Plynth re-runs those prompts there.

It re-runs them; it does not copy the diff. That is the whole point. A patch is welded to the tree it came from, but "add Slovenia and update everything downstream that depends on the country list" is correct in five codebases that share no lines of code. The prompt is the portable artifact.

Each replayed prompt lands on the target's timeline as normal work — checked, flagged, undoable one at a time — and remembers which prompt it came from.

Name a branch and Plynth creates and checks it out before starting, so the target repository's current work stays where it is. It refuses rather than reusing a branch that already exists.

Scratch copies and variants

Run in a scratch copy sits beside the composer, with a count next to it. Whatever the count, the prompt never touches your working tree until you say so.

1 is the default. The prompt executes once in a throwaway copy of the repository and you get the diff, the files touched and the check result before deciding. Keep it and it lands on the timeline as a normal prompt; throw it away and nothing happened. It costs the same as sending directly, and you see what you are getting first.

At 2 to 4 the same prompt runs that many times in parallel, each in its own copy and each with a fresh session, and you compare the results side by side. Worth it when a change has several reasonable shapes and you cannot tell which is right until you see them — a refactor that could be a lookup table, a class or a config file. It costs that many times the tokens for one result, so it is not the default.

A dry run and Review before applying both keep your files untouched while the prompt runs. The difference is what you get at the end: a dry run is all-or-nothing on the whole change, while review lets you take it apart and keep some hunks and not others.


Folders that are not repositories

Plenty of working folders were never put under version control. Plynth tracks them anyway, and adds nothing to them.

Add a folder that is not a git repository and Plynth asks before doing anything. Say yes and it creates a history in its own data folder, pointed at yours. Nothing is written into your files: no .git, no config, no marker. Stop using Plynth and the folder is exactly as it was.

Everything works the same — per-prompt diffs, hunk review, undo, blame, bisect, replay, commits. The commits simply live in Plynth's history rather than one you already had. In effect a folder that had no version control gains one, without being converted.

The rail shows tracked by Plynth instead of a branch name for these folders. Settings has a button to edit what gets skipped.

What gets skipped

A folder with no .gitignore would otherwise have its dependencies and build output snapshotted on every prompt, which is slow and useless. Plynth starts with a sensible list — vendor/, node_modules/, target/, build/, bin/, obj/, __pycache__/, editor folders, logs and object files — and you can edit it in Settings. Same syntax as a .gitignore.

Removing one

Removing the folder from Plynth leaves your files untouched either way. You are asked whether to delete the history Plynth kept. Leave it and adding the folder again later picks up where you left off.

git itself is still required. The folder does not have to be a repository, but the git program does have to be installed — snapshots, diffs, three-way merges, blame and bisect are all git doing the work. Reimplementing those in Python would be slower, larger and worse at exactly the job you need them for.


Other languages

The history layer is language-agnostic — it only sees text files and git objects. Only the checks need configuring.

Settings has a preset dropdown that fills in sensible commands. Two shapes, depending on the language:

LanguagePer-file checkTest command
PHPphp -l {file}
Pythonpython -m py_compile {file}python -m pytest -q
JavaScriptnode --check {file}npm test
TypeScriptnpx tsc --noEmit
C / C++g++ -fsyntax-only {file}cmake --build build
Javamvn -q -o compile
C#dotnet build -v q
Gogo build ./...
Rustcargo check

Interpreted languages can check one file on its own, which is why they get a per-file command. Java, C#, Go and Rust cannot — a single file means nothing without the rest of the project — so they leave that empty and put a whole project build in the test command instead.

Compiled projects are slower. A PHP lint takes milliseconds; a Maven or CMake build takes seconds to minutes, and it runs after every prompt. Bisect multiplies that by four or five. Use the fastest check that still catches real breakage — mvn compile rather than mvn test, cargo check rather than cargo build — and raise it only when you need to. The timeout is 300 seconds, set at the top of kwizmo/verify.py.

Keep build output out of git

Make sure target/, build/, bin/, obj/, *.class, *.o and friends are in your .gitignore. Snapshots skip ignored files, so a project that ignores its build output snapshots in milliseconds. One that does not will snapshot the whole build tree on every prompt.

Binary files

Images, jars and other binaries are snapshotted and undone correctly. The on-screen diff shows them as Binary files differ rather than dumping the blob, but the undo carries the full delta, so a reverted prompt restores them byte for byte.


Teaching it your stack

Put a CLAUDE.md at the root of each repository. Claude Code reads it automatically, and it does more for output quality than anything in this app.

# Project notes

- PHP 7.4. No Composer, no autoloader — require files by relative path.
- No arrow functions, union types, constructor promotion or enums.
- Never add a dependency. If a library seems necessary, say so and stop.
- MySQL via PDO. Prepared statements everywhere.
- The country list lives in three places and all three must stay in sync:
  includes/countries.php, js/form-validate.js, and the `country` enum in
  db/schema.sql.

That last line is what turns "add Slovenia to the dropdown" from a one-file edit into a correct one. Every time you find yourself repeating a constraint in the composer, move it here instead.


How the history works

Worth understanding, because it is what makes "twelve prompts, then one commit" safe.

Before and after every prompt, the whole working tree is written into git's object database using plumbing commands and a throwaway index file:

GIT_INDEX_FILE=<tmp>  git read-tree HEAD
GIT_INDEX_FILE=<tmp>  git add -A
GIT_INDEX_FILE=<tmp>  git write-tree      -> tree
git commit-tree <tree> -p <previous>      -> commit
git update-ref refs/kwizmo/snapshots <commit>

Those are real commits on a private ref. That one decision is what makes the rest possible:

Meanwhile git status, git log, your branch and your staging area are never touched. If you uninstall Plynth tomorrow, your repository is exactly as you left it.

One caveat. Files matched by .gitignore are not snapshotted. Normally what you want — no vendor directories, no logs — but a change Claude makes to an ignored file will not appear in the history and cannot be undone from here.


Plynth's own tests

If you change anything, run these first. They take a couple of seconds and they exist because a bug in this tool costs you a day of work, not five minutes.

python run_tests.py            everything
python run_tests.py gitsnap    one module
python run_tests.py -v         with each test named

94 tests, standard library only. Every one builds a real git repository in a temporary directory. Nothing is mocked — this whole tool is a thin layer over git plumbing, and a mocked git would only prove the mock does what it was told.

They cover the places where a silent break would be expensive: that snapshots never touch HEAD, your branch or your index; that undo restores binary files byte for byte and refuses cleanly when it cannot; that rejecting one hunk from the middle leaves its neighbours alone; that a selective commit leaves the rest uncommitted; and that the run lock cannot be freed by a late release from a job that already finished.

Writing them found three real bugs, including a secret-scanner rule that stayed quiet whenever the word "example" appeared anywhere on the line — which would have hidden a genuine key sitting next to a link to the docs.


Troubleshooting

The rail says git or claude is missing
Click the status in the left rail and paste the full path to the program, then press Save and test — it runs the program and reports what came back. Or run python kwizmo.py --check from a terminal to see where it looked. If you can change PATH, reinstalling with the PATH option ticked is the simpler fix.
The folder is not a git repository
Plynth offers to track it for you, keeping the history outside your files. See folders that are not repositories. If you would rather it were a real repository, run git init and make one commit first, then add it.
Port 8765 is already in use
Another copy of Plynth is probably still running — check for a stray console window. Otherwise change PORT at the top of kwizmo/server.py.
Every prompt shows checker missing
The check command is not installed. On Windows, php is often not on the PATH even when PHP is installed — use the full path in Settings, or turn checks off for that repo.
A prompt shows checks failed but the change looks right
Usually an earlier prompt left the file broken and this one inherited it. Open the check output in the detail panel — it names the file and line. Bisect will tell you which prompt actually introduced it.
Claude changed nothing
Check the permission mode. Plan only deliberately changes nothing. Otherwise read the model's summary in the detail panel — it usually explains why it declined.
Something returns "not found" after you updated Plynth
Open http://127.0.0.1:8765/api/health in your browser. It reports the version that is answering and the folder it is running from. If either is not what you expect, an older copy is still alive — it holds the port, so the one you just started never took over. Look for a second console window, or end the stray python process, then start Plynth again. It now refuses to start quietly in that situation and tells you which instance is in the way.
A prompt is stuck saying it is still running
Plynth was stopped while it was working — a closed console, or the machine sleeping. Restart it and the run is marked interrupted. There is no way to know now what it managed to do, so anything it did change shows up separately as an edit Plynth cannot attribute.
"This repository is already running a prompt"
Exactly what it says. Wait, or press Stop on the working banner. Running two at once would corrupt the history, so it is refused rather than queued. Bisect waits and tries again on its own, since its steps follow each other with nothing in between.
A prompt is missing from the timeline
Variants that you discarded are removed. Prompts whose only changes were to .gitignored files will show zero files, because those are not snapshotted.
Everything looks wrong and I want out
Plynth never touched your git history. git checkout . discards all uncommitted work and puts you back at your last commit.