Build accelerator + CLI for the repo-kit .NET subrepos. Wraps dotnet,
but the meat is a persistent Roslyn workspace daemon that drives
sub-second warm builds and checks by bypassing MSBuild entirely.
Originally pitched as a "dotnet condom" (thin output formatter). Has since grown teeth: csc-replay, FUTDC, Roslyn daemon, file-grouped diagnostics, hooks. The condom framing is historical — it's now the primary build path.
uv pip install -e ./rk # editable from repo-kit/rk/
# or one-shot:
uvx --from ./rk rk doctorPython ≥ 3.12. Deps: typer, click, rich, tomli-w.
The daemon keeps a warm Roslyn workspace per subrepo (vibekit/,
oomfi/, hoho/), pre-loads csc response files, and serves build /
check requests over a unix socket. Cold first build is normal MSBuild;
every build after is csc-replay.
rk daemon install --now # systemd user unit, auto-start
rk daemon start # foreground
rk daemon status # all units across the system
rk daemon list # installed units
rk daemon stopNumbers seen during development: cold full check ~11 s → warm rk check
~0.2 s on the same workspace.
Socket discovery is per-workspace (one daemon per subrepo); rk daemon status walks all installed units.
rk build [project] [--profile] [--force]
rk check [project] [--profile]
rk run <shortcut|path>
rk watch <project>
rk fmt [project|all]
rk publish <project> <rid> # AOT single-file
rk clean
rk sync # dotnet restore across all subreposprojectdefaults to the current subrepo (detected from cwd) orall.rk buildskips MSBuild entirely for unchanged projects via FUTDC (file-up-to-date check) and replays the cachedcsc.dll @file.rspfor source-only changes.--forcebypasses FUTDC for a full MSBuild build.--profileshows per-project timings (build) or per-project Roslyn analysis times (check).rk checkis a deep diagnostic pass via the warm Roslyn workspace — finds cascading errors that incremental MSBuild misses, with file-grouped output.
Shortcut targets for rk run: vibekit.hello, oomfi, hoho, plus any
explicit path/to/proj.csproj.
rk hooks install # PreToolUse hook redirecting `dotnet …` → rk
rk hooks uninstallInstalls to global ~/.claude/ (not per-workspace). Catches Claude Code
agents reaching for dotnet build and routes through the daemon
instead.
rk doctor # SDK + tools (uv, fd, rg) sanity checkDefaults: -nologo, quiet runs, file-grouped diagnostics with full
file:line:col clickable paths. RK_VERBOSE=1 for raw passthrough,
RK_CI=1 for plain text + structured logs.
src/rk/
├── cli.py typer entry; commands above
├── daemon.py per-workspace systemd units, socket relay, Roslyn host lifecycle
├── csc_replay.py capture csc invocation, replay with /shared
├── futdc.py file-up-to-date check (incl. dep ref-assemblies)
├── diag_group.py Roslyn diagnostic file-grouping output
├── dotnet.py dotnet wrappers (build/run/watch/fmt/publish/clean/restore)
├── runner.py run target dispatch
├── doctor.py env sanity
├── detect.py cwd → subrepo, project graph discovery
├── hooks.py Claude Code PreToolUse hook install/uninstall
├── header.py output banner
├── output.py rich console wrappers
├── paths.py well-known paths
└── clip.py clipboard capture for run output
Per DECISIONS.md (2026-04-21): keep every feature trivially portable.
No plugin system, env vars over config files, simple walk + regex over
frameworks. The Python rk is a ceremonial dagger until a single
cargo install rk binary takes over.
Constraint that already bent: the Roslyn daemon is unique-per-subrepo long-running state. The Rust rewrite needs to either keep using the .NET csc.dll for replay (cheap) or talk to a daemon written in C# directly. The CLI side stays trivially portable.