Skip to main content
PM integrates into Claude Code at the session level — a SessionStart hook automatically injects the project briefing every time you start or resume, even after a context compaction. This guide covers the commands you’ll use every day once your project is initialized with /pm:init.

Starting a session

The moment Claude Code opens, PM is already working. The SessionStart hook fires on startup, resume, and compact events and runs conductor.mjs brief automatically. Without typing a single command, you immediately see:
  • The active epic — its lane, priority, and current story progress
  • The detour stack — any paused work, with ⚠ reconcile-on-resume flags where applicable
  • The next-up queue — your highest-priority queued epics ordered P0 → P3
If no project has been initialized with /pm:init, the hook is a complete no-op — it exits silently and does not affect the session in any way.

The daily loop

A typical working day with PM follows a clear rhythm:
1

Open Claude Code

The SessionStart briefing fires automatically. You see the active epic, any paused work, and your queue — no command needed.
2

Confirm with /pm:status

Run /pm:status for a fresh-rendered view. This re-renders PROJECT.md from .conductor/state.json first, so the output always reflects the current state of record.
3

Get a work recommendation

Run /pm:next to receive a concrete recommendation for what to build right now. PM applies the full priority algorithm and tells you exactly which epic to pick up and why.
4

Build the active epic in its lane

Work through the epic using whatever workflow its lane requires — an OpenSpec proposal flow, a Superpowers plan, or a direct claude-code build. PM tracks status but stays out of your way while you build.
5

Commit and update status

After each git commit, PM’s commit hook nudges you to update the epic’s status with update-epic — the verb, never a hand-edit of .conductor/state.json. This keeps the index honest and re-renders PROJECT.md automatically.
6

Sync new work

Run /pm:sync to register any new OpenSpec proposals or Superpowers plans that appeared on disk since your last session. If a github-issues tracker is configured, this also pulls open issues in as untriaged epics.

Checking status

/pm:status re-renders the briefing on demand. What you see:
  • Active epic — the currently in-flight epic, its lane (e.g. openspec, superpowers, claude-code), and live progress: its inline stories and the checkbox state in tasks.md or your plan file, counted together (0.50.0) — an archived change is read from its archived tasks.md
  • Detour stack — every paused frame in order, with a ⚠ reconcile-on-resume warning on any frame whose detour touched code the paused epic depends on
  • Next-up queue — the highest-priority queued epics sorted P0 → P3, with depends-on blockers named inline
  • Per-lane counts — how many epics are active, queued, or paused in each lane
The underlying state of record is always .conductor/state.json. PROJECT.md is a generated view — re-render it any time by running /pm:status or the render subcommand directly.

Deciding what’s next

/pm:next applies a strict priority algorithm so you never have to guess:
  1. If the detour stack is non-empty — the top frame is the answer. Finish or resume that work before picking up anything new.
  2. Otherwise — find the highest-priority queued epic, ordered P0 → P3.
  3. Ties — if two epics share the same priority, PM surfaces both and asks you to decide rather than silently picking one.
The depends-on link also shapes the queue: if an epic’s declared dependency is not yet archived, that epic is skipped entirely and the blocker is named explicitly in the output. You always know why something was skipped, not just that it was.

After a context compaction

PM is designed around the assumption that context will compact. Two hooks collaborate to make the recovery seamless:
  • PreCompact fires right before the context window collapses. It runs conductor.mjs snapshot, which re-renders PROJECT.md and writes .conductor/brief.txt with the freshest possible state — so nothing is lost in the collapse.
  • SessionStart fires immediately after (on the compact event) and re-injects the full briefing via additionalContext. By the time you see the next response, PM has already re-oriented the session around the current queue.
You do not need to do anything special after a compaction. PM handles the handoff.

After every git commit

PM runs commit-nudge after every Bash tool use — on PostToolUse when the call succeeded and, as of 0.45.0, on PostToolUseFailure when it failed, so a commit made inside a call that then exited non-zero is no longer invisible. The output envelope names the event it answers. A failed Bash call now spawns one hook process (63 ms per no-op run, mean of 20); a successful one gains none. The hook asks whether a commit actually happened and does two things:
  1. Re-renders PROJECT.md so the index stays current.
  2. Inspects the commit to detect whether it looks like an unlogged minimal detour — a small, self-contained fix that happened silently. If it does, PM suggests running log-detour to append a timestamped record to .conductor/detours.log.
The nudge is non-blocking. It surfaces the suggestion and moves on — it does not pause execution or wait for a response.
As of 0.32.0 this observes the repository rather than reading the command. It used to check whether the command’s text contained git commit, which fired on a grep, a heredoc, or a doc edit that merely mentioned it — asserting a commit that never happened. It now records a HEAD watermark on every Bash call and classifies any move through the reflog.That is flag-form-blind by construction: -am, -F, an editor commit with no message flag, escaped quotes, and forms git has not invented yet are all covered, because the subject comes from the commit itself rather than from a pattern over your command. It also fixes the opposite failure, which was the quieter one — a commit made inside a wrapper script, whose text never says git commit, used to go unnoticed entirely.Every read is three-valued: no git, not a repository, no reflog, or a missing watermark all fall back to the previous behaviour rather than erroring.
As of 0.45.0 it reports EVERY commit since a reflog anchor, not just HEAD. The watermark was a single position, so a commit followed by a checkout in the same call read as no commit at all — and the watermark had already moved past it, so no later call reported it either. Two commits in one call reported only the last. The hook now keeps an anchor in .conductor/commit-observe.json — the reflog’s byte size and its last line, stored as bytes (anchor.lineBase64, since a reflog is not guaranteed to be UTF-8) — plus the set of shas it has already named, and reports every commit… entry after the anchor, oldest first. The attribution command carries every --attribute-commit, in landing order, as one invocation.Every report states its provenance. The commits “landed since the last observation — this call, another terminal, or a parallel call; the hook cannot tell which.” It never claims your call made them. A commit made in another terminal is still reported, and can still be auto-logged against the active epic — a stated residual, corrected with retract-detour.A commit it could not have attributed safely is not offered. One rewritten by pull --rebase or reset away is named as rewritten or abandoned, with no row and no --attribute-commit. An amend replaces: the replaced commit’s automatic row is retracted and, for each epic that attributed it, update-epic <id> --withdraw-commit <replaced> --withdrawal-reason "amended into <new>" is printed before any attribution command — or, where update-epic would refuse that withdrawal for a delivered epic, the hook says so rather than printing a command that fails. An undone amend replaces nothing.Overlapping runs cannot drop or repeat a commit. The observation runs under an O_EXCL lock; a run that cannot take it within 200 ms skips and the next run reports. The lock is broken on liveness — at once for a dead holder, after 10 s where liveness cannot be confirmed, never for age alone while its holder is confirmed alive — and a run releases only its own lock.
As of 0.33.0 it also names the attribution command. The gate procedure asks you to record each commit against its epic as the commit is made — and until now nothing checked until the archive gate read the array, by which time the commits exist and the append-only ordering may already be broken. Now that the hook knows a commit actually landed, it prints the exact runnable command for the epic that commit belongs to. As of 0.45.0 it names every candidate, and leaves the choice to you. While a detour is live it used to name only the detour epic; it now lists the detour epic and each paused epic — or the active epic when no detour is live — one runnable command each, states explicitly that choosing is the agent’s, and orders a candidate whose own files the commits touch first. It never adds a candidate from paths, and it never writes an attribution itself. It is louder exactly once: while an epic has attributed nothing, the clause carries the catch-up-in-order rule, because an empty array is when catching up is most likely owed. After the first attribution it is one line. As of 0.44.0 a later catch-up is still correct — a recorded Gate 2 head must reach EVERY attributed commit, not only the last — it is simply less likely to be needed.
A commit that only moves or deletes a change’s artifacts must not be attributed — the /opsx:archive move above all. That commit lands after the reviewed range by construction, so attributing it makes the epic’s own Gate 2 read stale at the instant the archive gate checks it. The hook states the rule; it never classifies your commit itself.
Keep PROJECT.md open in a split pane. It updates after every render call so you can see the live queue, active epic, and detour stack at a glance without running a command.