@stylusnexus/work-plan 2026.6.9

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 (113) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +478 -0
  3. package/VERSION +1 -0
  4. package/bin/work-plan +36 -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 +119 -0
  9. package/skills/work-plan/commands/__init__.py +0 -0
  10. package/skills/work-plan/commands/brief.py +247 -0
  11. package/skills/work-plan/commands/canonicalize.py +122 -0
  12. package/skills/work-plan/commands/close.py +83 -0
  13. package/skills/work-plan/commands/duplicates.py +111 -0
  14. package/skills/work-plan/commands/export.py +69 -0
  15. package/skills/work-plan/commands/group.py +234 -0
  16. package/skills/work-plan/commands/handoff.py +855 -0
  17. package/skills/work-plan/commands/hygiene.py +104 -0
  18. package/skills/work-plan/commands/init.py +96 -0
  19. package/skills/work-plan/commands/init_repo.py +90 -0
  20. package/skills/work-plan/commands/list_cmd.py +39 -0
  21. package/skills/work-plan/commands/new_track.py +148 -0
  22. package/skills/work-plan/commands/plan_status.py +296 -0
  23. package/skills/work-plan/commands/reconcile.py +172 -0
  24. package/skills/work-plan/commands/refresh_md.py +132 -0
  25. package/skills/work-plan/commands/set_field.py +54 -0
  26. package/skills/work-plan/commands/set_notes_root.py +53 -0
  27. package/skills/work-plan/commands/slot.py +139 -0
  28. package/skills/work-plan/commands/suggest_priorities.py +132 -0
  29. package/skills/work-plan/commands/where_was_i.py +325 -0
  30. package/skills/work-plan/lib/__init__.py +0 -0
  31. package/skills/work-plan/lib/closure.py +72 -0
  32. package/skills/work-plan/lib/config.py +82 -0
  33. package/skills/work-plan/lib/doc_discovery.py +41 -0
  34. package/skills/work-plan/lib/drift.py +32 -0
  35. package/skills/work-plan/lib/export_model.py +40 -0
  36. package/skills/work-plan/lib/frontmatter.py +48 -0
  37. package/skills/work-plan/lib/git_state.py +180 -0
  38. package/skills/work-plan/lib/github_state.py +296 -0
  39. package/skills/work-plan/lib/llm_evidence.py +45 -0
  40. package/skills/work-plan/lib/manifest.py +164 -0
  41. package/skills/work-plan/lib/new_issues.py +69 -0
  42. package/skills/work-plan/lib/next_up.py +98 -0
  43. package/skills/work-plan/lib/prompts.py +68 -0
  44. package/skills/work-plan/lib/reconcile_actions.py +34 -0
  45. package/skills/work-plan/lib/render.py +83 -0
  46. package/skills/work-plan/lib/scratch.py +14 -0
  47. package/skills/work-plan/lib/session_log.py +39 -0
  48. package/skills/work-plan/lib/status_header.py +60 -0
  49. package/skills/work-plan/lib/status_table.py +227 -0
  50. package/skills/work-plan/lib/tracks.py +109 -0
  51. package/skills/work-plan/lib/verdict.py +51 -0
  52. package/skills/work-plan/lib/write_guard.py +39 -0
  53. package/skills/work-plan/tests/__init__.py +0 -0
  54. package/skills/work-plan/tests/fixtures/notes_root/critforge/archive/shipped/old.md +10 -0
  55. package/skills/work-plan/tests/fixtures/notes_root/critforge/example.md +11 -0
  56. package/skills/work-plan/tests/fixtures/notes_root/critforge/no_frontmatter.md +1 -0
  57. package/skills/work-plan/tests/fixtures/notes_root/loose_at_root.md +1 -0
  58. package/skills/work-plan/tests/fixtures/track_with_frontmatter.md +14 -0
  59. package/skills/work-plan/tests/fixtures/track_without_frontmatter.md +3 -0
  60. package/skills/work-plan/tests/fixtures/with_status_table.md +9 -0
  61. package/skills/work-plan/tests/test_close.py +273 -0
  62. package/skills/work-plan/tests/test_closure.py +51 -0
  63. package/skills/work-plan/tests/test_config.py +85 -0
  64. package/skills/work-plan/tests/test_config_seed.py +41 -0
  65. package/skills/work-plan/tests/test_doc_discovery.py +51 -0
  66. package/skills/work-plan/tests/test_drift.py +38 -0
  67. package/skills/work-plan/tests/test_export.py +91 -0
  68. package/skills/work-plan/tests/test_export_command.py +295 -0
  69. package/skills/work-plan/tests/test_frontmatter.py +52 -0
  70. package/skills/work-plan/tests/test_git_state.py +51 -0
  71. package/skills/work-plan/tests/test_git_state_paths.py +51 -0
  72. package/skills/work-plan/tests/test_github_state.py +508 -0
  73. package/skills/work-plan/tests/test_handoff_append_rows.py +73 -0
  74. package/skills/work-plan/tests/test_handoff_attribution.py +152 -0
  75. package/skills/work-plan/tests/test_handoff_auto_next_skip.py +183 -0
  76. package/skills/work-plan/tests/test_handoff_collision_warning.py +149 -0
  77. package/skills/work-plan/tests/test_handoff_set_next.py +106 -0
  78. package/skills/work-plan/tests/test_init.py +289 -0
  79. package/skills/work-plan/tests/test_init_repo.py +251 -0
  80. package/skills/work-plan/tests/test_llm_evidence.py +77 -0
  81. package/skills/work-plan/tests/test_manifest.py +162 -0
  82. package/skills/work-plan/tests/test_new_issues.py +130 -0
  83. package/skills/work-plan/tests/test_new_track.py +445 -0
  84. package/skills/work-plan/tests/test_next_up.py +149 -0
  85. package/skills/work-plan/tests/test_plan_status.py +68 -0
  86. package/skills/work-plan/tests/test_plan_status_archive.py +61 -0
  87. package/skills/work-plan/tests/test_plan_status_foreign.py +55 -0
  88. package/skills/work-plan/tests/test_plan_status_issues.py +61 -0
  89. package/skills/work-plan/tests/test_plan_status_llm_apply.py +71 -0
  90. package/skills/work-plan/tests/test_plan_status_llm_prepare.py +66 -0
  91. package/skills/work-plan/tests/test_plan_status_stamp.py +70 -0
  92. package/skills/work-plan/tests/test_plugin_manifests.py +38 -0
  93. package/skills/work-plan/tests/test_reconcile_actions.py +60 -0
  94. package/skills/work-plan/tests/test_reconcile_readonly.py +166 -0
  95. package/skills/work-plan/tests/test_reconcile_wrappers.py +55 -0
  96. package/skills/work-plan/tests/test_refresh_md.py +98 -0
  97. package/skills/work-plan/tests/test_render.py +110 -0
  98. package/skills/work-plan/tests/test_repo_filter.py +52 -0
  99. package/skills/work-plan/tests/test_security_hardening.py +117 -0
  100. package/skills/work-plan/tests/test_session_log.py +39 -0
  101. package/skills/work-plan/tests/test_set_field.py +77 -0
  102. package/skills/work-plan/tests/test_set_notes_root.py +292 -0
  103. package/skills/work-plan/tests/test_slot.py +243 -0
  104. package/skills/work-plan/tests/test_slot_move.py +128 -0
  105. package/skills/work-plan/tests/test_smoke.py +46 -0
  106. package/skills/work-plan/tests/test_status_header.py +79 -0
  107. package/skills/work-plan/tests/test_status_table.py +162 -0
  108. package/skills/work-plan/tests/test_suggested_first_action.py +112 -0
  109. package/skills/work-plan/tests/test_tracks.py +56 -0
  110. package/skills/work-plan/tests/test_verdict.py +60 -0
  111. package/skills/work-plan/tests/test_where_was_i.py +382 -0
  112. package/skills/work-plan/tests/test_write_guard.py +53 -0
  113. package/skills/work-plan/work_plan.py +210 -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,478 @@
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
+ `work-plan` is a CLI-backed agent skill (a pure-Python-stdlib CLI + `SKILL.md`). It treats your daily work as a set of *tracks* — each track is a markdown file with YAML frontmatter listing its priority, milestone, GitHub issue numbers, and current status. The skill derives state live from GitHub (`gh`), git, and the markdown body, so the markdown stays light (it references issues by ID rather than duplicating their state).
11
+
12
+ ## Quick install
13
+
14
+ **Claude Code (recommended):**
15
+ ```
16
+ /plugin marketplace add stylusnexus/agent-plugins
17
+ /plugin install work-plan@stylus-nexus
18
+ ```
19
+ **Codex:** `codex plugin marketplace add stylusnexus/agent-plugins` → `codex plugin add work-plan@stylus-nexus`
20
+ **npm (any editor / standalone CLI):**
21
+ ```
22
+ npm install -g @stylusnexus/work-plan
23
+ ```
24
+ **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).
25
+ **Cursor / Copilot / direct:** clone + `./install.sh` (see [Install](#install)).
26
+
27
+ 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.
28
+
29
+ > **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`.
30
+
31
+ The five essentials you'll use 80% of the time are:
32
+
33
+ | Command | When |
34
+ |---|---|
35
+ | `/work-plan brief` | Morning. Multi-track snapshot — what's on your plate across every active track. Add `--repo=<key>` to scope to one project. |
36
+ | `/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. |
37
+ | `/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. |
38
+ | `/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. |
39
+ | `/work-plan hygiene [--repo=<key>]` | Weekly. Refresh status icons, reconcile labels, scan for duplicates. `--repo=<key>` scopes steps 1–2 to one repo (duplicates is global, so it's skipped in scoped mode). |
40
+
41
+ A dozen more subcommands cover slotting new issues into tracks, closing tracks (shipped/abandoned/parked), AI-clustering raw GitHub issues into thematic tracks, and one-time priority-label backfill.
42
+
43
+ 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).
44
+
45
+ ## How it works
46
+
47
+ 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.
48
+
49
+ ```mermaid
50
+ flowchart TB
51
+ subgraph sources["Data sources (canonical)"]
52
+ direction LR
53
+ gh["GitHub issues<br/>(via gh CLI)"]
54
+ git["git state<br/>(branch, commits, modified files)"]
55
+ md["Track markdown<br/>(YAML frontmatter + body)"]
56
+ end
57
+
58
+ subgraph cli["work-plan CLI (Python stdlib)"]
59
+ direction LR
60
+ brief["brief<br/><i>multi-track view</i>"]
61
+ handoff["handoff<br/><i>capture session + algorithmic or LLM next_up</i>"]
62
+ orient["orient<br/><i>paste-block context for new session</i>"]
63
+ hygiene["hygiene<br/><i>weekly drift + dup sweep</i>"]
64
+ end
65
+
66
+ subgraph outputs["Outputs"]
67
+ direction LR
68
+ paste["Paste-ready blocks<br/>(relayed verbatim to chat)"]
69
+ update["Updated frontmatter<br/>(next_up, session log, status table)"]
70
+ sync["GitHub label sync<br/>(reconcile, refresh-md)"]
71
+ end
72
+
73
+ sources --> cli
74
+ cli --> outputs
75
+ update -.->|"next day"| md
76
+ ```
77
+
78
+ **Daily rhythm**:
79
+
80
+ - **Morning** → `brief` shows multi-track plate, then `orient <track>` produces a ~15-line paste-block to drop into a fresh agent session.
81
+ - **End of work block** → `handoff <track>` captures what you touched. Three ways to set `next_up` for tomorrow:
82
+ - `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.
83
+ - `handoff <track> --set-next 4167,4148` — explicit numbers when you know exactly which issues are next.
84
+ - 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.
85
+ - 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.
86
+ - **Weekly** → `hygiene` runs `refresh-md --all` + `reconcile --all` + `duplicates` in sequence to keep status icons, GitHub labels, and dedup state honest.
87
+ > **When does the body status table get refreshed?** `handoff` already rewrites the ✅/🔲 icons for its own track on every run (live `gh` fetch → `update_row_status`). `brief` reads GitHub state live and never relies on the body table, so it's always accurate. The only drift `refresh-md` exists to fix is *cross-track*: a track you haven't `handoff`'d recently whose icons fell behind because issues moved while you were heads-down on a sibling track. That's why `hygiene --all` sweeps it weekly.
88
+
89
+ ## Plan & doc liveness (`plan-status`)
90
+
91
+ 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.
92
+
93
+ **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.
94
+
95
+ `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.
96
+
97
+ ```
98
+ $ /work-plan plan-status --repo=myproject
99
+
100
+ # plan-status — /path/to/myproject
101
+ 332 docs · 140 shipped · 20 partial · 172 manifest-less
102
+ lie-gap (shipped but <25% boxes checked): 134
103
+
104
+ ## ✅ shipped (140)
105
+ docs/plans/2026-03-16-idea-mode-ui.md
106
+ 9/9 declared files present (boxes stale)
107
+ ...
108
+ ## 🟡 partial (20)
109
+ docs/plans/2026-05-01-v0.4.0-one-week-closeout.md
110
+ 19/40 declared files present
111
+ ...
112
+ ```
113
+
114
+ Each doc reaches one of these verdicts:
115
+
116
+ | Verdict | Meaning |
117
+ |---|---|
118
+ | ✅ **shipped** | (nearly) all declared files present — done, even if the boxes say otherwise |
119
+ | 🟡 **partial** | some files present — genuinely in progress; *this is your to-do list* |
120
+ | 💀 **dead** | no files, long untouched — an abandonment candidate |
121
+ | 👻 **manifest-less** | a prose doc with no file-manifest (e.g. a design spec) — needs a judgment call |
122
+ | 🧳 **foreign** | a misfiled plan whose declared files live in *another* repo — not this repo's work at all |
123
+
124
+ **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`.
125
+
126
+ **Acting on the results (gated).** Once you trust the verdicts:
127
+ - `--archive` moves 💀 dead plans into `archive/abandoned/` (history-preserving `git mv`).
128
+ - `--issues` opens a GitHub issue per 🟡 partial plan, listing its unsatisfied files.
129
+
130
+ Both are confirmation-gated and honor `--draft` (preview, zero side effects).
131
+
132
+ **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:
133
+
134
+ ```markdown
135
+ # Idea Mode UI — Implementation Plan
136
+
137
+ <!-- plan-status: BEGIN -->
138
+ > **Status:** ✅ shipped · 9/9 files · last touched 2026-03-20
139
+ <!-- plan-status: END -->
140
+ ```
141
+
142
+ 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.
143
+
144
+ **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.
145
+
146
+ ## Requirements
147
+
148
+ The toolkit is a Python CLI that shells out to standard tools. You need **all four** installed before running `install.sh` / `install.ps1`:
149
+
150
+ | Tool | Min version | Why |
151
+ |---|---|---|
152
+ | 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). |
153
+ | `gh` | recent | Live GitHub state queries (issues, milestones, labels). Must be authenticated: `gh auth login` once before first run. |
154
+ | `git` | any 2.x | Detects current branch, ahead-of-upstream count, modified files. |
155
+ | `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. |
156
+
157
+ Install per platform (one-liners):
158
+
159
+ ```bash
160
+ # macOS (Homebrew)
161
+ brew install python@3 gh git yq
162
+
163
+ # Linux (Debian/Ubuntu)
164
+ sudo apt update && sudo apt install python3 git
165
+ # gh: https://github.com/cli/cli/blob/trunk/docs/install_linux.md
166
+ # 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
167
+
168
+ # Linux (Arch)
169
+ sudo pacman -S python github-cli git go-yq
170
+
171
+ # Windows (PowerShell with winget)
172
+ winget install Python.Python.3 GitHub.cli Git.Git MikeFarah.yq
173
+ ```
174
+
175
+ `install.sh` and `install.ps1` both verify all four are on `PATH` before doing anything else, and print install hints if any are missing.
176
+
177
+ After installing, authenticate `gh` once:
178
+
179
+ ```bash
180
+ gh auth login # follow the prompts; needs `repo` scope to read issues
181
+ ```
182
+
183
+ ## Compatible tools
184
+
185
+ 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:
186
+
187
+ | Layer | What it is | Claude Code | Codex | Cursor | GitHub Copilot |
188
+ |---|---|---|---|---|---|
189
+ | **1. Python CLI** | `work_plan.py` + subcommands. Pure stdlib, shells out to `gh`/`git`/`yq`. | ✅ Proven | ✅ Proven | ✅ Direct invocation | ✅ Direct invocation |
190
+ | **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) |
191
+ | **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 |
192
+
193
+ ### Install per platform
194
+
195
+ | Tool | Install command | Then invoke as |
196
+ |---|---|---|
197
+ | **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>` |
198
+ | **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>` |
199
+ | **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 |
200
+ | **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 |
201
+ | **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 |
202
+ | **Any other tool** | Skip installer. Just `git clone`. | Direct CLI |
203
+
204
+ Shell rc alias for the direct-CLI cases:
205
+
206
+ ```bash
207
+ # bash/zsh (~/.bashrc, ~/.zshrc)
208
+ alias wp="python3 /path/to/work-plan-toolkit/skills/work-plan/work_plan.py"
209
+
210
+ # PowerShell ($PROFILE)
211
+ function wp { python "C:\path\to\work-plan-toolkit\skills\work-plan\work_plan.py" @args }
212
+ ```
213
+
214
+ To install for **both** Claude Code AND Codex, run the installer twice with different `--target` values.
215
+
216
+ ### VS Code extension
217
+
218
+ 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.
219
+
220
+ ![Work Plan VS Code extension — sidebar and dependency graph](https://raw.githubusercontent.com/stylusnexus/work-plan-toolkit/main/vscode/media/screenshots/dependency-graph.png)
221
+
222
+ Install it from either registry:
223
+
224
+ - **VS Code Marketplace:** Extensions view → search **"Work Plan"** (publisher `stylusnexus`), or `code --install-extension stylusnexus.work-plan-viewer`.
225
+ - **Open VSX** (VS Codium / Cursor / Windsurf): search **"Work Plan"**, or `ovsx get stylusnexus.work-plan-viewer`.
226
+
227
+ 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.
228
+
229
+ ### Updating
230
+
231
+ | Installed via | Update with |
232
+ |---|---|
233
+ | **npm** | `npm install -g @stylusnexus/work-plan@latest` (or `npm update -g @stylusnexus/work-plan`) |
234
+ | **Claude Code / Codex plugin** | `/plugin update work-plan@stylus-nexus` (Codex: `codex plugin update …`) |
235
+ | **Script (`install.sh`)** | `git pull` in the toolkit repo, then re-run `./install.sh` (or `.\install.ps1`) |
236
+ | **VS Code extension** | Auto-updates; or Extensions view → update manually |
237
+
238
+ Check your version any time with `work-plan --version`.
239
+
240
+ ### What the shims do
241
+
242
+ 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.
243
+
244
+ 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.
245
+
246
+ ## Install
247
+
248
+ Pick the path for your tool. All three install the same CLI + skills.
249
+
250
+ ### Claude Code (recommended) — plugin, easy updates
251
+
252
+ ```
253
+ /plugin marketplace add stylusnexus/agent-plugins
254
+ /plugin install work-plan@stylus-nexus
255
+ ```
256
+
257
+ Commands are namespaced under the plugin: `/work-plan:brief`, `/work-plan:handoff`,
258
+ `/work-plan:orient`, `/work-plan:hygiene`, `/work-plan:status`, and
259
+ `/work-plan:run <subcommand>` for everything else. Update with
260
+ `/plugin update work-plan@stylus-nexus`. Works in the CLI and the VS Code / JetBrains extensions.
261
+
262
+ ### Codex — plugin
263
+
264
+ ```
265
+ codex plugin marketplace add stylusnexus/agent-plugins
266
+ codex plugin add work-plan@stylus-nexus
267
+ ```
268
+
269
+ ### Cursor / direct / other — script
270
+
271
+ ```bash
272
+ git clone <this-repo> work-plan-toolkit
273
+ cd work-plan-toolkit && ./install.sh # macOS / Linux / WSL
274
+ # or, on Windows native PowerShell: .\install.ps1
275
+ # or, for Codex's skills dir: ./install.sh --target=$HOME/.agents
276
+ ```
277
+
278
+ Gives the single bare `/work-plan <subcommand>` (no namespace). Re-run after `git pull` to refresh
279
+ (the plugin paths above update themselves).
280
+
281
+ The installer:
282
+
283
+ - **Copies** (not symlinks — for Windows compatibility) `skills/work-plan` and `skills/repo-activity-summary` into `~/.claude/skills/`
284
+ - 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)
285
+ - **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`
286
+ - Drops a `.installed-from` marker so `uninstall` knows what's safe to remove
287
+
288
+ External dependencies (verified by the installer): `gh`, `git`, `yq`, `python3`.
289
+
290
+ > **Versioning:** releases use CalVer (`YYYY.MM.DD+<sha>`, auto-bumped on deploy and synced into the plugin manifests) — not SemVer.
291
+
292
+ ### What gets created
293
+
294
+ After clone, the toolkit looks like this:
295
+
296
+ ```
297
+ work-plan-toolkit/
298
+ ├── README.md
299
+ ├── LICENSE
300
+ ├── .gitignore
301
+ ├── install.sh / install.ps1 # macOS+Linux+WSL / Windows
302
+ ├── uninstall.sh / uninstall.ps1
303
+ ├── skills/
304
+ │ ├── work-plan/
305
+ │ │ ├── SKILL.md
306
+ │ │ ├── work_plan.py # CLI entry
307
+ │ │ ├── commands/ # 16 subcommand modules
308
+ │ │ ├── lib/ # config, frontmatter, gh, git, prompts, …
309
+ │ │ └── tests/ # 234 unittest cases
310
+ │ └── repo-activity-summary/
311
+ │ └── SKILL.md # bundled companion skill
312
+ ├── commands/
313
+ │ └── work-plan.md # Claude Code slash command alias
314
+ ├── docs/
315
+ │ └── usage-examples.md
316
+ ├── shims/ # Drop-in instruction files for non-skill-aware tools
317
+ │ ├── README.md
318
+ │ ├── cursor/work-plan.cursorrules
319
+ │ └── copilot/work-plan-copilot-instructions.md
320
+ └── notes/
321
+ └── README.md # default notes_root (empty until init-repo)
322
+ ```
323
+
324
+ After running `install.sh` (or `install.ps1`), the installer creates this in your home directory:
325
+
326
+ ```
327
+ ~/.claude/
328
+ ├── skills/
329
+ │ ├── work-plan/ # copy of toolkit's skills/work-plan/
330
+ │ │ ├── SKILL.md
331
+ │ │ ├── work_plan.py
332
+ │ │ ├── commands/
333
+ │ │ ├── lib/
334
+ │ │ ├── tests/
335
+ │ │ └── .installed-from # marker file (toolkit absolute path)
336
+ │ └── repo-activity-summary/
337
+ │ ├── SKILL.md
338
+ │ └── .installed-from # marker file
339
+ ├── commands/
340
+ │ └── work-plan.md # copy of toolkit's commands/work-plan.md
341
+ └── work-plan/
342
+ └── config.yml # seeded from template, notes_root resolved
343
+ # to absolute toolkit path (edit this one)
344
+ ```
345
+
346
+ 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):
347
+
348
+ ```
349
+ <notes_root>/
350
+ └── myproject/ # created by init-repo
351
+ ├── archive/
352
+ │ ├── shipped/.gitkeep # close mv's shipped tracks here
353
+ │ └── abandoned/.gitkeep # close mv's abandoned tracks here
354
+ └── <track-slug>.md # active tracks live at top level
355
+ ```
356
+
357
+ ## Configure
358
+
359
+ After install, bootstrap your first repo with the **`init-repo`** subcommand:
360
+
361
+ ```bash
362
+ /work-plan init-repo myproject --github=your-org/myproject --local=/path/to/checkout
363
+ ```
364
+
365
+ 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.
366
+
367
+ You can also edit `~/.claude/work-plan/config.yml` directly:
368
+
369
+ ```yaml
370
+ notes_root: /absolute/path/to/your/notes/ # or keep the bundled default
371
+ repos:
372
+ myproject:
373
+ github: your-org/myproject
374
+ local: /path/to/local/checkout # optional, enables in-progress detection
375
+ ```
376
+
377
+ ### Where your config lives
378
+
379
+ 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.
380
+
381
+ After a fresh install, it looks like:
382
+
383
+ ```yaml
384
+ # work-plan config — created by install.sh. Edit this file to customize.
385
+ # Run /work-plan init-repo <key> --github=<org/repo> to populate repos:.
386
+ notes_root: /absolute/path/to/work-plan-toolkit/notes
387
+ repos: {}
388
+ ```
389
+
390
+ `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.
391
+
392
+ 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`.
393
+
394
+ ## Security & data handling
395
+
396
+ - **No credentials stored.** All GitHub access goes through your existing `gh auth`. This toolkit never reads, writes, or stores GitHub tokens.
397
+ - **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.
398
+ - **No telemetry, no network calls beyond `gh`.** All GitHub operations go through `gh` (your authenticated session); no direct HTTP requests are made.
399
+ - **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.
400
+ - **`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.
401
+ - **`install.sh` / `install.ps1` only touch user-owned dirs.** No `sudo`, no system-wide changes, no privilege escalation.
402
+
403
+ For vulnerability reporting, threat model, and past advisories, see [SECURITY.md](./SECURITY.md).
404
+
405
+ ## Usage walkthrough
406
+
407
+ See `docs/usage-examples.md` for end-to-end scenarios (morning brief, mid-work handoff, fresh-session orient, weekly hygiene).
408
+
409
+ ## Subcommand reference
410
+
411
+ | Subcommand | What it does |
412
+ |---|---|
413
+ | `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). |
414
+ | `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`. |
415
+ | `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. |
416
+ | `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). |
417
+ | `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. |
418
+ | `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. |
419
+ | `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. |
420
+ | `list [--all]` | List active tracks (or all including parked/archived). |
421
+ | `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. |
422
+ | `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. |
423
+ | `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. |
424
+ | `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. |
425
+ | `suggest-priorities --repo=<key>` | Two-step AI label backfill: CLI fetches unlabeled issues, Claude proposes priorities, `--apply` writes labels via `gh`. |
426
+ | `group [--milestone=X] [--label=Y]` | AI-cluster GitHub issues into thematic tracks (creates `<repo>/<slug>.md` per cluster). |
427
+ | `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`. |
428
+ | `duplicates [--repo=<key>]` | Find likely-duplicate issues by title similarity (stdlib `difflib`). Prints `gh issue close` consolidation commands. |
429
+ | `canonicalize <track>` | Add a canonical issue table to a track file (so `refresh-md` knows where to update). |
430
+ | `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. |
431
+
432
+ Run `python3 ~/.claude/skills/work-plan/work_plan.py --help` for the full list with examples.
433
+
434
+ ### Non-interactive writes & the public-repo gate
435
+
436
+ 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.
437
+
438
+ `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.**
439
+
440
+ `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.
441
+
442
+ ## Version
443
+
444
+ ```bash
445
+ python3 ~/.claude/skills/work-plan/work_plan.py --version
446
+ # work-plan 2026.04.30+a1b2c3d
447
+ ```
448
+
449
+ 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.
450
+
451
+ ## Composes with
452
+
453
+ - **`/repo-activity-summary`** (bundled) — Global "what's open across the whole repo" view. Use when you need a wider lens than per-track.
454
+ - 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.
455
+
456
+ ## Philosophy
457
+
458
+ - **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.
459
+ - **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.
460
+ - **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.
461
+ - **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.
462
+
463
+ ## Testing
464
+
465
+ ```bash
466
+ cd skills/work-plan
467
+ python3 -m unittest discover tests
468
+ ```
469
+
470
+ 234 tests, no external dependencies (mocks `gh`/`git` calls).
471
+
472
+ ## License
473
+
474
+ MIT — see `LICENSE`.
475
+
476
+ ## Maintainer
477
+
478
+ Stylus Nexus Holdings LLC · [@stylusnexus](https://github.com/stylusnexus)
package/VERSION ADDED
@@ -0,0 +1 @@
1
+ 2026.06.09+f3bc861
package/bin/work-plan ADDED
@@ -0,0 +1,36 @@
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
+ for c in \
28
+ "$root/skills/work-plan/work_plan.py" \
29
+ "${CLAUDE_PLUGIN_ROOT:-}/skills/work-plan/work_plan.py" \
30
+ "${PLUGIN_ROOT:-}/skills/work-plan/work_plan.py" \
31
+ "$HOME/.claude/skills/work-plan/work_plan.py" \
32
+ "$HOME/.agents/skills/work-plan/work_plan.py"; do
33
+ [ -n "$c" ] && [ -f "$c" ] && exec python3 "$c" "$@"
34
+ done
35
+ echo "work-plan: CLI not found (looked next to bin/.., CLAUDE_PLUGIN_ROOT, PLUGIN_ROOT, ~/.claude, ~/.agents)." >&2
36
+ exit 1
@@ -0,0 +1,9 @@
1
+ @echo off
2
+ rem work-plan CLI launcher (Windows). Resolves work_plan.py relative to this
3
+ rem shim's parent (the wrapper lives at <root>\bin\work-plan.cmd).
4
+ set "WP=%~dp0..\skills\work-plan\work_plan.py"
5
+ if exist "%WP%" ( python "%WP%" %* & goto :eof )
6
+ if defined CLAUDE_PLUGIN_ROOT if exist "%CLAUDE_PLUGIN_ROOT%\skills\work-plan\work_plan.py" ( python "%CLAUDE_PLUGIN_ROOT%\skills\work-plan\work_plan.py" %* & goto :eof )
7
+ if exist "%USERPROFILE%\.claude\skills\work-plan\work_plan.py" ( python "%USERPROFILE%\.claude\skills\work-plan\work_plan.py" %* & goto :eof )
8
+ echo work-plan: CLI not found. 1>&2
9
+ exit /b 1
package/package.json ADDED
@@ -0,0 +1,43 @@
1
+ {
2
+ "name": "@stylusnexus/work-plan",
3
+ "version": "2026.6.9",
4
+ "description": "Track-aware daily work planning over GitHub issues — the work-plan CLI. Pure Python stdlib; this package only ships + launches it.",
5
+ "bin": {
6
+ "work-plan": "bin/work-plan"
7
+ },
8
+ "files": [
9
+ "bin/",
10
+ "skills/work-plan/",
11
+ "scripts/npm-check-deps.js",
12
+ "VERSION",
13
+ "LICENSE",
14
+ "README.md"
15
+ ],
16
+ "scripts": {
17
+ "postinstall": "node scripts/npm-check-deps.js"
18
+ },
19
+ "engines": {
20
+ "node": ">=18"
21
+ },
22
+ "os": [
23
+ "darwin",
24
+ "linux"
25
+ ],
26
+ "keywords": [
27
+ "work-plan",
28
+ "github issues",
29
+ "cli",
30
+ "project planning",
31
+ "tracks"
32
+ ],
33
+ "repository": {
34
+ "type": "git",
35
+ "url": "https://github.com/stylusnexus/work-plan-toolkit.git"
36
+ },
37
+ "homepage": "https://github.com/stylusnexus/work-plan-toolkit#readme",
38
+ "bugs": {
39
+ "url": "https://github.com/stylusnexus/work-plan-toolkit/issues"
40
+ },
41
+ "license": "MIT",
42
+ "_note": "This package.json exists ONLY to distribute the Python CLI via npm (npm install -g @stylusnexus/work-plan). It adds no Node/Python dependencies and no build step — the CLI stays pure Python 3.9 stdlib. The vscode/ extension is a separate Node project with its own package.json."
43
+ }
@@ -0,0 +1,44 @@
1
+ #!/usr/bin/env node
2
+ // Postinstall check for the @stylusnexus/work-plan npm package.
3
+ //
4
+ // The work-plan CLI is pure Python and shells out to three external tools at
5
+ // runtime. npm can't install them (they're not npm packages), so we just check
6
+ // they're on PATH and print a friendly heads-up if any are missing. This NEVER
7
+ // fails the install — a missing tool is the user's to fix, and the CLI prints
8
+ // its own clear error if one is absent when actually run.
9
+
10
+ const { execSync } = require("node:child_process");
11
+
12
+ const TOOLS = [
13
+ { cmd: "python3", why: "runs the CLI", hint: "https://www.python.org/downloads/ (or `brew install python`)" },
14
+ { cmd: "yq", why: "parses config + frontmatter (mikefarah/yq, the Go one — NOT the python yq)", hint: "brew install yq · https://github.com/mikefarah/yq" },
15
+ { cmd: "gh", why: "reads GitHub issue state", hint: "brew install gh · https://cli.github.com" },
16
+ ];
17
+
18
+ function have(cmd) {
19
+ try {
20
+ const probe = process.platform === "win32" ? `where ${cmd}` : `command -v ${cmd}`;
21
+ execSync(probe, { stdio: "ignore", shell: true });
22
+ return true;
23
+ } catch {
24
+ return false;
25
+ }
26
+ }
27
+
28
+ try {
29
+ const missing = TOOLS.filter((t) => !have(t.cmd));
30
+ if (missing.length) {
31
+ const lines = [
32
+ "",
33
+ " work-plan installed. It needs a few tools on your PATH that npm can't install:",
34
+ ...missing.map((t) => ` • ${t.cmd} — ${t.why}\n ${t.hint}`),
35
+ "",
36
+ " (The CLI will tell you specifically if one is missing when you run it.)",
37
+ "",
38
+ ];
39
+ console.warn(lines.join("\n"));
40
+ }
41
+ } catch {
42
+ // Never let the check itself break the install.
43
+ }
44
+ process.exit(0);