Reference documentation

Koh — 34 commands.

Complete reference for every command, concept, and configuration option.

v1.2.0 Zig 0.15.0 SQLite 3.49.1 system zlib

01 Quick start

Koh is a single statically compiled binary with no runtime dependencies beyond system zlib.

$ curl -fsSL koh.asha.software | sh
  1. 01
    koh init

    Initialize a repository. Creates .koh/ with a SQLite database and default .kohconfig. Pass --config to open the config file immediately.

  2. 02
    koh save "message"

    Snapshot the working tree. Transactional — a failure at any stage leaves no partial state.

  3. 03
    koh log

    Browse history in the interactive pager. Tab cycles filter modes (all → green → red). Piped output disables interaction and color automatically.

  4. 04
    koh promote

    Promote dev HEAD to main. Requires confirmation. Undo with koh lane main && koh undo.

Add .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.

dev ─ e44bc2 ─ d3a901 ─ a3f9c2 ← HEAD
↓ koh promote
main ─ f21a0b ─ d44b81 ← HEAD

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

koh init [--config]

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.

koh save "<message>" [--agent "<model>"] [--session "<name>"] [--json]

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.

koh log [--all] [--since <expr>] [--session "<name>"] [--json]

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.

koh logs

Alias for koh log.

koh status [--json]

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.

koh diff <id1> <id2> [--json]

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.

koh diff --session last

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

koh lane [main|dev]

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.

koh promote [<id>]

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.

koh pluck <id> [--dry-run]

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.

koh diverge

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.

koh undo [<n>]

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.

koh undo --session

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.

koh load <id>

Restore the working tree to any save on the current lane. Validates same-lane membership before any other checks. Requires confirmation.

koh load --checkpoint

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.

koh discard

Throw away all working-tree changes and restore to the current HEAD. Requires confirmation. No history is altered.

koh peek <id>

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.

koh checkpoint "<message>"

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

koh rename <id> "<message>"

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.

koh clean [--keep <n>] [--before YYYY-MM-DD]

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.

koh recover [--verify]

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.

koh verify

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

koh tag <name> [<id>] [--message "..."]

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.

koh untag <name>

Remove a tag.

koh flag <name> [--reason "..."]

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.

koh tags [<id>]

List all tags attached to a save (defaults to HEAD).

koh mark [<id>]

Bookmark a save for quick reference (defaults to HEAD). Bookmarks appear highlighted in the pager's green filter mode.

koh unmark [<id>]

Remove a bookmark.

koh marks

List all bookmarked saves in the interactive pager.

koh changelog [--path <dir>]

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.

koh overview [--path <dir>]

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.

koh health

Analyse save history and report behavioural signals. Six signals, all with configurable thresholds in .kohconfig:

SignalWhat it flags
Frequent FilesFiles appearing in an unusually high proportion of saves — may belong in .kohignore. Threshold: health_frequent_file_pct (default 80%)
Large SavesSaves touching an unusually large number of files. Threshold: health_large_save_files (default 20 files)
Undo PatternsFiles that are frequently reverted — may indicate unstable decisions. Derived from undo history.
Dev Lane AgeDev lane not promoted to main in an unusually long time. Threshold: health_dev_age_days (default 30 days)
Save VelocityUnusually low rate of saves over recent time. Threshold: health_velocity_days
Storage GrowthMonth-over-month storage growth exceeds threshold. Threshold: health_storage_pct (default 200%)

10 Meta

koh config

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.

koh man

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.

koh --version / koh -v

Print the version string.

koh help / koh --help / koh -h

Print the usage summary.

Environment Variables

VariableEffect
KOH_DEBUGPrint the full error type on failure — useful for bug reports
NO_COLORDisable all ANSI color output
KOH_NO_COLORSame as NO_COLOR
EDITOREditor used by koh config and koh init --config
VISUALFallback 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.

CommandJSON fields
koh save --jsonid, lane, message, timestamp, size_bytes, agent, session
koh status --jsonlane, head_id, dirty, modified, added, deleted
koh log --jsonArray of save objects with all fields including marked, agent, session
koh diff --jsonfiles 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)

ModeShows
allEvery line
greenMarked saves and saves older than stable_days
redSaves 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.

koh steal <keeper-url>[@<tag|id>]

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.

koh offer [--local]

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.

koh offers [<id>]

List patches available from the connected Keeper instance in the interactive pager.

koh apply <keeper-url-or-path>

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.

koh bundle

Archive .koh/ as a deterministic tar+gzip bundle with a computed checksum, written to the current directory.

koh unbundle <file>

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.

KeyDefaultDescription
stable_days14Saves older than this (days) show a marker in koh log
promote_suggesttrueShow promote hint in log output when dev HEAD is promote-worthy
colortrueDisable with false, NO_COLOR, or KOH_NO_COLOR
page_size0Lines per pager screen. 0 = auto-detect from terminal row count
health_frequent_file_pct80% of saves a file must appear in to trigger the frequent-files signal
health_large_save_files20Files-changed count to trigger the large-save signal
health_dev_age_days30Days without a promote to trigger the dev-lane-age signal
health_storage_pct200Month-over-month storage growth % to trigger the storage-growth signal

16 Storage Architecture

Disk Layout

.koh/
├── koh.db SQLite database (cache layer, always rebuildable)
├── keeper Optional: single line containing Keeper remote URL
├── AGENTS.md Optional: agent behavior rules, printed as notice on startup
├── snapshots/
│ └── <id>.json Immutable manifest, one per save
├── objects/
│ └── <hh>/
│ └── <rest-of-hash> zlib-compressed file content, keyed by Blake3 hash
└── peek-<id>/ Temporary extraction directory for koh peek

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.

MigrationChange
v1 → v2Create tags table
v2 → v3Add size_bytes column to saves
v3 → v4Create marks table
v4 → v5Remove 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.