planrails 0.1.2 → 0.2.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 (41) hide show
  1. package/CHANGELOG.md +69 -16
  2. package/PLANNER.md +255 -0
  3. package/README.md +103 -176
  4. package/bin/planrails.mjs +90 -5
  5. package/package.json +14 -48
  6. package/tools/check-plans.mjs +213 -0
  7. package/docs/PLANNING_GUIDE.md +0 -632
  8. package/src/hooks/_lib.mjs +0 -32
  9. package/src/hooks/guard-never-delete.sh +0 -29
  10. package/src/hooks/install.mjs +0 -179
  11. package/src/hooks/plan-pre-tool.mjs +0 -71
  12. package/src/hooks/plan-session-start.mjs +0 -46
  13. package/src/hooks/plan-stop.mjs +0 -60
  14. package/src/hooks/plan-subagent-start.mjs +0 -27
  15. package/src/hooks/postcompact-journal.mjs +0 -35
  16. package/src/hooks/precompact-journal.mjs +0 -128
  17. package/src/hooks/selftest.mjs +0 -128
  18. package/src/init.mjs +0 -161
  19. package/src/issue.mjs +0 -55
  20. package/src/plan/fixtures/README.md +0 -7
  21. package/src/plan/fixtures/broken-cli.mjs +0 -27
  22. package/src/plan/fixtures/broken-hooks-root/.claude/settings.json +0 -83
  23. package/src/plan/fixtures/broken-hooks-root/.project-management/plans/.gitkeep +0 -0
  24. package/src/plan/fixtures/broken-hooks-root/CLAUDE.md +0 -9
  25. package/src/plan/fixtures/broken-root/.project-management/plans/broken/PLAN.md +0 -4
  26. package/src/plan/fixtures/broken-root/.project-management/plans/broken/gates.json +0 -1
  27. package/src/plan/fixtures/broken-root/.project-management/plans/broken/rules.json +0 -1
  28. package/src/plan/fixtures/broken-root/.project-management/plans/broken/state.json +0 -67
  29. package/src/plan/fixtures/broken-root/CLAUDE.md +0 -3
  30. package/src/plan/fixtures/broken-trial.mjs +0 -25
  31. package/src/plan/lib/brief.mjs +0 -116
  32. package/src/plan/lib/claude-md.mjs +0 -68
  33. package/src/plan/lib/glob.mjs +0 -81
  34. package/src/plan/lib/judgment.mjs +0 -19
  35. package/src/plan/lib/paths.mjs +0 -65
  36. package/src/plan/lib/schema.mjs +0 -199
  37. package/src/plan/lib/store.mjs +0 -338
  38. package/src/plan/lib/time.mjs +0 -21
  39. package/src/plan/plan.mjs +0 -843
  40. package/src/plan/run.mjs +0 -88
  41. package/src/plan/skill/SKILL.md +0 -15
package/README.md CHANGED
@@ -1,225 +1,152 @@
1
1
  # planrails
2
2
 
3
- **Plans that survive compaction, for Claude Code.** A plan lives in files, not in the chat. A task is done only when a gate ran and every condition of done was answered. Hooks put the plan in front of the agent at the moments it matters: session start, after a compaction, at the tool call, before it stops.
3
+ **Plans that survive a lost session, and "done" that means done.** A planner
4
+ prompt your AI agent reads, and one small checker. Set it up in one command.
4
5
 
5
- ```bash
6
- npx planrails init # in a new directory or an existing project
7
- # restart Claude Code, then type: /plan <your raw plan>
8
- ```
9
-
10
- Built and used on a real project for a day before it was extracted, with the reviews, the black-box trial and the measurements that shaped it recorded in the guide it ships with.
11
-
12
- ## The problem it solves
13
-
14
- A Claude Code session forgets. Compaction keeps your messages and drops every tool result, so the agent loses the record of what it already did. A new session starts blind. The usual fixes make things worse: progress notes written from memory at the end of a session go stale, and every rule learned gets pasted into CLAUDE.md until the file is huge and re-read on every call.
6
+ Long tasks lose their thread. A coding session compacts or ends, and the next one
7
+ starts blind: it repeats work, or it trusts a status line that says "done" over
8
+ work that is not. planrails fixes that with three plain rules and almost no code.
15
9
 
16
- Three habits break under that pressure, and planrails is built against each:
10
+ 1. **The plan reloads itself.** One line in the file your agent always reads
11
+ re-opens the plan after every compaction. The state is never lost.
12
+ 2. **Every task names its proof before the work starts** — the command that will
13
+ show it is done. No command, no way to fake it later.
14
+ 3. **A task is done only when its proof was run and pasted in.** An empty evidence
15
+ cell is not done, whatever the status says. A small, dependency-free checker
16
+ enforces this in your build.
17
17
 
18
- | the habit | what planrails does instead |
19
- |---|---|
20
- | Progress is remembered, then written down late. | Progress is written **as it happens**, through a CLI, into small append-only files. A hook renders a short brief from those files at every session start and after every compaction. |
21
- | Every rule goes into the always-loaded file. | A rule is attached to the step it applies to. A hook injects it at that tool call, once. The brief stays under 6,000 characters. |
22
- | "Done" is a word an agent types. | A task is done only through `task done`, which runs the task's gate, records the run, and needs every condition of done answered in words. The validator refuses a done task that no recorded run backs, whoever wrote the file. |
23
-
24
- ## Requirements
25
-
26
- - Node 20.10 or newer.
27
- - Claude Code (hooks measured on 2.1.269).
28
- - `jq` on the PATH only if you turn on the optional never-delete guard.
18
+ It works in any project Node, Python, Go, a monorepo, Windows — because it adds
19
+ two files and changes nothing else.
29
20
 
30
21
  ## Getting started
31
22
 
32
- ### A new repository
23
+ ### One command, any project
33
24
 
34
- You have an empty directory and an idea.
25
+ From the root of your project:
35
26
 
36
27
  ```bash
37
- mkdir my-app && cd my-app
38
- git init
39
28
  npx planrails init
40
29
  ```
41
30
 
42
- What you will see:
31
+ That copies two files into `.project-management/` and makes the `plans/` folder.
32
+ It writes **nothing else** — no `package.json`, no `npm install`, no hooks, no
33
+ edits to your `CLAUDE.md`. Run it again any time; it skips files that already
34
+ exist. What lands:
43
35
 
44
36
  ```
45
- planrails 0.1.0 → /Users/you/my-app
46
- installing planrails@0.1.0 as a devDependency…
47
-
48
- + wrote a minimal package.json
49
- + installed planrails@0.1.0 as a devDependency
50
- + copied docs/PLANNING_GUIDE.md
51
- + created .project-management/plans/
52
- + created CLAUDE.md with a Project management section
53
- + added the generated plans block to CLAUDE.md
54
- + added npm scripts (plan, plan:doctor, plan:brief, plan:validate)
55
- + added .planrails/ to .gitignore
56
- + wrote the hooks into .claude/settings.json
57
-
58
- doctor: healthy
59
-
60
- Next: restart Claude Code (or run /hooks), then type: /plan <your raw plan>
37
+ .project-management/
38
+ PLANNER.md the prompt your agent follows to plan and execute
39
+ check-plans.mjs the checker (the one machine-enforced rail)
40
+ plans/ your plans will live here, one folder each
61
41
  ```
62
42
 
63
- Then:
43
+ Then, two steps:
64
44
 
65
- 1. Start Claude Code in the directory (or run `/hooks` if it was already open) so the hooks load.
66
- 2. Type `/plan` followed by what you want to build, in your own words. For example: `/plan A CLI that greets people: greet.mjs prints "Hello, <name>!" from the first argument, or "Hello, world!" with none. A test file proves both. A README explains both.`
67
- 3. The planner asks the questions your text did not answer, in one message. Answer them.
68
- 4. It writes the plan, proves every gate can fail, activates the plan, and shows you its brief.
45
+ 1. **Tell your agent to plan with you.** In any coding agent:
69
46
 
70
- Commit everything, including `.claude/settings.json`: the hook commands use `$CLAUDE_PROJECT_DIR`, which Claude Code sets on every machine, so teammates get the hooks after `npm install`.
47
+ > Follow `.project-management/PLANNER.md` and tell me when you are ready to
48
+ > plan the next feature with me.
71
49
 
72
- ### An existing repository
50
+ It reads your repo, reports what it found in eight lines, and waits. Then you
51
+ plan together, and it writes the plan and wires the reload line.
73
52
 
74
- You have a project with its own CLAUDE.md, its own hooks, its own check script. `init` adds and never replaces.
53
+ 2. **Wire the checker into your build.** Add this to the command you run before
54
+ every commit (your `check` / `lint` / CI script):
75
55
 
76
- ```bash
77
- cd my-existing-project
78
- npx planrails init --dry-run # shows what would change, writes nothing
79
- npx planrails init
80
- ```
56
+ ```bash
57
+ node .project-management/check-plans.mjs
58
+ ```
81
59
 
82
- What changes, and what does not:
60
+ Now the build fails if any task is marked done without pasted evidence.
83
61
 
84
- | it | what init does |
85
- |---|---|
86
- | `package.json` | adds planrails as a devDependency and four `plan:*` scripts; appends `planrails validate --all --quiet` to an existing `check` script |
87
- | `CLAUDE.md` | appends a "Project management" section if there is none, and a small generated block that lists the active plans. Your text is untouched. |
88
- | `.claude/settings.json` | adds the six planrails hooks next to the hooks you already have. Other settings are untouched. A backup goes to `.planrails/backups/` first. |
89
- | `docs/PLANNING_GUIDE.md` | copied in, unless a file by that name exists |
90
- | `.project-management/plans/` | created, empty |
91
- | `.gitignore` | gains `.planrails/` |
62
+ That is the whole setup.
92
63
 
93
- Run it twice and the second run reports nothing added. `npx planrails uninstall` removes the hooks and the skill and leaves your plans alone.
64
+ ### New project vs existing project
94
65
 
95
- ### Planning your first feature, step by step
66
+ The command is the same; the difference is what your agent sees.
96
67
 
97
- Say the project is a web app and you want a weekly digest email.
68
+ - **New project:** there is nothing to read yet, so the agent asks you a few
69
+ questions (goal, done-when, what it must not change) and writes the first plan.
70
+ - **Existing project:** the agent first reads your `CLAUDE.md`/`README`, your
71
+ `docs/`, and recent commits, and reports what it found before planning — so the
72
+ plan fits how your code already works. `init` is safe to run in a project that
73
+ already has files or its own `.project-management/`; it only adds, never
74
+ overwrites (without `--force`).
98
75
 
99
- **1. Ask for a plan.** In a Claude Code session in the project:
76
+ ### Without npm, or a non-Node project
100
77
 
101
- ```
102
- /plan Weekly digest email: every Monday 07:00, send each subscriber the five most-read posts of the week. Reuse the existing email seam. A subscriber can turn it off from their settings page.
103
- ```
78
+ No npm? Copy the two files by hand from this repo:
79
+ [`PLANNER.md`](PLANNER.md) `.project-management/PLANNER.md`, and
80
+ [`tools/check-plans.mjs`](tools/check-plans.mjs) →
81
+ `.project-management/check-plans.mjs`. Make a `.project-management/plans/`
82
+ folder. Done.
104
83
 
105
- **2. Answer the questions.** The planner asks only what your text left open, in one message. For example: what proves done, which files may change, who approves the first real send.
84
+ The checker needs Node to run. If your project has no Node at all, skip it the
85
+ plan still works, and your agent enforces the gate the way `PLANNER.md` says. The
86
+ planner method does not depend on any language.
106
87
 
107
- **3. Read what it wrote.** A plan is one folder, `.project-management/plans/weekly-digest/`:
88
+ ### As a `/plan` command in Claude Code
108
89
 
109
- | file | what it holds |
110
- |---|---|
111
- | `PLAN.md` | why, the method, the rules, the map of files |
112
- | `state.json` | every task with its status, its gate, its evidence, its answered conditions. Written only by the CLI. |
113
- | `gates.json` | what proves each task done: a command, what it does not check, a case that must fail |
114
- | `rules.json` | rules a hook shows the agent when it touches a matching file or runs a matching command |
115
- | `log.jsonl`, `learnings.jsonl`, `decisions.jsonl`, `gate-runs.jsonl` | append-only history |
90
+ Copy [`skill/`](skill) to `~/.claude/skills/plan/`, and put a copy of `PLANNER.md`
91
+ beside it in the same folder. Then `/plan` starts the same flow in any project.
116
92
 
117
- The gates are the project's own commands: the test runner on the digest module, the lint, a script that enqueues one digest against a throwaway database. Each one declares a known-fail case, and the planner ran `gate verify --all` before activating, so you know each gate can actually fail.
93
+ ## How a plan works
118
94
 
119
- **4. Execute, one task at a time.** Open a new session, or continue. The brief is already in the agent's context:
95
+ Each plan is two files: `PLAN.md` (the map and tracker) and `LOG.md` (append-only
96
+ history). The top of `PLAN.md` is what a fresh session reads first:
120
97
 
121
98
  ```
122
- ## Plan weekly-digest — Weekly digest email
123
- active · 0/4 tasks done · 0 doing · 0 blocked
124
- RESUME: start T1: the digest query, lib/digest/query.ts
125
- NOW T1: — proves done by G1 · effort high
126
- RULES FOR THIS PLAN: …
127
- JUDGMENT OUTRANKS THE GATE: a red gate is never made to pass, a green gate is never trusted blind …
99
+ ## NOW
100
+ RESUME: T2 render the digest through lib/email (lib/digest/render.ts)
101
+ NEXT: T3 schedule · T4 docs
102
+ updated: 2026-09-12 14:20
103
+
104
+ ## Tasks
105
+ | id | task | status | proof | evidence |
106
+ |----|------|--------|-------|----------|
107
+ | T1 | the digest query (lib/digest/query.ts) | done | `npx vitest run tests/digest.test.ts` | 2026-09-12 14:05 · exit 0 · "6 passed" |
108
+ | T2 | render through the email seam | doing | `npx vitest run tests/render.test.ts` | |
128
109
  ```
129
110
 
130
- The loop for each task:
111
+ The full method how the agent gets ready, interviews you, writes the plan, and
112
+ runs one task at a time — is in [`PLANNER.md`](PLANNER.md). A complete worked plan
113
+ is in [`examples/weekly-digest/`](examples/weekly-digest).
114
+
115
+ ## The checker
131
116
 
132
117
  ```bash
133
- npx planrails task start weekly-digest T1
134
- # the work
135
- npx planrails log weekly-digest --task T1 --what "query returns the top 5 posts by reads over 7 days (lib/digest/query.ts)" --next "T2: render with the email seam"
136
- npx planrails task check weekly-digest T1 # every condition of done, plus the gate's blind spots
137
- npx planrails task done weekly-digest T1 --answer "C4: docs/EMAIL_GUIDE.md § Digest updated" --answer "C5: the 5 is the LIMIT in query.ts:12" --answer "C6: nothing new" --answer "C7: read query.ts whole; the gate cannot see a wrong week boundary, I checked Monday 00:00 by hand"
118
+ node .project-management/check-plans.mjs # done tasks must name a proof and carry evidence
119
+ node .project-management/check-plans.mjs --verify # also re-run each done task's proof, expect exit 0
120
+ npx planrails check # the same, using the latest published checker
138
121
  ```
139
122
 
140
- `task done` runs the gate. If the gate fails, the task stays open and the tool says so. If a condition is unanswered, the tool prints the whole checklist and refuses. That refusal is on purpose: it is the reminder, at the moment it matters, of what "done" means here.
141
-
142
- **5. Or let fresh sessions do it.** `npx planrails run weekly-digest --max-tasks 4` starts one `claude -p` session per task. Each starts empty, gets the brief from the hook, does one task, and exits. The driver reads `state.json` to decide what happens next, never the session's words.
143
-
144
- **6. Review and close.** `npx planrails review weekly-digest` shows every done task with its gate run and each answered condition. When the plan is done, `npx planrails close weekly-digest --confirmed-by-owner` runs every gate fresh and removes the plan's line from CLAUDE.md. Without the flag it stops and asks you.
145
-
146
- ## The commands
147
-
148
- | command | what it does |
149
- |---|---|
150
- | `init`, `update`, `uninstall`, `doctor` | set up, refresh, remove, check |
151
- | `new <id> --title "…" --paths "src/**"` | scaffold a plan (the `/plan` skill does this for you) |
152
- | `gate add\|verify\|run\|list <id>` | gates; `verify` proves each can fail |
153
- | `task add\|check\|start\|done\|block\|unblock\|drop <id> [T]` | the task lifecycle; `done … --answer "C4: …"` runs the gate |
154
- | `condition list\|add\|drop <id>` | the plan-level conditions of done |
155
- | `log`, `learn`, `decide` | record progress, a learning, a choice |
156
- | `brief [id]`, `status <id>`, `review <id> [T]`, `learnings --search x` | read |
157
- | `validate [--all]` | fail on any plan whose state lies (put it in CI) |
158
- | `agent-brief <id> <T>` | what a subagent gets instead of the whole plan |
159
- | `run <id> [--max-tasks N] [--dry-run]` | one fresh session per task |
160
- | `activate`, `pause`, `close`, `abandon` | plan lifecycle; the last two need `--confirmed-by-owner` |
161
- | `hooks install\|status\|uninstall\|selftest` | the hooks |
162
- | `issue [bug\|wish\|edge]` | open a prefilled GitHub issue |
163
- | `selftest` | prove the validator's refusals can fire |
164
-
165
- `npx planrails --help` prints the full list with flags.
166
-
167
- ## How it survives compaction
123
+ No dependencies. Node 20+, any OS (Windows included). The default is a fast
124
+ structural check: every task that claims to be finished must name a proof and
125
+ carry pasted evidence, and no spelling of "done" can slip past it.
168
126
 
169
- Four layers, cheapest first:
127
+ `--verify` goes further and **runs** each proof again. Because it executes the
128
+ commands written in the plan, use it only on plans you trust — run the default
129
+ structural check in CI that builds untrusted pull requests, and keep `--verify`
130
+ for your own branch or a trusted pipeline.
170
131
 
171
- | when | what the agent gets | size |
172
- |---|---|---|
173
- | always | one generated block in CLAUDE.md naming the active plans | a few lines |
174
- | session start, and after every compaction | a brief rendered from the plan's files: status, the RESUME line, the last log entries, the rules | ≤ 6,000 chars |
175
- | at a tool call | the rules that match this file or command, once per session | ≤ 1,200 chars each |
176
- | on demand | everything else, queried with the CLI | not loaded |
132
+ ## On a team
177
133
 
178
- Before every compaction a hook writes a journal of what the session did (files written, commands run) to `.planrails/journal/`, and after the compaction another hook hands the path back.
134
+ - **It will not break teammates' builds.** The checker has no dependencies and
135
+ exits 0 when there are no plans, so a teammate who never uses planrails is
136
+ unaffected.
137
+ - **The reload line is a plain file include.** `@.project-management/plans/…` in
138
+ `CLAUDE.md` just tells Claude Code to load that file; it commits like any doc.
139
+ - **Decide whether to commit `.project-management/`.** Committing it shares plans
140
+ and lets CI run the checker. If your repo gitignores it, the checker still runs
141
+ locally and the plan still reloads for whoever has the files.
179
142
 
180
- ## Done needs evidence, and judgment outranks the gate
143
+ ## History
181
144
 
182
- Every task has conditions of done. Three are checked by the tool: the gate passed in this very command, a log entry names the task, the listed files exist. Four are answered in words: the docs shipped, every number has a locator, what was learned is recorded, and you read the result whole and judged it right yourself. A plan can add its own; a task can add its own.
183
-
184
- A gate is a script. It cannot read, cannot see, and answers only the question it was written for. So the agent sees the same three lines at every closing moment:
185
-
186
- - gate red, work right: never make the gate pass. Write what it computed and what is true. Fix the gate and re-verify it, or block the task for the owner.
187
- - gate green, work wrong: refuse to close. Say what the gate cannot see.
188
- - anything crashed: never edit a plan file by hand. Log it, block, stop.
189
-
190
- The agent gets no "force pass with a reason" of its own. The override is `task block --needs owner`; the owner's word closes the task. Two rails back this: a gate edited after it was verified cannot close a task until it is verified again, and a gate that was never verified cannot close a task at all.
191
-
192
- ## Subagents and fresh sessions
193
-
194
- `agent-brief <id> <T>` renders what a subagent needs and nothing else: what to do, which files, what done means, where to write its report, what never to do. A subagent never closes a task; the main session verifies the report and closes it. A SubagentStart hook puts the same note in front of every subagent.
195
-
196
- `run <id>` is the answer to "clear the context after every task". Claude Code cannot compact or clear on command, so planrails starts a new session per task instead. Each session's only memory of the work is the plan's files.
197
-
198
- ## Updating, disabling, removing
199
-
200
- - `npm install planrails@latest && npx planrails update` refreshes the guide, the skill and the hook entries. Plans are never touched.
201
- - To turn one hook off, delete its entry from `.claude/settings.json`. `npx planrails doctor` will say it is missing; that is fine.
202
- - `npx planrails uninstall` removes the hooks and the skill. `npm uninstall planrails` removes the package. The plans, the guide and the CLAUDE.md section stay, so nothing you wrote is lost.
203
-
204
- ## Reporting a problem, an edge case, or a wish
205
-
206
- From any project that uses planrails:
207
-
208
- ```bash
209
- npx planrails issue bug # something broke or refused wrongly
210
- npx planrails issue edge # a gate, rule or condition was wrong in a case we did not think of
211
- npx planrails issue wish # something you wish it did
212
- ```
213
-
214
- It opens a GitHub issue with the environment filled in: versions, platform, what `doctor` and `validate` say. No file contents and no plan text leave your machine. Add `--print` to get the URL instead, or `--gh` to file it with the GitHub CLI.
215
-
216
- ## Development
217
-
218
- ```bash
219
- git clone https://github.com/vivmagarwal/planrails && cd planrails && npm install
220
- npm test # the CLI, init, the hooks, the issue command — all against throwaway directories
221
- npm run selftest # every refusal in the validator can fire; the hooks on payloads captured from a real run
222
- npm run acceptance # needs the claude CLI: a real session must quote the injected brief
223
- ```
145
+ Version 0.1.x was a heavier npm package with a full CLI and installed hooks. A
146
+ review found ten data-loss and silent-failure paths in that surface, and the
147
+ "works with any project" promise broke on pnpm, bun, non-Node projects,
148
+ monorepos and Windows. 0.2.0 keeps the idea and drops the weight: the same three
149
+ rails, as a prompt plus one checker, with a two-command CLI that only copies
150
+ files. See [`CHANGELOG.md`](CHANGELOG.md).
224
151
 
225
- See CONTRIBUTING.md. MIT licensed.
152
+ MIT.
package/bin/planrails.mjs CHANGED
@@ -1,7 +1,92 @@
1
1
  #!/usr/bin/env node
2
- // The `planrails` command. Everything is dispatched by src/plan/plan.mjs.
2
+ /**
3
+ * planrails — a planner prompt and one checker. Two commands, both safe:
4
+ *
5
+ * npx planrails init [--dir DIR] [--force]
6
+ * Copies PLANNER.md and the checker into <project>/.project-management/ and
7
+ * makes the plans/ folder. It writes nothing else — no package.json, no npm
8
+ * install, no hooks, no edits to your CLAUDE.md. Idempotent: it skips files
9
+ * that already exist unless you pass --force.
10
+ *
11
+ * npx planrails check [--dir DIR] [--verify]
12
+ * Runs the checker over the project's plans. --verify re-runs each done
13
+ * task's proof. Same as running the copied .project-management/check-plans.mjs.
14
+ *
15
+ * planrails --version | --help
16
+ */
17
+ import { readFileSync, copyFileSync, mkdirSync, existsSync, writeFileSync } from "node:fs";
18
+ import { join, dirname, resolve } from "node:path";
3
19
  import { fileURLToPath } from "node:url";
4
- import { resolve, dirname } from "node:path";
5
- const plan = resolve(dirname(fileURLToPath(import.meta.url)), "..", "src", "plan", "plan.mjs");
6
- process.argv[1] = plan;
7
- await import(plan);
20
+ import { checkPlans } from "../tools/check-plans.mjs";
21
+
22
+ const ROOT = join(dirname(fileURLToPath(import.meta.url)), "..");
23
+ const version = () => JSON.parse(readFileSync(join(ROOT, "package.json"), "utf8")).version;
24
+
25
+ const HELP = `planrails ${version()} — a planner prompt and one checker.
26
+
27
+ npx planrails init [--dir DIR] [--force] set a project up (copies 2 files, writes nothing else)
28
+ npx planrails check [--dir DIR] [--verify] check the project's plans
29
+ planrails --version | --help
30
+
31
+ After init, tell your agent: Follow .project-management/PLANNER.md and plan <the feature> with me.
32
+ Add to the command you run before every commit: node .project-management/check-plans.mjs
33
+ Full guide: https://github.com/vivmagarwal/planrails#readme`;
34
+
35
+ function flag(args, name) {
36
+ const eq = args.find((a) => a.startsWith(`${name}=`));
37
+ if (eq) return eq.slice(name.length + 1);
38
+ const i = args.indexOf(name);
39
+ return i !== -1 && args[i + 1] && !args[i + 1].startsWith("--") ? args[i + 1] : null;
40
+ }
41
+
42
+ function init(args) {
43
+ const target = resolve(flag(args, "--dir") || ".");
44
+ const force = args.includes("--force");
45
+ const pm = join(target, ".project-management");
46
+ const plans = join(pm, "plans");
47
+ mkdirSync(plans, { recursive: true });
48
+
49
+ const copy = (from, to, label) => {
50
+ if (existsSync(to) && !force) { console.log(` · ${label} already present (use --force to overwrite)`); return; }
51
+ copyFileSync(from, to);
52
+ console.log(` + ${label}`);
53
+ };
54
+ console.log(`planrails ${version()} → ${target}`);
55
+ copy(join(ROOT, "PLANNER.md"), join(pm, "PLANNER.md"), ".project-management/PLANNER.md");
56
+ copy(join(ROOT, "tools", "check-plans.mjs"), join(pm, "check-plans.mjs"), ".project-management/check-plans.mjs");
57
+ const keep = join(plans, ".gitkeep");
58
+ if (!existsSync(keep)) { writeFileSync(keep, ""); console.log(" + .project-management/plans/"); }
59
+ else console.log(" · .project-management/plans/ already present");
60
+
61
+ console.log(`
62
+ Next:
63
+ 1. Tell your agent: Follow .project-management/PLANNER.md and plan <the feature> with me.
64
+ (Or, in Claude Code, use /plan if you installed the skill.)
65
+ 2. Add to the command you run before every commit:
66
+ node .project-management/check-plans.mjs
67
+ 3. When the agent writes a plan, it adds one line to your CLAUDE.md so the plan
68
+ reloads after every compaction: @.project-management/plans/<id>/PLAN.md`);
69
+ return 0;
70
+ }
71
+
72
+ function check(args) {
73
+ const root = resolve(flag(args, "--dir") || ".");
74
+ if (!existsSync(root)) { console.error(`planrails check: --dir path does not exist: ${root}`); return 2; }
75
+ const verify = args.includes("--verify");
76
+ const { plans, problems } = checkPlans({ root, verify });
77
+ if (!plans.length && !problems.length) { console.log("check-plans: no plans under .project-management/plans/ — nothing to check"); return 0; }
78
+ if (problems.length) {
79
+ console.error(`check-plans: ${problems.length} problem(s):\n${problems.map((p) => ` - ${p}`).join("\n")}`);
80
+ return 1;
81
+ }
82
+ console.log(`check-plans: ${plans.length} plan(s) ok — every completion claim has a proof and pasted evidence${verify ? " (proofs re-run)" : ""}`);
83
+ return 0;
84
+ }
85
+
86
+ const [cmd, ...args] = process.argv.slice(2);
87
+ if (cmd === "--version" || cmd === "-v") { console.log(version()); process.exit(0); }
88
+ if (!cmd || cmd === "--help" || cmd === "-h") { console.log(HELP); process.exit(cmd ? 0 : 1); }
89
+ if (cmd === "init") process.exit(init(args));
90
+ if (cmd === "check") process.exit(check(args));
91
+ console.error(`planrails: unknown command "${cmd}"\n\n${HELP}`);
92
+ process.exit(2);
package/package.json CHANGED
@@ -1,53 +1,19 @@
1
1
  {
2
2
  "name": "planrails",
3
- "version": "0.1.2",
4
- "description": "Plans that survive compaction, for Claude Code: gates that prove a task done, conditions answered before it closes, briefs and rules injected by hooks, one fresh session per task.",
3
+ "version": "0.2.1",
4
+ "description": "Plans that survive a lost session, and \"done\" that means done. A planner prompt and a tiny checker.",
5
5
  "type": "module",
6
- "bin": {
7
- "planrails": "bin/planrails.mjs"
8
- },
9
- "files": [
10
- "bin",
11
- "src",
12
- "docs/PLANNING_GUIDE.md",
13
- "README.md",
14
- "LICENSE",
15
- "CHANGELOG.md"
16
- ],
17
- "engines": {
18
- "node": ">=20.10"
19
- },
20
- "scripts": {
21
- "test": "node scripts/run-tests.mjs",
22
- "selftest": "node bin/planrails.mjs selftest && node src/hooks/selftest.mjs",
23
- "acceptance": "node scripts/acceptance.mjs",
24
- "trial": "node scripts/trial/run.mjs",
25
- "prepack": "npm test",
26
- "prepublishOnly": "node scripts/release-check.mjs before",
27
- "postpublish": "node scripts/release-check.mjs after"
28
- },
29
- "dependencies": {
30
- "zod": "^4.4.3"
31
- },
32
- "repository": {
33
- "type": "git",
34
- "url": "git+https://github.com/vivmagarwal/planrails.git"
35
- },
36
- "bugs": {
37
- "url": "https://github.com/vivmagarwal/planrails/issues"
38
- },
6
+ "license": "MIT",
7
+ "bin": { "planrails": "bin/planrails.mjs" },
8
+ "files": ["bin/planrails.mjs", "PLANNER.md", "tools/check-plans.mjs", "README.md", "LICENSE", "CHANGELOG.md"],
9
+ "repository": { "type": "git", "url": "git+https://github.com/vivmagarwal/planrails.git" },
39
10
  "homepage": "https://github.com/vivmagarwal/planrails#readme",
40
- "keywords": [
41
- "claude-code",
42
- "claude",
43
- "agent",
44
- "planning",
45
- "hooks",
46
- "compaction",
47
- "gates",
48
- "project-management",
49
- "ai-agents"
50
- ],
51
- "author": "vivmagarwal",
52
- "license": "MIT"
11
+ "bugs": { "url": "https://github.com/vivmagarwal/planrails/issues" },
12
+ "keywords": ["planning", "claude-code", "agents", "llm", "compaction", "tracker"],
13
+ "engines": { "node": ">=20" },
14
+ "scripts": {
15
+ "test": "node --test tools/check-plans.test.mjs bin/planrails.test.mjs",
16
+ "check": "node bin/planrails.mjs check --dir examples/weekly-digest && npm test",
17
+ "prepublishOnly": "npm test"
18
+ }
53
19
  }