planrails 0.1.2 → 0.2.0
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.
- package/CHANGELOG.md +54 -16
- package/PLANNER.md +255 -0
- package/README.md +103 -176
- package/bin/planrails.mjs +90 -5
- package/package.json +14 -48
- package/tools/check-plans.mjs +199 -0
- package/docs/PLANNING_GUIDE.md +0 -632
- package/src/hooks/_lib.mjs +0 -32
- package/src/hooks/guard-never-delete.sh +0 -29
- package/src/hooks/install.mjs +0 -179
- package/src/hooks/plan-pre-tool.mjs +0 -71
- package/src/hooks/plan-session-start.mjs +0 -46
- package/src/hooks/plan-stop.mjs +0 -60
- package/src/hooks/plan-subagent-start.mjs +0 -27
- package/src/hooks/postcompact-journal.mjs +0 -35
- package/src/hooks/precompact-journal.mjs +0 -128
- package/src/hooks/selftest.mjs +0 -128
- package/src/init.mjs +0 -161
- package/src/issue.mjs +0 -55
- package/src/plan/fixtures/README.md +0 -7
- package/src/plan/fixtures/broken-cli.mjs +0 -27
- package/src/plan/fixtures/broken-hooks-root/.claude/settings.json +0 -83
- package/src/plan/fixtures/broken-hooks-root/.project-management/plans/.gitkeep +0 -0
- package/src/plan/fixtures/broken-hooks-root/CLAUDE.md +0 -9
- package/src/plan/fixtures/broken-root/.project-management/plans/broken/PLAN.md +0 -4
- package/src/plan/fixtures/broken-root/.project-management/plans/broken/gates.json +0 -1
- package/src/plan/fixtures/broken-root/.project-management/plans/broken/rules.json +0 -1
- package/src/plan/fixtures/broken-root/.project-management/plans/broken/state.json +0 -67
- package/src/plan/fixtures/broken-root/CLAUDE.md +0 -3
- package/src/plan/fixtures/broken-trial.mjs +0 -25
- package/src/plan/lib/brief.mjs +0 -116
- package/src/plan/lib/claude-md.mjs +0 -68
- package/src/plan/lib/glob.mjs +0 -81
- package/src/plan/lib/judgment.mjs +0 -19
- package/src/plan/lib/paths.mjs +0 -65
- package/src/plan/lib/schema.mjs +0 -199
- package/src/plan/lib/store.mjs +0 -338
- package/src/plan/lib/time.mjs +0 -21
- package/src/plan/plan.mjs +0 -843
- package/src/plan/run.mjs +0 -88
- 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
|
|
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
|
-
|
|
6
|
-
|
|
7
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
###
|
|
23
|
+
### One command, any project
|
|
33
24
|
|
|
34
|
-
|
|
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
|
-
|
|
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
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
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.
|
|
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
|
-
|
|
47
|
+
> Follow `.project-management/PLANNER.md` and tell me when you are ready to
|
|
48
|
+
> plan the next feature with me.
|
|
71
49
|
|
|
72
|
-
|
|
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
|
-
|
|
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
|
-
|
|
78
|
-
|
|
79
|
-
npx planrails init
|
|
80
|
-
```
|
|
56
|
+
```bash
|
|
57
|
+
node .project-management/check-plans.mjs
|
|
58
|
+
```
|
|
81
59
|
|
|
82
|
-
|
|
60
|
+
Now the build fails if any task is marked done without pasted evidence.
|
|
83
61
|
|
|
84
|
-
|
|
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
|
-
|
|
64
|
+
### New project vs existing project
|
|
94
65
|
|
|
95
|
-
|
|
66
|
+
The command is the same; the difference is what your agent sees.
|
|
96
67
|
|
|
97
|
-
|
|
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
|
-
|
|
76
|
+
### Without npm, or a non-Node project
|
|
100
77
|
|
|
101
|
-
|
|
102
|
-
|
|
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
|
-
|
|
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
|
-
|
|
88
|
+
### As a `/plan` command in Claude Code
|
|
108
89
|
|
|
109
|
-
|
|
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
|
-
|
|
93
|
+
## How a plan works
|
|
118
94
|
|
|
119
|
-
|
|
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
|
-
##
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
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
|
|
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
|
-
|
|
134
|
-
#
|
|
135
|
-
npx planrails
|
|
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
|
-
|
|
141
|
-
|
|
142
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
##
|
|
143
|
+
## History
|
|
181
144
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
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
|
-
|
|
152
|
+
MIT.
|
package/bin/planrails.mjs
CHANGED
|
@@ -1,7 +1,92 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
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 {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
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.
|
|
4
|
-
"description": "Plans that survive
|
|
3
|
+
"version": "0.2.0",
|
|
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
|
-
"
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
"
|
|
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
|
-
"
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
"
|
|
45
|
-
"
|
|
46
|
-
"
|
|
47
|
-
|
|
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
|
}
|