plankit

Release notes

Status names the flow

v1.8.0 · 2026-11-01

pk status names the flow on its release line. Merge flow now reads as trunk flow already did, so one line says which flow a repository is in and what pk ship will do there.

release:   merge flow: pk ship merges develop into main and tags it
release:   trunk flow: pk ship tags main, origin's default branch

pk help release says which flow to choose and how to move between them. Two flows has the rule in a paragraph: merge flow where unreleased work has somewhere to run, trunk flow where a second environment would have to be built, and the switch either way is a config change and a branch. Tags made under one flow stay valid under the other.

The commits decide the version

v1.7.0 · 2026-10-19

The commits decide the version, and guard asks before an override lowers it. pk ship --dry-run shows the bump the commits infer, and that bump is the release; a number named earlier in a plan or a conversation is a guess. A --bump lower than the inference, patch over a feat or minor over a breaking change, now gets a question from guard in a session, with the commits that call for the higher bump in the reason, the way a breaking marker does. The dial is guard.bump, ask by default, off to keep the rule without the question. At a terminal nothing is in the way: --bump is the developer's, and changelog names the override next to the version it generates.

Generating v0.8.1 (--bump patch; the commits infer minor)

The ship and changelog pages say what to do with an unexpected version. Stop and say so. A commit whose type is wrong is fixed by amending the type, while it is unpushed, never by overriding the bump. --bump and --exclude are used only when the developer asks, with the value given. pk help ship, Deciding the version, has the rule, and the brief carries a sentence of it into every session.

A pending plan can be discarded, and status shows it. The preserve hook records the approved plan in the repository the session runs in. When the plan belongs to another repository and was preserved there with --plan, pk preserve --discard drops the pointer left behind and names the plan it pointed at, in place of a hand delete under .git/. pk status shows the pending plan on its own line, marks one whose file is gone, and notes the two ways out.

pk preserve --discard

The first release covers everything

v1.6.0 · 2026-10-05

No tag means nothing has been released. The first pk changelog covers the whole history from an implied v0.0.0, root commit included, and says so first: no release yet: the first release covers all N commits. Its entry links to the tag's commits, since a compare needs two ends. To keep earlier history out of the first release, tag v0.0.0 at the last commit that is history; everything after it is the release. The old hint to tag the newest commit is gone, and with it the way to mark unreleased work as released by accident.

Init places no tag, and works from any start. pk init no longer tags v0.0.0, so work committed before init is in the first release like everything after it, and the order of init and first commits stops mattering. When commits precede init's own, it says how many and names the exact command that keeps them as history, git tag v0.0.0 <sha>. --no-baseline is gone with the tag it skipped. A repository started on develop runs pk init --release main, and main is created at the root commit, carrying no unreleased work. pk help init has the recipe.

git init -b main
pk init

Status reports the release branch as git shows it. A policy naming a release branch that does not exist gets the command that creates it at the root. In merge flow, a release branch that has moved past its last tag is noted with the number of commits not in a release. The tag line now always prints, none yet before the first release.

The plugin runs wherever pk is

v1.5.0 · 2026-10-04

The plugin works from any source once pk is on the PATH. The plugin's shim runs the binary bundled beside it, and when there is none, because the plugin came from the GitHub repository, a directory listing, or a checkout, it now runs the pk on the PATH instead. A go install or Homebrew pk is enough for every hook to fire whichever way the plugin files arrived. The marketplace archive is unchanged: its bundled binary is still tried first.

go install github.com/markwharton/plankit/cmd/pk@latest

The brief says when pk and the plugin disagree. A pk from the PATH can be a release behind the plugin's skills, or ahead of them. The shim tells pk where the plugin is, and the brief's first sentence then names both versions and the command that levels them, go install for pk or /plugin update plankit@plankit for the plugin. Nothing reaches the network for this: the plugin's version is read from its own manifest. pk help brief has the sentence.

Source builds report dev plus the commit again. On Go 1.24 and later a build from a checkout had started reporting the toolchain's pseudo-version. make build and make bin-local now print dev+<sha>, with .dirty for an uncommitted tree, as the version page says. Release and go install builds are unaffected.

A configured repository loads the plugin for its teammates

v1.4.0 · 2026-10-03

A configured repository carries the plugin for its teammates. pk init now adds two entries to .claude/settings.json and commits them with the policy: the plankit marketplace under extraKnownMarketplaces and plankit@plankit under enabledPlugins. A teammate who trusts the folder has the marketplace and is one /plugin install plankit@plankit from the plugin; Claude Code names it in the plugin errors tab until then. Project settings apply over user settings, so the entry holds for a teammate who disabled the plugin for themselves. pk help init has the run in full.

The settings file stays yours. The two entries are spliced in, so every other byte of the file, its key order, spacing, hooks, and permissions, comes back as it went in. An entry already under its key, pointing somewhere else, is left as it is and reported. A file with uncommitted changes, or one that does not parse, stops the run before anything is written and names what to fix. pk never writes permissions; the allow rule in the README is still yours to add.

Init no longer guesses the release branch from a working branch. Without --release, init guards the branch checked out. When the clone knows origin's default branch and it is another, init now stops and names both, so a run from dev never guards dev by accident. Name the branch you mean and the run goes through; the policy is committed on the branch you are on, as before.

pk init --release main

A repository configured before this release is one edit away. pk status notes a settings file that lacks an entry and prints the JSON to merge in, rendered by the same code init uses. --quiet drops the note as it drops the others.

pk status

The plans directory is yours to name

v1.3.0 · 2026-09-26

A plan revised after approval is preserved again. When a plan is approved, the preserve hook records its path in .git/pk-pending-plan, and a typed pk preserve consumes that pointer. A plan you revise after that has no pointer, so pk preserve --plan names the file itself. The same rules hold: the path is a Claude Code plan under ~/.claude/plans/, identical bytes report the record that already exists, and the revision becomes the next sequence number for the day. While the pointer names a different plan, --plan refuses and names both files, so the pending plan is preserved first. --dry-run previews the filename and the commit. pk help preserve says how the pointer is set and where it lives.

pk preserve --plan ~/.claude/plans/<name>.md

The plans directory is yours to name. preserve.dir in .pk.json is where preserved plans are written, relative to the repository root. With the key absent it is docs/plans, so a configured repository is unchanged. Every reader follows the one key: preserve writes there, protect keeps it immutable, the brief tells each session where it is, and pk status reports it. A value that leaves the repository, or is not a clean forward-slash path, fails the file when it loads and names the key. pk init writes the key explicitly.

"preserve": { "dir": "documentation/plans" }

A typed pk preserve needs the policy file. In a repository with no .pk.json, the command now says so and names pk init, instead of committing to docs/plans as if the repository were configured. The approval hook is unchanged: without the file it does nothing.

A repository is configured in one run

v1.2.0 · 2026-09-20

A repository is configured in one run. pk init writes .pk.json and commits it, tags v0.0.0 so the release commands have a starting point, and prints the brief, so the session that ran it is told the policy the way every later session is. When the release branch is the only branch, it creates a working branch and checks it out: develop, or the name you give --branch. A repository that already has a working branch keeps it. Nothing is pushed unless you say --push, and --no-commit leaves the file for you to read and commit yourself. pk help init has the rest.

pk init

pk status says what is left to do. A repository configured by hand, or by an earlier pk init, can have commits with no tag, or the release branch as its only branch. Status now notes each and names the command that finishes the setup, so the state the release commands rely on is visible rather than assumed.

Any command page is enough to run the command. --plain, --project-dir, and --quiet work on every command, and each command's page now ends with that line, pointing at pk help help for the full block. A session that has read one page can run pk against another directory without a second lookup.

Releases start when you ask

v1.1.0 · 2026-09-12

A release starts when you ask for one. pk ship is the developer's command. A session runs it when you ask for a release, not on its own reading of a preview. The ship page says so, and says which pk commands are safe for a session to allowlist: the ones that only read or stage. pk ship and pk release publish, so they stay yours. pk help ship prints the page.

The release commands say where a release stands. A release is pending while its tag is absent, and finished once the tag exists. So a second pk ship reports nothing to release and stops, instead of announcing a release that already shipped. pk release names where a tag sits, on HEAD or on another commit, and pk preserve says when no plan is waiting rather than returning without a word.

pk help reads as one document. pk help document prints every page in index order, rendered at a terminal and as markdown through a pipe. --format man writes the same set as a man page. The man writer reads the compiled pages the terminal and the site read, so the three cannot drift, and nothing is installed: redirect it where you want it.

pk help document > plankit.md
pk help document --format man > pk.1

Every session is told which plugin version it runs. The brief runs the plugin's own binary, so the first sentence a session reads names that version: plankit 1.1.0 is configured in this repository. That is the plugin's version, and not necessarily the pk on your PATH, which pk version reports. The README and pk help overview give the commands that update the plugin.

plankit is a plugin

v1.0.0 · 2026-09-06

plankit is a Claude Code plugin. Install it once per machine and it is present in every session; run pk init once in a repository and it is on there. The repository gains one file, .pk.json, committed with the code; docs/plans/ appears when the first plan is preserved. A behavior change arrives as a plugin update.

/plugin marketplace add https://plankit.com/marketplace.json
/plugin install plankit

.pk.json is the whole configuration. The pages for guard, preserve, changelog, and release show what may go in their section of it: the section's shape, each key's allowed values and default, and what a wrong value does. The file's first line names its JSON Schema, so an editor validates it as you type; pk status reads it back and reports the first problem; and a file that fails to load is reported by every hook rather than silently ignored.

The plugin carries four hooks and the documentation. Guard runs before every shell command the agent issues, protect before every file edit, preserve when a plan is approved, and brief as a session starts. Nothing is copied into .claude/; a repository set up by a v0.x release removes what setup copied there, and pk help overview lists the steps.

Every session in a plankit repository starts with a briefing. The brief hook renders .pk.json as a few sentences of policy and injects them as the session begins: the commit types, the breaking-marker rule, which branches are protected, how plans are kept. The text is derived from the file each time, so it cannot disagree with what the other hooks enforce. pk brief at a terminal prints what your sessions are told.

Guard asks before a breaking marker is committed. A ! or a BREAKING CHANGE: footer drives the next major version, and that claim is the developer's to make. When the agent writes one, guard answers with a question instead of a decision, and you confirm or have it reworded. guard.breaking in .pk.json is the dial, ask by default.

pk ship runs changelog then release as one command. Its only state is the Release-Tag trailer, so a ship interrupted between the halves resumes at release when you run it again. /plankit:ship in a session dry-runs first and stops only on a major, for your go.

The documentation is one set of pages, written once. Claude Code loads them as /plankit: shortcuts, pk help prints them in a terminal, and plankit.com renders them. The craft page carries the engineering standards and a prompt library. /plankit:craft loads the standards; /plankit:craft review runs the review prompt.

pk works without Claude Code. Guard, changelog, release, ship, and pin run in any terminal. go install builds it with Go, and the release binaries for six platforms are attached to every GitHub release, with plankit.com/pk/<os>-<arch> pointing at the latest.