Koh — 34 commands.
Complete reference for every command, concept, and configuration option.
01 Quick start
Koh is a single statically compiled binary with no runtime dependencies beyond system zlib.
curl -fsSL koh.asha.software | sh - 01
koh initInitialize a repository. Creates
.koh/with a SQLite database and default.kohconfig. Pass--configto open the config file immediately. - 02
koh save "message"Snapshot the working tree. Transactional — a failure at any stage leaves no partial state.
- 03
koh logBrowse history in the interactive pager. Tab cycles filter modes (all → green → red). Piped output disables interaction and color automatically.
- 04
koh promotePromote dev HEAD to main. Requires confirmation. Undo with
koh lane main && koh undo.
.koh/ to your .gitignore. Koh and Git share the working tree and do not interfere with each other.
02 Core concepts
Saves
A complete, immutable snapshot of your working tree — identified by the first six hex characters of its Blake3 content hash. Written once, never modified. History is append-only.
Two Lanes
Exactly two: main (stable, what works) and dev (experimental, what you're trying). No arbitrary branches, no merges, no rebase. Promote dev in one atomic operation when ready.
Content-Addressed Storage
Every file is identified by its Blake3 hash. Unchanged files between saves are stored once. Files under 512 bytes are inlined in the manifest; larger files live zlib-compressed in .koh/objects/.
Checkpoints
Saves with is_checkpoint = 1 — deliberate named safety points. Set one before handing off to an agent or starting a risky refactor. koh load --checkpoint finds the most recent one without needing an ID.
The SQLite database is a query cache, not the primary store. All structural data lives in manifest JSON files at .koh/snapshots/. If the database is lost, koh recover rebuilds it from manifests — no save is ever lost unless its manifest file is also gone.
03 Core commands
Initialize a new repository. Creates .koh/, a fresh SQLite database, and a default .kohconfig. With --config, opens .kohconfig in $EDITOR (then $VISUAL, then vi) immediately after initialization.
Snapshot the working tree. The save is transactional — manifest written to .tmp.json, database INSERT and HEAD update in a single SQLite transaction, then atomically renamed. A failure at any stage leaves no partial state.
--agent records the AI model that initiated the save. --session groups saves under a named session for filtering and undo. --json suppresses interactive output and emits a single JSON object — designed for agent consumption.
Show save history. Opens in the interactive pager on a TTY; prints unfiltered to stdout when piped. --all interleaves both lanes by timestamp. --session filters to saves with the specified session name. --json outputs all matching saves as a JSON array with no pagination.
--since accepts: yesterday, today, last week, N days ago, last Monday, YYYY-MM-DD, YYYY-MM-DDThh:mm, or a tag name.
Alias for koh log.
Show working-tree changes versus the current HEAD. Lists new files (+), modified files (~), and deleted files (-). --json outputs a JSON object with lane, head_id, dirty, modified, added, and deleted fields — no ANSI codes, no pagination.
Produce a unified diff between two saves. Pipes colorized output through $PAGER on a TTY; prints directly when piped. --json outputs a JSON object with a files array and a diff field.
Diffs between the earliest and latest saves of the most recent named session on the current lane. last is a keyword that resolves to the most recent session name.
04 Lane commands
Without arguments, print the current lane. With main or dev, switch to that lane. Lane switching warns if there are unsaved changes but does not block — switching lanes carries no risk of data loss.
Promote dev HEAD to main. With <id>, promote a specific ancestor of dev HEAD rather than the tip — essential when your dev tip has work-in-progress above stable saves you want to ship. Validates that the target is within dev's ancestry chain and refuses orphaned saves. The output shows the undo hint: koh lane main && koh undo.
Restore a single file from a specific save into the working tree — not the whole snapshot. --dry-run previews exactly what would change without writing anything.
Show a diff of dev HEAD versus main HEAD — everything that has accumulated on dev. Output is piped through the pager.
05 Undo, Load, Discard, Peek
These commands alter working-tree state or history. All require explicit confirmation. All require a clean working tree — no unsaved changes — before executing.
Rewind the current lane by n saves (default 1). Stores the pre-undo HEAD in the database (_pre_undo_main or _pre_undo_dev) so the operation can be manually reversed. Skipped saves are not deleted — they remain in the object store.
Undo all saves belonging to the most recent named session on the current lane in a single operation. Shows the session name and count before confirmation. Session saves remain in the saves table — they are accessible by ID.
Restore the working tree to any save on the current lane. Validates same-lane membership before any other checks. Requires confirmation.
Load the most recent checkpoint on the current lane without needing its ID. Shows the checkpoint message and timestamp before confirmation. Exits with an error if no checkpoints exist on the current lane.
Throw away all working-tree changes and restore to the current HEAD. Requires confirmation. No history is altered.
Non-destructively extract a save to .koh/peek-<id>/ for inspection. Nothing in the working tree is touched. Requires confirmation. The peek directory is temporary and cleaned up by subsequent operations.
06 Checkpoints
A checkpoint is a save with is_checkpoint = 1 — a deliberately named recovery point. The intended use: set one before handing off to an AI agent, before a risky refactor, or any time you want to be able to say "take me back to here" without remembering a save ID.
koh load --checkpoint always returns to the most recent checkpoint on the current lane automatically.
Create a checkpoint save on the current lane. The message is required — checkpoints must be intentional and described. Follows the same atomicity pattern as koh save. Checkpoints appear in koh log with a filled ✦ marker in amber. Unlike koh save, this command does not refuse on a dirty working tree — the entire point is to snapshot exactly where you are, including in-progress changes.
07 Cleanup & Recovery
Update the message on a save. The only command that mutates a manifest — specifically, only the message field via atomic re-serialization. Checksum and all other fields are unaffected.
Identify and delete orphaned saves — saves not reachable from either HEAD by following parent pointers. A recursive SQL CTE identifies the full reachable set; anything outside it is eligible for deletion. Requires confirmation.
Rebuild the SQLite database from manifest files. With --verify, validates all manifest checksums and object integrity before rebuilding. Run after any database corruption, accidental deletion, or schema mismatch.
Read-only integrity check. Recomputes the Blake3 checksum of every manifest and every stored object, reports any mismatch, and exits non-zero if anything is invalid. Opens results in the interactive pager.
08 Tags & Marks
Attach a named tag to a save (defaults to HEAD). Tags are stored in both the database and the manifest — tag state is fully recoverable from manifests after a complete database loss.
Remove a tag.
Mark a tag as problematic. Flagged tags trigger a warning when used as a target for koh steal, allowing others to cancel before local files are written. The reason is shown in the warning.
List all tags attached to a save (defaults to HEAD).
Bookmark a save for quick reference (defaults to HEAD). Bookmarks appear highlighted in the pager's green filter mode.
Remove a bookmark.
List all bookmarked saves in the interactive pager.
Show projects registered in ~/.config/koh/projects alongside their most recent save. Projects are auto-registered whenever any Koh command runs in a repository. Opens in the interactive pager. With --path, scans a specific directory instead.
Dashboard view of all tracked projects in the interactive pager.
09 Health
Read-only diagnostic command. Never modifies anything. Always exits 0 — health output is informational, never a gate. Signals are only shown when their threshold is exceeded; a clean repo shows all signals clean.
Analyse save history and report behavioural signals. Six signals, all with configurable thresholds in .kohconfig:
| Signal | What it flags |
|---|---|
| Frequent Files | Files appearing in an unusually high proportion of saves — may belong in .kohignore. Threshold: health_frequent_file_pct (default 80%) |
| Large Saves | Saves touching an unusually large number of files. Threshold: health_large_save_files (default 20 files) |
| Undo Patterns | Files that are frequently reverted — may indicate unstable decisions. Derived from undo history. |
| Dev Lane Age | Dev lane not promoted to main in an unusually long time. Threshold: health_dev_age_days (default 30 days) |
| Save Velocity | Unusually low rate of saves over recent time. Threshold: health_velocity_days |
| Storage Growth | Month-over-month storage growth exceeds threshold. Threshold: health_storage_pct (default 200%) |
10 Meta
Open .kohconfig in $EDITOR (then $VISUAL, then vi). Creates the file with defaults if it doesn't exist. Exits with an error if run outside a Koh repository.
Display the embedded man page in the interactive pager. Section headers are rendered in amber, command names highlighted. Tab and Shift+Tab navigate between section headers (not filter modes). The status bar shows the current section name.
Print the version string.
Print the usage summary.
Environment Variables
| Variable | Effect |
|---|---|
| KOH_DEBUG | Print the full error type on failure — useful for bug reports |
| NO_COLOR | Disable all ANSI color output |
| KOH_NO_COLOR | Same as NO_COLOR |
| EDITOR | Editor used by koh config and koh init --config |
| VISUAL | Fallback editor if EDITOR is unset (checked before vi) |
11 Agent Workflow
Koh has first-class support for AI agent workflows. The additions are minimal and composable — they do not change core save semantics, they add metadata and filtering that makes agent-produced history legible and reversible.
Save Metadata
koh save --agent "model-name" --session "session-name" records which AI model produced a save and groups it with other saves in the same logical session. Both fields are optional and stored in the saves table. When both are present, koh log shows them as a [agent · session] suffix.
Session Undo
koh undo --session rolls back all saves in the most recent named session in a single confirmation. The intended recovery path when an agent session produces unwanted results. Session saves are not deleted — they remain accessible by ID.
Checkpoints
Before handing off to an agent: koh checkpoint "before agent session". After the session, if anything went wrong: koh discard then koh load --checkpoint. No ID required.
JSON Output
Four commands accept --json for machine-readable output with no ANSI codes and no pagination.
| Command | JSON fields |
|---|---|
| koh save --json | id, lane, message, timestamp, size_bytes, agent, session |
| koh status --json | lane, head_id, dirty, modified, added, deleted |
| koh log --json | Array of save objects with all fields including marked, agent, session |
| koh diff --json | files array, diff unified diff string |
.koh/AGENTS.md
An optional file at .koh/AGENTS.md. When Koh starts and this file exists with content, it prints a single notice line so that agents checking Koh's output will see it. Intended to carry repo-specific rules — "always save before running tests", "do not touch src/generated/". Koh does not enforce the rules programmatically; it is a human-readable spec that travels with the repository. The notice is suppressed on non-TTY stdout and when --json is used.
12 Interactive Pager
Commands that produce multi-line output use the interactive pager on a TTY. On non-TTY output (piped or redirected), all lines are printed directly with no interaction and no color.
Commands using the interactive pager: log, changelog, overview, marks, verify, offers, clean, man.
Commands that pipe to $PAGER: diff, diverge, pluck --dry-run.
Filter Modes (history commands)
| Mode | Shows |
|---|---|
| all | Every line |
| green | Marked saves and saves older than stable_days |
| red | Saves with flagged tags |
Tab cycles forward. Shift+Tab cycles backward. Green = stable or noteworthy. Red = needs attention.
Section Navigation (koh man)
In the man pager, Tab and Shift+Tab navigate between section headers rather than cycling filter modes. A section header is a line that is all-caps and not indented. The status bar shows the current section name. All lines are always visible — there are no filter states.
13 Keeper Integration
Keeper is an optional remote service for sharing and syncing Koh repositories. Local use requires no Keeper instance. When connected via koh steal, the remote URL is stored in .koh/keeper and subsequent Keeper operations read it automatically.
Initialize a local repository from a remote Keeper snapshot. Stores the remote URL in .koh/keeper. If the target tag is flagged, the flag reason is shown and the user may cancel before any local files are written.
Generate a patch from the current save state. Without --local, uploads to the connected Keeper instance. With --local, writes a .koh.face file to disk.
List patches available from the connected Keeper instance in the interactive pager.
Apply a patch from a Keeper URL or a local .koh.face file.
14 Bundles
Self-contained archive format for the entire .koh/ directory. Repository transfer without a Keeper instance.
Archive .koh/ as a deterministic tar+gzip bundle with a computed checksum, written to the current directory.
Merge objects and manifests from a bundle into the current repository. Existing objects are not overwritten. The database is updated to reflect the merged history.
15 Configuration
Each repository may have a .kohconfig at its root. Use koh config to open it. The parser is forgiving — unknown keys are silently ignored, malformed values fall back to defaults, missing file means all defaults apply. Never included in snapshots.
| Key | Default | Description |
|---|---|---|
| stable_days | 14 | Saves older than this (days) show a ◐ marker in koh log |
| promote_suggest | true | Show promote hint in log output when dev HEAD is promote-worthy |
| color | true | Disable with false, NO_COLOR, or KOH_NO_COLOR |
| page_size | 0 | Lines per pager screen. 0 = auto-detect from terminal row count |
| health_frequent_file_pct | 80 | % of saves a file must appear in to trigger the frequent-files signal |
| health_large_save_files | 20 | Files-changed count to trigger the large-save signal |
| health_dev_age_days | 30 | Days without a promote to trigger the dev-lane-age signal |
| health_storage_pct | 200 | Month-over-month storage growth % to trigger the storage-growth signal |
16 Storage Architecture
Disk Layout
SQLite Schema (v5)
Schema auto-migrates on first open. A SchemaTooNew error is raised if the database version exceeds what the binary understands, preventing silent data corruption.
| Migration | Change |
|---|---|
| v1 → v2 | Create tags table |
| v2 → v3 | Add size_bytes column to saves |
| v3 → v4 | Create marks table |
| v4 → v5 | Remove focus_dev from state; add agent, session, is_checkpoint to saves |
Safety Design
Ten independent mechanisms make data loss essentially impossible: confirmation defaults to No (only lowercase y confirms, Enter alone cancels); unsaved-changes guards on all history-altering commands (koh checkpoint is the deliberate exception); same-lane validation before koh load; append-only history — undo moves HEAD, never deletes; transactional saves with atomic manifest rename; manifest checksums verified by koh verify; database as a rebuildable cache; orphan-safe cleanup via recursive SQL CTE; .kohignore change warnings on restore; and flagged-tag warnings before koh steal.
Technology
Zig 0.15.0. SQLite 3.49.1 vendored as a C amalgamation. Compression via system zlib (compress2/uncompress) — RFC 1950 format, the single runtime dependency. Blake3 hashing via Zig's standard library. HTTP for Keeper via std.http.Client.