@redue/cli 0.1.0-alpha.22 → 0.1.0-alpha.24

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 CHANGED
@@ -1,5 +1,73 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.1.0-alpha.24 — 2026-10-28
4
+
5
+ Checks keep working after a normal `npm install`, and agents that chain an
6
+ edit and a check get the same skip-or-record as a check run on its own.
7
+
8
+ - **Hard-linked installed files.** Without a pnpm store, any installed file
9
+ with a second hard link made a recorded run UNVERIFIED. esbuild's install
10
+ makes one (npm, not Yarn or Windows), so after an ordinary `npm install`
11
+ general-recipe and test checks in projects with esbuild stayed
12
+ UNVERIFIED. Such a file is now accepted when every one of its links is an
13
+ installed file the check observes; its link count is checked again before
14
+ every answer, so a link added later is noticed.
15
+ - **Checks inside a chain.** In a simple chain (`printf ... >> README.md &&
16
+ npm run lint`), the Claude Code hook rewrites each check to the new
17
+ `redue run CHECK --if-due`, which skips the check when its last pass still
18
+ applies at that moment and otherwise runs it through REDUE with the same
19
+ exit code. The hook changes the command without approving it, so your
20
+ permission rules still apply. Chains with subshells, substitutions,
21
+ here-documents, background jobs, shell keywords or builtins before a check
22
+ run untouched.
23
+ - The CLAUDE.md section asks agents to run each check as a command of its
24
+ own. Setups from earlier alphas are recognized and updated.
25
+ - The README's coverage numbers now come from a census that installs with
26
+ dependencies' install scripts on, as users do: of 120 public TypeScript
27
+ repositories, 86 installed and 30 have a qualifying check (typecheck 21,
28
+ lint 18, tests 2).
29
+ - Known limit, unchanged: a Vitest run writes under `node_modules/.vite`,
30
+ which makes a direct TypeScript typecheck STALE until it runs again.
31
+
32
+ - Contributors: census `--install-scripts`; the stranger test can install
33
+ from npm (`--npm`) and run a follow-up session that uses the hooks.
34
+
35
+ ## 0.1.0-alpha.23 — 2026-10-26
36
+
37
+ One command to set up REDUE with Claude Code, and one to undo it.
38
+
39
+ - **`redue setup claude`** runs `init` (when there is no
40
+ `redue.config.json`), connects Claude Code, starts the observer, waits for
41
+ it to index the project, and records each check that can qualify once. It
42
+ asks once (`--yes` for no prompts), prints every file it writes, and a
43
+ second run changes nothing. It stops before writing when dependencies are
44
+ not installed or nothing can be set up.
45
+ - **`redue remove`** undoes exactly what setup created: the integration (or
46
+ only this machine's hooks), the observer state, the local ignore line, and
47
+ the config if it is unchanged since setup wrote it. Anything that existed
48
+ before setup stays. Setup notes what it created in the clone's Git
49
+ directory, never in a committed file.
50
+ - **Hooks find REDUE.** The Claude Code hooks name plain `redue` only when
51
+ the `redue` that your shell and a login shell find is this install;
52
+ otherwise they name this install's Node and entry by absolute path, so a
53
+ Claude Code started from an IDE or with a shorter PATH still runs it.
54
+ Checks the hook runs for the agent follow the same rule.
55
+ - `init` keeps a typecheck that runs TypeScript 7's `tsc` (or `tsgo` from
56
+ `@typescript/native-preview`) recording-only and says why. The pnpm and
57
+ Yarn TypeScript recipes refuse TypeScript 7 with a clear reason instead
58
+ of an error from inside the compiler API.
59
+ - The README is rewritten around a 5-minute quickstart, what REDUE covers
60
+ today (census qualifiers) and what it won't do.
61
+ - Known limits, found by the stranger test: a Vitest run writes under
62
+ `node_modules/.vite`, which makes a direct TypeScript typecheck STALE until
63
+ it runs again; an installed file with a second hard link (esbuild's install
64
+ makes one) keeps the test check UNVERIFIED.
65
+
66
+ - Contributors: the census can run every recipe (`--all-recipes`) and
67
+ counts direct TypeScript checks; its unrecorded-program check no longer
68
+ flags Yarn's temporary `node` wrapper; `eval/stranger.mjs` runs fresh
69
+ Claude Code sessions that set REDUE up from the README alone.
70
+
3
71
  ## 0.1.0-alpha.22 — 2026-10-25
4
72
 
5
73
  A correctness fix for recorded runs. Earlier alphas could report CURRENT for
package/README.md CHANGED
@@ -1,229 +1,145 @@
1
1
  # REDUE
2
2
 
3
- Maintainer notice: alpha.4 withdraws automatic npm-launcher applicability.
4
- Npm commands still execute and record historical outcomes, but remain UNVERIFIED.
5
- [Why this changed and how to upgrade without deleting receipts](docs/user/npm-launcher-notice.md).
6
- Alpha.2 users should also review the [receipt correction](docs/user/receipt-selection-notice.md).
3
+ **Know which checks still hold. Redo what's due.**
7
4
 
8
- **Know what still holds. Redo what's due.**
5
+ You or your coding agent ran the typecheck, the linter or the tests. Then the
6
+ code changed, or a new agent session started with no memory of it. REDUE
7
+ records each check's result with the exact inputs it ran on, and tells you and
8
+ your agent whether that result still applies to the code you have now, so a
9
+ check that still holds is not run again and one that is stale is.
9
10
 
10
- You ran the checks. Then the code changed—or a new coding agent started with no
11
- conversation history. Which results can you still use?
11
+ ## Quickstart (about 5 minutes)
12
12
 
13
- REDUE keeps persistent verification state for your codebase so developers and
14
- coding agents can see which previous checks still apply to the code they have now.
15
- It records evidence, not proofs of correctness.
16
-
17
- For a qualified direct-compiler or supported Yarn/pnpm check (ordinary npm scripts are recording-only):
18
-
19
- ```text
20
- $ redue status --sync
21
- ✓ typecheck CURRENT / PASS
22
- ? test UNVERIFIED / PASS
23
- Recording-only: test runtime, transforms and cache inputs need review
24
-
25
- # Later, after a relevant edit:
26
- $ redue explain typecheck
27
- typecheck: STALE / PASS
28
- src/main.ts changed
29
- Relevant inputs changed:
30
- src/main.ts
31
- Due again when verification is needed: redue run "typecheck"
32
- ```
33
-
34
- Agent A verifies and exits. Agent B starts fresh. The receipt is still there;
35
- REDUE checks whether it still applies. Unrelated documentation edits can preserve
36
- it; relevant source edits make it stale. [Run the reproducible demo](docs/contributor/demo.md).
37
-
38
- ## Install the alpha
39
-
40
- The opt-in pilot uses **0.1.0-alpha.18**. Follow the
41
- [fixed-version pilot guide](docs/user/pilot.md) for supported projects, setup,
42
- updates and voluntary feedback. No external usage results are claimed yet.
43
-
44
-
45
- REDUE is published on npm as `@redue/cli`. The installed command is `redue`.
13
+ You need macOS or Linux, Node 22 or 24, Git, Claude Code, and a JavaScript or
14
+ TypeScript project with its dependencies installed (`npm install`,
15
+ `pnpm install` or `yarn`). On Linux, also Python 3.
46
16
 
47
17
  ```sh
48
18
  npm install --global @redue/cli@alpha
19
+ cd your-project
20
+ redue setup claude
49
21
  ```
50
22
 
51
- To test a source candidate, run `npm ci` and `npm pack --json`, then install the
52
- reported tarball filename with `npm install --global ./<filename>`.
53
-
54
- Use a Node installation you own; REDUE does not need administrator privileges.
55
- If a global prefix is not writable, use a user-owned `--prefix` or run the
56
- installed CLI with Node. [Installation and removal](docs/user/getting-started.md)
57
- includes PowerShell and isolated-prefix instructions.
58
-
59
- Inside an existing, trusted Git project with dependencies already installed:
60
-
61
- ```sh
62
- cd my-project
63
- redue init
64
- redue start
65
- redue run typecheck # use a check name printed by init
66
- redue status
67
- ```
23
+ `setup` shows what it will do and asks once (`--yes` skips the question). It
24
+ stops before writing anything if the dependencies are not installed. It:
68
25
 
69
- `init` discovers existing scripts, explains which can qualify and which are
70
- recording-only, and writes a small `redue.config.json`. It never runs your checks
71
- or changes your package manager, source, scripts, or dependencies. Commit the
72
- reviewed project config; local evidence stays outside the checkout.
26
+ - finds your typecheck, lint and test scripts and writes `redue.config.json`
27
+ (it never changes your scripts, source or dependencies). Checks REDUE can
28
+ only record, not reuse, are listed as recording-only;
29
+ - connects Claude Code: a short section in `CLAUDE.md`, a skill, and two hooks
30
+ in `.claude/settings.local.json`;
31
+ - starts REDUE's file observer, waits while it indexes the project (a minute
32
+ or more in a large one), then runs each check that can qualify once, so
33
+ there is a first result to reuse;
34
+ - prints every file it writes. Running it again changes nothing.
73
35
 
74
- Start with ordinary `status`. When it cannot establish caller context, use
75
- `redue status --sync` if that is worthwhile—or just rerun an inexpensive check.
76
- **Synchronization can cost more than rerunning a cheap check, particularly on
77
- Windows.** REDUE does not choose or execute that tradeoff for you.
36
+ Then start a new Claude Code session in the project and work as usual. When
37
+ the agent is about to rerun a check whose last pass still applies, the hook
38
+ skips it and tells the agent why. After an edit, the hook runs the check
39
+ through REDUE so the new result is recorded.
78
40
 
79
- `init` tries the strict recipes first: the [direct compiler recipe](docs/user/direct-typescript.md)
80
- for npm typechecks, the pnpm and Yarn TypeScript recipes, and the
81
- [direct test recipe](docs/user/tests-direct.md). A root typecheck or lint
82
- script none of them takes can use the [general recipe](docs/user/node-script.md),
83
- which runs the script through the package manager and records every Node
84
- process; a script that starts another program (git, esbuild, turbo) stays
85
- UNVERIFIED and the reason names it. Test scripts use the direct test recipe
86
- (Vitest, Jest); other test scripts stay recording-only.
41
+ To look yourself:
87
42
 
88
- ## Connect your agent (experimental)
43
+ ```text
44
+ $ redue status --sync
45
+ ✓ typecheck CURRENT / PASS
46
+ ✓ lint CURRENT / PASS
89
47
 
90
- ```sh
91
- redue agent setup codex --dry-run
92
- redue agent setup codex --apply # or claude
93
- redue agent doctor
48
+ # after editing src/main.ts
49
+ $ redue explain typecheck
50
+ typecheck: STALE / PASS
51
+ src/main.ts changed
52
+ Due again when verification is needed: redue run "typecheck"
94
53
  ```
95
54
 
96
- Start a fresh agent session and give it an ordinary task. Project-local guidance
97
- teaches it to inspect evidence before deciding whether verification needs repeating.
98
- Setup is explicit, reversible, and does not change tool permissions or run checks.
99
- [Activation, removal, and generic-agent guidance](docs/user/agents.md).
100
-
101
- The experimental claim is **opt-in project-local agent activation and direct TypeScript
102
- evidence, with fresh-session reuse demonstrated in the tested Codex/macOS
103
- configuration and measured for Claude Code below**. Cross-host reuse is not
104
- tested. This is experimental guidance, not guaranteed agent compliance, general
105
- sandbox compatibility, or a promise of time savings.
106
-
107
- ### Measured with Claude Code
108
-
109
- Four public TypeScript projects, four tasks each, three sessions per task with
110
- and without REDUE: 96 headless Claude Code sessions (Sonnet 5.5, CI Linux
111
- runner, alpha.12 code, October 2026). Plain `tsc --noEmit` takes 10–19 s on
112
- these projects. Medians per task, summed over the four projects:
113
-
114
- | Task | Session time, without → with REDUE | Input tokens | Typechecks skipped |
115
- |---|---|---|---|
116
- | Docs edit, then "make sure it still typechecks" | 97 → 45 s | 169k → 162k | 12 of 12 |
117
- | "Does it still typecheck?" after a pull | 74 → 22 s | 117k → 89k | 12 of 12 |
118
- | Source change, typecheck, then a docs edit | 141 → 112 s | 210k → 200k | 9 of 12 |
119
- | Source change with tests | 152 → 151 s | 269k → 234k | 0 of 12 |
120
- | **All tasks** | **464 → 330 s** | **765k → 685k** | 33 |
121
-
122
- - **Correctness:** every typecheck REDUE skipped was checked afterwards with
123
- plain tsc on the exact tree it was skipped on. All 33 agreed, and all 96
124
- final trees passed plain tsc and the task checks in both arms.
125
- - **Cost:** API cost was the same ($0.65 without, $0.66 with). One-time setup
126
- per checkout was 3–5 s, plus one recorded typecheck; the CLAUDE.md section
127
- REDUE adds is about 60 tokens.
128
- - **How:** each session gets a fresh clone at a pinned commit; the REDUE arm
129
- first runs `redue init`, `agent setup claude`, `start` and one
130
- `redue run typecheck`, standing for an earlier session's passing check.
131
- The harness, tasks and raw results are in [`eval/`](eval/README.md).
132
- - **Limits:** four projects and three runs per cell show direction, not a
133
- general rate. Savings come only when an earlier pass still applies: tasks
134
- that change source code save nothing, and on one project the docs edit came
135
- after source edits that made the result STALE, so nothing was skipped. The
136
- projects were chosen because they qualify for the direct TypeScript recipe;
137
- of 38 screened projects, 19 qualified at alpha.12 and 22 at alpha.13. Times are agent session
138
- time only, on one runner type.
139
-
140
- ### Unit tests, measured with Claude Code
141
-
142
- Three public projects (cheerio with Vitest 4, SwitchHosts with Vitest 5, twilio
143
- with Jest 29), four tasks each, three sessions per task with and without REDUE:
144
- 72 headless Claude Code sessions (Sonnet 5.5, CI Linux runner, alpha.14 code,
145
- October 2026), using the opt-in [direct test recipe](docs/user/tests-direct.md).
146
- Medians per task, summed over the three projects:
147
-
148
- | Task | Session time, without → with REDUE | Input tokens | Test runs skipped |
149
- |---|---|---|---|
150
- | Docs edit, then "make sure the unit tests still pass" | 95 → 41 s | 131k → 121k | 9 of 9 |
151
- | "Do the unit tests still pass?" after a pull | 80 → 22 s | 62k → 63k | 9 of 9 |
152
- | Source change, tests, then a docs edit | 171 → 111 s | 224k → 213k | 3 of 9 |
153
- | Source change with tests | 169 → 120 s | 172k → 164k | 0 of 9 |
154
- | **All tasks** | **515 → 295 s** | **589k → 559k** | 21 |
155
-
156
- - **Correctness:** every skipped test run was rerun plainly three times on the
157
- exact tree it was skipped on; all 21 passed every time. All 72 final trees
158
- passed the tests and the task checks in both arms.
159
- - **Limits:** one run of three projects shows direction, not a rate. The
160
- source-change task skipped nothing; its time difference is session variance.
161
- In two mixed-task sessions the agent left out its final test run on its own
162
- after a README-only edit, without asking REDUE, and said so; the eval report
163
- lists such sessions. Since alpha.15 the agent instructions ask agents to run
164
- the check and leave skipping to REDUE; in a 12-session rerun of that task,
165
- no agent left it out. Cost was the same ($0.54 without, $0.56 with).
55
+ `--sync` checks the files and your environment before it answers; this is
56
+ what the agent asks. Plain `redue status` is a fast cached answer and can say
57
+ UNVERIFIED ("cannot confirm your execution context") where `--sync` would say
58
+ CURRENT.
59
+
60
+ To undo everything setup did: `redue remove`.
61
+
62
+ ## What it covers today
63
+
64
+ In a census of 120 active public TypeScript repositories (October 2026, REDUE
65
+ at commit 0fcd19b, after 0.1.0-alpha.23), each installed as its users would,
66
+ with dependencies' install scripts on, 86 installed cleanly. A check counts
67
+ when two recorded runs on the same code both came out CURRENT on the same
68
+ inputs. 30 repositories had at least one such check:
69
+
70
+ | Check | Repositories | How |
71
+ |---|---|---|
72
+ | Typecheck | 21 | 15 by running the project's script with every Node process recorded ([general recipe](docs/user/node-script.md)); 6 by running the local `tsc` directly ([direct recipe](docs/user/direct-typescript.md)) |
73
+ | Lint | 18 | general recipe |
74
+ | Tests | 2 | Vitest run directly ([direct test recipe](docs/user/tests-direct.md)) |
75
+
76
+ Everything else stays UNVERIFIED, with a reason you can read in
77
+ `redue explain`. The usual ones: a script starts a program REDUE cannot see
78
+ into (esbuild, turbo, nx, oxlint's tsgolint, TypeScript 7's native compiler),
79
+ the project uses Bun, or a test suite writes into its own inputs. One you may
80
+ meet right after setup: a Vitest run writes its results under
81
+ `node_modules/.vite`, which the direct TypeScript recipe counts as an
82
+ installed input, so the typecheck turns STALE until it runs again.
83
+ [Supported projects](docs/user/supported-projects.md) has the details.
84
+
85
+ ## Measured with Claude Code
86
+
87
+ Headless Claude Code sessions (Sonnet 5.5, CI Linux runner) on the same tasks
88
+ with and without REDUE, three sessions per task. Session time, medians per
89
+ task summed over the projects:
90
+
91
+ - **Typecheck** (twilio-node, semiotic, Ultra-Card, SwitchHosts; four tasks;
92
+ 96 sessions; 0.1.0-alpha.12 code): 464 s without, 330 s with. REDUE skipped
93
+ 33 typechecks. Each was checked afterwards with plain `tsc` on the exact tree
94
+ it was skipped on, and all 33 agreed.
95
+ - **Unit tests** (cheerio with Vitest 4, SwitchHosts with Vitest 5, twilio-node
96
+ with Jest 29; four tasks; 72 sessions; 0.1.0-alpha.14 code): 515 s without,
97
+ 295 s with. REDUE skipped 21 test runs. Each was rerun plainly three times on
98
+ the exact tree, and all passed every time.
99
+
100
+ API cost was the same in both arms. The savings come only when an earlier
101
+ pass still applies: a task that changes source code saves nothing. A handful
102
+ of projects and three runs per task show direction, not a general rate. The
103
+ harness, tasks and raw results are in [`eval/`](eval/README.md).
104
+
105
+ ## What REDUE won't do
106
+
107
+ - **Prove your code is correct.** CURRENT means the inputs of the recorded
108
+ run are unchanged, not that the code works. A failure stays a failure.
109
+ - **Run checks for you.** Nothing runs in the background; checks run when you
110
+ or the agent run them.
111
+ - **Trust what it cannot see.** A program it cannot account for, a file it
112
+ does not observe, or a gap in observation makes the result UNVERIFIED and
113
+ says why. It never turns that into CURRENT by guessing.
114
+ - **Support yet:** TypeScript 7's native compiler, Bun, per-package checks in
115
+ large monorepos, results shared across machines, and the general recipe on
116
+ Windows (Windows has the strict recipes only).
166
117
 
167
118
  ## What the states mean
168
119
 
169
- | State | Meaning | Normal next step |
120
+ | State | Meaning | Next step |
170
121
  | --- | --- | --- |
171
- | CURRENT | The recorded result applies to the declared inputs. | Reuse a passing result; do not rerun merely for freshness. |
172
- | STALE | Relevant inputs changed since the recorded result. | Rerun when verification is needed. |
173
- | UNVERIFIED | Applicability is unknown: no receipt, incomplete coverage, pending observation, or missing context. | Read the reason; synchronize if worthwhile, or run the check. |
174
- | FAILED | The invocation failed. Its applicability is tracked separately. | Keep the failure visible; it is not green evidence. |
175
-
176
- A historical PASS remains PASS when stale or unverified. A historical failure
177
- never becomes a successful check. CURRENT does not mean every product behavior
178
- has been verified.
179
-
180
- ## Supported alpha path
181
-
182
- - macOS, Linux, and Windows on supported local filesystems; Node 22+.
183
- - Linux also requires Python 3 for observation. No WSL or Git Bash needed on Windows.
184
- - npm, pinned Yarn `node-modules`, and pinned pnpm 12 isolated/hoisted layouts.
185
- - Automatic qualification for supported Yarn/pnpm TypeScript invocations; ordinary npm
186
- launcher checks remain recording-only. Explicitly select the
187
- [direct local compiler recipe](docs/user/direct-typescript.md) for the reviewed
188
- npm/TypeScript boundary. Workspace
189
- boundaries follow the files actually consumed, including built declarations.
190
- - Opt-in [direct test recipe](docs/user/tests-direct.md) for Vitest 4/5 and
191
- Jest 29 in single-package npm projects on macOS and Linux.
192
- - Other test/lint/build and arbitrary commands can record results while
193
- their applicability remains unqualified.
194
-
195
- The [supported-project boundary](docs/user/supported-projects.md) is deliberately
196
- narrow. A script named `test` is not automatically a complete test contract.
197
-
198
- ## Alpha limitations
199
-
200
- - REDUE records evidence; it does not prove correctness.
201
- - Direct commands outside `redue run` do not create receipts.
202
- - Some tests, lints, and builds remain UNVERIFIED/PASS because their input contract is incomplete.
203
- - Synchronized applicability has nontrivial cost and can lose to an inexpensive rerun.
204
- - Unsupported/exotic install layouts and filesystems remain conservative; they do not become green through guesses.
205
- - No cloud/team synchronization, IDE extension, or automatic background check execution.
206
- - Alpha configuration and APIs may evolve. Neither cached nor synchronized status is an atomic snapshot against concurrent edits.
207
-
208
- ## Humans, shells, and agents
122
+ | CURRENT | The recorded result applies to the code as it is now. | Reuse a pass; no need to rerun. |
123
+ | STALE | An input changed since the recorded result. | Rerun when you need the answer. |
124
+ | UNVERIFIED | REDUE cannot tell: no result yet, an input it cannot see, or observation not ready. | Read the reason; rerun if it matters. |
125
+ | FAILED | The last run failed: the check ran and reported a failure, or could not run. | Fix and rerun. |
126
+
127
+ ## Without Claude Code, or step by step
209
128
 
210
129
  ```sh
211
- redue explain typecheck # synchronized explanation; may run read-only probes
212
- redue status --json # conservative cached data
213
- redue status --sync --json # caller-aware decision
214
- redue status --short # small cached indicator; never synchronizes
215
- redue stop # retains historical receipts
216
- redue remove-state # removes only this config's owned local state
130
+ redue init # write redue.config.json from your scripts
131
+ redue start # start the file observer
132
+ redue run typecheck # run a check through REDUE and record the result
133
+ redue status --sync # what still holds
217
134
  ```
218
135
 
219
- Agents should use [the documented JSON interface](docs/user/json.md), not parse
220
- terminal prose. [The canonical agent loop](docs/user/agents.md) keeps the
221
- sync-versus-rerun choice explicit. [Shell usage](docs/user/shell.md) does not put
222
- synchronized work in a prompt.
136
+ Codex is supported too (`redue agent setup codex --apply`); see
137
+ [agents](docs/user/agents.md). Agents should read
138
+ [the JSON interface](docs/user/json.md), not the terminal text.
223
139
 
224
- [User docs](docs/README.md) · [Contributing](CONTRIBUTING.md) ·
225
- [Architecture](docs/contributor/architecture.md) · [Acceptance evidence](docs/acceptance/README.md)
140
+ [Getting started](docs/user/getting-started.md) · [User docs](docs/README.md) ·
141
+ [Contributing](CONTRIBUTING.md) · [Architecture](docs/contributor/architecture.md)
226
142
 
227
143
  ## License
228
144
 
229
- REDUE is licensed under [Apache-2.0](LICENSE). Dependencies retain their own licenses.
145
+ REDUE is licensed under [Apache-2.0](LICENSE). Dependencies keep their own licenses.
package/bin/redue.mjs CHANGED
@@ -28,6 +28,9 @@ const help=`REDUE ${version}
28
28
  Usage: redue [--config FILE] [--state-dir DIR] COMMAND [OPTIONS]
29
29
 
30
30
  Commands:
31
+ setup claude [--yes] Set up REDUE and Claude Code in this project in one step:
32
+ init, agent setup claude, start, one recorded run per check
33
+ remove [--yes] Undo setup: integration, observer state, and the config setup created
31
34
  init [--dry-run] [--workspace NAME] [--check KIND] [--recipe typescript-direct|tests-direct|node-script]
32
35
  Discover existing scripts; optionally select one workspace/check
33
36
  agent setup codex|claude [--dry-run|--apply] [--json]
@@ -40,7 +43,8 @@ Commands:
40
43
  status --sync Reconcile inputs and caller context before answering
41
44
  explain [CHECK] [--json] Explain applicability (synchronized; may cost more than a rerun)
42
45
  detail [CHECK] [--json] Alias for explain
43
- run CHECK Execute one configured check and record its outcome
46
+ run CHECK [--if-due] Execute one configured check and record its outcome; with --if-due,
47
+ only when its last pass does not apply to the files as they are now
44
48
  remove-state Stop and remove only this configuration's owned local state
45
49
 
46
50
  No checks run in the background. Direct commands outside redue create no receipts.`;
@@ -58,7 +62,18 @@ if(args.length===3&&args[0]==='agent'&&args[1]==='hook'&&args[2]==='claude'){
58
62
  await runClaudeHook(fileURLToPath(import.meta.url));process.exit(0);
59
63
  }
60
64
  if(args.length===1&&args[0]==='--version'){console.log(version);process.exit(0);}
65
+ // Internal: the entry this command runs, so setup and the hook can tell
66
+ // whether the `redue` on PATH (a link, a package manager's shim, a wrapper)
67
+ // is this install.
68
+ if(args.length===1&&args[0]==='--entry'){console.log(fs.realpathSync(fileURLToPath(import.meta.url)));process.exit(0);}
61
69
  if(args.includes('--help')||args.includes('-h')||!args.length){console.log(help);await exit(0);}
70
+ if(args[0]==='setup'||args[0]==='remove'){
71
+ // Loaded on demand; setup works before a config exists.
72
+ const {setupCommand}=await import('../src/setup.mjs');
73
+ try{await setupCommand(args,{entry:fileURLToPath(import.meta.url)});}
74
+ catch(e){error(e.message);}
75
+ await exit(0);
76
+ }
62
77
  let configFile=null,stateOverride=null;
63
78
  for(let i=0;i<args.length;){
64
79
  if(args[i]==='--config'||args[i]==='--state-dir'){
@@ -82,6 +97,9 @@ if(command==='agent'){
82
97
  if(!['init','start','stop','status','detail','explain','run','remove-state'].includes(command))
83
98
  error(`unknown command ${command||'<none>'}; use --help`);
84
99
  const json=args.includes('--json');
100
+ // run --if-due: run the check only when its last pass does not apply now.
101
+ const ifDue=command==='run'&&args.includes('--if-due');
102
+ if(ifDue)args.splice(args.indexOf('--if-due'),1);
85
103
  const sync=args.includes('--sync');
86
104
  const dryRun=args.includes('--dry-run');
87
105
  const short=args.includes('--short');
@@ -124,7 +142,7 @@ if(command==='init'){
124
142
  checks:discovery.checks.map(({config:check,level,reason})=>({name:check.name,
125
143
  script:check.script||null,command:check.command||null,
126
144
  workspace:check.workspace||null,level,reason})),config:configFile,
127
- written:willWrite,existing:configured};
145
+ written:willWrite,existing:configured,writable:!configured&&!unsupported&&discovery.checks.length>0};
128
146
  if(!json) {
129
147
  console.log(`Node ${summary.node}; ${summary.package_manager||'unknown package manager'}`+
130
148
  `${summary.version?' '+summary.version:''}; ${summary.installation_layout} installation.`);
@@ -374,6 +392,20 @@ const internal=command==='remove-state'?'uninstall':
374
392
  // On macOS and Linux the engine runs in this process, which saves a Node startup
375
393
  // on every run; its own handlers receive SIGINT and SIGTERM. Windows keeps a
376
394
  // child engine because cancellation reaches it over IPC.
395
+ // Asked when this part of a command runs, after earlier parts may have
396
+ // changed files. REDUE_FORCE=1 runs it anyway.
397
+ if(ifDue&&process.env.REDUE_FORCE!=='1'){
398
+ const {verdict}=await import('../src/claude-hook.mjs');
399
+ const asked=spawnSync(process.execPath,[fileURLToPath(import.meta.url),'--config',configFile,...(stateOverride?['--state-dir',stateOverride]:[]),
400
+ 'status','--sync','--json'],{encoding:'utf8',stdio:['ignore','pipe','ignore'],env:process.env,timeout:600_000});
401
+ let status=null;try{status=JSON.parse(asked.stdout);}catch{}
402
+ if(verdict(status,check)){
403
+ const row=status.checks.find(item=>item.name===check);
404
+ if(json)console.log(JSON.stringify({check,skipped:true,result:'PASS',run_id:row.invocation.runId}));
405
+ else console.error(`REDUE: ${check} passed on these exact inputs (run ${row.invocation.runId}); not run again. REDUE_FORCE=1 runs it anyway.`);
406
+ await exit(0);
407
+ }
408
+ }
377
409
  if(command==='run'&&process.platform!=='win32'){
378
410
  process.argv=[process.argv[0],engine,runtimeFile,internal,check,...(!json?['--human']:[])];
379
411
  await import(pathToFileURL(engine).href);
@@ -74,10 +74,37 @@ observer when a session starts (below); otherwise use `redue start`.
74
74
 
75
75
  ## Claude Code typecheck hook
76
76
 
77
- `redue agent setup claude --apply` also adds a PreToolUse hook and a
78
- SessionStart hook to `.claude/settings.local.json`. Both run
79
- `redue agent hook claude`, which needs a local REDUE install, so they are
80
- per-developer; keep that file out of commits. Other settings in the file are kept,
77
+ `redue agent setup claude --apply` (and `redue setup claude`) also adds a
78
+ PreToolUse hook and a SessionStart hook to `.claude/settings.local.json`.
79
+ Both run `redue agent hook claude`, which needs a local REDUE install, so
80
+ they are per-developer; keep that file out of commits. Setup names REDUE
81
+ plainly only when the `redue` that both your shell and a login shell find on
82
+ PATH is the install running setup. Otherwise the hooks name this install's
83
+ Node and entry by absolute path, so a Claude Code started from an IDE, a
84
+ desktop app or another shell still runs the right REDUE; run setup again
85
+ after moving or upgrading Node. `redue agent doctor` reports a hook whose
86
+ paths no longer exist. When the hook runs a check through REDUE for the
87
+ agent, it likewise writes plain `redue run` only if its own PATH finds this
88
+ install, else the absolute form.
89
+
90
+ Agents often chain an edit and a check (`printf … >> README.md && npm run
91
+ lint`). In a simple chain (commands joined by `&&`, `||` or `;`, with no
92
+ subshell, here-document, command substitution, background job or shell
93
+ keyword) where every part before a check is a program on PATH (not a
94
+ builtin such as `cd` or `export`, a shell function such as `nvm`, or a bare
95
+ assignment), the hook rewrites each check to `redue run CHECK --if-due`. That
96
+ command decides when its turn comes, after the earlier parts may have
97
+ changed files: it skips the check (exit 0, and says so) when its last pass
98
+ still applies, and otherwise runs it through REDUE with the exit code a plain
99
+ run gives. A script that runs several checks becomes `{ redue run a --if-due
100
+ && redue run b --if-due; }`. The rest of the chain is unchanged. The hook
101
+ rewrites only while the observer is healthy and knows each check. It changes
102
+ such a command without approving it, so your permission rules apply to the
103
+ rewritten command; a rule that allows only `npm run lint` will ask about
104
+ `redue run lint --if-due`. Anything more complex runs untouched. Known
105
+ limit: if REDUE cannot start the check when its turn comes (another
106
+ `redue run` holds the project's run lock), that part exits with REDUE's
107
+ error instead of running the check. Other settings in the file are kept,
81
108
  but the file is rewritten as 2-space JSON. `.claude/settings.json` is not touched.
82
109
  `redue agent remove claude --apply` removes only the entries REDUE added.
83
110
 
@@ -7,7 +7,9 @@ not mean npm or npm lifecycle scripts completed.
7
7
  For a supported single-package npm project with a typecheck script of `tsc`
8
8
  runs (see [script forms](#script-forms)) or no typecheck script and a root
9
9
  `tsconfig.json`, installed dependencies, Node 22/24 and TypeScript 5.x or 6.0
10
- (see [compiler identity](#compiler-identity)):
10
+ (see [compiler identity](#compiler-identity)). TypeScript 7 is a native
11
+ compiler this recipe cannot run: `init` refuses it
12
+ (`direct-typescript-version-unsupported`):
11
13
 
12
14
  ```sh
13
15
  redue init --recipe typescript-direct --check typecheck --dry-run
@@ -67,7 +67,26 @@ the package alone does not update project instructions.
67
67
 
68
68
  ## First project
69
69
 
70
- From the project root:
70
+ With Claude Code, one command does the steps below and connects the agent:
71
+
72
+ ```sh
73
+ redue setup claude # asks once; --yes for no prompts
74
+ ```
75
+
76
+ It runs `init` when the project has no `redue.config.json`, `agent setup
77
+ claude`, and `start`, then records each check that can qualify once, unless
78
+ it is already CURRENT. It prints every file it writes, and running it again
79
+ changes nothing that is already set up. When `.claude/settings.local.json`
80
+ is not ignored by Git, it adds it to `.git/info/exclude`; it refuses when
81
+ that file is committed. What setup created is noted in the clone's Git
82
+ directory (`.git/redue-setup-<id>.json`, never committed). `redue remove`
83
+ undoes exactly that: the integration (or only this machine's hooks, when the
84
+ integration was already in the project), the observer and its state
85
+ (receipts included), the local ignore line, and the config if setup wrote it
86
+ and it has not been edited since. A config or observer state that existed
87
+ before setup stays. `remove` asks before it deletes; Enter means no.
88
+
89
+ Step by step, from the project root:
71
90
 
72
91
  ```sh
73
92
  redue init --dry-run
@@ -113,7 +113,10 @@ one REDUE can account for:
113
113
 
114
114
  A process that replaces itself with another program through
115
115
  `process.execve` (TypeScript 7's `tsc` does this to start its native
116
- compiler) is judged the same way as a program it starts.
116
+ compiler) is judged the same way as a program it starts. `init` reads
117
+ TypeScript 7's launchers (`tsc` from `typescript@7`, `tsgo` from
118
+ `@typescript/native-preview`) as that native compiler and keeps such a script
119
+ recording-only.
117
120
 
118
121
  Anything else makes the result UNVERIFIED and names the program: `git`,
119
122
  `sleep`, `find`, and native programs from installed packages such as esbuild,
@@ -83,7 +83,11 @@ entries stay inputs: an import can still reach an excluded file); a file the
83
83
  program reads outside them keeps the check recording-only. This avoids pnpm's task-run bookkeeping
84
84
  writes during `pnpm run`; it does not alter the project's script. Test, lint,
85
85
  build, and ambiguous TypeScript scripts still execute through `pnpm run`; root
86
- typecheck and lint ones can use the [general recipe](node-script.md). The generated `@typescript-bin:WORKSPACE` token resolves
86
+ typecheck and lint ones can use the [general recipe](node-script.md). The pnpm
87
+ and Yarn TypeScript recipes read TypeScript's compiler API, which TypeScript 7
88
+ does not ship: with TypeScript 7 installed they refuse and say so. The
89
+ general recipe runs such a script, but TypeScript 7's native compiler is a
90
+ program REDUE does not record, so the result is UNVERIFIED. The generated `@typescript-bin:WORKSPACE` token resolves
87
91
  the local compiler when the check runs; it is not an absolute machine path.
88
92
 
89
93
  The pnpm contract indexes consumed installed package contents inside the