{"id":50885884,"url":"https://github.com/stylusnexus/work-plan-toolkit","last_synced_at":"2026-06-15T17:00:36.666Z","repository":{"id":354562726,"uuid":"1224183656","full_name":"stylusnexus/work-plan-toolkit","owner":"stylusnexus","description":"Track-aware daily work planner for developers using Claude Code, Codex, Cursor, or GitHub Copilot across many GitHub issues. Self-contained Python CLI + skill bundle.","archived":false,"fork":false,"pushed_at":"2026-06-14T15:33:00.000Z","size":3016,"stargazers_count":1,"open_issues_count":12,"forks_count":2,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-14T17:21:21.446Z","etag":null,"topics":["agent-tools","ai-coding","claude-code","cli","codex","github-issues","gtd","productivity","python","task-management"],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/stylusnexus.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null}},"created_at":"2026-04-29T03:20:21.000Z","updated_at":"2026-06-13T17:37:10.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/stylusnexus/work-plan-toolkit","commit_stats":null,"previous_names":["stylusnexus/work-plan-toolkit"],"tags_count":19,"template":false,"template_full_name":null,"purl":"pkg:github/stylusnexus/work-plan-toolkit","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stylusnexus%2Fwork-plan-toolkit","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stylusnexus%2Fwork-plan-toolkit/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stylusnexus%2Fwork-plan-toolkit/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stylusnexus%2Fwork-plan-toolkit/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/stylusnexus","download_url":"https://codeload.github.com/stylusnexus/work-plan-toolkit/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stylusnexus%2Fwork-plan-toolkit/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34344497,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-15T02:00:07.085Z","response_time":63,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["agent-tools","ai-coding","claude-code","cli","codex","github-issues","gtd","productivity","python","task-management"],"created_at":"2026-06-15T17:00:27.055Z","updated_at":"2026-06-15T17:00:36.657Z","avatar_url":"https://github.com/stylusnexus.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# work-plan toolkit\n\n![License: MIT](https://img.shields.io/badge/license-MIT-blue)\n![Python 3.9+ stdlib](https://img.shields.io/badge/python-3.9%2B%20stdlib-3776AB)\n![Claude Code](https://img.shields.io/badge/Claude%20Code-plugin-7C3AED)\n![Codex](https://img.shields.io/badge/Codex-plugin-10A37F)\n\nTrack-aware daily work planning for developers running parallel Claude Code / Codex sessions across many GitHub issues.\n\n## What this is\n\nA daily work-planning system for developers running parallel AI sessions across many GitHub issues. It's made of three things: a pure-Python stdlib CLI, a set of YAML-frontmattered markdown files (\"tracks\"), and a `SKILL.md` that tells your AI how to use the CLI. Together they give you and your agent a shared, live picture of what's in flight — without asking you to maintain it manually.\n\n**Installs as a plugin** for Claude Code and Codex (see [Quick install](#quick-install) for the exact commands), as an npm global for any editor or terminal (`npm install -g @stylusnexus/work-plan`), and as a VS Code extension (search \"Work Plan\", publisher `stylusnexus`).\n\nThe system derives state *live* from GitHub (`gh`), `git`, and your track files on every run — nothing is mirrored or cached. AI sessions get a paste-ready context block; you stay oriented even when switching between a dozen parallel workstreams.\n\n**Why it exists:** born from the frustration of building detailed plans that die the moment you open a new agent session and start over. Inspired by [Andrej Karpathy's notes on vibe coding](https://gist.github.com/karpathy/442a6bf555914893e9891c11519de94f) and the specific pain of doing enthusiastic work on the wrong thing.\n\n## What this is not\n\n- **Not a Jira / Linear / GitHub Projects replacement.** It doesn't manage sprints, roadmaps, or capacity planning — it helps *you and your AI agent* stay oriented on GitHub issues you already have.\n- **Not a standalone issue tracker.** GitHub is canonical; `work-plan` just reads and references it.\n- **Not zero-setup.** Requires Python 3.9+, the `gh` GitHub CLI (authenticated), and `yq` (the Go version, not the Python one).\n- **Not a background service.** No daemon, no cache, no sync loop — `git pull` is the sync mechanism for shared tracks.\n- **Not a replacement for reading your code.** It tells you *what* to work on and *where you left off* — not what the code does.\n\n## Quick install\n\n**Claude Code (recommended):**\n```\n/plugin marketplace add stylusnexus/agent-plugins\n/plugin install work-plan@stylus-nexus\n```\n**Codex:** `codex plugin marketplace add stylusnexus/agent-plugins` → `codex plugin add work-plan@stylus-nexus`\n**npm (any editor / standalone CLI):**\n```\nnpm install -g @stylusnexus/work-plan\n```\n**VS Code extension** (the visual viewer): search **\"Work Plan\"** (publisher `stylusnexus`) in the Extensions view, or `code --install-extension stylusnexus.work-plan-viewer`. It drives the CLI above — see [VS Code extension](#vs-code-extension).\n**Cursor / Copilot / direct:** clone + `./install.sh` (see [Install](#install)).\n\nFull multi-agent guide: the [**agent-plugins** marketplace README](https://github.com/stylusnexus/agent-plugins). See [Install](#install) below for details, the npm path, and updating.\n\n\u003e **Command names:** examples below use the standalone form `/work-plan \u003csubcommand\u003e` (what `install.sh` gives you). **Installed as a plugin, commands are namespaced** — `/work-plan brief` → `/work-plan:brief`, `/work-plan handoff` → `/work-plan:handoff`, and the long tail is `/work-plan:run \u003csubcommand\u003e`. On Codex, invoke via `@work-plan` / `/skills`.\n\nThe five essentials you'll use 80% of the time are:\n\n| Command | When |\n|---|---|\n| `/work-plan brief` | Morning. Multi-track snapshot — what's on your plate across every active track. Add `--repo=\u003ckey\u003e` to scope to one project. |\n| `/work-plan handoff \u003ctrack\u003e` | End of a work block. Captures what you touched. Use `--auto-next` for an algorithmic priority-sorted `next_up` (no LLM), `--set-next 1,2,3` for explicit numbers, or pair with Claude in chat for a curated pick. |\n| `/work-plan orient \u003ctrack\u003e` | Switching context. ~15-line paste-block of priority / last session / next pick / git state — drop into a fresh Claude Code terminal. |\n| `/work-plan reconcile \u003ctrack\u003e \\| --all \\| --repo=\u003ckey\u003e [--draft] [--yes]` | Track frontmatter membership drifted from GitHub labels. Use on label-driven tracks only — for hand-curated tracks, use `refresh-md` instead. In an `--all`/`--repo` sweep it also moves issues relabeled from one track to another in the same repo. `--draft` previews proposed ADDs/MOVEs/FLAGs; `--yes` applies without prompting. `--repo=\u003ckey\u003e` scopes the sweep to one repo. |\n| `/work-plan hygiene [--repo=\u003ckey\u003e]` | **Weekly all-in-one cleanup.** Runs three steps: ① `refresh-md --all` (pull live GitHub state into every active track's status table), ② `reconcile --all` (sync frontmatter membership against GitHub labels), ③ `duplicates` (flag likely-duplicate issues). `--repo=\u003ckey\u003e` scopes steps ① and ② to one repo; step ③ is skipped in scoped mode. |\n| `/work-plan in-progress \u003cn\u003e [--clear]` | Starting or stopping active work on an issue. Adds (or removes with `--clear`) the `work-plan:in-progress` label on GitHub. Repo-resolved from the issue number, or pass `--repo=\u003ckey\\|slug\u003e` to disambiguate. `brief`/`orient`/the VS Code viewer also detect in-progress automatically from a hot `feat/\u003cn\u003e-`/`fix/\u003cn\u003e-` branch. |\n\nA dozen more subcommands cover slotting new issues into tracks, closing tracks (shipped/abandoned/parked), and one-time priority-label backfill. Three capabilities worth calling out explicitly:\n\n**Shared tracks** — track files can live *inside your repo clone* (`.work-plan/\u003cslug\u003e.md`) so teammates see the same planning state via `git pull`. The default is private (`notes_root/\u003crepo\u003e/`); register a local clone with `init-repo --local=\u003cpath\u003e` to opt into shared mode. Pass `--private` to any write command to keep a specific track local. See [Shared tracks](#shared-tracks).\n\n**AI-powered clustering (`group`)** — hand a flat list of GitHub issues to your AI and get back thematic track files. Run `group --milestone=X` to fetch all issues in a milestone, get a clustering prompt, save the JSON answer, then `group --apply` creates the tracks. Pairs with `auto-triage` for ongoing maintenance: once tracks exist, `auto-triage` assigns newly-filed untracked issues back into them.\n\n**Coverage + auto-triage** — `coverage --repo=\u003ckey\u003e` reports how many open issues fall outside the track model (42% on a real production repo). `auto-triage --repo=\u003ckey\u003e` then produces an AI prompt to assign those orphans to existing tracks. Run both periodically to keep the backlog visible.\n\n**Cross-track dependencies** — set `depends_on: [\u003ctrack-slug\u003e]` in a track's frontmatter to declare explicit dependencies between tracks. The VS Code viewer renders these as thick amber `==\u003e` edges in the dependency graph, and the detail panel shows clickable dependency chips that navigate directly to the dependent track. Set via `/work-plan set \u003ctrack\u003e depends_on=slug1,slug2` or the \"Edit Track Fields\" right-click menu in VS Code. Complementary to the issue-derived \"owns\" edges already inferred from blockers.\n\nBeyond issue tracking, **`plan-status`** answers a different question — *which of your accumulated plan/spec docs actually shipped, half-shipped, or died*. It correlates each plan's declared file-manifest (`Create:`/`Modify:`/`Test:` paths) against git and the filesystem rather than trusting checkboxes (which are routinely left unchecked even for shipped work). Read-only by default; optionally stamp the verdict into each doc (`--stamp`), get an AI verdict on prose/ambiguous docs (`--llm`), and act on the results behind confirmation gates (`--archive` dead plans, `--issues` for partial ones). See [Plan \u0026 doc liveness](#plan--doc-liveness-plan-status).\n\n## How it works\n\nThe toolkit treats GitHub as the canonical source of issue state and never tries to mirror it. Track markdown files are lightweight references — they list issue numbers and a few pieces of derived metadata (priority, milestone, `next_up`, `depends_on`, last session timestamp). The CLI re-derives everything else live from `gh`, `git`, and the markdown body.\n\n```mermaid\nflowchart TB\n    subgraph sources[\"Data sources (canonical)\"]\n        direction LR\n        gh[\"GitHub issues\u003cbr/\u003e(via gh CLI)\"]\n        git[\"git state\u003cbr/\u003e(branch, commits, modified files)\"]\n        md[\"Track markdown\u003cbr/\u003e(YAML frontmatter + body)\"]\n    end\n\n    subgraph cli[\"work-plan CLI (Python stdlib)\"]\n        direction LR\n        brief[\"brief\u003cbr/\u003e\u003ci\u003emulti-track view\u003c/i\u003e\"]\n        handoff[\"handoff\u003cbr/\u003e\u003ci\u003ecapture session + algorithmic or LLM next_up\u003c/i\u003e\"]\n        orient[\"orient\u003cbr/\u003e\u003ci\u003epaste-block context for new session\u003c/i\u003e\"]\n        hygiene[\"hygiene\u003cbr/\u003e\u003ci\u003eweekly drift + dup sweep\u003c/i\u003e\"]\n    end\n\n    subgraph outputs[\"Outputs\"]\n        direction LR\n        paste[\"Paste-ready blocks\u003cbr/\u003e(relayed verbatim to chat)\"]\n        update[\"Updated frontmatter\u003cbr/\u003e(next_up, session log, status table)\"]\n        sync[\"GitHub label sync\u003cbr/\u003e(reconcile, refresh-md)\"]\n    end\n\n    sources --\u003e cli\n    cli --\u003e outputs\n    update -.-\u003e|\"next day\"| md\n```\n\n**Daily rhythm**:\n\n- **Morning** → `brief` shows multi-track plate, then `orient \u003ctrack\u003e` produces a ~15-line paste-block to drop into a fresh agent session.\n- **End of work block** → `handoff \u003ctrack\u003e` captures what you touched. Three ways to set `next_up` for tomorrow:\n  - `handoff \u003ctrack\u003e --auto-next` — algorithmic (no LLM): top-3 by priority then most-recently-updated, blockers excluded. Interactive `[Y/n/edit]` prompt — accept, edit, or skip. (`--suggest-next` is the read-only, non-interactive sibling: it prints the same suggestion as JSON and writes nothing — the feed the VS Code **Suggest Next-Up (auto)** picker confirms, since the TTY prompt can't run under the extension.)\n  - `handoff \u003ctrack\u003e --set-next 4167,4148` — explicit numbers when you know exactly which issues are next.\n  - Free-form via Claude in your agent session, which can review project memory and write a curated list back. The two `--*-next` flags are the no-LLM paths.\n  - For tracks where you don't want to bother curating at all, set `next_up_auto: true` in the track's frontmatter — `brief` will then derive the list live each invocation, ignoring whatever's stored.\n  - **Ranking presets** — when `next_up_auto: true` is on, the default ranking is `flow` (milestone → dependency → priority → recency). Override per-track with `set-next-up \u003ctrack\u003e --preset=\u003cname\u003e`, or set `next_up_default: \u003cname\u003e` in your config for a global fallback. Named presets: `flow` (the default), `priority-driven` (priority first, no milestone bias — good for backlogs with no milestones), `backlog` (oldest issues first — surfaces stalled work). Custom criterion order: `set-next-up \u003ctrack\u003e --order=aging,priority,dependency`. Clear a track's override with `--clear`. Toggle auto-derivation itself with `--auto=on|off` (no hand-editing frontmatter required).\n- **Weekly** → `hygiene` runs `refresh-md --all` + `reconcile --all` + `duplicates` in sequence to keep status icons, GitHub labels, and dedup state honest.\n\n\u003e **When should I run `refresh-md`?** Any time you close or merge issues and want the track body to reflect the new state. `handoff` rewrites the status table for one track on every run, but `brief` reads GitHub live without writing anything back — so a track you haven't `handoff`'d recently stays stale on disk. `refresh-md \u003ctrack\u003e` (or **Sync Issue States from GitHub** in VS Code) fixes that on-demand; `hygiene` sweeps all tracks weekly.\n\n\u003e **`brief` and `orient` annotate blocked issues with `⊘ blocked by #N`** — read-only, surfaced from GitHub's native dependency edges, nothing is written back. Cross-repo blockers show as `owner/repo#N`; same-repo duplicates of manually-declared blockers are deduplicated.\n\n\u003e **GitHub access is read-only by default, with three explicit, opt-in write actions.** Issue *data* always comes from read-only `gh` calls (`gh issue list`, `gh issue view`), and every routine write (frontmatter, status table, session log) goes to your local markdown files only. The three GitHub-*mutating* actions are all opt-in and gated: `plan-status --issues` **creates** a GitHub issue per partial plan (`gh issue create`, prompts before opening); `close-issue` (#305) **closes** an issue via `gh issue close` — for the common case where a PR merged to `dev` left its issue OPEN (GitHub auto-closes only from the default branch), with the VS Code viewer firing a mandatory \"Close on GitHub? — cannot be undone\" modal on every close; and `in-progress` (#271) **adds or removes** the `work-plan:in-progress` label on an issue, public-repo gated via the confirm-token flow. Nothing else touches GitHub state.\n\n## Shared tracks\n\nBy default track files live under `notes_root/\u003crepo\u003e/` — local only, never committed. **Shared tracks** live inside the repo clone itself (`.work-plan/\u003cslug\u003e.md`) and travel with the repo via `git pull`/`git push`. Teammates see the same planning state without a separate notes sync.\n\n**Set up shared tracks for a repo:**\n\n```bash\n# Register the local clone path in your config\n/work-plan init-repo myproject --github=org/myproject --local=/path/to/clone\n```\n\nOnce `local:` is set and points to a valid git repo, all new tracks for that repo go into `.work-plan/` automatically.\n\n**Syncing:**\n\n```bash\ngit pull                          # pull teammates' track changes\ngit add .work-plan/ \u0026\u0026 git commit \u0026\u0026 git push  # share your own\n```\n\nThe CLI never auto-pushes. When you create or update a shared track, it prints a reminder:\n```\n↑ shared — commit + push to share with teammates.\n```\n\n### Canonical plan branch (`plan-branch`)\n\nStoring the plan as files *in* the repo makes it branch-scoped — but a track's status and priorities are facts about the **project**, not about a code branch. On a repo with `dev` + `main` + feature branches that means cross-branch plan divergence, merge conflicts on status-table edits, and planning churn polluting feature PRs and the deploy diff.\n\n`plan-branch` pins the shared tier to **one canonical branch** per repo, read and written through a dedicated git worktree, so the plan lives off your code branches entirely — yet the CLI and VS Code viewer always show it from any checkout.\n\n```bash\n/work-plan plan-branch init myproject        # orphan branch `work-plan/plan` + skeleton (local only)\n/work-plan plan-branch status myproject      # exists? published? how many unpushed commits?\n/work-plan plan-branch push myproject        # share it — gated with a confirm token on PUBLIC repos\n```\n\nThe default branch is an **orphan** `work-plan/plan` (no shared history with your code, like `gh-pages`) — override with `--branch=\u003cname\u003e`. `init` is **local only**; a teammate who already published the branch is auto-**connected** instead of re-created. Once `plan_branch` is set, every shared-track write is committed onto that branch via the worktree (never your working branch), and `push` is the one deliberate step that shares it. `push --dry-run` previews exactly what would be exposed first.\n\n\u003e **Tip:** exclude the plan branch from CI by adding `!work-plan/**` to your workflow's `on: push` branch filter, so planning commits never trigger builds or deploys.\n\n**Opt out per-command:** pass `--private` to route a specific track to `notes_root` instead:\n\n```bash\n/work-plan group --milestone='v1.0' --private   # keep clusters local\n/work-plan new-track myproject exploration       # --private for one-off tracks\n```\n\n**Multi-repo disambiguation:** if the same track slug exists in two repos, qualify with `@\u003crepo\u003e` or `--repo=\u003ckey\u003e`:\n\n```bash\n/work-plan slot 4234 auth-flow@myproject\n/work-plan close auth-flow --repo=myproject\n```\n\n## Plan \u0026 doc liveness (`plan-status`)\n\nThe track commands above are about *issues*. `plan-status` is about *documents* — the plans, specs, and design docs that pile up in a repo (especially the ones planning workflows like Superpowers generate) and then quietly **go to die**. Months later, nobody can tell what actually got built, what's half-done, and what was abandoned.\n\n**The problem is specific and measurable: the checkboxes lie.** A plan's `- [ ]` / `- [x]` boxes are supposed to track completion, but the agent executing the plan tracks progress in its own scratchpad and rarely edits the file. So boxes stay empty even when the whole feature shipped. On one real repo, **134 of 140 shipped plans showed fewer than 25% of their boxes checked** — they looked abandoned; they were done.\n\n`plan-status` ignores the checkboxes and reads a quieter, honest signal. A well-formed plan declares the **exact files it will create, modify, and test** — so every plan is really a *manifest of files that should exist*. The tool asks git and the filesystem: *of the files this plan promised, how many now exist and were committed?* That number is the real completion.\n\n```\n$ /work-plan plan-status --repo=myproject\n\n# plan-status — /path/to/myproject\n332 docs · 140 shipped · 20 partial · 172 manifest-less\nlie-gap (shipped but \u003c25% boxes checked): 134\n\n## ✅ shipped (140)\n  docs/plans/2026-03-16-idea-mode-ui.md\n      9/9 declared files present (boxes stale)\n  ...\n## 🟡 partial (20)\n  docs/plans/2026-05-01-v0.4.0-one-week-closeout.md\n      19/40 declared files present\n  ...\n```\n\nEach doc reaches one of these verdicts:\n\n| Verdict | Meaning |\n|---|---|\n| ✅ **shipped** | (nearly) all declared files present — done, even if the boxes say otherwise |\n| 🟡 **partial** | some files present — genuinely in progress; *this is your to-do list* |\n| 💀 **dead** | no files, long untouched — an abandonment candidate |\n| 👻 **manifest-less** | a prose doc with no file-manifest (e.g. a design spec) — needs a judgment call |\n| 🧳 **foreign** | a misfiled plan whose declared files live in *another* repo — not this repo's work at all |\n\n**Judging the ambiguous ones (`--llm`).** Prose specs (no manifest) and plans whose files look absent get a two-step AI pass: `--llm` gathers each candidate plus its git evidence and prints a prompt; you save the model's JSON verdicts to the cache; `--llm --apply` merges them in. The CLI never calls an LLM itself — same two-step contract as `group`/`suggest-priorities`.\n\n**Acting on the results (gated).** Once you trust the verdicts:\n- `--archive` moves 💀 dead plans into `archive/abandoned/` (history-preserving `git mv`).\n- `--issues` opens a GitHub issue per 🟡 partial plan, listing its unsatisfied files.\n\nBoth are confirmation-gated and honor `--draft` (preview, zero side effects).\n\n**Stamping (`--stamp`).** Add `--stamp` and the verdict is written *into the doc itself* as a small, idempotent header, so the truth lives next to the plan:\n\n```markdown\n# Idea Mode UI — Implementation Plan\n\n\u003c!-- plan-status: BEGIN --\u003e\n\u003e **Status:** ✅ shipped · 9/9 files · last touched 2026-03-20\n\u003c!-- plan-status: END --\u003e\n```\n\nThe block is derived entirely from evidence (no timestamp), so re-stamping unchanged docs produces zero diff — run it as often as you like. `--draft` previews exactly which docs would change and writes nothing.\n\n**Safety:** read-only by default — it mutates nothing unless you pass `--stamp`, `--archive`, or `--issues`, and those last two prompt before acting. Git is the only local state it touches, so stamps and archives are reversible with `git restore`. Point it at a repo with `--repo=\u003ckey\u003e` (from your config) or just run it from inside the repo. In a Claude session you don't need the flags — ask in plain language (\"*which plans in this repo are done vs unfinished?*\", \"*stamp the plan statuses*\", \"*archive the dead plans*\") and the skill maps it to the right command.\n\n## Requirements\n\nThe toolkit is a Python CLI that shells out to standard tools. You need **all four** installed before running `install.sh` / `install.ps1`:\n\n| Tool | Min version | Why |\n|---|---|---|\n| Python | **3.9+** | The CLI itself. Uses PEP 585 generics (`list[dict]`, `dict[int, str]`), no 3.10+ features, no third-party libraries (stdlib only — no `pip install` step). |\n| `gh` | recent | Live GitHub state queries (issues, milestones, labels). Must be authenticated: `gh auth login` once before first run. |\n| `git` | any 2.x | Detects current branch, ahead-of-upstream count, modified files. |\n| `yq` (mikefarah/yq, Go-based) | 4.x | Reads + edits YAML frontmatter and config. **Note**: Python `yq` (kislyuk/yq, the jq wrapper) won't work — install the Go version. |\n\nInstall per platform (one-liners):\n\n```bash\n# macOS (Homebrew)\nbrew install python@3 gh git yq\n\n# Linux (Debian/Ubuntu)\nsudo apt update \u0026\u0026 sudo apt install python3 git\n# gh: https://github.com/cli/cli/blob/trunk/docs/install_linux.md\n# yq: sudo wget -qO /usr/local/bin/yq https://github.com/mikefarah/yq/releases/download/v4.53.2/yq_linux_amd64 \u0026\u0026 sudo chmod +x /usr/local/bin/yq\n\n# Linux (Arch)\nsudo pacman -S python github-cli git go-yq\n\n# Windows (PowerShell with winget)\nwinget install Python.Python.3 GitHub.cli Git.Git MikeFarah.yq\n```\n\n`install.sh` and `install.ps1` both verify all four are on `PATH` before doing anything else, and print install hints if any are missing.\n\nAfter installing, authenticate `gh` once:\n\n```bash\ngh auth login   # follow the prompts; needs `repo` scope to read issues\n```\n\n## Compatible tools\n\nA skill has two distinct contracts: (1) the underlying **CLI** that does the work, and (2) the **SKILL.md prompt-engineering** that tells the LLM how to use it (when to relay output verbatim, how to pick `next_up`, etc.). The CLI is portable; the prompt-engineering is model-specific. Honest split:\n\n| Layer | What it is | Claude Code | Codex | Cursor | GitHub Copilot |\n|---|---|---|---|---|---|\n| **1. Python CLI** | `work_plan.py` + subcommands. Pure stdlib, shells out to `gh`/`git`/`yq`. | ✅ Proven | ✅ Proven | ✅ Direct invocation | ✅ Direct invocation |\n| **2. Skill auto-discovery** | LLM client reads SKILL.md frontmatter, surfaces skill on relevant prompts | ✅ Proven via `~/.claude/skills/` | ⚠️ Per spec via `~/.agents/skills/` — **unverified** | ❌ No native skill system; use the Cursor shim (see below) | ❌ No native skill system; use the Copilot shim (see below) |\n| **3. Instruction compliance** | Model follows prompt-engineered rules (verbatim relay, Claude-driven `next_up` flow, two-step AI subcommands) | ✅ Tested with Opus 4.x and Sonnet 4.x | ⚠️ Likely with GPT-4 class, may degrade with smaller models | ⚠️ Depends on which model Cursor is set to; less reliable than purpose-built skill systems | ⚠️ Copilot Chat models often ignore long context; basic CLI usage works, prompt-engineered behaviors don't |\n\n### Install per platform\n\n| Tool | Install command | Then invoke as |\n|---|---|---|\n| **npm (standalone CLI / any editor)** | `npm install -g @stylusnexus/work-plan` (requires `python3` + `yq` + `gh` already on PATH — the CLI warns on first run if any are missing). | `work-plan \u003csubcommand\u003e` |\n| **Claude Code** | **Plugin (recommended):** `/plugin marketplace add stylusnexus/agent-plugins` → `/plugin install work-plan@stylus-nexus`. Or script: `./install.sh` / `.\\install.ps1` | Plugin: `/work-plan:brief` … `/work-plan:run \u003csub\u003e`. Script: bare `/work-plan \u003csubcommand\u003e` |\n| **Codex** | **Plugin:** `codex plugin marketplace add stylusnexus/agent-plugins` → `codex plugin add work-plan@stylus-nexus`. Or script: `./install.sh --target=$HOME/.agents` | Plugin: `@work-plan` / `/skills`. Script: direct CLI |\n| **Cursor** | Skip installer. Clone repo + copy `shims/cursor/work-plan.cursorrules` into your project's `.cursorrules` (or merge it in) | `python3 \u003ctoolkit\u003e/skills/work-plan/work_plan.py \u003csub\u003e` — alias `wp` recommended |\n| **GitHub Copilot** | Skip installer. Clone repo + copy `shims/copilot/work-plan-copilot-instructions.md` into your project's `.github/copilot-instructions.md` (merge if it already exists) | Direct CLI as above |\n| **Any other tool** | Skip installer. Just `git clone`. | Direct CLI |\n\nShell rc alias for the direct-CLI cases:\n\n```bash\n# bash/zsh (~/.bashrc, ~/.zshrc)\nalias wp=\"python3 /path/to/work-plan-toolkit/skills/work-plan/work_plan.py\"\n\n# PowerShell ($PROFILE)\nfunction wp { python \"C:\\path\\to\\work-plan-toolkit\\skills\\work-plan\\work_plan.py\" @args }\n```\n\nTo install for **both** Claude Code AND Codex, run the installer twice with different `--target` values.\n\n### VS Code extension\n\nThe **Work Plan** extension is the visual face of the CLI — a sidebar tree (repos → tracks, with per-track open/closed counts and an **activity-bar badge** for blocked/open status), a Mermaid dependency graph (with focus toggle and repo-scoped full map) that draws **GitHub-native blocked-by edges**, per-track detail with an **open/closed progress bar**, a one-click **Plan** link, a **per-issue in-progress badge + toggle**, expandable **blocked-by / blocking dependency chips**, the Untracked bucket, cross-track dependency chips, per-issue move/close buttons, **keyword issue search** (`%wildcard%` substitution, results in a dedicated tab), the daily-driver **Brief / Re-orient / Handoff** commands, **next-up controls** — a **Set Next-Up** button and a **Set Next-Up Order…** preset picker (`flow` / `priority-driven` / `backlog`) with the active preset shown inline, an inline **active lens + sort** indicator under the view title, a **Plans view** with confirm-gated frontmatter writes (verdict / acknowledge / drift-baseline) and a fast-fail GitHub-auth banner, and full read/write (slot/close/edit/move/new-track/push-track/…) with a public-repo confirm modal.\n\n![Work Plan VS Code extension — sidebar and dependency graph](https://raw.githubusercontent.com/stylusnexus/work-plan-toolkit/main/vscode/media/screenshots/dependency-graph.png)\n\nInstall it from either registry:\n\n- **VS Code Marketplace:** Extensions view → search **\"Work Plan\"** (publisher `stylusnexus`), or `code --install-extension stylusnexus.work-plan-viewer`.\n- **Open VSX** (VS Codium / Cursor / Windsurf): search **\"Work Plan\"**, or `ovsx get stylusnexus.work-plan-viewer`.\n\nThe extension **shells out to the `work-plan` CLI**, so install the CLI too (npm or any method above). If `work-plan` isn't on your editor's `PATH` — common when VS Code is launched from the Dock/Finder rather than a terminal — set **`workPlan.cliPath`** in Settings to an absolute launcher path (e.g. `/path/to/work-plan-toolkit/bin/work-plan`, or the npm global bin), then reload the window. Extensions auto-update from the registry.\n\nUseful settings:\n\n| Setting | Default | What it does |\n|---|---|---|\n| `workPlan.cliPath` | `\"work-plan\"` | Absolute path to the CLI, if it's not on the editor's PATH |\n| `workPlan.autoRefreshInterval` | `0` (off) | Re-poll the CLI silently in the background (seconds). Set to 30, 60, 300, or 900 if teammates are pushing shared-track changes and you want the tree to stay current without manual refresh |\n| `workPlan.expandReposByDefault` | `false` | Expand all repo groups on load (single-repo workspaces always expand) |\n\nShared tracks show a **`shared`** tag in the tree description so you can tell at a glance which tracks travel via `git push/pull` and which are local-only.\n\n### Updating\n\n| Installed via | Update with |\n|---|---|\n| **npm** | `npm install -g @stylusnexus/work-plan@latest` (or `npm update -g @stylusnexus/work-plan`) |\n| **Claude Code / Codex plugin** | `/plugin update work-plan@stylus-nexus` (Codex: `codex plugin update …`) |\n| **Script (`install.sh`)** | `git pull` in the toolkit repo, then re-run `./install.sh` (or `.\\install.ps1`) |\n| **VS Code extension** | Auto-updates; or Extensions view → update manually |\n\nCheck your version any time with `work-plan --version`.\n\n### What the shims do\n\nFor tools without a native skill system (Cursor, Copilot), `shims/` contains drop-in files that give the LLM the same prompt-engineered behavior the SKILL.md provides on Claude Code: condensed CLI usage, when to relay verbatim, the two-step AI subcommand pattern, and a pointer to the full toolkit docs.\n\nThe shims are **per-project** — copy them into each repo where you want the work-plan tool surfaced to your agent. They don't auto-load globally.\n\n## Install\n\nPick the path for your tool. All three install the same CLI + skills.\n\n### Claude Code (recommended) — plugin, easy updates\n\n```\n/plugin marketplace add stylusnexus/agent-plugins\n/plugin install work-plan@stylus-nexus\n```\n\nCommands are namespaced under the plugin: `/work-plan:brief`, `/work-plan:handoff`,\n`/work-plan:orient`, `/work-plan:hygiene`, `/work-plan:status`, and\n`/work-plan:run \u003csubcommand\u003e` for everything else. Update with\n`/plugin update work-plan@stylus-nexus`. Works in the CLI and the VS Code / JetBrains extensions.\n\n### Codex — plugin\n\n```\ncodex plugin marketplace add stylusnexus/agent-plugins\ncodex plugin add work-plan@stylus-nexus\n```\n\n### Cursor / direct / other — script\n\n```bash\ngit clone \u003cthis-repo\u003e work-plan-toolkit\ncd work-plan-toolkit \u0026\u0026 ./install.sh          # macOS / Linux / WSL\n# or, on Windows native PowerShell:  .\\install.ps1\n# or, for Codex's skills dir:        ./install.sh --target=$HOME/.agents\n```\n\nGives the single bare `/work-plan \u003csubcommand\u003e` (no namespace). Re-run after `git pull` to refresh\n(the plugin paths above update themselves).\n\nThe installer:\n\n- **Copies** (not symlinks — for Windows compatibility) `skills/work-plan` and `skills/repo-activity-summary` into `~/.claude/skills/`\n- Installs the `work-plan` launcher (`bin/work-plan` + `bin/work-plan.cmd` on Windows) and copies the standalone dispatcher (`installer/work-plan.md`) into `~/.claude/commands/work-plan.md` (the per-verb suite is plugin-only)\n- **Self-seeds** `~/.claude/work-plan/config.yml` on first run if absent (one config home for every install mode), with `notes_root` at `~/.claude/work-plan/notes`\n- Drops a `.installed-from` marker so `uninstall` knows what's safe to remove\n\nExternal dependencies (verified by the installer): `gh`, `git`, `yq`, `python3`.\n\n\u003e **Versioning:** releases use CalVer (`YYYY.MM.DD+\u003csha\u003e`, auto-bumped on deploy and synced into the plugin manifests) — not SemVer.\n\n### What gets created\n\nAfter clone, the toolkit looks like this:\n\n```\nwork-plan-toolkit/\n├── README.md\n├── LICENSE\n├── .gitignore\n├── install.sh / install.ps1            # macOS+Linux+WSL / Windows\n├── uninstall.sh / uninstall.ps1\n├── skills/\n│   ├── work-plan/\n│   │   ├── SKILL.md\n│   │   ├── work_plan.py                # CLI entry\n│   │   ├── commands/                   # 24 subcommand modules\n│   │   ├── lib/                        # config, frontmatter, gh, git, prompts, …\n│   │   └── tests/                      # 600+ unittest cases\n│   └── repo-activity-summary/\n│       └── SKILL.md                    # bundled companion skill\n├── commands/\n│   └── work-plan.md                    # Claude Code slash command alias\n├── docs/\n│   └── usage-examples.md\n├── shims/                              # Drop-in instruction files for non-skill-aware tools\n│   ├── README.md\n│   ├── cursor/work-plan.cursorrules\n│   └── copilot/work-plan-copilot-instructions.md\n└── notes/\n    └── README.md                       # default notes_root (empty until init-repo)\n```\n\nAfter running `install.sh` (or `install.ps1`), the installer creates this in your home directory:\n\n```\n~/.claude/\n├── skills/\n│   ├── work-plan/                      # copy of toolkit's skills/work-plan/\n│   │   ├── SKILL.md\n│   │   ├── work_plan.py\n│   │   ├── commands/\n│   │   ├── lib/\n│   │   ├── tests/\n│   │   └── .installed-from             # marker file (toolkit absolute path)\n│   └── repo-activity-summary/\n│       ├── SKILL.md\n│       └── .installed-from             # marker file\n├── commands/\n│   └── work-plan.md                    # copy of toolkit's commands/work-plan.md\n└── work-plan/\n    └── config.yml                      # seeded from template, notes_root resolved\n                                        # to absolute toolkit path (edit this one)\n```\n\nThen, when you run `/work-plan init-repo myproject --github=your-org/myproject`, the toolkit's `notes/` folder gets a per-repo subdir (under whatever `notes_root` resolves to):\n\n```\n\u003cnotes_root\u003e/\n└── myproject/                          # created by init-repo\n    ├── archive/\n    │   ├── shipped/.gitkeep            # close mv's shipped tracks here\n    │   └── abandoned/.gitkeep          # close mv's abandoned tracks here\n    └── \u003ctrack-slug\u003e.md                 # active tracks live at top level\n```\n\n## Configure\n\nAfter install, bootstrap your first repo with the **`init-repo`** subcommand:\n\n```bash\n/work-plan init-repo myproject --github=your-org/myproject --local=/path/to/checkout\n```\n\nThis creates `\u003cnotes_root\u003e/myproject/archive/{shipped,abandoned}/` and adds the repo block to your `~/.claude/work-plan/config.yml` (idempotent; errors if the key already exists). Skip `--github` / `--local` to be prompted interactively.\n\nYou can also edit `~/.claude/work-plan/config.yml` directly:\n\n```yaml\nnotes_root: /absolute/path/to/your/notes/    # or keep the bundled default\nrepos:\n  myproject:\n    github: your-org/myproject\n    local: /path/to/local/checkout           # optional, enables in-progress detection\nnotes_vcs:\n  auto_commit: true                          # opt-in local history for notes_root (set by `notes-vcs init`)\n```\n\n`notes_vcs.auto_commit` is added by `/work-plan notes-vcs init` (or `enable`) — when on, every track-mutating command commits `notes_root` so private-tier edits are undoable. Absent → off.\n\n### Where your config lives\n\nThe active config the skill reads is **`~/.claude/work-plan/config.yml`** — created by `install.sh` (or `install.ps1`) on first run. There's no template file in the repo to confuse with the runtime config; install just writes the right two lines directly.\n\nAfter a fresh install, it looks like:\n\n```yaml\n# work-plan config — created by install.sh. Edit this file to customize.\n# Run /work-plan init-repo \u003ckey\u003e --github=\u003corg/repo\u003e to populate repos:.\nnotes_root: /absolute/path/to/work-plan-toolkit/notes\nrepos: {}\n```\n\n`notes_root` is the **absolute path of the toolkit's bundled `notes/` folder**, so the default works out of the box. To change it (e.g., to `~/Documents/Project Notes/`), edit this file.\n\nThe bundled `notes/` folder stays empty until you run `/work-plan init-repo \u003ckey\u003e`, which adds a per-repo subdir + writes the repo block back into this same config file via `yq`.\n\n## Security \u0026 data handling\n\n- **No credentials stored.** All GitHub access goes through your existing `gh auth`. This toolkit never reads, writes, or stores GitHub tokens.\n- **Writes are local by default; every remote/GitHub write is opt-in and gated.** The skill writes to `~/.claude/skills/work-plan/`, `~/.claude/skills/repo-activity-summary/`, `~/.claude/commands/work-plan.md`, `~/.claude/work-plan/config.yml`, and your `notes_root`. Repo-confined writes: the `plan-status` action flags (`--stamp` writes a status header into discovered plan docs; `--archive` `git mv`s dead plans into `archive/abandoned/`; all honor `--draft` and prompt), and the frontmatter-only plan writers `plan-confirm` (`verdict_override`), `plan-ack` (`acknowledged`), `plan-baseline` (`verdict_baseline`) — each writes one key into a plan doc's **YAML frontmatter only** (never its body/checkboxes/manifest), public-repo gated. **GitHub-mutating** (opt-in, gated): `plan-status --issues` *creates* an issue per partial plan; `close-issue` *closes* an issue (`gh issue close`); and `in-progress` *adds or removes* the `work-plan:in-progress` label on an issue. **Remote git push** (opt-in, public-repo gated): `plan-branch push` and `push-track` publish the shared plan branch. Nothing else.\n- **No telemetry, no network calls beyond `gh`.** All GitHub operations go through `gh` (your authenticated session); no direct HTTP requests are made.\n- **AI subcommands (`group`, `suggest-priorities`) send issue titles to Claude** via Claude Code's existing integration. Body content, code, and PR contents are NOT sent. If your repo is private and you're cautious about what reaches the model, skip these subcommands.\n- **`init-repo` writes to your config via `yq -i`.** Inputs are JSON-encoded before being passed to `yq`, so a maliciously crafted `--github=` value can't break out of the YAML edit.\n- **`install.sh` / `install.ps1` only touch user-owned dirs.** No `sudo`, no system-wide changes, no privilege escalation.\n\nFor vulnerability reporting, threat model, and past advisories, see [SECURITY.md](./SECURITY.md).\n\n## Usage walkthrough\n\nSee `docs/usage-examples.md` for end-to-end scenarios (morning brief, mid-work handoff, fresh-session orient, weekly hygiene).\n\n## Subcommand reference\n\n| Subcommand | What it does |\n|---|---|\n| `brief [--repo=\u003ckey\u003e]` | Multi-track snapshot of all active tracks across configured repos. `--repo=\u003ckey\u003e` filters to one project (matches the folder name under `notes_root` or the `org/repo` GitHub slug; archived-reopen callouts are also scoped). |\n| `handoff \u003ctrack\u003e [--auto-next \\| --set-next 1,2,3]` | Wrap up a work block. Writes a `### Session — \u003cts\u003e` entry. `--auto-next` suggests a priority-sorted top-3 from open issues (interactive: apply / edit / skip). `--set-next 1,2,3` is the explicit form — note it writes the session entry too; for a field-only `next_up` change with no session log, use `set next_up=…`. Without either flag, just captures the session summary and reads any pre-existing `next_up`. |\n| `orient [track]` (alias: `where-was-i`) | Read-only paste block. With a track name: ~15-line track summary (priority, last session, next pick, git state). With no track: cwd snapshot (branch, recent commits, modified files) for non-track work. Add `--pick` for the interactive track picker. |\n| `slot \u003cissue-num\u003e [track]` | A new GitHub issue should belong to a track — adds it to the track's `github.issues` list. Non-interactive flags: `--move`/`--no-move` (relocate the issue off its prior track, or leave it; default no-move), `--confirm=\u003ctoken\u003e` (public-repo gate, see below). |\n| `close \u003ctrack\u003e [--state=shipped\\|parked\\|abandoned] [--note=\u003ctext\u003e]` | Mark track shipped, parked, or abandoned. Moves to `archive/\u003cstate\u003e/` for shipped/abandoned. Pass `--state=` (and an optional `--note=`) to run without prompts. |\n| `refresh-md \u003ctrack\u003e` `\\|` `--all` `\\|` `--repo=\u003ckey\u003e` | Sync issue STATE (open/closed, status labels) from GitHub into the track body's status table. Does NOT change track membership — this is the right tool for \"refresh the work I just completed.\" For a **canonical** table it re-derives the whole block from live data, milestone-ordered (active milestone first; see `canonicalize`), so the table self-heals and stays grouped instead of decaying; narrative (non-canonical) tables are updated conservatively in place. If the live fetch comes back incomplete (GitHub timeout/permission error, or a frontmatter issue that no longer resolves), that track is **skipped and left untouched** rather than rewriting valid rows as `(not fetched)`, and the command exits nonzero so sweeps can flag the degraded run. `--all` sweeps every active track; `--repo=\u003ckey\u003e` scopes the sweep to one repo. |\n| `hygiene [--repo=\u003ckey\u003e]` | Weekly all-in-one: `refresh-md` + `reconcile` + `duplicates`. With `--repo=\u003ckey\u003e`, steps 1 and 2 scope to that repo and the global `duplicates` step is skipped. |\n| `list [--all] [--sort=recent\\|priority]` | List active tracks (or all including parked/archived). `--sort=recent` orders by `last_touched` (most recent first); `--sort=priority` orders by `launch_priority` (P0→P3) with recency as tiebreaker. Default keeps discovery order. |\n| `init \u003cpath\u003e [--priority=P0..P3] [--milestone=\u003cm\u003e]` | Add frontmatter to a brand-new track .md file (the file must already exist). Pass `--priority=`/`--milestone=` to skip the prompts. |\n| `init-repo \u003ckey\u003e --github=\u003cslug\u003e [--local=\u003cpath\u003e] [--update [--clear-local]]` | Bootstrap a new repo: create `\u003cnotes_root\u003e/\u003ckey\u003e/archive/{shipped,abandoned}/` and add the repo block to your config. `--github` is required for an add; `--local` is optional. `--update` on an existing key changes its local/github; `--update --clear-local` forgets the saved local path (keeps github + other fields). `--clear-local` and `--local` are mutually exclusive. |\n| `remove-repo \u003ckey\u003e` | Unregister a repo: delete its block from your config. **Config-only** — the notes folder, any tracks, and the local clone are left untouched (a notes folder or tracks that referenced it are now orphaned and can be removed by hand). Completes the add/update/remove trio with `init-repo`. |\n| `new-track \u003crepo\u003e \u003cslug\u003e [--priority=P0..P3] [--milestone=\u003cm\u003e]` | One-shot, non-interactive: create a new track file under `notes_root` for `\u003crepo\u003e` (a config key **or** an `org/repo` slug) with frontmatter. Unlike `init`, it makes the file for you — the headless creation path the VS Code viewer uses. |\n| `rename-track \u003cold-slug \\| old@repo\u003e \u003cnew-slug\u003e [--repo=\u003ckey\u003e] [--fix-refs] [--commit]` | Rename an active track's slug: moves its `.md` file and updates the frontmatter `track` field + `last_touched`. Validates `\u003cnew-slug\u003e` like `new-track` and rejects a name already taken in the same repo/tier. For shared tracks, `--commit` stages + commits the move (otherwise it prints a \"commit to share\" hint). `--fix-refs` rewrites sibling tracks' `depends_on` that reference the old slug; without it they're just warned about. Archived tracks are immutable. Public-repo gated. |\n| `set-next-up \u003ctrack\u003e (--preset=\u003cname\u003e \\| --order=a,b,c \\| --clear \\| --auto=on\\|off) [--repo=\u003ckey\u003e] [--confirm=\u003ctoken\u003e]` | Configure the ranking preset and/or auto-derivation flag for a track's next_up list. `--preset` sets a named preset: `flow` (default — milestone → dependency → priority → recency), `priority-driven` (priority first, ignores milestone bias, for backlogs with no milestones), `backlog` (oldest issues first — surfaces stalled work), or `custom` (requires `--order`). `--order=a,b,c` sets a custom comma-separated criterion list (`milestone`, `dependency`, `priority`, `recency`, `aging`). `--clear` reverts to the global `next_up_default` config or the default `flow`. `--auto=on` activates `next_up_auto` so brief/orient/export auto-derive the next-up list live from the ranking preset (ignoring the curated list); `--auto=off` clears it to revert to the curated list. `--auto` can be used standalone or combined with `--preset`/`--order`/`--clear`. Writes `next_up_order` and/or `next_up_auto` into the track's frontmatter; does NOT touch the `next_up` issue-list. Global default: add `next_up_default: \u003cpreset\u003e` to `~/.claude/work-plan/config.yml`. Public-repo gated. |\n| `set-notes-root \u003cpath\u003e` | Relocate where your private track notes live (updates `notes_root` in config). Does not move existing tracks — it warns if any would be orphaned. |\n| `notes-vcs \u003cinit\\|enable\\|disable\\|status\\|undo\u003e [\u003csha\u003e] [--no-enable] [--json]` | Opt-in **local** version control for the private `notes_root` tier — history/undo for tracks on your machine, never pushed. `init` git-inits `notes_root` as a personal repo (baseline commit of existing tracks) and turns on auto-commit (`--no-enable` skips that). For safety it **refuses** a `notes_root` that already has a git remote or is a repo work-plan didn't create. When on, every track-mutating command (`slot`/`group`/`handoff`/`close`/`set`/…) commits **only the files it changed** (pre-existing uncommitted edits are left alone) as an undoable commit. The shared tier is unaffected — it's versioned by its own repo. `status` shows whether `notes_root` is a repo, whether auto-commit is on, and the last commit (`--json` for the machine shape the VS Code viewer polls). `undo [\u003csha\u003e]` reverts a commit (default HEAD) — reverses the last edit. |\n| `push-track \u003ctrack\\|track@repo\u003e [--repo=\u003ckey\u003e] [--no-push] [--confirm=\u003ctoken\u003e]` | **Promote a private track to the shared tier and publish it** (#306). Moves the track's `.md` from `notes_root` into the repo's `.work-plan/` (on its `plan_branch`, via a worktree), removes the private copy so it isn't duplicated, commits to the plan branch, and pushes — unless `--no-push`. Tier is derived from location, so this is a file move, not a frontmatter edit. Requires a local clone + a configured `plan_branch` (else hints `plan-branch init`). Pushing to a **public** repo makes the track world-visible → confirm-token gated. |\n| `plan-branch \u003cinit\\|status\\|push\u003e \u003crepo\u003e [--branch=\u003cname\u003e] [--confirm=\u003ctoken\u003e] [--dry-run] [--json]` | Set up and share a repo's canonical **shared-tier** plan branch. The `.work-plan/` tier is pinned to ONE per-repo `plan_branch`, read/written through a dedicated git worktree, so planning never diverges across code branches or pollutes PR / deploy diffs. `init` creates that branch + a `.work-plan/` skeleton (default an **orphan** `work-plan/plan` — zero shared history with code, like `gh-pages`; override with `--branch`) and records `plan_branch` in config — or **connects** to a teammate's already-published branch if one exists. `init` is **local only** (no push). `status` reports whether the branch exists, is published to origin, and how many commits are unpushed (`--json` for the machine shape). `push` shares it: on a **public** repo it prints a confirm heads-up + token and exits (re-run with `--confirm=\u003ctoken\u003e`); `--dry-run` previews the commits that would push. Requires a repo registered via `init-repo` with a local clone path. |\n| `suggest-priorities --repo=\u003ckey\u003e` | Two-step AI label backfill: CLI fetches unlabeled issues, Claude proposes priorities, `--apply` writes labels via `gh`. |\n| `group [--milestone=X] [--label=Y] [--repo=Z] [--private] [--apply] [--limit=N]` | AI-cluster GitHub issues into thematic track files. Two-step: CLI prints prompt → you save JSON answer → `--apply` creates the tracks. `--private` routes to `notes_root` instead of `.work-plan/`. `--limit` controls how many issues are shown in the prompt (default 100). |\n| `auto-triage [--repo=\u003ckey\u003e] [--apply] [--limit=N]` | AI-assign untracked open issues to existing tracks. Two-step (same pattern as `group`). Run `coverage` first to measure the gap. `--limit` controls how many untracked issues are shown (default 100). |\n| `coverage [--repo=\u003ckey\u003e] [--list] [--limit=N]` | Report how many open issues are not in any track. `--list` prints titles. Read-only. |\n| `reconcile \u003ctrack\u003e` `\\|` `--all` `\\|` `--repo=\u003ckey\u003e [--draft] [--yes]` | Update track MEMBERSHIP (the `github.issues` list in frontmatter) by syncing against a GitHub label. Read-only on GitHub. Default label is `track/\u003cslug\u003e`; override per-track via `github.labels: [...]` in frontmatter (OR semantics). In an `--all`/`--repo` sweep it also detects **MOVEs** — an issue relabeled from one track to another in the same repo is moved (removed from the old track, added to the new); ambiguous targets stay as FLAGs. `--draft` previews the label drift (ADDs/MOVEs/FLAGs) without prompting or writing. `--yes` applies without prompting (non-interactive, e.g. the VS Code extension); PUBLIC-repo move destinations are skipped under `--yes`. `--repo=\u003ckey\u003e` scopes the sweep to one repo. NOT for hand-curated tracks (it'll propose dropping curated issues every run) — use `refresh-md` if you only want to update issue state. When \u003e50% of frontmatter issues lack the label, reconcile prints a hint pointing to `refresh-md`. |\n| `duplicates [--repo=\u003ckey\u003e]` | Find likely-duplicate issues by title similarity (stdlib `difflib`). Prints `gh issue close` consolidation commands. |\n| `canonicalize \u003ctrack\u003e` | Add a canonical issue table to a track file (so `refresh-md` knows where to update). The table carries a `Milestone` column and is ordered active-milestone-first — issues in the track's `milestone_alignment` milestone, then other milestones grouped (blank divider row between groups), then no-milestone last — so \"what's next\" sits above \"someday\" (#101). It's one table (not per-milestone sub-tables) so `refresh-md` re-derives it cleanly. |\n| `plan-status [--repo=\u003ckey\u003e] [--json] [--stamp [--draft]] [--llm [--apply]] [--archive \\| --issues] [--draft]` | Reach a verdict on every plan/spec doc in a repo by correlating its declared file-manifest against git + the filesystem: ✅ shipped / 🟡 partial / 💀 dead / 👻 manifest-less / 🧳 foreign. Read-only by default. `--stamp` writes an idempotent status header into each doc (`--draft` previews); `--llm` runs a two-step AI verdict on prose/ambiguous docs; `--archive` moves dead plans to `archive/abandoned/` and `--issues` opens issues for partial plans (both gated, both honor `--draft`); `--json` for machine output. |\n| `plan-confirm --repo=\u003ckey\u003e --verdict=shipped\\|partial\\|dead [--clear] [--confirm=\u003ctoken\u003e] -- \u003crel\u003e` | Affirm a **human** verdict on ONE plan/spec doc by writing `verdict_override` into its YAML **frontmatter only** (never the body, checkboxes, manifest, or status banner). `plan-status` then pins that verdict over the mechanical one and silences the \"shipped but boxes unchecked\" lie-gap. Use when a genuinely-shipped plan is flagged only because its phase checkboxes were never ticked. `\u003crel\u003e` is the repo-relative doc path. Public-repo gated (`--confirm=\u003ctoken\u003e`); `--clear` removes the override. |\n| `plan-ack --repo=\u003ckey\u003e [--clear] [--confirm=\u003ctoken\u003e] -- \u003crel\u003e` | Persist a **durable acknowledgment** into ONE plan/spec doc's YAML **frontmatter only** (`acknowledged: true`) — a \"stop flagging this\" that's committed with the repo and shared with teammates, unlike the VS Code viewer's per-machine `workspaceState` ack. `plan-status` reads it back (emits `acknowledged`) and demotes the doc. `\u003crel\u003e` is the repo-relative doc path. Public-repo gated (`--confirm=\u003ctoken\u003e`); `--clear` removes it. |\n| `plan-baseline --repo=\u003ckey\u003e [--clear] [--confirm=\u003ctoken\u003e] -- \u003crel\u003e` | Stamp the **current computed verdict** into ONE plan/spec doc's YAML **frontmatter only** (`verdict_baseline`) as a drift tripwire. `plan-status` then flags **drift** (emits `verdict_drift`) when the live verdict later diverges from the baseline — catching a once-shipped plan that silently **regressed** (its declared files were deleted/moved), the third \"started, then drifted off\" signal beyond stalled + lie-gap. The value is computed authoritatively (not taken from the caller); a human `verdict_override` suppresses drift. Public-repo gated; `--clear` removes it. |\n| `close-issue --repo=\u003ckey\\|slug\u003e [--reason=completed\\|not_planned] [--comment=\u003ctext\u003e] -- \u003cnumber\u003e` | ⚠️ A GitHub-mutating command — closes a GitHub issue via `gh issue close`. PRs merged to `dev` don't auto-close issues (GitHub auto-closes only from the default branch), so done-but-OPEN issues pile up; this closes one explicitly. `--reason` maps to GitHub's completed/not-planned; `--comment` posts a closing note. The VS Code viewer gates this behind a mandatory \"Close on GitHub? — cannot be undone\" modal. |\n| `in-progress \u003cn\u003e [--clear] [--repo=\u003ckey\\|slug\u003e] [--confirm=\u003ctoken\u003e]` | ⚠️ A GitHub-mutating command — marks a tracked issue in-progress by adding the `work-plan:in-progress` label (or removes it with `--clear`). Repo-resolved from the issue number; pass `--repo` to disambiguate. The label is auto-created on first use. Public-repo gated (`--confirm=\u003ctoken\u003e`). Note: `brief`/`orient`/the VS Code viewer also derive in-progress automatically from a hot `feat/\u003cn\u003e-`/`fix/\u003cn\u003e-` branch — the label is for issues with no hot branch yet. |\n\nRun `python3 ~/.claude/skills/work-plan/work_plan.py --help` for the full list with examples.\n\n### Non-interactive writes \u0026 the public-repo gate\n\nEvery write verb the VS Code extension drives runs **without a TTY** — explicit flags instead of prompts — and surfaces the public-repo heads-up as structured JSON instead of blocking on input. When a write targets a **public** repo (or one whose visibility `gh` can't determine), the command makes no change and prints `{\"needs_confirm\": true, \"reason\": …, \"token\": …}`; the caller re-invokes with `--confirm=\u003ctoken\u003e` to proceed. Private repos write straight through.\n\n`needs_confirm` fails **closed** — unknown visibility prompts too. An all-private team can opt out of the *unknown-visibility* case (e.g. when a `gh` lookup flakes) by setting `assume_private_when_unknown: true` in `~/.claude/work-plan/config.yml`; **public repos always prompt regardless.**\n\n`export --json` is the viewer's read surface (schema 1): every frontmatter'd track plus an additive `untracked` list of open issues that no track references, per repo. It also emits a top-level `repos` list — every configured repo (`folder`/`repo`/`local`/`has_local`/`visibility`), tracked or not — so the viewer can show a freshly registered repo in the sidebar independent of whether it has any tracks yet.\n\n**Track ↔ plan link (#285).** A track can name its plan/spec doc with a `plan: \u003crepo-relative-path\u003e` field in its frontmatter — set it with `set \u003ctrack\u003e plan=docs/superpowers/plans/\u003cfile\u003e.md` (empty `plan=` clears it; a path that doesn't resolve in the repo checkout is saved with an advisory warning). `export --json` then resolves that link into a per-track `plan` object — `{rel, resolved, verdict, glyph, files_present/declared, checkboxes_done/total, lie_gap, stalled, override}` — computed by the **same evaluator `plan-status` uses**, so the badge never disagrees with the Plans view. An unresolvable link (no local clone, or the file is absent) emits `{rel, resolved:false}`. Only the *declared* link is trusted — there is no filename-to-slug guessing (which false-matches and misses). The viewer surfaces this as a one-click **Plan** affordance on the track detail panel.\n\n`list-open-issues --repo=\u003cowner/name\u003e [--exclude=\u003ccsv\u003e]` is a second viewer read surface: it emits a repo's **open** issues as JSON (`{repo, issues:[…]}`, the same per-issue shape as `export`). The extension's **Slot** command uses it to offer a pick-list instead of a typed number; `--exclude` drops the track's current issues so they don't reappear. Unlike `export`'s `untracked` (open issues in *no* track), this includes issues tracked by *other* tracks — they're valid slot targets. Read-only.\n\n`auth-status [--json]` is the viewer's **auth probe**: it runs `gh auth status` and emits `{gh_present, authenticated, user, error}` (exit `0` authenticated / `1` gh present but not signed in / `2` gh not found). Because every GitHub read goes through `gh` and the fetch helpers return empty rather than erroring, an unauthenticated session would otherwise look like an empty-but-working one. The extension calls this at activation (and after every refresh) to **fast-fail with a clear \"Not signed in to GitHub\" banner + a Sign in path** instead of rendering a misleadingly empty tree — and distinguishes \"not signed in\" (`gh auth login`) from \"gh not installed\" (a different fix). Read-only.\n\n`plan-status --json` is the viewer's **Plans view** read surface: alongside each doc's verdict it now also emits `manifest_last_touched` (the most recent commit date across the plan's declared files), `stalled`, `lie_gap`, `unchecked_items`, and `stall_days`. The staleness window honors `stall_days:` in `~/.claude/work-plan/config.yml` and a `--stall-days=\u003cn\u003e` flag (precedence: flag → config → default 14). The viewer consumes these to flag plans whose declared-file build has gone cold — a `partial` plan with no recent commit on its manifest (\"stalled\") — and plans scored shipped whose own phase checkboxes are mostly unticked (\"lie-gap\"). It also emits `override` (the human `verdict_override`, `shipped`/`partial`/`dead` or `null`, set via `plan-confirm` — when present the CLI pins the verdict to it and forces `lie_gap` false), `acknowledged` (the durable frontmatter ack set via `plan-ack`), and `verdict_baseline` + `verdict_drift` (the drift tripwire set via `plan-baseline` — `verdict_drift` is true when the live verdict no longer matches the stamped baseline, suppressed under an override). It also emits `offtree_paths`: declared manifest paths that resolve **outside** the repo (absolute, `~`, `..`-escape, junk `/`) — a read-only flag for a typo or misfiled plan that would otherwise silently drag the file score down (the 🧳 foreign verdict only fires when *most* paths are off-tree; this surfaces the sub-threshold ones too). Never auto-fixed — surfacing only.\n\n**The Plans view can write to plan-doc frontmatter — and only frontmatter.** Right-click a plan in the viewer → **Confirm Verdict…** / **Clear Confirmation** (writes `verdict_override`), **Acknowledge \u0026 Save to Doc** / **Clear Saved Acknowledgment** (writes `acknowledged`), or **Stamp Baseline — Watch for Drift** / **Clear Baseline** (writes `verdict_baseline`) drives the matching CLI write behind a mandatory modal that names the exact file and states the write touches only the doc's YAML frontmatter — never its prose body, checkboxes, or declared-file manifest. These are the only viewer-initiated writes to a plan doc, each touches one frontmatter key and nothing else, and on a public repo each additionally passes through the public-repo confirm modal above. The default **Acknowledge (stop flagging)** remains per-machine and writes nothing to git; **Acknowledge \u0026 Save to Doc** is the opt-in durable, shared variant. Everything else the Plans view does (scan, local acknowledge) remains read-only on git.\n\n## Version\n\n```bash\npython3 ~/.claude/skills/work-plan/work_plan.py --version\n# work-plan 2026.04.30+a1b2c3d\n```\n\nThe version is **calver + git short SHA** of the deploy commit on `main`. It's auto-bumped on every push to main by `.github/workflows/version-bump.yml` — there's no hand-maintained version constant. Re-running `./install.sh` (or `.\\install.ps1`) after a `git pull` refreshes the value in your installed copy. Include this string in any bug report so the maintainer knows exactly which commit you're on.\n\n## Composes with\n\n- **`/repo-activity-summary`** (bundled) — Global \"what's open across the whole repo\" view. Use when you need a wider lens than per-track.\n- For non-track-bound work (you're in a directory but no track exists yet for what you're doing), run `/work-plan orient` with no track arg — it falls through to a cwd snapshot of branch + recent commits + modified files. No external skill needed.\n\n## Philosophy\n\n- **Derive, don't duplicate.** GitHub is canonical for issue state; markdown references issues by ID. The skill queries `gh` live and synthesizes the answer rather than caching what `gh` already knows.\n- **Session-bootstrap commands output paste blocks, not data dumps.** `orient` returns ~15 lines you can drop into a fresh Claude Code terminal with full context. Never bury the suggested next move under historical noise.\n- **Track files are durable across parallel sessions.** Five Claude Code terminals open on five different tracks shouldn't conflict — each session reads/writes its own track file, and the frontmatter `last_handoff` timestamp lets you tell which session last touched a track.\n- **Heuristic priority backfill is one-shot, not continuous.** `suggest-priorities` is a migration tool; once issues are labeled, GitHub stays canonical. The skill doesn't reclassify on every run.\n\n## Testing\n\n```bash\ncd skills/work-plan\npython3 -m unittest discover tests\n```\n\n600+ tests, no external dependencies (mocks `gh`/`git` calls).\n\n## License\n\nMIT — see `LICENSE`.\n\n## Maintainer\n\nStylus Nexus Holdings LLC · [@stylusnexus](https://github.com/stylusnexus)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fstylusnexus%2Fwork-plan-toolkit","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fstylusnexus%2Fwork-plan-toolkit","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fstylusnexus%2Fwork-plan-toolkit/lists"}