~ knotless Search/
↑↓ move↵ open⇧↵ all results

johnandersen777.bsky.social/knotless

git that lives in your Atmosphere account

Forked from chadtmiller.com/knotless

0
0
675 lines · 61.6 KB Raw History

Changelog

What changed in each version of knotless for those who use it, from the change files in .changeset/ (see knope.toml).

0.12.0 (2026-11-08)

Features

A repository's crates can be published and used with Cargo

knotless-serve answers Cargo's registry protocol for each repository, so cargo publish, cargo yank and cargo search work with index = "sparse+https://knotless.dev/cargo/<did>/<repo key>/", and the token is <handle>:<app password>.

Pulls, issues, diffs and files are MCP resources

knotless-mcp and /mcp offer resource templates, so a client that reads MCP resources opens a pull, an issue, a pull's diff or a file by a URI like its page's, as knotless://alice.test/proj/pull/<did>/<rkey>/diff, answered as the matching tool answers.

Merge a pull by itself once its checks pass

knotless pull merge <url> <pull> --auto sets a pull to merge by itself once its checks pass, on a branch with no merge queue. The runner that lands the repository's merge queues merges it then. A push by anyone who may not merge it, or retargeting it, cancels it, and --auto --undo cancels it, as the pull's author may too. pull show says where it stands. The MCP tool is pull_merge with auto, and undo. Logging in again gives an existing OAuth login the permission to do it.

The pages set a pull to merge when its checks pass

A pull's merge box offers Merge when checks pass while its checks run or fail, says who set it, and cancels it.

The merge queue is off until a branch's rule turns it on

knotless pull merge --queue and the pull_merge tool's queue now refuse a pull unless the branch it lands on is protected with --queue. Turn it on with knotless branch protect <url> <branch> --queue, and every pull into that branch then lands through the queue. A pull already queued keeps its place.

Withdraw a bad release with knotless release yank

knotless release yank <url> <package> <version> withdraws a version without breaking anyone who pinned it: its files can still be read by version, but it is no longer the latest, and the releases page marks it Yanked. --undo takes that back. The MCP tool is release_yank. Logging in again gives an existing OAuth login the permission to do it.

A review agent's rounds count only its requests for changes

knotless-agent review --rounds now limits how many times it asks for changes on a pull, not how many times it reviews it. A pull it approved is reviewed again at each later push, and the command is told the commit it approved (approved), and how many times it has asked for changes (changes). A request for changes is no longer carried to a head that only merges the base in when the pull's title or body was edited after it.

An agent watching an owner finds their private repositories shared with it

knotless repo share leaves the account shared with an invite at the appview, and knotless-agent --owner <handle> watches, besides that account's public repositories, every private one it shared with the agent's account, so a reviewer need not be told each private repository by name.

Fixes

Commands reading one repository at once no longer fail on its history

Two commands fetching the same repository at once could fail with git commit-graph: Could not read, when one found a commit whose parents the other had not fetched yet. Fetches now take turns.

A connector whose login has ended asks you to sign in again

When your PDS ended the login a knotless server keeps for your connector, its tools carried on as no one: private repositories read as not there, and writes said to run knotless login. The server now answers so that your assistant has you sign in again.

A git killed part way no longer leaves a repository unreadable

A server or command stopped while it set a mirror's refs could leave git's lock file behind, and every later read of that repository then failed with cannot lock ref. The lock is now cleared the next time the refs move.

An OAuth login is kept when refreshing it fails

When an OAuth login's refresh failed for any reason, knotless logged in with KNOTLESS_PASSWORD instead and saved that over the OAuth login, which can't read private repositories. Now the OAuth login is kept and tried again, and the failure is logged. The password stands in only once the server turns the refresh down for good.

A merge queue's next batch goes first once the batch before it lands

A batch built ahead of one still being checked waits for a job behind pulls' runs. Once the batch under it lands, a runner now ranks what is left of it with pushes to the default branch, so it lands next instead of waiting behind every pre-flight queued meanwhile.

A merge queue batches a pull as soon as its pre-flights pass

A runner landing a merge queue now looks again the moment a pull's run ends, on it or on another runner the repository names. A pull whose last pre-flight passed used to wait for the runner's next sweep, up to 10 minutes, before going into a batch.

A release that fails to publish says why

Publishing a version now says it is published already only when it is. Any other failure, such as a login that was refused, gives its own reason.

0.11.0 (2026-11-05)

Features

Pulls based on a pull's branch move when it lands

When a pull lands and its branch goes, open pulls based on that branch now target what it landed on, and show only their own changes. Before, they stayed on the old branch, which was kept for them.

A stack queued lands in turn

Queue each pull of a stack and each waits for the one below it, then moves to what that one landed on and lands in turn. knotless queue list lists one waiting as stacked. Leaving the queue takes those waiting on the pull with it.

Fixes

A moved pull lands with an older git

Merging or queueing a pull moved off a landed pull no longer needs git 2.40, and git failing during a merge is no longer reported as a conflict.

Waiting pulls list as they land

knotless queue list lists pulls waiting for the pull below them lowest first, the order they land in.

0.10.0 (2026-11-05)

Features

Commands are grouped

Every knotless command is now knotless <group> <action>: knotless pull create, knotless repo star, knotless run list, knotless branch protect. An opposite is --undo on the action, so unstar is repo star --undo. Only login, logout, upgrade and status stand alone. The old names, such as knotless pull and knotless checks, work for one more release and then go. knotless --help lists the new ones. The MCP tools are renamed to match, <group>_<action>: repo_star, pull_merge, run_list. A tool is now the twin of the command of its name, so what the CLI could do and the tools could not, and the other way round, is added: repo tree, repo read, repo log, pull update-branch, pull dismiss-review and release read on the command line, and the commands that lacked tools, in the tools. Update any MCP client config that names a tool.

Private repositories have sites

knotless repo site now deploys a private repository's site. It is kept in the repository's space and read only by its members, at a host of its own, https://<repo key>-<plc id>.private.knotless.page/. A member who opens it logs in at knotless.dev if they are not logged in already. Someone who is not a member gets a 404, and a member you remove stops reading it within a minute.

The owner cancels runs

knotless run cancel <url> --ref <name> | --pull <at-uri> [--workflow <name>], and the MCP run_cancel tool, ask a repository's runners to cancel a ref's or a pull's runs going or waiting, a deploy's too, or only one workflow's.

0.9.0 (2026-11-04)

Features

A keeper for the merge queue

knotless-agent keep is an account that keeps the pulls the merge queue turned away moving. One turned away for a conflict that is only lines both sides added it brings up to date and queues again. Where both sides changed the same lines, it tells the author on the pull which files. Keeper: off on a line of a pull's body has it left alone.

Deploy a repository's website

knotless site <url> <dir> deploys a directory as the repository's website, served at https://<owner>.knotless.page/<repo>/, uploading only the files the live site lacks. --fallback serves a single-page app, --not-found a 404 page, and --down takes the site down.

Fixes

A pull held for a review no longer tells its author to approve it

The message for a pull that lacks a review now says that anyone but its author who may write can approve it, and that the owner merges past it with --force, their own pull too.

Pushes and fetches no longer wait on a listing of the packs

Each ref now names the pack holding its commit, so git fetch reads just those packs, and git push, git ls-remote and a fetch with nothing new no longer list the packs at all. On a repository with many recent small pushes that listing ran to megabytes, and about half of each call's time. The owner's git push now starts knotless compact --due after every push, which counts the packs itself and compacts only at KNOTLESS_COMPACT_THRESHOLD.

0.8.0 (2026-11-04)

Features

Code search loads more results

The code search page shows its first 20 files, then loads more below them, 200 files at most. dev.knotless.searchCode takes a cursor for the page after.

Code search

Search finds code: pick Code beside Repositories, or the palette's code row, to search the default branch of every public repository, with repo:owner/name for one and lang: for a language. Each match is shown in its lines, highlighted as its file is. knotless status shows the server's code search index: whether it answers, and the repositories, shards and size it holds.

Dismiss a review

knotless dismiss <url> <pull> <reviewer> <reason> dismisses a review as the owner or a collaborator, and so does MCP's dismiss_review. The review counts for nothing until its reviewer reviews again, so a gate's request for changes no longer needs --force, even on your own pull. show marks a dismissed review.

Go to file

A repository's code page has Go to file beside Code, and t opens it: type part of a path to find any folder or file at the branch shown, and Enter goes to it.

The install script brings the runner, labeller and reviewer

curl -fsSL https://knotless.dev/install.sh | sh now installs knotless-run, knotless-triage, knotless-agent and its knotless-claude-review script beside knotless, git-remote-knotless and knotless-mcp. A runner, labeller or reviewer no longer needs a clone of knotless and Rust to build it. Pinning KNOTLESS_VERSION to a release from before these shipped still installs the first three.

Merges always name their pull, and a push that lands a pull merges it

knotless merge into a branch that is not protected as linear now always makes a merge commit naming the pull, even where it could have fast-forwarded, so the branch's history says which pull each commit came from. Its method is merge or squash; fastForward is no longer made. A pull whose head is already on its base is marked merged without moving the branch.

A git push that puts an open pull's head on the branch it targets now marks that pull merged, and closes the issues it names, as knotless merge would, saying so on git's output. The pages link each commit to the pull that brought it in, whether it was squashed, merged or pushed.

A branch's gates are its required reviewers

Every pull into a protected branch now asks the gates its rule names to review it, so they show among its reviewers before they review, marked "Required reviewer for main" on its page. unask-review refuses to stop asking one: change the rule instead.

A stopping runner hands over to the next one

On SIGTERM, knotless-run finishes the runs it has started, along with a merge-queue batch's runs and those at a running deploy's commit. It writes a note beside its state file, so a runner started beside it takes up everything else at once. The new runner leaves the old one's checks and caches alone, and takes no job the old one's runs hold.

Update a pull's branch

knotless update-branch <url> <pull> and the MCP update_branch tool merge a pull's base into its branch and push it, with no checkout, as GitHub's Update branch button does. Where both sides only added lines at the same place, as two pulls each adding a section to one docs page, the base's lines come first and the pull's after; any other conflict is refused, naming its paths.

Workflows run after others

A workflow can name others in after: the runner starts it only once their checks at the same commit have passed, and it holds no job while it waits. If one fails or is cancelled, it does not run.

Fixes

knotless login works in Chrome and Firefox again

The login page's address had grown past what Chrome and Firefox send back to it, so approving a login on bsky.social and other PDSes failed with "Invalid referer". knotless login now asks for the same access in a shorter form. A login made before stays current.

The MCP pull_diff tool gives a long diff a few files at a time

A pull's diff too long for one answer used to be cut off by the MCP client, so an agent read only its first files. Now pull_diff lists the files of such a diff instead, with how many lines each adds and removes, and takes paths to give those files' parts of it.

The queue says when a pull's pre-flight failed

knotless queue lists a queued pull whose pre-flight failed as failed <workflows>, and show says which failed, rather than queued and "running its checks again" while nothing runs, until the runner ejects it or runs it again.

A review agent reads a pull again when its body answers a request for changes

After knotless-agent review asks for changes, editing the pull's title or body has it review the pull again at the same head, as one of its rounds, so a request about what the pull says no longer needs a push to clear. knotless-claude-review also no longer asks for changes over what a required check already fails the pull for.

0.7.0 (2026-11-03)

Features

Pulls and issues are assigned, and maintainers ask for reviews

The owner and collaborators assign a pull or an issue to accounts with knotless assign and take them off with unassign, and ask anyone to review a pull, not only its author, with knotless ask-review and unask-review. Each stops asking only those it asked. knotless show lists the assignees, knotless list -q and the MCP's list_items take assignee:<handle, did or @me> and no:assignee, and the MCP's assign_item does either. Those assigned hear of it as an assigned notification, and those asked as a review-requested one. The pages do the same under an item's title.

An organization's admins change its repositories' settings from the command line

knotless describe, archive, compact, protect, unprotect, labels, triagers, agents, share, unshare and runners, and the MCP tools that do the same, now work on an organization's repository from an admin's own login. Not logged in as the owner, they ask the appview, which makes the change as the organization. Log in again (knotless login) if your login is older than this, so it may ask. Renaming, deleting and transferring a repository still take the organization's own login, on the pages too.

A branch waits for a review agent's verdict with knotless protect --gate

knotless protect <url> <branch> --gate <handle> names an account, such as a review agent, whose review of a pull's head the branch waits for, though it may not write to the repository. The merge queue holds a pull until the gate has reviewed its head, and a request for changes from it blocks merging and queueing until it reviews again. An approval of the head from the owner or a collaborator, given after the gate's review, stands in for it. knotless rules lists each gate, merge --check says a review of its head from @<gate> while one is awaited, and MCP's protect_branch takes gates. knotless-run's --review-wait <secs> (30 minutes) is how long the queue waits before ejecting a pull no gate has reviewed.

A branch's gates are named on its settings page

A protected branch's settings on the pages now have "Wait for a review from", which names an account, such as a review agent, as a gate of the branch by its handle. The page lists each gate, with Remove to take it out, as knotless protect --gate does.

Fixes

knotless checks --images keeps the newest run's pictures

When the checks listed hold more than one run of a workflow, as a branch's reruns or a pull's heads do, an older run's picture no longer overwrites the newest's of the same name. The older one is saved beside it, named for its commit too: look notifications-desktop-light 450b27ae11f7.webp.

Exclusive labels with no scope stand together, and a failed retarget changes nothing

An exclusive label with no / in its name no longer takes other such labels off: only labels in the same scope, such as priority/high and priority/low, rival each other. knotless retarget with both --base and --head checks the head first, so one refused for its head no longer moves the pull's base. Several workflow files with names that are not valid each give their own error in the one invalid check, rather than a duplicate-name error. knotless queue and knotless uses update no longer panic on a commit ID shorter than 12 characters, and the gateway takes SHA-256 commit IDs as pushes do.

The page after logging in is easier to read

The page the browser shows once you have approved a login now has a heading and the look of knotless's own pages, in light and dark, and says so when the server refused the login rather than that you are logged in.

A merge no longer leaves a pull open when its head branch cannot be deleted

Merging a pull whose head branch is protected keeps that branch, and says why, instead of trying to delete it. A delete that is refused for another reason no longer fails the merge after the target has moved. A git push also no longer fails when the compaction after it does, and KNOTLESS_INLINE_MAX is held to 262144 bytes, the most a pack record takes.

0.6.0 (2026-11-02)

Features

knotless admin logs reads the lines the appview logged

An appview's operator can read the lines it logged, without SSH: knotless admin logs --trace <id> shows one request with the calls it made and how long each took, and --message, --level and --min-ms show the last of the rest. The MCP logs tool does the same. The appview keeps them across deploys, for 30 days at most, and logs each read with who asked.

An organization's admins change its repositories' settings on the pages

An admin who joined an organization now sees the Settings tab on its repositories, and changes everything there as the organization: description, branches and their rules, labels, collaborators, triagers, runners and secrets, and archiving. Transferring and deleting a repository stay with the organization's own login.

Deleting a branch deletes its record

Deleting a branch or tag now deletes its ref record from your PDS, rather than leaving an empty one so that its count of moves never repeats. A new ref starts its count at the time instead. Compaction removes the empty records older clients left once they are a day old, so a repository that has had hundreds of branches reads only the ones it has now on every push, fetch and page.

Upgrade every client that pushes: an older one making a branch again under a name deleted less than a day before can have its pushes to it refused until the next compaction.

A shared step can be passed a secret under the name it asks for

A uses step's secrets can be a map from the name the step file asks for to the workflow's own, as secrets: {SSH_KEY: DEPLOY_SSH_KEY}: its steps see the value as SSH_KEY, and nothing as DEPLOY_SSH_KEY. A list still passes each secret under its own name.

A private repository's pulls, issues, comments and checks are named by their URI in its space

knotless names a record in a private repository's space as atproto's permissioned data proposal does, at://<owner>/space/dev.knotless.space/<name>/<author>/<collection>/<rkey>, wherever it gives one: what knotless pull, issue, comment and checks print, what show and list name, and what new records written into the space refer to. A URI in the public form, at://<author>/<collection>/<rkey>, is still taken for one in the repository being asked about, and records written before are read as they were. A knotless older than the one that reads both forms does not find a record named in full: upgrade with knotless upgrade.

Watch a repository, and subscribe to or mute a pull or issue

knotless notifications watch <url> [all | releases | ignore | participating] chooses what you hear of a repository: all it does, its releases alone, or nothing. knotless notifications subscribe <at-uri> hears of every comment on a pull or issue, mute hears nothing of it but a review asked of you, and unsubscribe goes back to hearing of it as your part in it says. knotless notifications subscriptions lists what you chose. The MCP subscribe tool does the same. Notifications gain the reasons subscribed and watching. They call two new appview methods, so a login made before this needs knotless login again.

Workflows run on a schedule

A workflow's on: {schedule: [{cron: '30 3 * * *'}]} runs it at the default branch's tip at the minutes its cron expressions name, in UTC, at most every five minutes. Its steps see KNOTLESS_SCHEDULE, the expression that ran it, and its log says ==> on its schedule. The runner reads schedules from the default branch's tip as it starts and whenever that tip moves. A time that comes while the last run is still going is skipped. knotless workflows lists each workflow's schedules at the default branch.

Fixes

A feature branch's pulls start from its own warm caches

A branch whose pushes and pulls both run a workflow, as on.push.refs and on.pull.base both naming refs/heads/feature/* do, keeps that workflow's caches for itself: each passing push to it leaves what the pulls into it start from, and a pull landed there at the tree it was checked at is taken as the push's run, caches and all. A branch with none yet starts from the default branch's, and its caches go when it is deleted.

knotless checks --ref takes a branch named bare

knotless checks --ref main lists the checks of refs/heads/main, as rerun and workflows read a bare name, rather than none; a tag of that name, when there is no such branch. A branch deleted since, as a merged pull's, still names its checks. The MCP checks tool's ref takes one the same way.

knotless admin logs --trace reads up to 500 lines of the request

A read by trace now answers up to 500 lines unless --limit says fewer, instead of the last 100, so a long request's first lines are not cut off. Lines serve fails to write to its log store, as when another serve holds the file during a deploy, are kept and written with the next batch instead of lost.

An organization's admins set its repositories' secrets

knotless secret <url> set, ls and rm, and the MCP secret tool, now work on a repository an organization owns when you are logged in as an admin who joined it, not only as the organization's own account. The values are sealed as the organization's, so its runners open them as before.

Queueing says to read an ejection's logs before queueing again

knotless merge --queue ends by saying to read an ejected pull's check logs before queueing it again. The MCP merge tool's description says the same, says to merge the target branch in just before queueing, and says that a pull conflicting only with one ahead of it waits for that one rather than merging its branch in.

A queued pull's record names the head it was queued at

knotless merge --queue, and the MCP merge tool with queue, write the pull's head into its dev.knotless.queue record, as an ejection already does, and knotless show --json gives it. The queue's history then says whether a pull queued again after an ejection had changed.

Queueing a pull again at the head it was ejected at says so

knotless merge --queue says when the pull is going back into the queue at the very head the queue last ejected, and why it was ejected: unless that was not the pull's doing, it will be ejected again. The MCP merge tool answers it as queued.ejectedBefore, and dev.knotless.queuePull as ejectedBefore.

A queued pull says what it waits for

knotless show, the MCP show_item tool and knotless queue --json say what a queued pull in no batch is waiting for: Queued, 2nd of 3, running its checks, running its checks again on main as it is now when one failed on an older tip, or ready for the next batch. Before, each just said Queued, except while a pre-flight was running. --json also gives each one's stage, its pre-flights' newest runs (preflights), and when the batch ahead of it started (ahead).

Reading a repository with many deleted branches is faster

A repository keeps a record for every branch it ever had. Past the first 100, knotless now reads those records in ranges side by side rather than one page after another. Pushing to, cloning and listing a repository that has had hundreds of branches, as a busy one does, takes a fraction of the time, for example 0.37 seconds rather than 1.7 for 590.

A private repository read for longer than a space credential lasts goes on reading

A space credential lasts minutes. What reads a private repository for longer, as knotless-run watching one does, renews it before it runs out rather than failing every read after with JwtExpired, and a session read with renews the same way.

A record in a private repository's space can be named by its full URI

Wherever knotless takes a pull's, an issue's or a comment's at:// URI, as knotless show, comment, merge, checks --pull and a pull's Fixes, it reads a private repository's record by its URI in the space too, at://<owner>/space/dev.knotless.space/<name>/<author>/<collection>/<rkey>, as atproto's permissioned data proposal names it, as well as by at://<author>/<collection>/<rkey>.

knotless admin stats says which queries were slow, why actions failed, and which crawlers came

An operator's knotless admin stats, and the MCP stats tool, now list the index's slow statements with how often each ran slowly, their mean and their longest; why MCP tool calls failed, with like errors counted together; and the requests each crawler or script made, by its user agent. Each is kept for 30 days.

0.5.0 (2026-11-01)

Features

A workflow step can use a step another repository shares

A step with uses: <owner>/<repo>/<step>@<version> runs the steps of .knotless/steps/<step>.yml in that repository, at the commit .knotless/uses.lock pins it to, with the inputs with gives, only the secrets the step passes it, and its own repository checked out beside the checkout as KNOTLESS_STEP_DIR. A step file can use shared steps of its own. Release a step with knotless publish <url> @step/<step> <version> .knotless/steps/<step>.yml --commit <sha>, and pin the ones a checkout uses with knotless uses update. Each step is now given its secrets as it starts, rather than the container holding them all.

show lists the pulls that mention an issue

knotless show on an issue prints mentioned in <pull> for each pull on the repository whose title or body names it, oldest first, ending , to close it where the pull names it after a closing keyword. MCP's show_item gives the same as mentionedBy.

A login and a gateway's grant name knotless's own space types

knotless login asks for space:dev.knotless.space and space:dev.knotless.orgSpace, each with the collections knotless writes there, in place of every space of every app (space:*); knotless delegate asks the same of a gateway's grant. A login or grant from before still works, and is asked for again as any older one is.

A workflow step can run only at some refs

A step with on: {refs: [refs/heads/main, refs/tags/*]} runs only at a push to, or a run asked for at, a ref those patterns match, as on.push.refs are matched; anywhere else it passes unrun, listed skipped, its log saying why. A pull's run and a merge-queue batch's run are at no ref, so such a step runs in neither. On a step with uses, its on holds for every step the shared step comes to, beside their own.

Themes of your own

The pages' Appearance settings make a theme of your own colours, checked as you choose them for text that reads on what it is drawn on, and publish it as a dev.knotless.theme record for anyone to choose from Appearance. Choosing one copies its colours into your preferences, so its author changing or deleting it later leaves your pages as they are. Logging in again asks to write dev.knotless.theme records.

The command line does the same: knotless theme check, publish, use and delete, and knotless themes to find them; and over MCP, the theme and list_themes tools. A palette is held to the same contrast wherever it is made.

Fixes

knotless show names the command that reads an ejected pull's logs

A pull the merge queue ejected as its checks failed shows logs: knotless checks <url> --pull <pull> --log, or --queue <branch> for a batch, after where it stands. The runner no longer writes that command into the reason it records, which the web pages show beside links to the checks that failed.

An organization's space has a type of its own, dev.knotless.orgSpace

An organization keeps its invitations and hidden members in a space of the type dev.knotless.orgSpace, which has its own lexicon, so that a login can name it. Its gateway moves an organization out of its old space, of the type dev.knotless.org, as it starts and as the organization next changes, keeping each member where they stood. A hidden member's record of joining stays in the old space, and counts, until they join again: knotless org join <org> --hidden moves it.

knotless status shows the server's disk

knotless status adds a disk line: how much room is left on the disk a knotless-serve keeps its mirrors on. Under 15% free the disk shows as low among the problems, and under 5% the server counts as unhealthy.

0.4.0 (2026-10-30)

Features

Say an account is a bot

knotless profile --bot says your account is a bot, such as a runner, a triager or a reviewer, and --no-bot takes that back. The MCP server's set_profile takes bot, and the profile settings page has a box for it. A bot's profile shows a "Bot" tag, and the front page's feed leaves out what it says and pushes.

Fixes

The feed leaves out bots

The front page's feed no longer shows what a repository's runners and triagers say or push on it, which repeated what people did and crowded them out. A runner that is the repository's owner or a collaborator still shows. A pull the merge queue landed shows as merged by whoever queued it. A bot's own profile still shows everything it did.

The appview's gateway is the one its DID document names

knotless share --write, knotless delegate with no gateway named, and a runner landing a merge queue through the gateway now ask the appview for its DID instead of making one from KNOTLESS_APPVIEW's address. A runner that reaches its appview at an internal address, as one beside it does, lands its queue again rather than failing to reach a gateway that does not exist.

Private repositories work on the October 29 spaces alpha

The spaces alpha of October 29 moved spaces from com.atproto.simplespace to a policy on com.atproto.space, and creating a private repository or sharing one failed against a PDS running it. knotless now creates each private repository's space under the memberspace policy and manages its members with com.atproto.memberspace. A PDS on an older alpha can't create private repositories with this version.

0.3.0 (2026-10-30)

Features

Archive a repository

knotless archive <url> makes a repository you own read only, and knotless archive <url> --undo makes it writable again. While it is archived, pushes and commits are refused, nobody can open pulls or issues or comment on it, and its workflows stop running. Its pages show it as archived. You can also archive and unarchive it from its settings, in the Danger zone, or with the MCP server's archive_repo tool.

Hear of your pulls' failed checks and merge queue ejections

knotless notifications, the MCP notifications tool and the pages now say when checks fail at your pull's latest run (checks-failed) and when the merge queue takes your pull out (ejected), each naming the checks that failed. Either can be turned off with knotless notifications settings --off <reason>. The pages' notification settings can also show new notifications on your screen while knotless is open in a tab.

Change part of a long file without sending all of it

knotless commit --edit <path> <old> <new> and the MCP commit tool's edits replace text in a file as the branch holds it, so an agent changing one line of a long file sends that line, not the file. On a branch that moved meanwhile, edits are made in the file as it is there. knotless search and the search tool take --path/path to look only under a directory or glob, and --context/context to show the lines around each match, enough to write an edit from. The read_file tool takes start and end to read only some lines, and blame (--lines <first>-<last> on the command line) blames only some.

Notifications of one kind

knotless notifications --kind <kind> lists only mentions, reviews, comments, pulls-and-issues, checks or stars-and-follows, and the MCP server's notifications tool takes the same kind. A listing of one kind marks nothing seen, so the others stay new.

Owners give their runners secrets

knotless secret <url> set <name> [--ref <pattern>]... gives a repository's runners a secret, read from stdin, sealed to the key each runner publishes so only it can read it, and left at the appview. knotless secret <url> ls lists them, never a value, and rm takes one back. The MCP server has a secret tool to match. A runner reads them for each run whose workflow lists a name its secrets file does not grant. knotless runners <url> remove deletes the secrets sealed to that runner, and add says which secrets must be set again to reach it.

A read-only knotless-mcp, and reviews of private repositories

knotless-mcp --read-only offers only the tools that read, and runs no other, whatever account it is logged in as. The review agent reads through it with its own login, so it reviews the private repositories shared with it, and a repository says what it wants of its reviews in .knotless/review.md on its base branch.

Topics and collaborators on a repository

knotless describe <url> --topic rust --topic cli names what a repository is about, and --topic '' drops its topics. They show under its description, each linked to the other repositories about it, and the MCP server's update_repo tool and the repository's settings change them too. A repository's page also shows who may push to it besides its owner.

Review agents

knotless-agent review runs an account of your own that reviews the pulls asking it for a review, as GitHub's Claude review bot does. Ask it with knotless pull --reviewer <its handle> or knotless edit --reviewer; it reads the pull with Claude Code, through knotless-mcp's read tools, and writes a review with a verdict and comments on lines, then reviews again as you push, up to --rounds times. It uses your Claude subscription (claude setup-token) or an API key, and --command takes any other reviewer that reads and writes its JSON. The guide is docs/guide/reviews.md.

The reviewer can watch every repository an account holds

knotless-agent review --owner <handle> reviews the pulls of every public repository that account holds, listed again at each read, so a repository made later is reviewed without restarting the agent. Repeat --owner for more accounts, and name repositories besides as before.

knotless-run keeps a key for secrets

knotless-run now makes a P-256 key on its first start, kept in --secrets-key <file> (secrets-key.json beside its state file by default), and publishes its public half as its dev.knotless.runnerKey record. Owners will seal the secrets they give a runner to it, so only the runner can read them.

The appview keeps secrets sealed to a runner

knotless-serve answers dev.knotless.putSecret, deleteSecret and listSecrets: an owner leaves a repository's runners secrets, each sealed to one runner's key, and the appview holds only ciphertext. knotless's login asks for these methods, so a session from before asks again at its next login.

Owners manage secrets on the repository's settings

The repository's Settings, under Actions, list its secrets by name and add, replace and remove them, signed in as its owner. Each value is sealed in the browser to the keys the runners publish, as knotless secret seals it, so the appview still holds only ciphertext.

Hear of new versions, and upgrade

Once a day, knotless asks its appview for the newest release. When one is out, a command ends by saying so on stderr. knotless upgrade installs it over the copy you run. When the appview asks for a newer version than yours, because yours is known to harm repositories, knotless and knotless-mcp refuse to run until you upgrade. KNOTLESS_NO_UPDATE_CHECK turns the check off, and KNOTLESS_IGNORE_MINIMUM runs anyway.

A workflow can name the runners that run it

A workflow's runners key names, by handle or DID, which of the repository's runners run it; the others leave it be and file no check of it. knotless secret <url> set <name> --runner <handle> gives a secret to those runners alone and takes back the copy any other had, as the MCP secret tool's runners does. Together they keep a deploy and its secrets on a runner of your own while the hosted runner checks everything else.

Fixes

Clones no longer break after a branch is deleted and compacted

A clone or fetch could fail with "the remote has no pack holding" a commit of a deleted branch, such as a landed pull's. A newer pack still named that commit as a base, and compaction had dropped it. Compaction now keeps every base that the packs it leaves name.

Compaction stops rather than lose a commit

If compaction can't fetch a commit that a newer push still needs, it now stops and deletes nothing. Before, it left that commit out and deleted the packs anyway, which broke clones with "the remote has no pack holding" that commit.

A conflict is named a conflict

A 409 from knotless-serve, such as a gateway commit whose edit no longer finds its text, now says Conflict (409) rather than InternalServerError (409), so it no longer reads as the server failing.

A pull the merge queue landed shows the checks it landed on

A merged pull's page now shows the checks of the batch the merge queue landed it in, not older runs of its own heads. Over MCP, list_items and show_item name that batch's tip as merged_batch.

Joining an organization reads both places at once

knotless org join reads whether you joined publicly and in the organization's space at the same time. Switching between them no longer reads again after the organization takes you in.

Pages left open catch up when you come back

A page left in a background tab for a minute or more loads fresh data when you look at it again, and any page does the same when your connection comes back. A repository's page that loses its live connection now catches up on what it missed once it reconnects.

Pictures show at the size they were taken

A picture attached with --image, or images over MCP, keeps the size it is drawn at: a PNG saying it was made at 2x, as a Mac's screenshot does, shows at half its pixels, so a phone's screenshot is phone-sized on a pull or issue.

A run says which secrets it was not granted

A run whose workflow lists a secret the runner does not grant it now says so at the top of its log, ==> NAME is not granted to <repo> at <ref>, rather than leaving the secret silently absent. A pull's or merge-queue batch's run says the same of each, as it gets none.

An edit or reply in progress survives the page updating

A comment you are editing, or a line comment you are replying to, on a pull's or issue's page no longer loses what you typed when the page refreshes and an earlier event shows up.

0.2.0 (2026-10-29)

Features

Blame a file without a clone

knotless blame <url> <path> [--rev <rev>] prints who last changed each run of a file's lines, as git blame finds it, read from the cache mirror: the lines, the commit, its author, when it was written and its subject, tab-separated. The MCP server's blame tool answers the same as JSON, with the commit's parent and the file's path there, to blame again before it.

Reviews and merges say what they mean

merge --check lists each approval that does not count, and why, as an uncounted line (the MCP merge check as reviewsUncounted), and a merge refused for its reviews says so too. It says when the asker may not merge the pull, and counts a review written a moment ago. Over MCP, a refusal for reviews names the comment tool and merge with force rather than CLI commands; merge answers how it merged (method) and why it kept the head branch (kept); show_item gives handles beside reviewers' DIDs, marks reviews outdated, maintainer and uncounted, gives comments' mention facets, marks every comment of a resolved thread resolved, has a review's own comment name itself as review, and says draft: false; comment notes an approval that will not count; notifications says markedSeen.

Comment on a pull's lines, in threads you can resolve

knotless comment takes --line <path>:<n>[-<m>][:old] <text>, as many as you like, to comment on lines of a pull's diff, alone or as one review with --approve, --changes or a body, and prints the URI of each comment it wrote. Given a comment's URI, it answers that comment in its thread. knotless resolve and unresolve resolve a thread or open it again, and knotless show says which lines each comment is on and which answer which. Over MCP, comment takes lines and answers written too, set_state resolves a thread, and show_item gives each comment's line, reply, review and resolved. On the pages, the + beside a line of a pull's diff comments on it, or starts a review sent from the "Review changes" menu.

Delete all your knotless data

knotless delete-data <handle> lists what knotless keeps in your atproto account (your repositories, those others push to or read named, and the records of each kind), then, once you type your handle again (or pass --yes), deletes all of it: your private repositories' spaces, each public repository as knotless delete deletes one, then your pulls, issues, comments, stars, follows and the rest. knotless.dev then forgets you and logs you out. A run cut short carries on where it stopped when run again. Your atproto account, and other apps' records in it, stay. The delete_account_data MCP tool does the same, and the pages' account settings have a "Delete your knotless data" section. Logging in again asks to call dev.knotless.deleteAccountData.

Draft pulls and requested reviewers

knotless pull --draft opens a pull that neither merges nor queues until you mark it ready with knotless edit <url> <at-uri> --ready (--draft makes it one again); merge --check says draft and why. --reviewer <handle>, on pull and edit, asks someone to review your pull, and edit --no-reviewer stops asking: they hear of it as a review-requested notification once the pull is not a draft. knotless show lists both, and list -q takes is:draft, -is:draft and review-requested:<handle or @me>. MCP's open_pull and edit_item take draft and reviewers too. The pages mark drafts in the pulls list, show requested reviewers in the merge box, and let the author mark a draft ready there.

The gateway holds collaborators to required reviews and checks

Through the gateway, a collaborator moves a branch whose rule asks for reviews only by merging a pull reviewed as it asks, with its checks passed: a plain push or knotless commit to it is refused, saying to open a pull. In a repository that names runners, a collaborator's merge of a pull, pushed by any client, is refused until the pull's checks pass, as knotless merge refuses it.

Mentions

@handle in a pull's or an issue's title or body, or in a comment, now mentions that account: a full handle, with a dot, not in code or a link. Writing the record, from the command line, MCP or the pages, resolves each handle and keeps the account's DID in it as facets, so the mention still names the account after its handle changes. The pages link each mention to the account's profile, and the account hears of it with a new notification reason, mention, on unless turned off with knotless notifications settings --off mention, MCP's notification_settings or the settings page. On a private repository only those who can read it hear of a mention.

Organizations

Any account can be an organization. As that account, after delegating to a gateway (knotless delegate), knotless org add <handle> invites another account, and knotless org join <org> --public or --hidden accepts: from then on they push to each of the organization's repositories, made before or after, and read the private ones, until knotless org leave or knotless org remove. A public membership shows to anyone; a hidden one only to the organization's members, and on its public repositories a hidden member pushes and opens pulls while merging, closing and labelling take a public membership. Invitations lapse after 7 days. --admin makes an admin, who invites, removes and makes repositories as the organization from their own login (--org <org>, or knotless create knotless://<org>/<name>). knotless org members lists who is in (--public as anyone outside sees it), knotless org list which organizations an account is in, and knotless org delete --yes ends it. knotless members names an organization's members on each repository. The org MCP tool does the same, and so do the pages: Start an organization in your account menu, then a Members tab to invite, change roles, join, hide, leave and remove, and Delete organization in settings. An invitation shows in knotless notifications, MCP's notifications and the pages' notifications until it is answered or lapses, and the pages answer it there. Logging in again, and delegating again, asks to write dev.knotless.org, dev.knotless.orgMember and dev.knotless.orgAcceptance records and to call the organization methods.

knotless merge --check, and MCP's merge with check, tell a hidden member why they may not merge on an organization's public repositories: that it takes a public membership.

Pictures in pulls, issues and comments

Paste or drop a picture into a pull's, an issue's or a comment's box on the pages and it is uploaded to your account and linked where you put it, as GitHub does. From the command line, --image <file> on pull, issue, comment and edit does the same, and over MCP images on open_pull, open_issue, comment and edit_item. The record attaches each picture its body shows, so your PDS keeps it, and lets it go when an edit takes it out. A picture over 1 MB is made smaller rather than refused, and so is a profile picture, which may now be a WebP or GIF too.

A pull's diff over MCP numbers its lines

The MCP server's pull_diff tool now leads each line of a hunk with its line number in the old file and in the new, as comment's lines takes them: a + line is side: new at its new number, a - line side: old at its old number. An agent no longer counts lines from the @@ headers to comment on one.

Branches that take pulls only through the merge queue

knotless protect <url> <branch> --queue makes a branch take pulls only through the merge queue, as GitHub's "Require merge queue" does: knotless merge (and MCP merge) queues a pull into it instead of merging, saying so, and only the owner's --force merges at once. The gateway moves such a branch for no collaborator but the runner that lands the queue. knotless rules shows queue, MCP protect_branch takes it, and the repository settings' Branches page has a "Require the merge queue" checkbox for each protected branch, whose pulls' pages then offer only the queue.

Repositories show their releases

A repository's pages show what it publishes: its latest release and its packages under its name, and a Releases tab listing every version, with its notes, files and, for an image, the docker pull command. knotless-serve indexes dev.knotless.release records and answers dev.knotless.listReleases.

Repositories show their links

A repository can name up to five links, its website first, as a profile names its links: knotless create <url> --link <url> [--title <text>], and knotless describe <url> --link … to replace them, or --link '' to drop them. The MCP create_repo and update_repo tools take links, and its settings page has a Links field. Its pages show the first link under the description, before its latest release, with a count of any others that opens the list of them all. knotless describe prints a link: line for each.

Protected branches can require reviews, from code owners too

knotless protect <url> <branch> --reviews <n> makes a pull merge into the branch only once <n> of the owner and collaborators, other than its author, approve it, and while none of them has asked for changes. --code-owners also asks, for each path it changes that the branch's CODEOWNERS file (.knotless/CODEOWNERS, CODEOWNERS or .github/CODEOWNERS) gives owners, for one of them to approve it; --dismiss-stale counts only approvals of the pull's head as it is now. merge, merge --check (which lists what is missing, and CODEOWNERS lines it skipped), the merge queue, MCP protect_branch and merge, and the pages' branch settings and merge box all follow it. The owner's --force merges past it.

Revert a merged pull

knotless revert <url> <pull at-uri> [-m <reason>] undoes a merged pull as GitHub's Revert button does: one commit that undoes everything its merge brought in, on a new branch revert/<rkey> cut from its target branch, and a pull of that branch into it, titled Revert "<title>", its body linking the merged pull and saying why. It prints the new pull's URI; check and merge it, or queue it, like any pull. It stops on a conflict, naming the paths. The owner and collaborators may. The revert MCP tool does the same, and a merged pull's page has a Revert button.

Reviews stand as GitHub's do

Each reviewer's latest verdict is where they stand: a later --approve or --changes replaces an earlier one, and a comment without a verdict changes nothing. knotless show lists each reviewer as a review: line with the head they looked at, and the MCP list_items and show_item give an item's reviews in place of approvals. You can no longer review your own pull, or an issue. The pages show reviewers in a pull's merge box, and a review of a head the pull has since moved past as one of an earlier version.

Sharing for writing delegates first, and reviews say more

knotless share --write and MCP's share delegate your writes to the appview's gateway first when you have not, approved in the browser as delegate is, and say they did. Notifications name what each is about (the pull or issue, or for a star the repository) and its repository as handle/name; a review says how many line comments it holds and whether it mentions you. show_item gives each line comment its side and startLine, resolved only on threads, superseded on a review a later one replaced, and whether each requested review is still pending; list_items says draft: false too. A pull whose only code owner is its author says why it can never have that approval, and protect_branch and list_rules warn when you alone own a path.

Tag a commit without a clone

knotless tag <url> <name> [<from>] tags a commit, the default branch's or that of a branch, tag or commit, and prints it; --delete deletes a tag. The MCP server's create_tag and delete_tag tools do the same. Each release of knotless is now tagged v<version> at the commit it was built from.

The branch menu switches to tags too

On a repository's code, commits, file and blame pages, the menu that switches branches has a Tags tab beside Branches, as GitHub's does, to see the same page at a tag. Viewing a tag, the menu's button shows the tag icon and opens on Tags.

Fixes

Buttons show their action under way

On the pages, the button you pressed shows a spinner and says what it is doing, such as "Inviting…" or "Joining…", until the server answers. The rest wait, dimmed. This covers organizations and their invitations, notifications, merging, settings, labels, reactions and the rest.

Commands and MCP tools say when your login is out of date

While your login is older than what knotless asks for now, every command ends by saying so and naming the knotless login to run. An MCP tool a PDS refuses for want of scope tells the agent to have you sign in again, by reconnecting knotless in your assistant or running knotless login.

Deleting a repository leaves nothing of it

knotless delete, the delete_repo tool and the pages' Delete now also delete the repository's releases and reruns, and what you wrote about it yourself: your pulls, issues, comments, states, labels, hides, queue entries and blocks naming it, your star of it, and your reactions to those. The repository's own record goes last, so a delete cut short carries on when run again. knotless.dev drops what it kept of a deleted repository, its mirror included, and a runner drops its checks, caches and secrets grants. Logging in again asks to write dev.knotless.release records, which knotless publish needs with OAuth.

Organization changes take fewer round trips

Inviting, removing and leaving wait on fewer calls to a PDS. The gateway reads what it needs at once rather than one after another, and no longer tries to make the organization's space each time. On the pages, a change this server's gateway made is no longer indexed a second time.

Handle suggestions when inviting and sharing

On the pages, the fields that take someone else's handle suggest accounts as you type, as the login dialog does: inviting to an organization, adding a collaborator or a reader, and offering a repository to another account.

Say how to grant what a login lacks

A command a PDS refuses because your login is older than what knotless asks for now says so, and to run knotless login again. Logins now keep the scope they asked for, and the pages offer to log in again, or to delegate again, when knotless asks for more.

Collaborators and organization members hear of new pulls and issues

A pull or an issue opened on a repository is now a notification for its collaborators, and for the members of the organization that owns it, hidden members too, as it was for its owner.

Suggestions for @ while writing

Typing @ in a comment, a review, an edit or a new pull or issue on the pages suggests whom to mention: those in the thread, then whom you follow, then accounts whose handles match. Arrows choose, Enter or Tab picks, Escape closes. A mention an edit adds is heard of as of the edit, not as of when the record was first written.

A new repository never replaces one of the same name

create, fork, accept and rename refuse a name a repository of yours already has, public or private, with <url> already exists, before writing anything. On a PDS that took a create over an existing record as an update, creating a repository under a taken name replaced the old one's record and orphaned its history. The record is now written with a compare and swap that the PDS refuses when any record is there. On the web, the new repository form, the fork menu and the rename setting say as you type whether the name is free, and hold their button while it is taken.

Organization changes read at once

Joining, inviting, removing and ending an organization read what they need at the same time instead of one after another. They also read the organization's space once instead of two or three times.

Pasted pictures show in the preview

A picture pasted or dropped into a pull's, an issue's or a comment's box shows in its preview, and a profile picture picked shows before it is saved: the pages let a picture be drawn from what was pasted or picked.

Notification settings live with your other preferences

knotless notifications settings and the notification_settings MCP tool read and change which notifications you hear of through the appview's preferences (dev.knotless.getPreferences and putPreferences), which also keep the theme you choose on the pages. They behave as before, but need an appview at this version or later. Notification settings made before this were not carried over: everyone starts from hearing every reason, from anyone.

Say why a private repository can't be made yet

knotless create --private, and the create_repo MCP tool with private, on a PDS without spaces now say that your PDS can't hold private repositories yet, rather than passing on its UpstreamFailure or MethodNotImplemented. Nothing is made.

Notifications from private repositories reach members who never logged in to the appview

Members of a private repository who use only the command line or MCP now hear of what is said there: a mention, a comment on their issue, a reply. Writing a pull, an issue, a comment, a review or a state to a private repository, or sharing one, now shows the appview the repository with a delegation token, so its index has what was written. Sharing a private repository to read now names the account in the record's new readers, which the appview takes as the owner's word that they may hear of it. Someone mentioned there who is not a member still hears nothing.

A merged pull's diff once its branch is gone

The MCP pull_diff tool on a merged pull answers what its merge brought in, from the commits its merge recorded, rather than failing once merging deleted its branch.

Pushes through a gateway are credited to whoever made them

A push or commit a collaborator or runner makes through a gateway names them as pusher on the claims and refs it writes, and the feed and activity pages credit it to them rather than to the repository's owner, while the repository lists them as a collaborator or runner.

A reverted pull names its revert

Once a revert pull merges, the pull it undid names it: knotless show says reverted by, as do the MCP show_item (item.revertedBy) and the pull's page. A merged pull's compare in show_item is what its merge brought in, even once its branch is deleted, and the MCP diff tool says when there are no differences rather than answering nothing.

Usage keeps who did what for 30 days

knotless-serve's usage counts (--usage) delete the accounts they noted for MCP, the gateway and the pages' logins once 30 days have passed, keeping only counts. In knotless admin stats, an event's accounts are of the days asked, at most the last 30, and a funnel starts with an account that had not done its first step in the 30 days before.

0.1.1 (2026-10-28)

Features

Container images as releases

knotless publish --oci <dir> publishes an OCI image layout as a release, each blob named for its digest, and knotless-serve answers docker pull <host>/<owner>/<repo>/<package>:<version> from those releases at /v2/.

Agents publish and read releases

The MCP tools publish, releases and read_release publish a version of a package, list a repository's releases, and read one file of a release, as knotless publish, releases and download do.

Images can be pushed to knotless-serve's registry, which lists their tags

docker login with the owner's handle and an app password, then docker push to a version's tag, publishes the image as that version's release; tags/list lists an image's versions.

Releases as records

knotless publish publishes a version of a package from a repository, as dev.knotless.release records in its owner's account, never changed once made; knotless releases lists them and knotless download fetches and checks a version's files.

knotless-serve installs from a repository's releases

--releases knotless://<owner>/<name> serves /install.sh and /releases/ from that repository's releases of the knotless package, each file checked against its record and kept, rather than from a directory.

Fixes

Releases stream

knotless publish reads each file a part at a time as it uploads it, and knotless download writes each part as it reads it, beside the file until it is whole and checked, so a file of any size takes only a few parts' memory.

A runner's pass that runs long says where it is

knotless-run logs each pass over a repository as pass ended, with how long it took, and one still going after 5 minutes as pass running long, with the stage it is at, every 5 minutes. A git command slower than KNOTLESS_SLOW_MS is logged at warn, as git. The same failure of a repository is logged once a minute while it goes on, with a count, rather than every time.

knotless-serve's image is published as a release

Each new version's image is the release of the knotless-serve package: docker pull knotless.dev/chadtmiller.com/knotless/knotless-serve.