koh

@june

Koh is version control for the solo developer.

V

v3.8.7 · 297 saves · 13,847 lines · updated 3 days ago

103 files
9236af88c567
back to HEAD
shell/7.0 KB · 1 week ago
src/614.8 KB · 4 days ago
apply/6.7 KB · 2 weeks ago
build/29.2 KB · 4 days ago
cli/182.2 KB · 4 days ago
args_test.v445 B · 2 weeks agoargs.vCommand line parsing.2.6 KB · 2 weeks agocli.vCommand line entry: dispatch table, usage text and exit codes.3.1 KB · 1 week agocmd_back_test.v4.0 KB · 1 week agocmd_back.vGoing back: koh undo, koh revert and koh load.9.7 KB · 2 weeks agocmd_build.vkoh build — build with koh.build, and upload nothing.2.6 KB · 2 weeks agocmd_diff.vkoh diff — unified diff of the working tree against a save, of one save against another, or across a session.3.3 KB · 2 weeks agocmd_doctor_test.v20.1 KB · 4 days agocmd_doctor.vRepository checks, remote verification, repairs and explicit maintenance.14.3 KB · 6 days agocmd_flag.vkoh flag — mark a save unsafe, clear the mark, or list what is marked.2.4 KB · 2 weeks agocmd_ignore.vBatch ignore toggles: plan from the original rules, then confirm one file replacement.6.3 KB · 1 week agocmd_init.vkoh init — write the .koh file and a default ignore list.964 B · 2 weeks agocmd_man.vkoh man — the manual, built into the binary.3.4 KB · 2 weeks agocmd_migrate.vkoh migrate — one-time conversion from Koh 2.4.3 KB · 2 weeks agocmd_offer.vkoh offer — send the local saves to this machine's remote, and, with --builds, publish a release built from them.7.0 KB · 1 week agocmd_peek.vkoh peek — put a save's tree in a temporary folder and print where.1.7 KB · 2 weeks agocmd_pluck.vkoh pluck — put one save's change onto the current tree.5.1 KB · 2 weeks agocmd_remote.vkoh remote — the machine's remote, set up once and used by every repo.14.1 KB · 1 week agocmd_save.vkoh save and koh checkpoint.2.9 KB · 2 weeks agocmd_status.vkoh status — where things stand: the save you are on, and what the working tree has that it does not.4.3 KB · 2 weeks agocmd_steal_test.v386 B · 2 weeks agocmd_steal.vkoh steal — bring another machine's saves in.18.8 KB · 1 week agocmd_strip.vkoh strip — remove a path from history, permanently.6.9 KB · 1 week agocmd_update.vkoh update — put a newer published koh in place of this one (§12).2.8 KB · 2 weeks agocmd_view.vkoh view — the history, browsed or printed.7.1 KB · 2 weeks agodoctor_checks.vDoctor's local history, event and working-tree checks.9.3 KB · 4 days agodoctor_remote.vRead-only remote inspection; Doctor never calls sync or the write probe.7.9 KB · 4 days agodoctor_repair.vDoctor's repairs (spec §7.6.2).8.3 KB · 6 days agohandover.vRefuse repository commands until a Koh 2 repository has been migrated.731 B · 1 week agooutput.vOutput for the command line: the ✦ / ✗ habit, and the --json shapes agents read.1.6 KB · 2 weeks agoprogress_test.v673 B · 1 week agoprogress.vThe line a long action draws while it works (§4, "never leave the person wondering"): one line on stderr, redrawn in place and cleared when the step ends, and only when stderr is a terminal, so pipes, logs and --json never see it.3.2 KB · 1 week agoprompt.vConfirmation shared by destructive commands.796 B · 1 week agorepo.vOpening the repo a command acts on, and the tree lookups commands share.949 B · 2 weeks ago
events/8.4 KB · 6 days ago
exchange/146.9 KB · 4 days ago
fmt/3.5 KB · 2 weeks ago
history/25.9 KB · 1 week ago
machine/8.2 KB · 2 weeks ago
migrate/31.8 KB · 1 week ago
release/13.8 KB · 1 week ago
remote/38.2 KB · 1 week ago
snapshot/39.9 KB · 1 week ago
storage/27.2 KB · 6 days ago
update/10.8 KB · 1 week ago
view/41.9 KB · 1 week ago
koh.vProgram entry: hands the command line to the CLI and exits with its code.257 B · 1 week ago
tests/98.3 KB · 6 days ago
vendor/9.4 MB · 2 weeks ago
build.shBuild one native Koh release into build/releases/koh-<target>.1.8 KB · 1 week agokoh.build129 B · 1 week agoREADME.mdKoh is version control for a solo developer working across a few machines.9.7 KB · 6 days agov.mod122 B · 3 days agoVERSION6 B · 3 days ago
73d1388db304record the emptied queue, the flagged offer JSON, literal strip bans and the Kepr sweep in continuity (3.1.5)2 weeks agoabb551b2f221ban a stripped path as itself: its * and ? are escaped in .kohignore and the walk's ban, and a backslash makes a rule's next character literal (3.1.5)2 weeks agoc5d37aafc8b8name the saves that came in flagged during a stolen repo's upgrade in koh offer --json, as steal does (3.1.4)2 weeks ago2bc5d60cf723record the closed offer/strip race, the 11,500 ceiling and what stays open in continuity (3.1.3)2 weeks ago66eb40a24894close the offer/strip race: offer checks for strips again after uploading and, before head moves, takes back the records and stripped objects it put (3.1.3)2 weeks agof03dc4f4c998raise the line ceiling to 11,500 and the tripwire to 11,300, June's call, in AGENTS.md and the master spec (3.1.2)2 weeks ago6281c4a6e2c2record the fixed strip and tie findings, the git terminology sweep and the open offer/strip race in continuity (3.1.2)2 weeks agoc99a73d26672rename commit, push, pull, merge, clone and stage out of the source, tests and docs: transactions begin and end, strips are sent, a repo is stolen (3.1.2)2 weeks ago1ca868c479bfapply every strip held here, not only those in the fetch, to saves arriving later and to the clash preview (3.1.1)2 weeks agoc9861f20776dwrite the handoff: the three open codex findings, the tripwire and the discipline for the next session (3.1.0)2 weeks ago89b933c49b04release Koh 3.1.0: strips ban their path from all of history, recorded in continuity (3.1.0)2 weeks ago9c78d9c43299add a Strict Koh Discipline section to the manual (3.0.110)2 weeks agoccb80e87aea2make a strip ban its path: every save loses it whenever made, no walk picks it up again, and strip adds it to .kohignore (3.0.109)2 weeks ago2d6d872724e3record the 11,000 ceiling, the https rule, offer's clash stop and the approved strip timing in AGENTS.md, the manual and continuity (3.0.108)2 weeks agod247213bdd9drefuse a steal address whose repo is not a name Kepr serves, before it reaches a URL or a folder (3.0.107)2 weeks ago7ec030db3e06match the guided steal screens to the addendum: the repo named, relative times, rows cut to the terminal, the kept and taken files listed, and d and ? under test (3.0.106)2 weeks ago29a1ceda1bfddrop the forwarding kepr_host wrapper, the nil backend, the unused ask hook and the duplicated strip filter from steal and offer (3.0.105)2 weeks ago934062214af8refuse plain http for a Kepr host that is not this machine, since the connect reply carries the bucket's secret key (3.0.104)2 weeks agoaece2a878d50ask or refuse on a clash when offer upgrades a stolen repo, as steal does, instead of quietly taking theirs (3.0.103)2 weeks agoc0fab999e938walk through steal on a terminal: unsaved work, overview, collisions, summary and undo (3.0.102)2 weeks ago1cace06dc178wip: add the first green guided steal slice and record the remaining 8e work (3.0.101)2 weeks ago128ef9e650adrecord the second discarded 8e attempt and where it is recoverable (3.0.100)2 weeks agod7ed022ef3cdrevert: 2c06162b58e12 weeks ago2c06162b58e1note 8e's discarded attempt, its recoverable checkpoint and the plan it works from (3.0.99)2 weeks ago982557ae0ed0revert: 3a0d4b911f902 weeks ago3a0d4b911f90close the public steal review: a sourced repo is never auto-offered, and a failed clone leaves nothing (3.0.98)2 weeks ago2b1e04f61099steal public repos from a Kepr over HTTPS, read-only, with a per-repo source (3.0.97)2 weeks ago1af82597b777close the review of the Kepr remote form: validation, no shell, no redirects (3.0.96)2 weeks ago8288d349f168set up S3 by signing in to a Kepr with koh remote <host> (3.0.95)2 weeks ago4d8f6c23e57cgive every command on the help screen its own paragraph (3.0.94)2 weeks ago4259fb5dff46close the log removal's review findings: completion, stale names, three sharper tests (3.0.93)2 weeks agoea43d4f3e936remove the log command: view prints the whole line, checkpoints included (3.0.92)2 weeks ago81c95eea13a7record koh view and the campaign's final counts in continuity (3.0.91)2 weeks agoa4086cd3f7dareplace the saves and checkpoints aliases with koh view, which opens on saves and lists it on the help screen (3.0.90)2 weeks agoe86a58245a22close the update review: a no at the question refuses, the download is checked against its declared size, the swap is a real rename, a document with no version is refused and --json without --yes is a usage error (3.0.89)2 weeks agobbf4bfcaed9arecord the campaign in AGENTS.md and continuity: the new modules, the ceiling June raised to 10,500, and the traps the phases taught (3.0.88)2 weeks ago05916a2b7e91read a file:// download in koh update, which is what a Kepr with a folder bucket hands out (3.0.87)2 weeks ago416aa9fce19dwrap the help screen's also line once it passes 80 columns, now that build and update are on it (3.0.86)2 weeks agofe3253a128c9add koh update: read a release document from Kepr, install only a newer version, verify its Blake3, sign it ad-hoc and rename it over the running binary (3.0.85)2 weeks agob760c74f453bcheck release output paths before any build runs, and pin the duplicate platform, the missing --notes value and the unchanged migration in tests (3.0.84)2 weeks ago1278e421b5fcclose the release review: refuse two platforms sharing an output, a platform named twice, stray offer arguments and --notes with an empty value, and check both build files before migrate moves anything (3.0.83)2 weeks agoe042d2bfc1ffpublish releases: build every platform before uploading, write the files, the record and releases/latest, and rename kepr.build in migrate (3.0.82)2 weeks ago819e88eff38dsignal the build's process group by id, since koh's own guard skips an exited command, and hold it with a test that leaves a child behind (3.0.81)2 weeks ago818acd921e18close the phase 7a review: read only a quoted literal as a version, refuse an empty artifact, take stray build children with it, and prove koh builds itself in a test (3.0.80)2 weeks agoba2f1a3bcb27add the build module and koh build: parse koh.build, read each project's own version in the spec's order with gradle last, run the build environment per platform and report every output path (3.0.79)2 weeks ago662cda477689colour the help screen's version line as one light line, closing the review of the shorter screen (3.0.78)2 weeks ago0961967f3e9dkeep checkpoints out of koh saves when piped or asked for json, so the list agrees with the view (3.0.77)2 weeks ago6d6918095a35shorten the help screen to commands and plain words, flags moved to koh man (3.0.76)2 weeks ago0c0e6474868dkeep checkpoints out of the saves tab, so the two history lists never overlap (3.0.75)2 weeks ago546abb43431bclose the format write race by reading back the value the winner wrote, and check the format before the name clash so a remote this koh cannot read says so (3.0.74)2 weeks ago

← newerolder →

details

Koh

Koh is version control for a solo developer working across a few machines. Save your project, see what changed, and bring back earlier work when you need it. Each repository keeps its history in one SQLite file, .koh, with one line of saves. Koh is written in V.

Install

Koh runs on Apple silicon macOS and Linux on ARM64 or x86_64. New releases do not support Intel macOS or Windows.

curl -fsSL https://koh.asha.software/install.sh | sh

The installer verifies the release checksum and offers an optional zsh plugin. For later updates, run koh update. Use koh update --check to check without installing.

Start saving

Run these commands in your project folder:

koh init
koh save "record the first working version"
koh status
koh view

init creates .koh and a starter .kohignore. Check the ignore rules before your first save. Each save snapshots every file Koh tracks, including new files; unchanged contents are stored only once. save refuses when nothing changed.

Before a change you may want to undo, make a checkpoint:

koh checkpoint "before changing the parser"

A checkpoint records the project even when nothing changed. After editing, koh diff shows the changes and koh save "describe the result" records them. Commands work from subfolders too.

koh view opens an interactive history browser in a terminal. Use the arrow keys or j and k to move, / to search, Enter for details, and Tab to switch between saves and checkpoints. With --json or when piped, it prints history. LOG.md is a generated summary of the latest 20 saves.

Bring back earlier work

Command What it does
koh undo Restore the previous save.
koh undo 3 Step back three saves.
koh load <id> Restore a particular save.
koh load --checkpoint Restore the latest checkpoint.
koh revert Discard unsaved changes, keeping them in a checkpoint first.
koh peek <id> Unpack a save into a temporary folder for inspection.
koh pluck <id> Apply one save's changes to the current tree as a new save.

Restoring earlier work adds a new save. The saves you stepped away from remain available, so you can load them again. Koh has no branches or lanes.

undo, load, and pluck require a clean working tree. Commands that rewrite your files show a preview and ask for confirmation. Enter cancels; only y or Y approves. Scripts must supply --yes to approve the action.

Sync your machines

Choose an S3 bucket or a folder remote once per machine. Every repository on that machine uses the same remote.

For a folder, such as one on a removable drive:

koh remote folder /Volumes/usb/koh

For S3, supply the endpoint, bucket, and region. Koh asks for the access keys and tests the connection before keeping the settings:

koh remote s3 https://s3.fr-par.scw.cloud my-bucket fr-par

If you run a Kepr, koh remote <kepr-host> opens its sign-in page and lets you connect with the code it supplies. koh remote shows the current setting. Replacing or forgetting an existing remote requires consent.

Once configured, saves and checkpoints offer their history automatically. A failed transfer leaves the local save intact; run koh offer to retry. On another machine, fetch a repository from outside an existing repo:

koh steal my-project
cd my-project

Inside it, koh steal brings in new history. If both machines changed a file, Koh shows both versions and asks which one to keep. It resolves clashes per file, without blending lines. The original saves remain available, and the result names the save you can load to return to your previous state.

Incoming changes require confirmation. For scripts, --mine or --theirs chooses a side for every clash; --yes separately approves applying the changes.

You can also fetch a public repository with koh steal <kepr-host>/<repo>. That copy syncs from its public source and cannot offer back until you sign in to that Kepr as its operator.

Ignore files and remove saved content

.kohignore holds rules for the repository and travels with its saves. ~/.kohignore adds rules for every repository on this machine and stays local. Koh also excludes its own history files, LOG.md, and symlinks.

koh ignore cache/ output/ notes.txt
koh ignore --global scratch.txt

ignore previews the exact rules and asks once for the batch. A path already matched in the selected ignore file proposes removing its matching rules; removing a broad rule can affect other files, which the preview explains. Local paths are relative to the current directory. Global names and paths match at any depth in every repository.

Ignoring a file leaves its earlier saved versions in history. koh strip <path> permanently removes a file or folder from all saves and bans that path from future saves. It propagates the removal to the remote and other machines as they sync. The working file stays on disk. --save <id> limits removal to one save and does not ban the path.

Strip is the exception to Koh's growing history: it cannot be undone. It requires a terminal and each affected path typed exactly. --yes skips only the yes/no question. See koh man strip before using it.

Work with coding agents

Saves can record the agent's model and a session name:

koh save --agent gpt-6 --session "fix parser errors" "reject incomplete input"
koh view --session "fix parser errors" --json
koh diff --session "fix parser errors"

koh undo --session restores the state before the current session. Use the actual model identifier and keep one session name for related work.

Commands that print data support --json. Koh refuses when a required prompt cannot be answered, so unattended commands never wait for input. Exit codes are 0 for success, 1 for refusal or failure, and 2 for bad usage.

Build projects and publish releases

A project's koh.build file names its build command, output, and platforms. koh build runs it for the current machine without uploading or installing anything. The toolchain must already be on PATH.

koh offer --builds builds every configured platform and publishes the artifacts as a release. It requires a saved tree and a version in the project's own files, such as VERSION, v.mod, or package.json. Ordinary saves never publish builds. See the specification for the file format, and koh man build or koh man offer for command options.

Back up a repository

koh doctor
koh doctor --backup /path/to/new-backup.koh

doctor checks the database, stored content, history, events, working files and configuration. It also verifies remote history and release files by default. Use --fast for a local check. The report names each check, its result, and any part it could not verify. A failed check says how to fix it, and Doctor offers to repair what it can without losing anything: on a terminal it asks first, and --yes runs every repair. Its backup command writes a consistent copy of .koh. Use it instead of copying the database while Koh is using it. Keep a live .koh out of cloud folder sync services; use offer and steal between machines.

Koh does not encrypt history. Anyone who can read the database or remote can read its saved files. Koh is intended for code and text; individual files over 512 MiB are refused.

Upgrade from Koh 2

Save unfinished work with Koh 2 first. Run koh update in a terminal and confirm the upgrade, then run koh migrate in each repository.

Migration keeps save IDs and archives the old repository in .koh-v2/. It imports the dev ancestry by default, falling back to main if dev has no saves; --lane main selects the other line. Koh 3 refuses repository commands in an unmigrated Koh 2 repository. See koh man migrate for missing file recovery and other options.

Build from source

Use the pinned compiler, V 0.5.2 b9a2d87. A different V revision can break Koh, and the tests reject it.

To compile V, install a C compiler and extract the V source archive. Copy v.c from the vc bootstrap archive into the extracted V directory, then run these commands there. The vc revision is pinned too because newer bootstraps can be incompatible with this V source.

mkdir -p .git
printf '%s\n' b9a2d87bc6c317739113db1d96ca89a05f8903f4 > .git/HEAD
cc -O2 -w -o v1 v.c -lm -lpthread
./v1 -o v2 cmd/v
./v2 -o v cmd/v
./v self
./v version

The .git/HEAD file lets V report the pinned revision when built from an archive. Add the directory containing the resulting v executable to PATH. From Koh's repository root, compile Koh with:

mkdir -p build
v -d use_bundled_libgc -o build/koh3 src/koh.v

The flag builds the GC library from V's bundled source. SQLite 3.49.1 is vendored and compiled into the executable. Every Koh build requires Boehm garbage collection. Release builds use sh build.sh or koh build on the target OS and CPU. Linux releases use the host C compiler (cc) and static C runtime libraries. Install the toolchain's static runtime development package if needed. Zig is not required. Cross-compiling Koh with the pinned V disables garbage collection, so the release script refuses it.

Manual and shell integration

Run koh man for the full manual or koh man <command> for one command. The manual source is also available in this repository.

Source shell/koh3.plugin.zsh from ~/.zshrc for a prompt segment and tab completion. ! marks unsaved changes; ⚑ marks a flagged head save. The plugin's header covers custom prompts, Starship, and Powerlevel10k.

Made by Asha Software.