@stylusnexus/work-plan 2026.6.9-1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (124) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +554 -0
  3. package/VERSION +1 -0
  4. package/bin/work-plan +59 -0
  5. package/bin/work-plan.cmd +9 -0
  6. package/package.json +43 -0
  7. package/scripts/npm-check-deps.js +44 -0
  8. package/skills/work-plan/SKILL.md +152 -0
  9. package/skills/work-plan/commands/__init__.py +0 -0
  10. package/skills/work-plan/commands/auto_triage.py +230 -0
  11. package/skills/work-plan/commands/brief.py +247 -0
  12. package/skills/work-plan/commands/canonicalize.py +139 -0
  13. package/skills/work-plan/commands/close.py +98 -0
  14. package/skills/work-plan/commands/coverage.py +100 -0
  15. package/skills/work-plan/commands/duplicates.py +124 -0
  16. package/skills/work-plan/commands/export.py +69 -0
  17. package/skills/work-plan/commands/group.py +272 -0
  18. package/skills/work-plan/commands/handoff.py +867 -0
  19. package/skills/work-plan/commands/hygiene.py +128 -0
  20. package/skills/work-plan/commands/init.py +128 -0
  21. package/skills/work-plan/commands/init_repo.py +132 -0
  22. package/skills/work-plan/commands/list_cmd.py +39 -0
  23. package/skills/work-plan/commands/new_track.py +225 -0
  24. package/skills/work-plan/commands/plan_status.py +296 -0
  25. package/skills/work-plan/commands/reconcile.py +225 -0
  26. package/skills/work-plan/commands/refresh_md.py +145 -0
  27. package/skills/work-plan/commands/set_field.py +61 -0
  28. package/skills/work-plan/commands/set_notes_root.py +53 -0
  29. package/skills/work-plan/commands/slot.py +154 -0
  30. package/skills/work-plan/commands/suggest_priorities.py +132 -0
  31. package/skills/work-plan/commands/where_was_i.py +325 -0
  32. package/skills/work-plan/lib/__init__.py +0 -0
  33. package/skills/work-plan/lib/closure.py +72 -0
  34. package/skills/work-plan/lib/config.py +88 -0
  35. package/skills/work-plan/lib/doc_discovery.py +41 -0
  36. package/skills/work-plan/lib/drift.py +32 -0
  37. package/skills/work-plan/lib/export_model.py +42 -0
  38. package/skills/work-plan/lib/frontmatter.py +48 -0
  39. package/skills/work-plan/lib/git_state.py +180 -0
  40. package/skills/work-plan/lib/github_state.py +296 -0
  41. package/skills/work-plan/lib/llm_evidence.py +45 -0
  42. package/skills/work-plan/lib/manifest.py +164 -0
  43. package/skills/work-plan/lib/new_issues.py +69 -0
  44. package/skills/work-plan/lib/next_up.py +98 -0
  45. package/skills/work-plan/lib/notes_readme.py +38 -0
  46. package/skills/work-plan/lib/prompts.py +68 -0
  47. package/skills/work-plan/lib/reconcile_actions.py +34 -0
  48. package/skills/work-plan/lib/render.py +83 -0
  49. package/skills/work-plan/lib/scratch.py +14 -0
  50. package/skills/work-plan/lib/session_log.py +39 -0
  51. package/skills/work-plan/lib/status_header.py +60 -0
  52. package/skills/work-plan/lib/status_table.py +227 -0
  53. package/skills/work-plan/lib/tracks.py +248 -0
  54. package/skills/work-plan/lib/verdict.py +51 -0
  55. package/skills/work-plan/lib/write_guard.py +39 -0
  56. package/skills/work-plan/tests/__init__.py +0 -0
  57. package/skills/work-plan/tests/fixtures/notes_root/critforge/archive/shipped/old.md +10 -0
  58. package/skills/work-plan/tests/fixtures/notes_root/critforge/example.md +11 -0
  59. package/skills/work-plan/tests/fixtures/notes_root/critforge/no_frontmatter.md +1 -0
  60. package/skills/work-plan/tests/fixtures/notes_root/loose_at_root.md +1 -0
  61. package/skills/work-plan/tests/fixtures/track_with_frontmatter.md +14 -0
  62. package/skills/work-plan/tests/fixtures/track_without_frontmatter.md +3 -0
  63. package/skills/work-plan/tests/fixtures/with_status_table.md +9 -0
  64. package/skills/work-plan/tests/test_auto_triage.py +324 -0
  65. package/skills/work-plan/tests/test_close.py +273 -0
  66. package/skills/work-plan/tests/test_close_tier.py +166 -0
  67. package/skills/work-plan/tests/test_closure.py +51 -0
  68. package/skills/work-plan/tests/test_config.py +85 -0
  69. package/skills/work-plan/tests/test_config_seed.py +41 -0
  70. package/skills/work-plan/tests/test_config_shared.py +57 -0
  71. package/skills/work-plan/tests/test_coverage.py +192 -0
  72. package/skills/work-plan/tests/test_doc_discovery.py +51 -0
  73. package/skills/work-plan/tests/test_drift.py +38 -0
  74. package/skills/work-plan/tests/test_export.py +169 -0
  75. package/skills/work-plan/tests/test_export_command.py +295 -0
  76. package/skills/work-plan/tests/test_frontmatter.py +52 -0
  77. package/skills/work-plan/tests/test_git_state.py +51 -0
  78. package/skills/work-plan/tests/test_git_state_paths.py +51 -0
  79. package/skills/work-plan/tests/test_github_state.py +508 -0
  80. package/skills/work-plan/tests/test_group_apply.py +348 -0
  81. package/skills/work-plan/tests/test_handoff_append_rows.py +73 -0
  82. package/skills/work-plan/tests/test_handoff_attribution.py +152 -0
  83. package/skills/work-plan/tests/test_handoff_auto_next_skip.py +183 -0
  84. package/skills/work-plan/tests/test_handoff_collision_warning.py +149 -0
  85. package/skills/work-plan/tests/test_handoff_set_next.py +106 -0
  86. package/skills/work-plan/tests/test_init.py +289 -0
  87. package/skills/work-plan/tests/test_init_repo.py +379 -0
  88. package/skills/work-plan/tests/test_init_shared.py +185 -0
  89. package/skills/work-plan/tests/test_llm_evidence.py +77 -0
  90. package/skills/work-plan/tests/test_manifest.py +162 -0
  91. package/skills/work-plan/tests/test_new_issues.py +130 -0
  92. package/skills/work-plan/tests/test_new_track.py +610 -0
  93. package/skills/work-plan/tests/test_next_up.py +149 -0
  94. package/skills/work-plan/tests/test_notes_readme.py +78 -0
  95. package/skills/work-plan/tests/test_plan_status.py +68 -0
  96. package/skills/work-plan/tests/test_plan_status_archive.py +61 -0
  97. package/skills/work-plan/tests/test_plan_status_foreign.py +55 -0
  98. package/skills/work-plan/tests/test_plan_status_issues.py +61 -0
  99. package/skills/work-plan/tests/test_plan_status_llm_apply.py +71 -0
  100. package/skills/work-plan/tests/test_plan_status_llm_prepare.py +66 -0
  101. package/skills/work-plan/tests/test_plan_status_stamp.py +70 -0
  102. package/skills/work-plan/tests/test_plugin_manifests.py +38 -0
  103. package/skills/work-plan/tests/test_reconcile_actions.py +60 -0
  104. package/skills/work-plan/tests/test_reconcile_readonly.py +239 -0
  105. package/skills/work-plan/tests/test_reconcile_wrappers.py +55 -0
  106. package/skills/work-plan/tests/test_refresh_md.py +98 -0
  107. package/skills/work-plan/tests/test_render.py +110 -0
  108. package/skills/work-plan/tests/test_repo_filter.py +52 -0
  109. package/skills/work-plan/tests/test_security_hardening.py +117 -0
  110. package/skills/work-plan/tests/test_session_log.py +39 -0
  111. package/skills/work-plan/tests/test_set_field.py +77 -0
  112. package/skills/work-plan/tests/test_set_notes_root.py +292 -0
  113. package/skills/work-plan/tests/test_slot.py +243 -0
  114. package/skills/work-plan/tests/test_slot_move.py +128 -0
  115. package/skills/work-plan/tests/test_smoke.py +46 -0
  116. package/skills/work-plan/tests/test_status_header.py +79 -0
  117. package/skills/work-plan/tests/test_status_table.py +162 -0
  118. package/skills/work-plan/tests/test_suggested_first_action.py +112 -0
  119. package/skills/work-plan/tests/test_track_resolution.py +295 -0
  120. package/skills/work-plan/tests/test_tracks.py +385 -0
  121. package/skills/work-plan/tests/test_verdict.py +60 -0
  122. package/skills/work-plan/tests/test_where_was_i.py +382 -0
  123. package/skills/work-plan/tests/test_write_guard.py +53 -0
  124. package/skills/work-plan/work_plan.py +220 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Stylus Nexus Holdings LLC
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,554 @@
1
+ # work-plan toolkit
2
+
3
+ ![License: MIT](https://img.shields.io/badge/license-MIT-blue)
4
+ ![Python 3.9+ stdlib](https://img.shields.io/badge/python-3.9%2B%20stdlib-3776AB)
5
+ ![Claude Code](https://img.shields.io/badge/Claude%20Code-plugin-7C3AED)
6
+ ![Codex](https://img.shields.io/badge/Codex-plugin-10A37F)
7
+
8
+ Track-aware daily work planning for developers running parallel Claude Code / Codex sessions across many GitHub issues.
9
+
10
+ ## What this is
11
+
12
+ A 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.
13
+
14
+ **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`).
15
+
16
+ The 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.
17
+
18
+ **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.
19
+
20
+ ## What this is not
21
+
22
+ - **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.
23
+ - **Not a standalone issue tracker.** GitHub is canonical; `work-plan` just reads and references it.
24
+ - **Not zero-setup.** Requires Python 3.9+, the `gh` GitHub CLI (authenticated), and `yq` (the Go version, not the Python one).
25
+ - **Not a background service.** No daemon, no cache, no sync loop — `git pull` is the sync mechanism for shared tracks.
26
+ - **Not a replacement for reading your code.** It tells you *what* to work on and *where you left off* — not what the code does.
27
+
28
+ ## Quick install
29
+
30
+ **Claude Code (recommended):**
31
+ ```
32
+ /plugin marketplace add stylusnexus/agent-plugins
33
+ /plugin install work-plan@stylus-nexus
34
+ ```
35
+ **Codex:** `codex plugin marketplace add stylusnexus/agent-plugins` → `codex plugin add work-plan@stylus-nexus`
36
+ **npm (any editor / standalone CLI):**
37
+ ```
38
+ npm install -g @stylusnexus/work-plan
39
+ ```
40
+ **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).
41
+ **Cursor / Copilot / direct:** clone + `./install.sh` (see [Install](#install)).
42
+
43
+ Full 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.
44
+
45
+ > **Command names:** examples below use the standalone form `/work-plan <subcommand>` (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 <subcommand>`. On Codex, invoke via `@work-plan` / `/skills`.
46
+
47
+ The five essentials you'll use 80% of the time are:
48
+
49
+ | Command | When |
50
+ |---|---|
51
+ | `/work-plan brief` | Morning. Multi-track snapshot — what's on your plate across every active track. Add `--repo=<key>` to scope to one project. |
52
+ | `/work-plan handoff <track>` | 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. |
53
+ | `/work-plan orient <track>` | Switching context. ~15-line paste-block of priority / last session / next pick / git state — drop into a fresh Claude Code terminal. |
54
+ | `/work-plan reconcile <track> \| --all \| --repo=<key> [--draft]` | Track frontmatter membership drifted from GitHub labels. Use on label-driven tracks only — for hand-curated tracks, use `refresh-md` instead. `--draft` previews proposed ADDs/FLAGs without prompting or writing. `--repo=<key>` scopes the sweep to one repo. |
55
+ | `/work-plan hygiene [--repo=<key>]` | **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=<key>` scopes steps ① and ② to one repo; step ③ is skipped in scoped mode. |
56
+
57
+ A 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:
58
+
59
+ **Shared tracks** — track files can live *inside your repo clone* (`.work-plan/<slug>.md`) so teammates see the same planning state via `git pull`. The default is private (`notes_root/<repo>/`); register a local clone with `init-repo --local=<path>` to opt into shared mode. Pass `--private` to any write command to keep a specific track local. See [Shared tracks](#shared-tracks).
60
+
61
+ **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.
62
+
63
+ **Coverage + auto-triage** — `coverage --repo=<key>` reports how many open issues fall outside the track model (42% on a real production repo). `auto-triage --repo=<key>` then produces an AI prompt to assign those orphans to existing tracks. Run both periodically to keep the backlog visible.
64
+
65
+ Beyond 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 & doc liveness](#plan--doc-liveness-plan-status).
66
+
67
+ ## How it works
68
+
69
+ The 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`, last session timestamp). The CLI re-derives everything else live from `gh`, `git`, and the markdown body.
70
+
71
+ ```mermaid
72
+ flowchart TB
73
+ subgraph sources["Data sources (canonical)"]
74
+ direction LR
75
+ gh["GitHub issues<br/>(via gh CLI)"]
76
+ git["git state<br/>(branch, commits, modified files)"]
77
+ md["Track markdown<br/>(YAML frontmatter + body)"]
78
+ end
79
+
80
+ subgraph cli["work-plan CLI (Python stdlib)"]
81
+ direction LR
82
+ brief["brief<br/><i>multi-track view</i>"]
83
+ handoff["handoff<br/><i>capture session + algorithmic or LLM next_up</i>"]
84
+ orient["orient<br/><i>paste-block context for new session</i>"]
85
+ hygiene["hygiene<br/><i>weekly drift + dup sweep</i>"]
86
+ end
87
+
88
+ subgraph outputs["Outputs"]
89
+ direction LR
90
+ paste["Paste-ready blocks<br/>(relayed verbatim to chat)"]
91
+ update["Updated frontmatter<br/>(next_up, session log, status table)"]
92
+ sync["GitHub label sync<br/>(reconcile, refresh-md)"]
93
+ end
94
+
95
+ sources --> cli
96
+ cli --> outputs
97
+ update -.->|"next day"| md
98
+ ```
99
+
100
+ **Daily rhythm**:
101
+
102
+ - **Morning** → `brief` shows multi-track plate, then `orient <track>` produces a ~15-line paste-block to drop into a fresh agent session.
103
+ - **End of work block** → `handoff <track>` captures what you touched. Three ways to set `next_up` for tomorrow:
104
+ - `handoff <track> --auto-next` — algorithmic (no LLM): top-3 by priority then most-recently-updated, blockers excluded. Interactive `[Y/n/edit]` prompt — accept, edit, or skip.
105
+ - `handoff <track> --set-next 4167,4148` — explicit numbers when you know exactly which issues are next.
106
+ - 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.
107
+ - 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.
108
+ - **Weekly** → `hygiene` runs `refresh-md --all` + `reconcile --all` + `duplicates` in sequence to keep status icons, GitHub labels, and dedup state honest.
109
+
110
+ > **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 <track>` (or **Refresh Track Body** in VS Code) fixes that on-demand; `hygiene` sweeps all tracks weekly.
111
+
112
+ > **GitHub access is read-only.** The toolkit never writes to GitHub. All issue data comes from read-only `gh` CLI calls (`gh issue list`, `gh issue view`). Every write (frontmatter, status table, session log) goes to your local markdown files only.
113
+
114
+ ## Shared tracks
115
+
116
+ By default track files live under `notes_root/<repo>/` — local only, never committed. **Shared tracks** live inside the repo clone itself (`.work-plan/<slug>.md`) and travel with the repo via `git pull`/`git push`. Teammates see the same planning state without a separate notes sync.
117
+
118
+ **Set up shared tracks for a repo:**
119
+
120
+ ```bash
121
+ # Register the local clone path in your config
122
+ /work-plan init-repo myproject --github=org/myproject --local=/path/to/clone
123
+ ```
124
+
125
+ Once `local:` is set and points to a valid git repo, all new tracks for that repo go into `.work-plan/` automatically.
126
+
127
+ **Syncing:**
128
+
129
+ ```bash
130
+ git pull # pull teammates' track changes
131
+ git add .work-plan/ && git commit && git push # share your own
132
+ ```
133
+
134
+ The CLI never auto-pushes. When you create or update a shared track, it prints a reminder:
135
+ ```
136
+ ↑ shared — commit + push to share with teammates.
137
+ ```
138
+
139
+ **Opt out per-command:** pass `--private` to route a specific track to `notes_root` instead:
140
+
141
+ ```bash
142
+ /work-plan group --milestone='v1.0' --private # keep clusters local
143
+ /work-plan new-track myproject exploration # --private for one-off tracks
144
+ ```
145
+
146
+ **Multi-repo disambiguation:** if the same track slug exists in two repos, qualify with `@<repo>` or `--repo=<key>`:
147
+
148
+ ```bash
149
+ /work-plan slot 4234 auth-flow@critforge
150
+ /work-plan close auth-flow --repo=critforge
151
+ ```
152
+
153
+ ## Plan & doc liveness (`plan-status`)
154
+
155
+ The 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.
156
+
157
+ **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.
158
+
159
+ `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.
160
+
161
+ ```
162
+ $ /work-plan plan-status --repo=myproject
163
+
164
+ # plan-status — /path/to/myproject
165
+ 332 docs · 140 shipped · 20 partial · 172 manifest-less
166
+ lie-gap (shipped but <25% boxes checked): 134
167
+
168
+ ## ✅ shipped (140)
169
+ docs/plans/2026-03-16-idea-mode-ui.md
170
+ 9/9 declared files present (boxes stale)
171
+ ...
172
+ ## 🟡 partial (20)
173
+ docs/plans/2026-05-01-v0.4.0-one-week-closeout.md
174
+ 19/40 declared files present
175
+ ...
176
+ ```
177
+
178
+ Each doc reaches one of these verdicts:
179
+
180
+ | Verdict | Meaning |
181
+ |---|---|
182
+ | ✅ **shipped** | (nearly) all declared files present — done, even if the boxes say otherwise |
183
+ | 🟡 **partial** | some files present — genuinely in progress; *this is your to-do list* |
184
+ | 💀 **dead** | no files, long untouched — an abandonment candidate |
185
+ | 👻 **manifest-less** | a prose doc with no file-manifest (e.g. a design spec) — needs a judgment call |
186
+ | 🧳 **foreign** | a misfiled plan whose declared files live in *another* repo — not this repo's work at all |
187
+
188
+ **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`.
189
+
190
+ **Acting on the results (gated).** Once you trust the verdicts:
191
+ - `--archive` moves 💀 dead plans into `archive/abandoned/` (history-preserving `git mv`).
192
+ - `--issues` opens a GitHub issue per 🟡 partial plan, listing its unsatisfied files.
193
+
194
+ Both are confirmation-gated and honor `--draft` (preview, zero side effects).
195
+
196
+ **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:
197
+
198
+ ```markdown
199
+ # Idea Mode UI — Implementation Plan
200
+
201
+ <!-- plan-status: BEGIN -->
202
+ > **Status:** ✅ shipped · 9/9 files · last touched 2026-03-20
203
+ <!-- plan-status: END -->
204
+ ```
205
+
206
+ The 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.
207
+
208
+ **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=<key>` (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.
209
+
210
+ ## Requirements
211
+
212
+ The toolkit is a Python CLI that shells out to standard tools. You need **all four** installed before running `install.sh` / `install.ps1`:
213
+
214
+ | Tool | Min version | Why |
215
+ |---|---|---|
216
+ | 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). |
217
+ | `gh` | recent | Live GitHub state queries (issues, milestones, labels). Must be authenticated: `gh auth login` once before first run. |
218
+ | `git` | any 2.x | Detects current branch, ahead-of-upstream count, modified files. |
219
+ | `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. |
220
+
221
+ Install per platform (one-liners):
222
+
223
+ ```bash
224
+ # macOS (Homebrew)
225
+ brew install python@3 gh git yq
226
+
227
+ # Linux (Debian/Ubuntu)
228
+ sudo apt update && sudo apt install python3 git
229
+ # gh: https://github.com/cli/cli/blob/trunk/docs/install_linux.md
230
+ # yq: sudo wget -qO /usr/local/bin/yq https://github.com/mikefarah/yq/releases/download/v4.53.2/yq_linux_amd64 && sudo chmod +x /usr/local/bin/yq
231
+
232
+ # Linux (Arch)
233
+ sudo pacman -S python github-cli git go-yq
234
+
235
+ # Windows (PowerShell with winget)
236
+ winget install Python.Python.3 GitHub.cli Git.Git MikeFarah.yq
237
+ ```
238
+
239
+ `install.sh` and `install.ps1` both verify all four are on `PATH` before doing anything else, and print install hints if any are missing.
240
+
241
+ After installing, authenticate `gh` once:
242
+
243
+ ```bash
244
+ gh auth login # follow the prompts; needs `repo` scope to read issues
245
+ ```
246
+
247
+ ## Compatible tools
248
+
249
+ A 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:
250
+
251
+ | Layer | What it is | Claude Code | Codex | Cursor | GitHub Copilot |
252
+ |---|---|---|---|---|---|
253
+ | **1. Python CLI** | `work_plan.py` + subcommands. Pure stdlib, shells out to `gh`/`git`/`yq`. | ✅ Proven | ✅ Proven | ✅ Direct invocation | ✅ Direct invocation |
254
+ | **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) |
255
+ | **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 |
256
+
257
+ ### Install per platform
258
+
259
+ | Tool | Install command | Then invoke as |
260
+ |---|---|---|
261
+ | **npm (standalone CLI / any editor)** | `npm install -g @stylusnexus/work-plan` (requires `python3` + `yq` + `gh` already on PATH — the postinstall warns if any are missing). | `work-plan <subcommand>` |
262
+ | **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 <sub>`. Script: bare `/work-plan <subcommand>` |
263
+ | **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 |
264
+ | **Cursor** | Skip installer. Clone repo + copy `shims/cursor/work-plan.cursorrules` into your project's `.cursorrules` (or merge it in) | `python3 <toolkit>/skills/work-plan/work_plan.py <sub>` — alias `wp` recommended |
265
+ | **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 |
266
+ | **Any other tool** | Skip installer. Just `git clone`. | Direct CLI |
267
+
268
+ Shell rc alias for the direct-CLI cases:
269
+
270
+ ```bash
271
+ # bash/zsh (~/.bashrc, ~/.zshrc)
272
+ alias wp="python3 /path/to/work-plan-toolkit/skills/work-plan/work_plan.py"
273
+
274
+ # PowerShell ($PROFILE)
275
+ function wp { python "C:\path\to\work-plan-toolkit\skills\work-plan\work_plan.py" @args }
276
+ ```
277
+
278
+ To install for **both** Claude Code AND Codex, run the installer twice with different `--target` values.
279
+
280
+ ### VS Code extension
281
+
282
+ The **Work Plan** extension is the visual face of the CLI — a sidebar tree (repos → tracks), a Mermaid dependency graph, the Untracked bucket, and full read/write (slot/close/edit/new-track/…) with a public-repo confirm modal.
283
+
284
+ ![Work Plan VS Code extension — sidebar and dependency graph](https://raw.githubusercontent.com/stylusnexus/work-plan-toolkit/main/vscode/media/screenshots/dependency-graph.png)
285
+
286
+ Install it from either registry:
287
+
288
+ - **VS Code Marketplace:** Extensions view → search **"Work Plan"** (publisher `stylusnexus`), or `code --install-extension stylusnexus.work-plan-viewer`.
289
+ - **Open VSX** (VS Codium / Cursor / Windsurf): search **"Work Plan"**, or `ovsx get stylusnexus.work-plan-viewer`.
290
+
291
+ The 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.
292
+
293
+ Useful settings:
294
+
295
+ | Setting | Default | What it does |
296
+ |---|---|---|
297
+ | `workPlan.cliPath` | `"work-plan"` | Absolute path to the CLI, if it's not on the editor's PATH |
298
+ | `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 |
299
+ | `workPlan.expandReposByDefault` | `false` | Expand all repo groups on load (single-repo workspaces always expand) |
300
+
301
+ Shared 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.
302
+
303
+ ### Updating
304
+
305
+ | Installed via | Update with |
306
+ |---|---|
307
+ | **npm** | `npm install -g @stylusnexus/work-plan@latest` (or `npm update -g @stylusnexus/work-plan`) |
308
+ | **Claude Code / Codex plugin** | `/plugin update work-plan@stylus-nexus` (Codex: `codex plugin update …`) |
309
+ | **Script (`install.sh`)** | `git pull` in the toolkit repo, then re-run `./install.sh` (or `.\install.ps1`) |
310
+ | **VS Code extension** | Auto-updates; or Extensions view → update manually |
311
+
312
+ Check your version any time with `work-plan --version`.
313
+
314
+ ### What the shims do
315
+
316
+ For 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.
317
+
318
+ The 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.
319
+
320
+ ## Install
321
+
322
+ Pick the path for your tool. All three install the same CLI + skills.
323
+
324
+ ### Claude Code (recommended) — plugin, easy updates
325
+
326
+ ```
327
+ /plugin marketplace add stylusnexus/agent-plugins
328
+ /plugin install work-plan@stylus-nexus
329
+ ```
330
+
331
+ Commands are namespaced under the plugin: `/work-plan:brief`, `/work-plan:handoff`,
332
+ `/work-plan:orient`, `/work-plan:hygiene`, `/work-plan:status`, and
333
+ `/work-plan:run <subcommand>` for everything else. Update with
334
+ `/plugin update work-plan@stylus-nexus`. Works in the CLI and the VS Code / JetBrains extensions.
335
+
336
+ ### Codex — plugin
337
+
338
+ ```
339
+ codex plugin marketplace add stylusnexus/agent-plugins
340
+ codex plugin add work-plan@stylus-nexus
341
+ ```
342
+
343
+ ### Cursor / direct / other — script
344
+
345
+ ```bash
346
+ git clone <this-repo> work-plan-toolkit
347
+ cd work-plan-toolkit && ./install.sh # macOS / Linux / WSL
348
+ # or, on Windows native PowerShell: .\install.ps1
349
+ # or, for Codex's skills dir: ./install.sh --target=$HOME/.agents
350
+ ```
351
+
352
+ Gives the single bare `/work-plan <subcommand>` (no namespace). Re-run after `git pull` to refresh
353
+ (the plugin paths above update themselves).
354
+
355
+ The installer:
356
+
357
+ - **Copies** (not symlinks — for Windows compatibility) `skills/work-plan` and `skills/repo-activity-summary` into `~/.claude/skills/`
358
+ - 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)
359
+ - **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`
360
+ - Drops a `.installed-from` marker so `uninstall` knows what's safe to remove
361
+
362
+ External dependencies (verified by the installer): `gh`, `git`, `yq`, `python3`.
363
+
364
+ > **Versioning:** releases use CalVer (`YYYY.MM.DD+<sha>`, auto-bumped on deploy and synced into the plugin manifests) — not SemVer.
365
+
366
+ ### What gets created
367
+
368
+ After clone, the toolkit looks like this:
369
+
370
+ ```
371
+ work-plan-toolkit/
372
+ ├── README.md
373
+ ├── LICENSE
374
+ ├── .gitignore
375
+ ├── install.sh / install.ps1 # macOS+Linux+WSL / Windows
376
+ ├── uninstall.sh / uninstall.ps1
377
+ ├── skills/
378
+ │ ├── work-plan/
379
+ │ │ ├── SKILL.md
380
+ │ │ ├── work_plan.py # CLI entry
381
+ │ │ ├── commands/ # 16 subcommand modules
382
+ │ │ ├── lib/ # config, frontmatter, gh, git, prompts, …
383
+ │ │ └── tests/ # 234 unittest cases
384
+ │ └── repo-activity-summary/
385
+ │ └── SKILL.md # bundled companion skill
386
+ ├── commands/
387
+ │ └── work-plan.md # Claude Code slash command alias
388
+ ├── docs/
389
+ │ └── usage-examples.md
390
+ ├── shims/ # Drop-in instruction files for non-skill-aware tools
391
+ │ ├── README.md
392
+ │ ├── cursor/work-plan.cursorrules
393
+ │ └── copilot/work-plan-copilot-instructions.md
394
+ └── notes/
395
+ └── README.md # default notes_root (empty until init-repo)
396
+ ```
397
+
398
+ After running `install.sh` (or `install.ps1`), the installer creates this in your home directory:
399
+
400
+ ```
401
+ ~/.claude/
402
+ ├── skills/
403
+ │ ├── work-plan/ # copy of toolkit's skills/work-plan/
404
+ │ │ ├── SKILL.md
405
+ │ │ ├── work_plan.py
406
+ │ │ ├── commands/
407
+ │ │ ├── lib/
408
+ │ │ ├── tests/
409
+ │ │ └── .installed-from # marker file (toolkit absolute path)
410
+ │ └── repo-activity-summary/
411
+ │ ├── SKILL.md
412
+ │ └── .installed-from # marker file
413
+ ├── commands/
414
+ │ └── work-plan.md # copy of toolkit's commands/work-plan.md
415
+ └── work-plan/
416
+ └── config.yml # seeded from template, notes_root resolved
417
+ # to absolute toolkit path (edit this one)
418
+ ```
419
+
420
+ Then, 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):
421
+
422
+ ```
423
+ <notes_root>/
424
+ └── myproject/ # created by init-repo
425
+ ├── archive/
426
+ │ ├── shipped/.gitkeep # close mv's shipped tracks here
427
+ │ └── abandoned/.gitkeep # close mv's abandoned tracks here
428
+ └── <track-slug>.md # active tracks live at top level
429
+ ```
430
+
431
+ ## Configure
432
+
433
+ After install, bootstrap your first repo with the **`init-repo`** subcommand:
434
+
435
+ ```bash
436
+ /work-plan init-repo myproject --github=your-org/myproject --local=/path/to/checkout
437
+ ```
438
+
439
+ This creates `<notes_root>/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.
440
+
441
+ You can also edit `~/.claude/work-plan/config.yml` directly:
442
+
443
+ ```yaml
444
+ notes_root: /absolute/path/to/your/notes/ # or keep the bundled default
445
+ repos:
446
+ myproject:
447
+ github: your-org/myproject
448
+ local: /path/to/local/checkout # optional, enables in-progress detection
449
+ ```
450
+
451
+ ### Where your config lives
452
+
453
+ The 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.
454
+
455
+ After a fresh install, it looks like:
456
+
457
+ ```yaml
458
+ # work-plan config — created by install.sh. Edit this file to customize.
459
+ # Run /work-plan init-repo <key> --github=<org/repo> to populate repos:.
460
+ notes_root: /absolute/path/to/work-plan-toolkit/notes
461
+ repos: {}
462
+ ```
463
+
464
+ `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.
465
+
466
+ The bundled `notes/` folder stays empty until you run `/work-plan init-repo <key>`, which adds a per-repo subdir + writes the repo block back into this same config file via `yq`.
467
+
468
+ ## Security & data handling
469
+
470
+ - **No credentials stored.** All GitHub access goes through your existing `gh auth`. This toolkit never reads, writes, or stores GitHub tokens.
471
+ - **Local-only writes.** 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`. The exceptions are the `plan-status` action flags, all confined to the repo you point it at and all opt-in: `--stamp` writes a status header into discovered plan docs; `--archive` `git mv`s dead plans into `archive/abandoned/`; `--issues` opens GitHub issues for partial plans (via `gh`). All honor `--draft` (preview, no writes) and the two mutating actions prompt for confirmation. Nothing else.
472
+ - **No telemetry, no network calls beyond `gh`.** All GitHub operations go through `gh` (your authenticated session); no direct HTTP requests are made.
473
+ - **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.
474
+ - **`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.
475
+ - **`install.sh` / `install.ps1` only touch user-owned dirs.** No `sudo`, no system-wide changes, no privilege escalation.
476
+
477
+ For vulnerability reporting, threat model, and past advisories, see [SECURITY.md](./SECURITY.md).
478
+
479
+ ## Usage walkthrough
480
+
481
+ See `docs/usage-examples.md` for end-to-end scenarios (morning brief, mid-work handoff, fresh-session orient, weekly hygiene).
482
+
483
+ ## Subcommand reference
484
+
485
+ | Subcommand | What it does |
486
+ |---|---|
487
+ | `brief [--repo=<key>]` | Multi-track snapshot of all active tracks across configured repos. `--repo=<key>` filters to one project (matches the folder name under `notes_root` or the `org/repo` GitHub slug; archived-reopen callouts are also scoped). |
488
+ | `handoff <track> [--auto-next \| --set-next 1,2,3]` | Wrap up a work block. Writes a `### Session — <ts>` 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. Without either flag, just captures the session summary and reads any pre-existing `next_up`. |
489
+ | `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. |
490
+ | `slot <issue-num> [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=<token>` (public-repo gate, see below). |
491
+ | `close <track> [--state=shipped\|parked\|abandoned] [--note=<text>]` | Mark track shipped, parked, or abandoned. Moves to `archive/<state>/` for shipped/abandoned. Pass `--state=` (and an optional `--note=`) to run without prompts. |
492
+ | `refresh-md <track>` `\|` `--all` `\|` `--repo=<key>` | Update issue STATE (open/closed, status labels) inside the track body's status table. Does NOT change track membership — this is the right tool for "refresh the work I just completed." `--all` sweeps every active track; `--repo=<key>` scopes the sweep to one repo. |
493
+ | `hygiene [--repo=<key>]` | Weekly all-in-one: `refresh-md` + `reconcile` + `duplicates`. With `--repo=<key>`, steps 1 and 2 scope to that repo and the global `duplicates` step is skipped. |
494
+ | `list [--all]` | List active tracks (or all including parked/archived). |
495
+ | `init <path> [--priority=P0..P3] [--milestone=<m>]` | Add frontmatter to a brand-new track .md file (the file must already exist). Pass `--priority=`/`--milestone=` to skip the prompts. |
496
+ | `init-repo <key> --github=<slug> [--local=<path>]` | Bootstrap a new repo: create `<notes_root>/<key>/archive/{shipped,abandoned}/` and add the repo block to your config. `--github` is required; `--local` is optional. |
497
+ | `new-track <repo> <slug> [--priority=P0..P3] [--milestone=<m>]` | One-shot, non-interactive: create a new track file under `notes_root` for `<repo>` (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. |
498
+ | `set-notes-root <path>` | Relocate where your private track notes live (updates `notes_root` in config). Does not move existing tracks — it warns if any would be orphaned. |
499
+ | `suggest-priorities --repo=<key>` | Two-step AI label backfill: CLI fetches unlabeled issues, Claude proposes priorities, `--apply` writes labels via `gh`. |
500
+ | `group [--milestone=X] [--label=Y] [--repo=Z] [--private] [--apply]` | 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/`. |
501
+ | `auto-triage [--repo=<key>] [--apply]` | AI-assign untracked open issues to existing tracks. Two-step (same pattern as `group`). Run `coverage` first to measure the gap. |
502
+ | `coverage [--repo=<key>] [--list] [--limit=N]` | Report how many open issues are not in any track. `--list` prints titles. Read-only. |
503
+ | `reconcile <track>` `\|` `--all` `\|` `--repo=<key> [--draft]` | Update track MEMBERSHIP (the `github.issues` list in frontmatter) by syncing against a GitHub label. Read-only on GitHub. Default label is `track/<slug>`; override per-track via `github.labels: [...]` in frontmatter (OR semantics). `--draft` previews ADDs/FLAGs without prompting or writing. `--repo=<key>` 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 >50% of frontmatter issues lack the label, reconcile prints a hint pointing to `refresh-md`. |
504
+ | `duplicates [--repo=<key>]` | Find likely-duplicate issues by title similarity (stdlib `difflib`). Prints `gh issue close` consolidation commands. |
505
+ | `canonicalize <track>` | Add a canonical issue table to a track file (so `refresh-md` knows where to update). |
506
+ | `plan-status [--repo=<key>] [--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. |
507
+
508
+ Run `python3 ~/.claude/skills/work-plan/work_plan.py --help` for the full list with examples.
509
+
510
+ ### Non-interactive writes & the public-repo gate
511
+
512
+ Every 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=<token>` to proceed. Private repos write straight through.
513
+
514
+ `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.**
515
+
516
+ `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.
517
+
518
+ ## Version
519
+
520
+ ```bash
521
+ python3 ~/.claude/skills/work-plan/work_plan.py --version
522
+ # work-plan 2026.04.30+a1b2c3d
523
+ ```
524
+
525
+ The 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.
526
+
527
+ ## Composes with
528
+
529
+ - **`/repo-activity-summary`** (bundled) — Global "what's open across the whole repo" view. Use when you need a wider lens than per-track.
530
+ - 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.
531
+
532
+ ## Philosophy
533
+
534
+ - **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.
535
+ - **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.
536
+ - **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.
537
+ - **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.
538
+
539
+ ## Testing
540
+
541
+ ```bash
542
+ cd skills/work-plan
543
+ python3 -m unittest discover tests
544
+ ```
545
+
546
+ 234 tests, no external dependencies (mocks `gh`/`git` calls).
547
+
548
+ ## License
549
+
550
+ MIT — see `LICENSE`.
551
+
552
+ ## Maintainer
553
+
554
+ Stylus Nexus Holdings LLC · [@stylusnexus](https://github.com/stylusnexus)
package/VERSION ADDED
@@ -0,0 +1 @@
1
+ 2026.06.09+f7e5ff5
package/bin/work-plan ADDED
@@ -0,0 +1,59 @@
1
+ #!/usr/bin/env bash
2
+ # work-plan CLI launcher. Resolves work_plan.py relative to this wrapper's PARENT
3
+ # (the wrapper lives at <root>/bin/work-plan, so the CLI is <root>/skills/...),
4
+ # then falls back to plugin-root env vars and the install.sh locations.
5
+
6
+ # GUI editors (VS Code launched from Finder/Dock, not a terminal) inherit a
7
+ # stripped PATH with no Homebrew — but the CLI shells out to python3/yq/gh,
8
+ # which on macOS live in /opt/homebrew/bin (Apple Silicon) or /usr/local/bin
9
+ # (Intel). Prepend the common tool dirs so a GUI-spawned CLI still finds them.
10
+ # Harmless elsewhere (missing dirs are simply ignored by PATH lookup).
11
+ export PATH="/opt/homebrew/bin:/usr/local/bin:$PATH"
12
+
13
+ # Resolve symlinks before computing the package root: `npm install -g` puts the
14
+ # launcher in the global bin as a SYMLINK to the package, so $0 is the symlink,
15
+ # not the real file. Walk the symlink chain to the actual launcher so
16
+ # "<root>/skills/work-plan" resolves to the npm package, not the global bin dir.
17
+ src="${BASH_SOURCE[0]:-$0}"
18
+ while [ -L "$src" ]; do
19
+ dir="$(cd -P "$(dirname "$src")" && pwd)"
20
+ link="$(readlink "$src")"
21
+ case "$link" in
22
+ /*) src="$link" ;;
23
+ *) src="$dir/$link" ;;
24
+ esac
25
+ done
26
+ root="$(cd -P "$(dirname "$src")/.." && pwd)"
27
+
28
+ # Runtime preflight: surface a clear, per-tool message if a required external
29
+ # tool is missing, instead of a Python traceback from deep in config-load (the
30
+ # classic "yq not found" confusion). yq/gh aren't needed for --version/--help,
31
+ # so don't block those.
32
+ _wp_have() { command -v "$1" >/dev/null 2>&1; }
33
+ _wp_missing=""
34
+ _wp_have python3 || _wp_missing="$_wp_missing python3"
35
+ case "${1:-}" in
36
+ --version|-v|--help|-h|"") ;;
37
+ *)
38
+ _wp_have yq || _wp_missing="$_wp_missing yq"
39
+ _wp_have gh || _wp_missing="$_wp_missing gh"
40
+ ;;
41
+ esac
42
+ if [ -n "$_wp_missing" ]; then
43
+ echo "work-plan: missing required tool(s) on PATH:$_wp_missing" >&2
44
+ case "$_wp_missing" in *python3*) echo " python3 — the CLI runtime (brew install python · https://python.org)" >&2 ;; esac
45
+ case "$_wp_missing" in *yq*) echo " yq — YAML config/frontmatter (brew install yq — the Go mikefarah/yq, NOT the python yq)" >&2 ;; esac
46
+ case "$_wp_missing" in *gh*) echo " gh — GitHub issue state (brew install gh, then: gh auth login · https://cli.github.com)" >&2 ;; esac
47
+ exit 1
48
+ fi
49
+
50
+ for c in \
51
+ "$root/skills/work-plan/work_plan.py" \
52
+ "${CLAUDE_PLUGIN_ROOT:-}/skills/work-plan/work_plan.py" \
53
+ "${PLUGIN_ROOT:-}/skills/work-plan/work_plan.py" \
54
+ "$HOME/.claude/skills/work-plan/work_plan.py" \
55
+ "$HOME/.agents/skills/work-plan/work_plan.py"; do
56
+ [ -n "$c" ] && [ -f "$c" ] && exec python3 "$c" "$@"
57
+ done
58
+ echo "work-plan: CLI not found (looked next to bin/.., CLAUDE_PLUGIN_ROOT, PLUGIN_ROOT, ~/.claude, ~/.agents)." >&2
59
+ exit 1