Repository navigation
v0.13.0
Added
-
A subagent can work in a git worktree of its own. Give the
subagenttoolisolation: "worktree", or putisolation: worktreein
an agent definition's frontmatter as in Claude Code's agent files (the
call's value wins), and the subagent runs in a new worktree of your
repository, on a branchkrowk/<8 hex>, under
~/.local/share/krowk/worktrees(or$XDG_DATA_HOME/krowk/worktrees).
Subagents started together no longer overwrite each other's files. The
worktree starts from your files as they are, uncommitted changes
included: modified, new and deleted files, and what you staged (ignored
files only if you force-added them, nested repositories never), are put
in one commit,krowk: working state for <hex>, on top of yourHEAD, and
the branch starts there; with nothing uncommitted it starts atHEAD.
Your index,HEADand files are not touched. When
it finishes having changed nothing, the worktree and its branch are
removed. When it changed something, its changes are applied to your
working tree (see below). Outside a git repository the call fails
withisolation: worktree needs a git repository. Without the field, or
with"none", a subagent runs in your directory as before. Your
repository's git hooks do not run when the worktree is made. -
A subagent's work in its worktree comes back to your working tree.
When a subagent withisolation: "worktree"finishes having changed
something, krowk applies its commits and uncommitted changes, new,
deleted and binary files included, to the working tree the parent
agent works in, as uncommitted changes: your index andHEADare not
touched. The worktree and its branch are then removed, its final state
kept asrefs/krowk/snapshots/<hex>for 30 days, and the summary the
parent gets ends withChanges applied to your working tree: <files>.
Subagents finishing at once apply one after another. When a file
doesn't apply cleanly (you, or a sibling, changed the same lines),
nothing of it is applied, the worktree and branch are kept, and the
summary ends withChanges not applied (conflicts in <files>). Worktree: <path>, branch krowk/<hex>. A subagent that changed a
submodule is kept the same way, as krowk doesn't apply submodule
changes. So is one that changed files inside a.git,.claude,
.codexor.krowkdirectory, which the file tools only change with
your say: its summary ends withChanges not applied (they touch protected files: <files>), the worktree and branch, and the
krowk worktrees apply <hex>you run to apply them.
krowk worktrees apply <hex|path> [--to <dir>]does the same for any
kept worktree, protected files included, into your repository's main
checkout by default: it is how you bring a--worktreesession's work
home. It refuses (exit 4) while that session is still running, or when
a file conflicts, and refuses (exit 1) a--tothat is a checkout of
another repository. -
A subagent's worktree has your submodules checked out. Before the
subagent starts, krowk initialises every submodule of its worktree,
submodules inside submodules too, at the commits the worktree records.
One you have initialised in your main checkout is copied from there,
with no download; itsoriginstays its own URL. One you haven't is
cloned from its URL, with no terminal to prompt on and a 2-minute limit.
A submodule that can't be initialised stays empty, and the subagent's
first prompt says which and what git said. URLs from.gitmodulesthat
would run a command (ext::) or copy a repository from a local path are
refused. Your repository's config is not changed, so a submodule you
deinitialised stays that way in your checkout. The agent can edit files
in a submodule but not commit in it. An edit or a new file in any
submodule, at any depth, keeps the worktree; an unchanged one is removed
with its submodules' git data. -
A subagent's worktree starts with your build output. On a file
system that can clone files without copying their blocks (btrfs, XFS
with reflink, APFS on macOS), krowk clones your main checkout'starget
andnode_modulesinto a new worktree before the subagent starts, so its
firstcargo buildfinds nothing to compile and the clone takes almost
no disk.targetis skipped while a build is running in it, and
node_modulesunless the worktree's lockfile (package-lock.json,
pnpm-lock.yaml,yarn.lock,bun.lockorbun.lockb) is the same as
your checkout's. Anywhere else, and on Windows, nothing is copied. Choose
the directories withworktrees.seedinconfig.json, for example
{"worktrees": {"seed": ["target", ".venv"]}};[]turns it off.
What was skipped and why is inseed.log, beside the repository's
worktrees. -
Worktrees appear almost instantly on btrfs. When krowk's worktrees
directory is on btrfs, on the same file system as your repository, and
thebtrfsprogram is installed, a new worktree for a subagent or a
krowk --worktreesession is a snapshot of a copy of your source tree
that krowk keeps up to date, with your build output already in it:
about a tenth of a second whatever the repository's size, and its first
cargo buildcompiles only what differs from your last build. Twenty
agents starting at once in one repository each get theirs in under half
a second, rather than waiting on each other. Anywhere else, or if a
snapshot fails, the worktree is checked out as before. -
A subagent's worktree gets the ignored files you list in
.worktreeinclude. As in Claude Code, put a.worktreeincludeat the
top of your main checkout, in.gitignoresyntax (.env*,
config/local.yml), and every file it matches that git ignores is
copied into a new worktree before the subagent starts, with its mode.
They are copies, never links, so the agent can't change your real
.env; on a file system that clones files they cost no disk. Tracked
files are never copied over the worktree's, nothing is read through a
symlink or written over a file the worktree has, and the
worktrees.seeddirectories are left to seeding. A file that can't be
copied is skipped and noted inseed.log. Without a
.worktreeinclude, nothing is copied. -
A subagent's worktree can run your project's setup command first.
Setworktrees.setupto a shell command (npm ci, a code generator,
a database script) and krowk runs it in each new worktree before the
subagent starts, so the agent doesn't burn turns finding out the
project isn't installed. Put it in~/.krowk/config.jsonfor yourself,
or in a repository's.krowk/config.json, where it runs only once you
have trusted the repository (the same question that turns its hooks
on); a trusted repository's command is the one that runs. It runs in
the sandbox: network on, the worktree writable, your main checkout and
your home not, so a cloned repository's install can't touch anything
else. On a machine with no sandbox it runs only for a trusted
repository. It getsKROWK_PROJECT_ROOT(your main checkout),
KROWK_WORKTREE_PATHandKROWK_PORT_BASE: each live worktree holds
its own ten ports from 20000 up (20000, 20010, …), and the subagent's
own commands seeKROWK_PORT_BASEtoo, so dev servers in two worktrees
don't collide. A build or install command waits for a build slot as the
agent's would. Its output is inkrowk-setup.login the worktree's git
directory. When it fails, or runs pastworktrees.setupTimeout(600
seconds by default), the subagent starts anyway and its first prompt
says so, with the exit code or "timed out" and the last 50 lines of
output. -
krowk worktreeslists, removes and prunes the worktrees krowk
kept.krowk worktreesshows every one, across repositories: its
path, repository, branch, base commit, commits ahead of the base,
whether it has uncommitted changes, the session that made it, its age,
and whether that session is still running (--jsonfor scripts).
krowk worktrees remove <hex|path>removes one, and keeps its branch.
It refuses while a running session uses it, and refuses one with
uncommitted changes or commits ahead of its base unless you add
--force. A forced removal first saves the uncommitted changes, new
files included, asrefs/krowk/snapshots/<hex>, and aHEADits
branch doesn't hold (the agent detached it, or switched branch, and
committed) asrefs/krowk/snapshots/<hex>-head, and prints the commands
that bring the worktree back. Ignored files, such as build output and
the copies.worktreeincludemade, don't count as changes and are
deleted with it.krowk worktrees pruneclears git's record of krowk's
worktrees whose directory you deleted (only krowk's: your own
worktrees' records are left alone, even when their drive isn't
mounted), deletes worktree directories git no longer knows when their
files match their branch (one with changes is left in place and
named), and deletes snapshots older than 30 days. A repository that has
moved, or whose drive isn't mounted, is skipped and named. Prune also
runs by itself in the background, at most once a day, when a session
starts. Worktrees made by an earlier krowk are
listed too, with their base shown as unknown, so removing one of them
needs--force. -
krowk --worktreeandkrowk -p --worktreestart a session in a git
worktree of its own. Sessions you start in separate terminals no
longer share one checkout. krowk makes the worktree from the repository
you are in, your uncommitted changes included, on a branch
krowk/<8 hex>, readies it as it does a subagent's (submodules, build
output,.worktreeinclude,worktrees.setup), and starts the session
there; the TUI's header shows the branch, and the agent's commands get
KROWK_PORT_BASE. When the session ends having changed nothing, the
worktree and its branch are removed. When it changed something, both
are kept and krowk saysWorktree kept: <path> (branch krowk/<hex>)on
stderr, with thekrowk worktrees apply <hex>that brings its changes
into your checkout;-p --output-format jsonorstream-jsonnames it in the
result instead, asworktree: {path, branch, commits, uncommittedChanges}. Resuming such a session runs it in its worktree
again; if that directory is gone, krowk says so (exit 2) and points to
krowk worktrees. Outside a git repository--worktreeis refused
(exit 1). It can't be combined with--resume, or with-p --daemon:
the session runs in krowk's own process, which holds the worktree and
finishes it on the way out, and the TUI does the same rather than using
the host daemon. A worktree is trusted as the repository it was made
from.krowk sessions --worktree <path>filters the listing as before. -
An agent can commit inside a worktree krowk made for it. When a
sandboxed session runs in a worktree under
~/.local/share/krowk/worktrees(or$XDG_DATA_HOME/krowk/worktrees) on
akrowk/…branch,git addandgit commitnow work there, and the
commit shows up in your main checkout under your own name and email:
krowk readsuser.nameanduser.emailfor that worktree and passes
them in, without exposing your.gitconfig. The agent can move only
its ownkrowk/…branches. Your other branches, tags, replace refs and
packed-refsstay read-only, and so do the repository's config, hooks
andHEAD, and your other worktrees. Sogit tagfails in there. A
symlink the agent leaves among those branches, their reflogs or the
worktree's git files is removed after the command, and the command
fails and says so.
Every other repository, your own linked worktrees included, keeps
.gitread-only as before. -
Builds and test runs take turns across every krowk on the machine.
Agents spend most of their time waiting on the model, so one machine
can run many of them, until several startcargo testor
npm installat once and it runs out of memory. Now, when the agent's
bash command runs a build or test program (cargo,make,npm,
pnpm,yarn,go,pytest,gradle,mvn,dotnet,swiftand
the like, wherever the command line itself runs one, but not inside a
script it calls or abash -c "…"), it first waits for one of a fixed
number of build slots that every krowk session and subagent on the
machine shares, and holds it until the command ends. Other commands run
straight away. While it waits, the command shows
waiting for a build slot (N in use), and its result says how long it
waited when that was over a second. Interrupting the turn stops the
wait. A krowk that is killed or crashes frees its slot at once. A build
the command starts in the background (cargo build &) is not held to a
slot once the command returns. There are a quarter as many slots as
cores by default (at least one); set"builds": {"slots": 2}in
~/.krowk/config.jsonto change that. A build also gets
CARGO_BUILD_JOBSset to the cores divided by the slots, so even a
build running alone gets only that share; setCARGO_BUILD_JOBS
yourself, or raisebuilds.slots, to change it. Your own value is
passed on in the sandbox too. This applies to krowk's own agent;
Claude Code and Codex run their own shells. -
Agents take turns across every krowk on the machine, and worktrees
wait for disk.subagents.maxParallellimits one turn, so ten
sessions each starting four subagents started forty at once. Now every
subagent, and every--worktreesession, holds one of a fixed number of
agent slots that every krowk on the machine shares, for as long as it
runs. When all are held, a subagent shows
waiting for an agent slot (N in use)and starts when one frees up;
interrupting the turn stops the wait. A--worktreesession prints the
same line before it starts, and Ctrl-C stops it. A krowk that is killed
or crashes frees its slot at once. Sessions without--worktreenever
wait, and a--worktreesession's subagents run on its slot rather
than taking more, so a session holding the last slot never waits on
its own subagents. The default is twice your cores or twice your memory in GiB,
whichever is fewer, between 4 and 64; set
"subagents": {"maxHost": 8}in~/.krowk/config.jsonto change it (0 is
refused). Before making a worktree, krowk also checks that its disk will
have 4 GiB free beyond twice the size the worktree is expected to be
(the last one krowk made of that repository, else your tracked files).
When it would not, nothing is made, and the session or subagent fails
withnot enough disk for a worktree: <free> free, <needed> needed,
pointing atkrowk worktrees pruneandkrowk worktrees remove. -
The README covers worktrees, and
scripts/bench-worktreesmeasures
them. The README's new Worktrees section sums upisolation, applying
back,--worktree,.worktreeinclude,worktrees.setup,builds.slots,
subagents.maxHostandkrowk worktrees. In a checkout of krowk,
scripts/bench-worktreesmakes 20 worktrees of a scratch clone at once,
runscargo checkin all of them behind the build slots, applies three
subagents' changes back (one a conflict), removes everything and prints
creation p50/p95, check time, peak memory, slot wait and leftovers. It
calls no model. -
A host says why a viewer stays on the relay. When a viewer offered
the direct path never reaches it — a firewall on the host, or a tailnet
access policy that doesn't allow the port, drops the connection before
krowk sees it —krowk sync hostnow says so once per device, 30 seconds
after the viewer joined:
krowk: session …: device 0123abcd stays on the relay — nothing it sent reached this machine's direct addresses (100.64.0.1:51915, …); if it is on this tailnet, a firewall here or the tailnet's access policy may be refusing the port. A viewer that reached the direct path once and fell
back is never named. The session goes on by the relay, as before. -
A bare krowk link no longer lands in a pull request or an issue. On
GitHub a barekrowk.com/a/…link doesn't unfurl. It shows up as a blue
link that says nothing about the file. When an agent running in krowk
callsgh pr create|edit|commentorgh issue create|edit|commentwith
one in the body, krowk refuses that one call and tells the agent to paste
the krowk block instead, and where to get it (paste.markdownfrom
krowk uploads show <link> --json). The agent rewrites the body and posts
again without you stepping in. The body is checked whether it is given
inline, through a heredoc, or with--body-filewhen the file can be
read. A card link inside a krowk block is fine, and nothing else is
touched: otherghcommands, bodies without a card link, and posts to
Slack or Basecamp, where a bare link unfurls. This isn't a permission
rule, so it holds in every mode,bypassPermissionsandunhinged
included, on krowk's own models, Claude Code and Codex alike. On Codex,
krowk passes the check as a hook, and the first time, has Codex record it
as trusted in itsconfig.toml(ahooks.stateentry for that one
command), because Codex runs only trusted hooks. krowk's Codex accounts
share your~/.codex/config.toml, so that is where the entry lands. On
Windows, Codex sessions don't have the check yet. A code span or fenced
block that quotes a card link is left alone. -
The agent can ask you which way to go. When a choice is yours, the
agent asks up to four questions with options to pick from, and you answer
over the prompt: a number or Enter picks, Space toggles where several may
be picked, Tab moves between questions, and typing gives your own answer
instead. Esc declines and the agent carries on with its best judgment.
The native agent gets anask_usertool for this. Claude Code's
AskUserQuestionand Codex's questions, which krowk used to refuse, now
reach you the same way, in plan mode too. Questions are only asked where
someone can answer:krowk -pdoesn't offer the tool, and a backend that
asks anyway is told to decide and say what it assumed. For protocol
clients, anapproval.requestedframe can carryquestions, and
approveanswers them withanswers. -
Paste a screenshot into the prompt. Ctrl-V (or Alt-V, where the
terminal keeps Ctrl-V for itself) puts the clipboard's image in the prompt
as[Image #1], and the model sees it with the text. Dragging an image
file onto the terminal, or pasting its path, does the same, several at
once included. A file is read the moment it's dropped, so a macOS
screenshot dragged from its thumbnail still works.[Image #N]is one
unit to the caret: Backspace takes it whole. Typing goes on while a large
screenshot is read, and the image lands where you pasted it. PNG, JPEG,
GIF and WebP are taken, scaled down to 2,000 pixels a side and under the
providers' size limits as they're pasted, and kept beside the session's
log in~/.krowk/sessions/<id>/images/. Every provider and backend gets them
(Anthropic, OpenAI, xAI and other Chat Completions servers, Claude Code
and Codex), and a model the catalog says can't read images is refused
with the prompt handed back. The clipboard is read withwl-pasteor
xclipon Linux (krowk names the one to install if neither is there),
osascripton macOS, and PowerShell on Windows and under WSL. Ctrl-V with
only text on the clipboard pastes the text. -
On Linux,
krowk -pnow makes edits without asking, inside a sandbox.
A-prun with no--permission-modeand nodefaultModeused to refuse
every edit; where bubblewrap works it now runs inacceptEdits, with its
commands inside theworkspacesandbox below, and inside a container in
acceptEditswith the file tools held to the same fences. Pass
--permission-mode defaultto keep the old behaviour.--sandbox workspace|read-only|strict|offputs thebashtool inside bubblewrap.
The sandbox allows rather than lists what it hides: your home is replaced
by an empty one, and only the workspace and the Rust toolchain's homes
come back, so nothing else in it —~/.ssh,.git-credentials,.netrc,
agents' logins,~/Documents— is in reach, and programs installed under
the home (mise's shims,~/.local/bin) are not on the sandbox'sPATH.workspacewrites
the working directory and its added directories and keeps the network;
read-onlywrites nothing and has no network, not even DNS;strict
writes the workspace and has no network. Every.gitin
the workspace, nested repositories and gitdir files included, and every
hooks directory a repository'score.hooksPathnames there — found by
searching the workspace once a turn and re-checking only what changed —
stays
read-only, as do.claude,.codexand.krowk; a.gita command
creates is removed after the call. The file tools hold the same lines
under every permission mode, and open exactly the path they checked, so a
symlink swapped in meanwhile fails the call. A sandboxed command gets
onlyPATH,TERM, the locale,USER, a privateHOMEandTMPDIR—
no provider key, token or agent socket — no inherited file descriptor, and
a session of its own.RUSTUP_HOMEandCARGO_HOMEare bound read-only
with cargo'scredentials.tomlhidden, socargo buildof what is
already fetched works and fetching a new dependency does not.PreToolUse
hooks are your own and run outside the sandbox. A sandbox that cannot be
enforced refuses the run with a fix instead of running unsandboxed:
bubblewrap missing or blocked, a workspace too large to search for
repositories, a Claude Code or Codex backend,--daemon, macOS (Seatbelt
is not built yet) and Windows. -
krowk's own agent uses your MCP servers. It reads the
mcpServers
you set up for Claude Code (~/.claude.json, itssettings.json), those
in~/.krowk/config.json, and a repository's.mcp.jsononce you trust
the repository, since a.mcp.jsonnames programs to run. Stdio and
streamable-HTTP servers work. The model gets two tools,mcp_searchand
mcp_call, instead of every server's tools, so fifty MCP tools cost each
turn about 140 tokens. No server starts until the model searches.
Mcp(server:tool)permission rules allow, ask about or deny each call,
and search leaves out tools a deny rule covers. A repository's servers
are asked about again when its.mcp.jsonchanges after you trusted it,
and no server inherits your provider keys unless its config sets them. -
Idle sessions move off the machine as weekly vintages.
krowk sessions archivetakes every native session idle for more than 14 days
(--older-than DAYS, orKROWK_ARCHIVE_AFTER_DAYS) and stores each ISO
week's sessions in the registry as one vintage: zstd-compressed JSONL,
sealed under the account key, so the registry only ever holds ciphertext.
An existing vintage for the week is merged, never overwritten.--weekly
runs only when a week has passed since the last run, so it can be put on
a schedule. An archived session keeps its title, summary, directory,
dates, models and cost on the machine, sokrowk sessionsstill lists
it, andkrowk sessions showorkrowk -p --resumefetches its week and
restores it into krowk.db first.krowk sessions restore <id>does that
on its own.krowk sessions pin <id>keeps a session from ever being
archived, andunpinundoes it. It needskrowk syncset up on the
machine. -
The Claude Code plugin brings the MCP server. Installing
krowk@krowk
in Claude Code now adds the krowk MCP server (npx -y @krowk/mcp) beside
the skill, so publishing works without a separateclaude mcp add; if you
addedkrowk-mcpthat way, runclaude mcp remove krowkin the project
where you added it, or the model sees every krowk tool twice there. When
krowk drives Claude Code as a backend the plugin's server is not started,
and krowk's own MCP server is the only one. The plugin's version now
moves with every release: 0.12.0 and 0.12.1 shipped without moving it, so
Claude Code offered no update.
Changed
- krowk's own git calls now run with repository hooks, fsmonitor and
commit signing turned off. The git krowk runs itself — the branch on
the status line, the commit a synced session is at, the files a search
lists — looks for hooks in an empty directory in krowk's home
(~/.krowk/no-hooks), runs nocore.fsmonitorcommand and signs
nothing. Filters such as git-lfs still run, as a checkout needs them.
Git the agent runs through its bash tool is unchanged. - Uploads are now
krowk artifacts, matching the API and the JSON.
krowk artifacts create | list | show | attach | delete | claimis the
full set, named as/v1/artifactsanddata.artifactsalready were.
krowk pushis the short form ofartifacts create, andkrowk claim
ofartifacts claim; both stay and work as before.krowk uploads …
still runs every command it did, andkrowk help uploadsshows the
artifacts help, but help no longer lists it. Breadcrumbs and error fixes
now namekrowk artifacts …, so an agent that copies them gets the new
spelling. - krowk's messages read like sentences, and failures say what to try.
An error leads with what went wrong in bold, puts the command that fixes
it on aTry:line of its own, and ends with its code. It no longer drops
the alternative a fix offers ("…, or upload anonymously"). A mistyped
command, subcommand, flag or help topic gets a "did you mean". Flag errors
are plain English (unknown flag --nope,--limit takes a whole number)
rather than Go'sflag provided but not defined: -nope, and point at the
command's own help. Barekrowk runsnames the subcommands it takes
instead of callingrunsunknown. Successes, warnings (a yellow!),
labels and empty lists are capitalised, andkrowk doctorshows a
checklist instead of raw JSON. Colour followsNO_COLORandTERM=dumb,
and an error is coloured only when stderr is a terminal. The JSON
envelopes are unchanged except for thefixwording. krowk --helpis the readable page even when piped. An agent running
krowk --helpused to get the whole command catalog as about 50 KB of JSON.
It now gets the overview, which has an Examples section and points to the
newkrowk help agentspage. That page explains the JSON envelope,
breadcrumbs, exit codes and which command answers which task.
krowk help --json, or--jq, still prints the catalog. Help headings
are in title case and bold on a terminal.krowk hostslists your machines that host, with no Tailscale tag.
It used to list only tailnet machines taggedtag:krowk-host, which took
an edit to the tailnet policy in the Tailscale admin console. Now it lists
every machine on your device list that hosts a synced session — from the
registry's sessions, so it needs this machine's sync keys — with whether
it hosts one now and whether this machine's Tailscale reaches it
directly. A host names its own tailnet node, sealed, in each session it
hosts, so a session hosted by an older krowk shows its host as reachable
only once a newer one hosts it again. Machines that aren't yours, and
yours that host nothing, aren't listed.tag:krowk-hostis no longer
read. Inkrowk hosts --json,nameis now the machine's name on your
device list rather than Tailscale's (dnsNameis still Tailscale's), and
rows also carrydevice,os,thisMachine,hosting,directand
sessions;onlinestill means online on the tailnet.- The same-user check refuses to run on a tagged machine. Tailscale
names every tagged machine of a tailnet as one shared user,
tagged-devices, so on a tagged hostKROWK_TAILSCALE_SAME_USER=1would
have let in any tagged machine and turned away your own. It now says so
and offers no direct path; untag the machine, or leave the check off. - A synced session goes direct over Tailscale with no setup. With
Tailscale running on both machines,krowk sync hostoffers the direct
path by itself: it fetches the registry's relay ticket public keys
(GET /v1/relay/ticket_keys) at every hosting, so a viewer's session
moves off the relay with noKROWK_RELAY_TICKET_KEYSfile to write, and
a key the registry rotates in reaches the next session without a
reinstall. It keeps them in~/.krowk/cache/for a day, used only when
the registry can't be reached. Admission is unchanged: only the
registry's ticket and the device's signed challenge get in, and being on
the tailnet admits nobody. When there is no direct path, the host says
why in one line on stderr, such askrowk: no direct path (Tailscale isn't running: it is Stopped); the session goes by the relay, and the
session goes by the relay.KROWK_RELAY_TICKET_KEYSstill names the keys
by hand, and wins over the fetched ones, for a stand-in registry. - The Gemini extension, the MCP registry entry and
@krowk/mcpdescribe
Krowk as a coding agent harness. They used to describe a permalink
uploader. Each now says that it's Krowk's publishing, for Gemini CLI or
for any MCP client, and what it turns into links. With
the Claude Code plugin's, they no longer say links unfurl in GitHub and
Linear, which don't unfurl links. Those links unfurl in Slack and Basecamp;
in GitHub and Linear, images show inline. Theserver.jsonhere was over the
MCP registry's 100-character description limit and didn't validate against
its schema; it's within it now.make release-checkand the release
workflow now hold it to that limit, and the other descriptions to one line. - What you say in a session is shown in markdown, as answers are.
`code`is in the code colour,**bold**is bold, and links, lists,
quotes and fenced blocks look as they do in an answer, all on the band of
your prompt. Ctrl-Y still copies the prompt as you typed it. - Reading your synced sessions is signed by this machine's device key.
krowk sync sessions, attaching,--resumeand the host now sign the
calls that list a session, show it and list its chunks, as they already
signed every write. Once the registry requires it, a copy of your API key
without the device's key reads none of your sessions, not even their
sizes and times. Two reads signed in the same millisecond on one machine
are told apart by signing the second again. - The new-release notice speaks up only when the release is worth it.
A major release (before 1.0, a new middle number, like 0.12 → 0.13) is
mentioned once and once more a week later, a minor release once, and a
patch release never — unless a release you skipped has a security fix,
which is mentioned once a day until you upgrade. In the agent it's one dim
Update:row under the session header, never a banner, and Ctrl-O's
session details always say whether a newer release is out. The agent
never waits on the check: it uses the last answer and asks for the next
one in the background. After other commands the line on stderr follows
the same rules.KROWK_NO_UPDATE_CHECK=1still turns it all off.
Fixed
-
A Mac with the Tailscale app can host over the direct path. The app
has notailscaledsocket: its LocalAPI is on 127.0.0.1 behind a token.
On macOS,krowk sync hostandkrowk hostsnow try the open-source
tailscaled's sockets, then the App Store app'ssameuserprooffile in
its group container, then the standalone app's in/Library/Tailscale.
KROWK_TAILSCALE_LOCALAPInames the app's LocalAPI by hand
(http://127.0.0.1:<port>withKROWK_TAILSCALE_LOCALAPI_TOKEN, or the
sameuserprooffile's path); only loopback is taken.
KROWK_TAILSCALE_SOCKETstill names a socket, and wins over it. -
A synced host no longer hangs on a relay link that stopped working
without saying so. If the relay kept answering heartbeats but acked none
of what the host sent, viewers stopped receiving updates while the host
kept writing into the void. The host now drops such a link once eight
batches have gone unacked for ten seconds, joins the relay again, and
resends what was missed. -
krowk devices addno longer says "nothing was added" when the new
device may have your key. If the answer to the step that sends the key
was lost and checking on it failed too, the pairing used to end as if
nothing had happened, although the new device might hold your key
without being on your list. Now the laptop goes on: it waits for the new
device to confirm and adds it, or, if it never confirms, still lists it so
krowk devices removecan take it off. -
krowk sync hostnotices a device removed while it runs. It used to
check your device list only when it started, so a long-running host went
on as before after you removed a device. It now reads the list again every
minute. If another device was removed, it hosts the session again from
the top, under the new list; if this device was removed, or the list was
started over, it stops. -
The installer says when another krowk would run instead. If a krowk
earlier on yourPATH(an older install, or a build from source) would
answer before the one just installed, the installer now names it, says
its version, and how to fix it. Before,krowk --versionquietly showed the
old one. -
Pairing refuses a malformed key exchange outright. If the other side
ofkrowk devices add/krowk sync joinsends a degenerate SPAKE2 point
(one of small order, or one spelled non-canonically), the pairing ends at
once instead of carrying on. An honest device never sends one; this only
closes the door on a misbehaving peer. -
Running
krowk sync init --start-overagain says what it did. While
the old keys from a start-over are kept, running it again carries on that
start-over (sealing any sessions left under the new list) and never begins
a new one. It now says so, and that a new start-over needs
krowk sync recovery discard-oldfirst. -
A new device can't take a name that only looks like one already on your
list. Adding or pairing a device is refused when its name reads like
another device's: a Cyrillicаfor a Latina, full-width letters, or
1orIforl. Each name in a removal or recovery review now names
exactly one device. -
A synced session moves to a new key when you host it again after
removing a device. A session published before you removed a device used
to go on under the session key that device held, however often it was
hosted again. Now the host first moves it to a new key, sealed under your
current user key, which the removed device never had; a start-over does
the same for the sessions it brings along. Everything written before
still reads on all your devices. A host already running when you remove
the device keeps its key until the session is next hosted. A session that
was never moved is stored exactly as before and still opens on older
krowk versions; one that was needs this version, and a viewer that
opened it before the move has to open it again. -
Copying from the TUI no longer drags the layout along. Text in the
transcript starts at the first column: a mouse selection of your prompt,
an answer, a list or a code block comes without the two-column margin,
the▎bar before your prompts, the padding inside code blocks or the
spaces that filled a band to the edge. Code blocks are left for the
terminal to wrap, so a long line of code copies as one line, the block's
language sits on the row above the code, and tabs in code are four
spaces rather than one. Prose still wraps between words, so a selection
across a wrapped paragraph has a line break where each row ended:
Ctrl-Y now offers the last answer, each code block in it and your last
prompt, and copies the one you choose exactly as written, tabs included.
In Ghostty, and in herdr, which is built on it, text from the first
screen of a session used to copy with a break at every row the terminal
had wrapped, because the prompt was kept at the bottom by moving the
rows above it; the prompt now sits at the bottom from the start, the
conversation filling the screen down to it, and nothing printed is ever
moved. -
Two quick Ctrl-Cs right after a prompt still print the resume line.
Leaving that fast could beat the session's start to the TUI, so krowk
exited 130 withoutkrowk --resume <id>although the host had already
started the session. The TUI now reads what the host already sent before it leaves. -
The prompt stays on the bottom row when the window grows taller, in
Ghostty and other terminals that add the new rows at the bottom. -
What you said survives a narrower window. Each row is banded only as
wide as its text, so the band no longer wraps onto rows of its own; a row
wider than the new window still wraps, as any text does.
Install
# npm — the bare `krowk` name is blocked by npm's typosquat filter
npx @krowk/cli push screenshot.png
# The installer: the full build on a workstation, the lean one in CI
# and containers (or with --lean)
curl -fsSL https://krowk.com/install | bash
# Direct download — no runtime, no package manager. krowk_… is the
# full build (the agent, its TUI, sessions); krowk-lean_… the lean
# agent-container build.
curl -sSfL https://github.com/krowkcom/krowk/releases/download/v0.13.0/krowk_0.13.0_$(uname -s | tr A-Z a-z)_$(uname -m | sed 's/x86_64/amd64/;s/aarch64/arm64/').tar.gz \
| tar -xz -C /usr/local/bin krowk krowk-mcp
# From source
cargo install --locked --git https://github.com/krowkcom/krowk --tag v0.13.0 --features harness krowkchecksums.txt covers every archive on this release, and
checksums.txt.sigstore.json is its signature. To verify a
download (with cosign):
cosign verify-blob checksums.txt --bundle checksums.txt.sigstore.json \
--certificate-identity https://github.com/krowkcom/krowk/.github/workflows/release.yml@refs/tags/v0.13.0 \
--certificate-oidc-issuer https://token.actions.githubusercontent.com
sha256sum --check --ignore-missing checksums.txtThe .cdx.json files are CycloneDX SBOMs of the full and lean
builds, each signed the same way. SECURITY.md in the repository says
how to rebuild a release and compare the bytes; this release's
reproduce job already did it once for Linux x86-64.