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.