@redue/cli 0.1.0-alpha.16 → 0.1.0-alpha.18
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 +97 -0
- package/README.md +1 -1
- package/bin/redue.mjs +25 -14
- package/docs/user/agents.md +11 -0
- package/docs/user/configuration.md +4 -0
- package/docs/user/direct-typescript.md +62 -5
- package/docs/user/pilot.md +6 -5
- package/docs/user/supported-projects.md +29 -7
- package/docs/user/tests-direct.md +28 -4
- package/package.json +2 -2
- package/src/agent-integration.mjs +2 -2
- package/src/claude-hook.mjs +100 -27
- package/src/cli.mjs +9 -4
- package/src/daemon.mjs +21 -7
- package/src/declared-plan.mjs +11 -4
- package/src/direct-typescript-probe.mjs +5 -3
- package/src/direct-typescript.mjs +58 -11
- package/src/glob.mjs +8 -0
- package/src/index.mjs +29 -12
- package/src/installed-inputs.mjs +4 -0
- package/src/node-onboarding.mjs +90 -47
- package/src/npm-integrity.mjs +124 -0
- package/src/npmrc.mjs +16 -0
- package/src/plan-worker.mjs +4 -1
- package/src/pnpm-inputs.mjs +111 -52
- package/src/pnpm-typecheck-probe.mjs +11 -6
- package/src/test-direct.mjs +198 -36
- package/src/test-recorder.cjs +3 -2
- package/src/typescript-input-facts.mjs +7 -7
- package/src/typescript-invocation.mjs +92 -0
- package/src/typescript-list.mjs +2 -2
- package/src/typescript-qualification.mjs +8 -9
- package/src/workspace-manifests.mjs +22 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,102 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.1.0-alpha.18 — 2026-10-23
|
|
4
|
+
|
|
5
|
+
Typecheck in more project shapes, and pnpm test reuse on Linux. In the census
|
|
6
|
+
of 108 public TypeScript repos, the ones `init` makes ready went from 8 to 11
|
|
7
|
+
for typecheck (the census clones each repo's current head, so one or two
|
|
8
|
+
repos move between runs) and stayed at 22 for tests.
|
|
9
|
+
|
|
10
|
+
- pnpm test checks are reused on Linux. A recorded run accepts an installed
|
|
11
|
+
file hard-linked into the pnpm store the install recorded, when the store
|
|
12
|
+
holds it at its content address (pnpm names store files by their sha512).
|
|
13
|
+
Links anywhere else stay UNVERIFIED. Each file is hashed once; REDUE keeps
|
|
14
|
+
its identity and store path in its state and hashes it again only when the
|
|
15
|
+
identity changes. A 46 MB install takes about 160 ms to check the first
|
|
16
|
+
time. Limit: an in-place edit of an installed file in another project that
|
|
17
|
+
shares the store file is not seen until the next recorded run.
|
|
18
|
+
|
|
19
|
+
- Typecheck scripts in more shapes. A script of `tsc --noEmit` runs with
|
|
20
|
+
`-p`/`--project`, `--skipLibCheck` or `--pretty`, in any argument order,
|
|
21
|
+
qualifies for the direct npm recipe and the pnpm contract. A chain of such
|
|
22
|
+
runs joined by `&&` becomes one check per run (`typecheck.1`,
|
|
23
|
+
`typecheck.2`); the hook skips the script only when every run is CURRENT
|
|
24
|
+
and otherwise runs the stale ones through `redue run`. `check-types` joins
|
|
25
|
+
the recognized script names.
|
|
26
|
+
- The hook acts on a typecheck script's form only while the script in
|
|
27
|
+
package.json still has exactly the configured `tsc` runs. Before, a script
|
|
28
|
+
changed to `tsc --noEmit && eslint .` was rewritten to `redue run
|
|
29
|
+
typecheck`, which ran tsc alone.
|
|
30
|
+
- Without a typecheck script, `init` proposes a `tsc --noEmit` check for a
|
|
31
|
+
root `tsconfig.json` (direct npm recipe, pnpm) and for each pnpm workspace
|
|
32
|
+
package with one, unless a script runs a TypeScript checker in another form
|
|
33
|
+
(`tsc -b`, `vue-tsc`, ...). package.json is not changed; the check is
|
|
34
|
+
written only when the contract holds.
|
|
35
|
+
- The pnpm contract no longer needs `include: ["src"]`. Its source inputs are
|
|
36
|
+
the directories the config's `include` covers, as TypeScript computes them
|
|
37
|
+
for new files, minus `node_modules`. `exclude` entries stay inputs, since an
|
|
38
|
+
import can still reach an excluded file. A config extended from an installed
|
|
39
|
+
package is an installed input.
|
|
40
|
+
- Fixed: a config with three or more pnpm or test checks never got a
|
|
41
|
+
synchronized answer. The observer dropped control requests over 4 KB, and a
|
|
42
|
+
sync request carries about 1.5 KB of context digests per check; status
|
|
43
|
+
showed "Unexpected end of JSON input" for every check. The limit is 1 MiB.
|
|
44
|
+
|
|
45
|
+
- The direct npm TypeScript recipe takes any TypeScript 5.x or 6.0, not only
|
|
46
|
+
the five reviewed releases. Another release qualifies when package-lock.json
|
|
47
|
+
records the registry's `typescript` at that version with an integrity
|
|
48
|
+
(sha512, or sha1 from registries that record only sha1), npm's cache holds
|
|
49
|
+
the tarball under it, and the installed package is exactly the tarball's
|
|
50
|
+
files. Nothing is downloaded; a missing tarball
|
|
51
|
+
keeps the check recording-only with `npm-tarball-uncached`. The check takes
|
|
52
|
+
about 100 ms per full probe (TypeScript 5.x and 6.0 tarballs, 120-140 files).
|
|
53
|
+
CI runs the direct contract suite against 5.0.4, 5.1.6, 5.2.2, 5.3.3, 5.4.5,
|
|
54
|
+
5.5.4, 5.7.3 and 5.8.3.
|
|
55
|
+
|
|
56
|
+
- Contributors: `npm test` runs up to four test files at a time, keeping two
|
|
57
|
+
cores free (a 3-core CI macOS runner runs one at a time), and takes about
|
|
58
|
+
6 minutes on the owner's Mac (was 19.6). The two TypeScript 6 fixture
|
|
59
|
+
suites are separate files so they run side by side.
|
|
60
|
+
|
|
61
|
+
## 0.1.0-alpha.17 — 2026-10-22
|
|
62
|
+
|
|
63
|
+
Workspaces and package managers beyond npm. In the census of 108 public
|
|
64
|
+
TypeScript repos, the ones `init` makes ready went from 5 to 8 for typecheck
|
|
65
|
+
and from 6 to 22 for tests.
|
|
66
|
+
|
|
67
|
+
- pnpm 9-12 for the pnpm TypeScript contract (it took only an exact pnpm 12
|
|
68
|
+
pin). The installed `.modules.yaml` names the version; a pin is optional,
|
|
69
|
+
with or without Corepack's `+sha512` hash (pins with a hash never matched
|
|
70
|
+
before). `.npmrc` settings that only change what gets installed are
|
|
71
|
+
accepted; settings that move or copy installed files, or run code during
|
|
72
|
+
install, are refused in either file: custom modules, virtual-store or
|
|
73
|
+
lockfile directories, the global virtual store, injected workspace
|
|
74
|
+
packages, separate lockfiles, pnpmfile hooks, `configDependencies`.
|
|
75
|
+
- The tests recipe takes single-package pnpm 9-12 and Yarn `node-modules`
|
|
76
|
+
installs, and gives each workspace package (npm or Yarn `workspaces`, pnpm
|
|
77
|
+
`packages`) whose test script runs Vitest directly its own `PACKAGE:test`
|
|
78
|
+
check, run in the package directory. Inputs stay the whole checkout; every
|
|
79
|
+
package's node_modules is an installed input. Jest and root test scripts in
|
|
80
|
+
workspaces stay recording-only. A package added later makes the
|
|
81
|
+
workspace's checks UNVERIFIED until `init` runs again.
|
|
82
|
+
- A recorded test run is UNVERIFIED when an installed file has another hard
|
|
83
|
+
link: a package store outside the checkout (pnpm's default on Linux, Yarn's
|
|
84
|
+
hardlinks modes) could change it unseen. pnpm on macOS clones files and is
|
|
85
|
+
not affected; on Linux, pnpm test checks qualify but are not reused unless
|
|
86
|
+
pnpm copies files.
|
|
87
|
+
- Observer: an installed link to a directory (pnpm links every package) is
|
|
88
|
+
observed by its link chain when the target is itself observed installed;
|
|
89
|
+
any other directory link still makes observation unavailable.
|
|
90
|
+
- Workspace patterns honour `!pattern` exclusions; before, a negated pattern
|
|
91
|
+
matched every directory.
|
|
92
|
+
- Fixed: a new workspace package manifest did not re-plan a test check, for
|
|
93
|
+
npm workspaces too.
|
|
94
|
+
|
|
95
|
+
Eval on alpha.17 code (CI 39828002753, 84 sessions, test tasks on twilio,
|
|
96
|
+
cheerio and SwitchHosts plus the pnpm project devops-daily): 0 critical
|
|
97
|
+
findings; 23 skipped test runs, each passing three plain reruns; nothing was
|
|
98
|
+
skipped on devops-daily, whose recorded runs were UNVERIFIED on the Linux
|
|
99
|
+
runner.
|
|
3
100
|
## 0.1.0-alpha.16 — 2026-10-22
|
|
4
101
|
|
|
5
102
|
Changes to the Claude Code integration, and one fix for commands that meet
|
package/README.md
CHANGED
|
@@ -37,7 +37,7 @@ it; relevant source edits make it stale. [Run the reproducible demo](docs/contri
|
|
|
37
37
|
|
|
38
38
|
## Install the alpha
|
|
39
39
|
|
|
40
|
-
The opt-in pilot uses **0.1.0-alpha.
|
|
40
|
+
The opt-in pilot uses **0.1.0-alpha.18**. Follow the
|
|
41
41
|
[fixed-version pilot guide](docs/user/pilot.md) for supported projects, setup,
|
|
42
42
|
updates and voluntary feedback. No external usage results are claimed yet.
|
|
43
43
|
|
package/bin/redue.mjs
CHANGED
|
@@ -4,7 +4,8 @@ import {mark,traceBase,traceContext,traceEnvironment,traceId} from '../src/decis
|
|
|
4
4
|
traceBase({...traceContext(),request:traceId(),work:'client'});
|
|
5
5
|
mark('client.loaded');
|
|
6
6
|
import {directCompiler,directQualification,directEnvironment,directInterpretation} from '../src/direct-typescript.mjs';
|
|
7
|
-
import {
|
|
7
|
+
import {runArgv,tscRun} from '../src/typescript-invocation.mjs';
|
|
8
|
+
import {testEnvironment,testRunnerAt,testsInterpretation,testsQualification} from '../src/test-direct.mjs';
|
|
8
9
|
import os from 'node:os';
|
|
9
10
|
import path from 'node:path';
|
|
10
11
|
import {createHash} from 'node:crypto';
|
|
@@ -194,7 +195,7 @@ function which(name){
|
|
|
194
195
|
if(found)return found;
|
|
195
196
|
error(`cannot resolve executable ${name} on PATH`);
|
|
196
197
|
}
|
|
197
|
-
function token(value){
|
|
198
|
+
function token(value,cwd='.'){
|
|
198
199
|
if(value==='@node')return process.execPath;
|
|
199
200
|
if(value==='@project')return root;
|
|
200
201
|
if(value==='@typescript-compiler:.'){
|
|
@@ -202,8 +203,8 @@ function token(value){
|
|
|
202
203
|
}
|
|
203
204
|
if(value.startsWith('@test-runner:')){
|
|
204
205
|
// Qualification reports a missing runner; status and stop still work.
|
|
205
|
-
try{return
|
|
206
|
-
catch{return path.join(root,'node_modules',value.slice('@test-runner:'.length),'<unavailable>');}
|
|
206
|
+
try{return testRunnerAt(root,cwd,value.slice('@test-runner:'.length)).entry;}
|
|
207
|
+
catch{return path.join(root,cwd,'node_modules',value.slice('@test-runner:'.length),'<unavailable>');}
|
|
207
208
|
}
|
|
208
209
|
if(value==='@vstate/direct-typescript-probe')return path.join(productRoot,'src','direct-typescript-probe.mjs');
|
|
209
210
|
if(value.startsWith('@typescript-bin:')){
|
|
@@ -226,10 +227,10 @@ function token(value){
|
|
|
226
227
|
if(value.startsWith('@which:'))return which(value.slice(7));
|
|
227
228
|
return value;
|
|
228
229
|
}
|
|
229
|
-
function argv(values,label){
|
|
230
|
+
function argv(values,label,cwd){
|
|
230
231
|
if(!Array.isArray(values)||!values.length||values.some(v=>typeof v!=='string'||!v))
|
|
231
232
|
error(`${label} must be nonempty exact argv`);
|
|
232
|
-
return values.map(token);
|
|
233
|
+
return values.map(value=>token(value,cwd));
|
|
233
234
|
}
|
|
234
235
|
const categories=['source','generated','installedDependencies','environment','toolchain','runtime',
|
|
235
236
|
'probeContinuity'];
|
|
@@ -256,11 +257,12 @@ const checks=publicConfig.checks.map(check=>{
|
|
|
256
257
|
const yarnWorkspace=check.qualification==='yarn-workspace-tsc-v1';
|
|
257
258
|
if(check.qualification&&!npmAutomatic&&!npmDirect&&!yarnWorkspace&&!pnpmTypecheck&&!testsDirect)
|
|
258
259
|
error(`${check.name}: unknown qualification contract`);
|
|
259
|
-
if(npmDirect&&(manager!=='npm'
|
|
260
|
-
|
|
260
|
+
if(npmDirect&&(manager!=='npm'||check.cwd&&check.cwd!=='.'||!Array.isArray(check.command)||
|
|
261
|
+
check.command[0]!=='@node'||check.command[1]!=='@typescript-compiler:.'||
|
|
262
|
+
!tscRun({},{script:null,args:check.command.slice(2)}).run?.noEmit))
|
|
261
263
|
error(`${check.name}: direct recipe requires its explicit local compiler command`);
|
|
262
264
|
// The script's own arguments follow the runner; qualification compares them with the script.
|
|
263
|
-
if(testsDirect&&(
|
|
265
|
+
if(testsDirect&&(!['npm','pnpm','yarn'].includes(manager)||!check.script||check.cwd&&check.cwd!=='.'&&!check.workspace||!Array.isArray(check.command)||
|
|
264
266
|
check.command[0]!=='@node'||!/^@test-runner:(?:vitest|jest)$/.test(check.command[1]||'')))
|
|
265
267
|
error(`${check.name}: test recipe requires its explicit local test runner command`);
|
|
266
268
|
if(npmAutomatic&&(manager!=='npm'||check.cwd&&check.cwd!=='.'))
|
|
@@ -270,22 +272,29 @@ const checks=publicConfig.checks.map(check=>{
|
|
|
270
272
|
check.command[1]!=='workspace'||check.command[2]!==check.workspace||
|
|
271
273
|
check.command[3]!=='run'))
|
|
272
274
|
error(`${check.name}: Yarn workspace qualification needs its discovered workspace command`);
|
|
273
|
-
if(pnpmTypecheck&&(manager!=='pnpm'||!
|
|
275
|
+
if(pnpmTypecheck&&(manager!=='pnpm'||!Array.isArray(check.command)||
|
|
274
276
|
check.command[0]!=='@node'||
|
|
275
277
|
check.command[1]!==`@typescript-bin:${check.cwd||'.'}`||
|
|
276
278
|
check.workspace&&check.cwd==='.'||
|
|
277
|
-
!check.command.slice(2).
|
|
279
|
+
!tscRun({},{script:null,args:check.command.slice(2)}).run))
|
|
278
280
|
error(`${check.name}: pnpm qualification needs its discovered standalone TypeScript command`);
|
|
279
281
|
const reason=check.kind==='test'?'test runtime, transforms and cache inputs need review':
|
|
280
282
|
check.kind==='lint'?'lint plugins, config and resolver inputs need review':
|
|
281
283
|
check.kind==='build'?'build environment and generated inputs need review':
|
|
282
284
|
'check inputs need review';
|
|
283
|
-
|
|
285
|
+
// A chained script has one check per tsc run: each check's run counts them,
|
|
286
|
+
// so a run added to or removed from the script invalidates them all.
|
|
287
|
+
const runCount=(npmDirect||pnpmTypecheck)&&check.script?publicConfig.checks.filter(other=>
|
|
288
|
+
other.qualification===check.qualification&&other.script===check.script&&
|
|
289
|
+
(other.cwd||'.')===(check.cwd||'.')&&(other.workspace||null)===(check.workspace||null)).length:1;
|
|
290
|
+
if(runCount>16)error(`${check.name}: more than 16 checks for script ${check.script}`);
|
|
291
|
+
const run=npmDirect||pnpmTypecheck?runArgv({script:check.script??null,args:check.command.slice(2),count:runCount}):null;
|
|
292
|
+
const autoProbes=npmDirect?[['@node','@vstate/direct-typescript-probe','@project',...run]]:npmAutomatic?[['@node','@vstate/typescript-contract-probe',
|
|
284
293
|
'@project',check.script,'@which:npm']]:yarnWorkspace?
|
|
285
294
|
[['@node','@vstate/yarn-workspace-typecheck-probe','@project',
|
|
286
295
|
check.workspace,check.command[4],'@which:yarn']]:pnpmTypecheck?
|
|
287
296
|
[['@node','@vstate/pnpm-typecheck-probe','@project',
|
|
288
|
-
check.workspace||'.'
|
|
297
|
+
check.workspace||'.',...run]]:[];
|
|
289
298
|
const windowsContext=process.platform==='win32'?
|
|
290
299
|
['ComSpec','COMSPEC','PATHEXT','USERPROFILE']:[];
|
|
291
300
|
const autoEnvironment=npmDirect?directEnvironment:testsDirect?testEnvironment:npmAutomatic?{variables:['NODE_OPTIONS','NODE_PATH','CI','HOME',
|
|
@@ -297,7 +306,8 @@ const checks=publicConfig.checks.map(check=>{
|
|
|
297
306
|
{variables:['NODE_OPTIONS','NODE_PATH','CI','HOME','NODE_ENV','BASH_ENV','ENV',...windowsContext],
|
|
298
307
|
prefixes:['PNPM_','COREPACK_','npm_config_','DYLD_','TSGO_'],
|
|
299
308
|
pathExecutables:['node'],executableIdentity:true}:{};
|
|
300
|
-
return {name:check.name,command:argv(scriptCommand||check.command,`${check.name}.command
|
|
309
|
+
return {name:check.name,command:argv(scriptCommand||check.command,`${check.name}.command`,
|
|
310
|
+
typeof check.cwd==='string'&&check.cwd&&!path.isAbsolute(check.cwd)&&!check.cwd.split('/').includes('..')?check.cwd:'.'),
|
|
301
311
|
cwd:check.cwd,inputs:check.inputs||[],generatedInputs:check.generatedInputs||[],
|
|
302
312
|
installedInputs:check.installedInputs||(
|
|
303
313
|
npmAutomatic||npmDirect||testsDirect?['node_modules/**']:[]),
|
|
@@ -309,6 +319,7 @@ const checks=publicConfig.checks.map(check=>{
|
|
|
309
319
|
coverageReasons:check.coverageReasons||Object.fromEntries(
|
|
310
320
|
categories.map(category=>[category,reason])),
|
|
311
321
|
qualification:check.qualification||null,script:check.script||null,
|
|
322
|
+
...(runCount>1?{runCount}:{}),
|
|
312
323
|
...(npmAutomatic?{qualificationInterpretation:'npm-launcher-withheld@1'}:{}),
|
|
313
324
|
...(npmDirect?{qualificationInterpretation:directInterpretation}:{}),
|
|
314
325
|
...(testsDirect?{qualificationInterpretation:testsInterpretation}:{}),
|
package/docs/user/agents.md
CHANGED
|
@@ -122,6 +122,17 @@ post-scripts. In every other case the command runs as typed: turbo caches its
|
|
|
122
122
|
own results, so the hook never rewrites it. Filters (`--filter`) and nx are not
|
|
123
123
|
matched.
|
|
124
124
|
|
|
125
|
+
For a typecheck check that runs the compiler, the script's forms act only when
|
|
126
|
+
the script in package.json still has exactly the configured runs: one `tsc`
|
|
127
|
+
run per check with the same arguments, and no pre- or post-script. Otherwise
|
|
128
|
+
the command runs as typed. A script that chains `tsc` runs with `&&` has one
|
|
129
|
+
check per run, `typecheck.1`, `typecheck.2` and so on (see
|
|
130
|
+
[direct TypeScript](direct-typescript.md#script-forms)); each run's own
|
|
131
|
+
`npx tsc ...` form matches its check. The hook skips the script when all its
|
|
132
|
+
runs are CURRENT, PASS and `reuse_eligible`, and otherwise rewrites it to
|
|
133
|
+
`redue run` of the runs that are not, in the script's order, joined by `&&`.
|
|
134
|
+
In a turbo or `pnpm -r` fan-out, a package's script counts only the same way.
|
|
135
|
+
|
|
125
136
|
Each form may end with `2>&1` and then one `| tail -n N`, `| head -n N`,
|
|
126
137
|
`| tail -N` or `| head -N`. It may also be the first command of a `;` or `&&`
|
|
127
138
|
chain. Nothing else matches. Each form has its own `if` rule, so other Bash
|
|
@@ -56,6 +56,10 @@ Advanced explicit contracts may declare `generatedInputs`, `installedInputs`, na
|
|
|
56
56
|
reviewed escape hatches, not onboarding requirements. A coverage rationale cannot
|
|
57
57
|
override unsupported storage, unresolved links, missing inputs, or observation failure.
|
|
58
58
|
|
|
59
|
+
A chained typecheck script gets one generated check per `tsc` run, all with
|
|
60
|
+
the script's name (see [script forms](direct-typescript.md#script-forms)). A
|
|
61
|
+
proposed check for a project without a typecheck script has no `script`.
|
|
62
|
+
|
|
59
63
|
Input patterns are relative to the project root. They cannot name a `.git`
|
|
60
64
|
directory, because REDUE does not observe one; `!.git/**` is fine.
|
|
61
65
|
|
|
@@ -4,8 +4,10 @@ This check means: the reviewed local TypeScript compiler completed the configure
|
|
|
4
4
|
check against declared, observed inputs and relevant execution context. It does
|
|
5
5
|
not mean npm or npm lifecycle scripts completed.
|
|
6
6
|
|
|
7
|
-
For a supported single-package npm project with
|
|
8
|
-
|
|
7
|
+
For a supported single-package npm project with a typecheck script of `tsc`
|
|
8
|
+
runs (see [script forms](#script-forms)) or no typecheck script and a root
|
|
9
|
+
`tsconfig.json`, installed dependencies, Node 22/24 and TypeScript 5.x or 6.0
|
|
10
|
+
(see [compiler identity](#compiler-identity)):
|
|
9
11
|
|
|
10
12
|
```sh
|
|
11
13
|
redue init --recipe typescript-direct --check typecheck --dry-run
|
|
@@ -33,9 +35,64 @@ A generated check contains:
|
|
|
33
35
|
|
|
34
36
|
`script` identifies the reviewed source recipe; `command` is what actually runs:
|
|
35
37
|
the resolved Node executable and local TypeScript implementation, with exact argv
|
|
36
|
-
and no shell.
|
|
37
|
-
without its outer compile-cache-enabling shim
|
|
38
|
-
|
|
38
|
+
and no shell. The entry is `lib/_tsc.js` where the package has one (5.7 and
|
|
39
|
+
later), without its outer compile-cache-enabling shim, and `lib/tsc.js`
|
|
40
|
+
otherwise. No compiler is fetched or bundled.
|
|
41
|
+
|
|
42
|
+
## Compiler identity
|
|
43
|
+
|
|
44
|
+
The entire installed `node_modules/typescript` must be an official package,
|
|
45
|
+
byte for byte, in one of two ways:
|
|
46
|
+
|
|
47
|
+
- TypeScript 5.5.2, 5.6.3, 5.9.3, 6.0.2 or 6.0.3: its files match the digest
|
|
48
|
+
REDUE ships for that release.
|
|
49
|
+
- Any other TypeScript 5.x or 6.0 release: package-lock.json records the
|
|
50
|
+
registry's `typescript` package at the installed version (no alias, an
|
|
51
|
+
`https` tarball URL, not a file or git source) with an integrity (sha512,
|
|
52
|
+
or sha1 where a registry records only sha1), npm's cache holds the tarball
|
|
53
|
+
under that integrity, and the installed files are exactly the tarball's
|
|
54
|
+
files. REDUE reads the cache only; it never downloads. The cache is the one
|
|
55
|
+
npm uses: `npm_config_cache`, else `cache` in the project's `.npmrc`, then
|
|
56
|
+
the user's, else `~/.npm` (`%LocalAppData%\npm-cache` on Windows); the
|
|
57
|
+
global npmrc is not read. `npm-shrinkwrap.json` is not read on this path.
|
|
58
|
+
`npm ci` fills the cache. When the tarball is missing the check stays
|
|
59
|
+
recording-only with the reason `npm-tarball-uncached`; `npm cache add
|
|
60
|
+
typescript@VERSION` adds it. The check's compiler identity is then the
|
|
61
|
+
lockfile integrity, so a lockfile change re-probes.
|
|
62
|
+
|
|
63
|
+
CI runs the same contract suite against the last release of each minor from
|
|
64
|
+
5.0 to 5.8 that the reviewed releases do not cover. Later minors (6.1 and up)
|
|
65
|
+
wait until CI covers them. TypeScript 7 (the native compiler) is not
|
|
66
|
+
supported.
|
|
67
|
+
|
|
68
|
+
## Script forms
|
|
69
|
+
|
|
70
|
+
A typecheck script qualifies when it is one or more `tsc` runs joined by `&&`,
|
|
71
|
+
each with `--noEmit` and only these arguments, in any order: `-p`/`--project`
|
|
72
|
+
with a config file or directory inside the project, `--skipLibCheck`, and
|
|
73
|
+
`--pretty` (optionally `true` or `false`). For example
|
|
74
|
+
`tsc -p tsconfig.app.json --noEmit` or
|
|
75
|
+
`tsc --noEmit && tsc --noEmit -p tsconfig.node.json`. `tsc -b`, `--watch`,
|
|
76
|
+
other options, quotes, variables and other commands keep the script
|
|
77
|
+
recording-only.
|
|
78
|
+
|
|
79
|
+
A chain gets one check per run, named `typecheck.1`, `typecheck.2` and so on,
|
|
80
|
+
each with the script's name and that run's arguments in its command. Each run
|
|
81
|
+
is checked as its own program against its own config, and is reused only while
|
|
82
|
+
the script still has as many runs as the config has checks for it, one of them
|
|
83
|
+
with exactly those arguments. Adding or removing a run, or a pre- or
|
|
84
|
+
post-script, makes every check of the script not reusable until `init` runs
|
|
85
|
+
again. The script as a whole
|
|
86
|
+
(`npm run typecheck`) is skipped only when its current runs and the configured
|
|
87
|
+
checks correspond one to one and all are CURRENT (see [agents](agents.md)).
|
|
88
|
+
|
|
89
|
+
With no typecheck script and a root `tsconfig.json`, init proposes a
|
|
90
|
+
`tsc --noEmit` check with no `script` field, unless a script already runs a
|
|
91
|
+
TypeScript checker in another form (`tsc -b`, `vue-tsc`, `svelte-check`,
|
|
92
|
+
`tsgo`, `astro check`, `nuxi typecheck`). package.json is not changed. The
|
|
93
|
+
check is written only when the contract holds; otherwise no typecheck check is
|
|
94
|
+
added. The pnpm contract does the same for each workspace package with a
|
|
95
|
+
`tsconfig.json` and no typecheck script.
|
|
39
96
|
|
|
40
97
|
Existing configs are never overwritten. To compare choices, use a new project-local
|
|
41
98
|
`--config redue.direct.config.json` with init; this explicitly selects separate
|
package/docs/user/pilot.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Opt-in development pilot: alpha.
|
|
1
|
+
# Opt-in development pilot: alpha.18
|
|
2
2
|
|
|
3
3
|
Use one fixed build for several ordinary development sessions. The owner will
|
|
4
4
|
invite two or three willing testers and approve their projects; nothing is
|
|
@@ -9,8 +9,9 @@ controlled compiler/agent trials are acceptance evidence, not independent adopti
|
|
|
9
9
|
|
|
10
10
|
Start with a trusted, single-package npm Git checkout, normally installed locked
|
|
11
11
|
dependencies, and an existing standalone `tsc --noEmit` script without pre/post
|
|
12
|
-
scripts. The direct recipe accepts only
|
|
13
|
-
|
|
12
|
+
scripts. The direct recipe accepts only an official, unchanged TypeScript **5.x or
|
|
13
|
+
6.0** package (a reviewed release, or one matching the tarball package-lock.json
|
|
14
|
+
names in npm's cache) and Node **22 or 24**. No workspaces, named
|
|
14
15
|
tsconfig selection, emitted/incremental/composite builds, custom loaders or
|
|
15
16
|
outside-root inputs are added by this pilot. Do not downgrade or change your
|
|
16
17
|
project to fit; record an unsupported setup as feedback.
|
|
@@ -35,7 +36,7 @@ are **NOT RUN**. The recording shortcut is macOS/direct-recipe only.
|
|
|
35
36
|
For a new default npm installation:
|
|
36
37
|
|
|
37
38
|
```sh
|
|
38
|
-
npm install --global @redue/cli@0.1.0-alpha.
|
|
39
|
+
npm install --global @redue/cli@0.1.0-alpha.18
|
|
39
40
|
redue --version
|
|
40
41
|
cd my-project
|
|
41
42
|
redue --config redue.config.json init --recipe typescript-direct --check typecheck --dry-run
|
|
@@ -89,7 +90,7 @@ using it; keep the same npm prefix, config and any explicit state selection.
|
|
|
89
90
|
|
|
90
91
|
```sh
|
|
91
92
|
redue --config redue.config.json stop
|
|
92
|
-
npm install --global @redue/cli@0.1.0-alpha.
|
|
93
|
+
npm install --global @redue/cli@0.1.0-alpha.18
|
|
93
94
|
redue --config redue.config.json agent setup codex --dry-run
|
|
94
95
|
redue --config redue.config.json agent setup codex --apply
|
|
95
96
|
redue --config redue.config.json start
|
|
@@ -18,8 +18,12 @@ matches are reported as ambiguous rather than guessed. It never creates a new
|
|
|
18
18
|
project script. Automatic npm-launcher applicability was withdrawn in alpha.4;
|
|
19
19
|
ordinary npm scripts remain recording-only. See the [maintainer notice](npm-launcher-notice.md).
|
|
20
20
|
The direct recipe is opt-in, available since alpha.5. The opt-in `init --recipe typescript-direct` supports
|
|
21
|
-
a
|
|
22
|
-
|
|
21
|
+
a typecheck script of `tsc --noEmit` runs in any argument order, chained runs,
|
|
22
|
+
or no typecheck script at all (a proposed `tsc --noEmit`; see
|
|
23
|
+
[script forms](direct-typescript.md#script-forms)), in a single-package npm installation using
|
|
24
|
+
TypeScript 5.x or 6.0 (a reviewed release, or one whose installed files match the
|
|
25
|
+
tarball package-lock.json names in npm's cache; see
|
|
26
|
+
[compiler identity](direct-typescript.md#compiler-identity)) and Node 22/24. It executes the local compiler,
|
|
23
27
|
not npm. Full installed compiler identity, config/membership, installed inputs
|
|
24
28
|
and relevant Node environment are checked before reuse. See the
|
|
25
29
|
[exact direct boundary](direct-typescript.md). Unsupported plugins, preloads,
|
|
@@ -45,13 +49,31 @@ project whose test script runs Vitest 4/5 or Jest 29 directly, the opt-in
|
|
|
45
49
|
In particular, test caches and build-generated inputs are not excluded to
|
|
46
50
|
make evidence green.
|
|
47
51
|
|
|
48
|
-
|
|
49
|
-
the lockfile, and the installed `node_modules/.modules.yaml`
|
|
52
|
+
pnpm 9-12 projects are read from `packageManager`, `pnpm-workspace.yaml`,
|
|
53
|
+
`.npmrc`, the lockfile, and the installed `node_modules/.modules.yaml`
|
|
54
|
+
metadata. The installed metadata names the pnpm version; a `packageManager`
|
|
55
|
+
pin is optional, and when present (with or without Corepack's `+sha512` hash)
|
|
56
|
+
it must name the installed version. Settings that only change what gets
|
|
57
|
+
installed are accepted, because the installed files are what REDUE observes.
|
|
58
|
+
Settings that move or copy installed files, or run code during install, are
|
|
59
|
+
not: custom `modulesDir`, `virtualStoreDir` or `lockfileDir`, the global
|
|
60
|
+
virtual store, injected workspace packages, separate workspace lockfiles,
|
|
61
|
+
pnpmfile hooks and `configDependencies`. They are refused when either
|
|
62
|
+
`pnpm-workspace.yaml` or `.npmrc` sets them. The linker is read from the file
|
|
63
|
+
the installed major reads (`.npmrc` for pnpm 9, `pnpm-workspace.yaml` then
|
|
64
|
+
`.npmrc` for pnpm 10, `pnpm-workspace.yaml` for pnpm 11 and 12) and must match
|
|
65
|
+
the installed one. Only the project's files are read: settings from a user or
|
|
66
|
+
global rc file, or `npm_config_*` variables during install, are not. The
|
|
50
67
|
supported local layouts are pnpm's standard isolated and hoisted
|
|
51
68
|
`node_modules` with a project-local `.pnpm` virtual store. `init` can discover
|
|
52
|
-
root and workspace scripts. For a
|
|
53
|
-
script
|
|
54
|
-
|
|
69
|
+
root and workspace scripts. For a typecheck script of `tsc` runs
|
|
70
|
+
([script forms](direct-typescript.md#script-forms); `tsc -p .` without
|
|
71
|
+
`--noEmit` is also accepted), or a package with a `tsconfig.json` and no
|
|
72
|
+
typecheck script, it records the exact installed compiler invocation directly.
|
|
73
|
+
The source inputs are the directories the config's `include` covers, as
|
|
74
|
+
TypeScript itself computes them for new files, minus `node_modules` (`exclude`
|
|
75
|
+
entries stay inputs: an import can still reach an excluded file); a file the
|
|
76
|
+
program reads outside them keeps the check recording-only. This avoids pnpm's task-run bookkeeping
|
|
55
77
|
writes during `pnpm run`; it does not alter the project's script. Test, lint,
|
|
56
78
|
build, and ambiguous TypeScript scripts still execute through `pnpm run` and
|
|
57
79
|
start recording-only. The generated `@typescript-bin:WORKSPACE` token resolves
|
|
@@ -2,8 +2,24 @@
|
|
|
2
2
|
|
|
3
3
|
`redue init --recipe tests-direct --check test` records a project's unit test
|
|
4
4
|
run and reports later whether that result still applies to the files you have.
|
|
5
|
-
It supports a single-package
|
|
6
|
-
|
|
5
|
+
It supports a single-package project installed by npm, pnpm 9-12 (isolated or
|
|
6
|
+
hoisted `node_modules`) or Yarn with the `node-modules` linker, whose test
|
|
7
|
+
script runs Vitest 4 or 5, or Jest 29, directly. It runs on macOS and Linux with
|
|
8
|
+
Node 22 or 24. The project has one lockfile kind, matching any
|
|
9
|
+
`packageManager` declaration; Yarn needs that declaration. With pnpm, the
|
|
10
|
+
runner is the package `node_modules/RUNNER` links to, inside the checkout.
|
|
11
|
+
Jest also needs jest-util 29 at `node_modules/jest-util`, which pnpm does not
|
|
12
|
+
place there unless it hoists.
|
|
13
|
+
|
|
14
|
+
In a workspace (npm or Yarn `workspaces`, or `packages` in
|
|
15
|
+
`pnpm-workspace.yaml`), `init` gives each package whose test script runs
|
|
16
|
+
Vitest directly its own check, `PACKAGE:test`, run in the package's directory
|
|
17
|
+
with the Vitest that Node finds from there. Its inputs are still the whole
|
|
18
|
+
checkout, so an edit in any package makes it STALE. The root's and every
|
|
19
|
+
package's `node_modules` are observed as installed files, without Vite's
|
|
20
|
+
caches in them. Jest is not supported in workspace packages, and neither is a
|
|
21
|
+
test script at the workspace root. A package added later needs `init` again:
|
|
22
|
+
its manifest appearing makes the workspace's checks UNVERIFIED until then.
|
|
7
23
|
|
|
8
24
|
## Setup
|
|
9
25
|
|
|
@@ -21,8 +37,8 @@ runner invocation: no `&&`, quotes, globs or variable assignments. Watch mode,
|
|
|
21
37
|
Jest's `--onlyChanged` are refused. Vitest needs `run`, `--run` or
|
|
22
38
|
`--watch=false`. Other scripts stay recording-only.
|
|
23
39
|
|
|
24
|
-
REDUE runs the local runner with Node directly.
|
|
25
|
-
`package.json` are not changed or executed.
|
|
40
|
+
REDUE runs the local runner with Node directly. The package manager, its
|
|
41
|
+
lifecycle scripts and `package.json` are not changed or executed.
|
|
26
42
|
|
|
27
43
|
## What REDUE claims
|
|
28
44
|
|
|
@@ -63,6 +79,10 @@ kept but marked UNVERIFIED, with the reason, when the run:
|
|
|
63
79
|
about a name other than localhost;
|
|
64
80
|
- started a process that did not load the recorder: a shell, a binary, or a
|
|
65
81
|
Node process with its own environment;
|
|
82
|
+
- had an installed file with another hard link, other than a link into the
|
|
83
|
+
pnpm store the install recorded in `node_modules/.modules.yaml` (pnpm's
|
|
84
|
+
default on Linux). A change through a link elsewhere, such as Yarn's
|
|
85
|
+
hardlinks modes, is not seen.
|
|
66
86
|
|
|
67
87
|
Lookups that can be asked again become run facts, checked before every CURRENT
|
|
68
88
|
answer: whether a path outside the checkout exists, where a link resolves, the
|
|
@@ -103,4 +123,8 @@ Tests that write files git does not ignore change the run's inputs.
|
|
|
103
123
|
are not indexed at start), makes the result STALE.
|
|
104
124
|
- A config written by an earlier build of this recipe is UNVERIFIED with "run
|
|
105
125
|
redue init again"; move `redue.config.json` aside and run init.
|
|
126
|
+
- pnpm's store is trusted not to change a file in place, as pnpm never does.
|
|
127
|
+
Editing an installed file in another project that shares the same store
|
|
128
|
+
file changes it here too, unseen. Use `pnpm patch`, or a copying install
|
|
129
|
+
(`package-import-method=copy`), if you edit installed files.
|
|
106
130
|
- Windows is not supported; the check stays recording-only there.
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@redue/cli",
|
|
3
3
|
"private": false,
|
|
4
|
-
"version": "0.1.0-alpha.
|
|
4
|
+
"version": "0.1.0-alpha.18",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public",
|
|
7
7
|
"tag": "alpha"
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
"redue": "./bin/redue.mjs"
|
|
12
12
|
},
|
|
13
13
|
"scripts": {
|
|
14
|
-
"test": "node
|
|
14
|
+
"test": "node test/run-suite.mjs",
|
|
15
15
|
"test:package": "node test/acceptance/package-smoke.mjs",
|
|
16
16
|
"demo": "node test/acceptance/package-smoke.mjs --demo",
|
|
17
17
|
"pretest": "npm --prefix test/fixtures/typescript-5-5-2 ci --no-audit --no-fund && npm --prefix test/fixtures/typescript-6-0-2 ci --no-audit --no-fund && npm --prefix test/fixtures/typescript-6-0-3 ci --no-audit --no-fund"
|
|
@@ -134,7 +134,7 @@ function earlierBlock(host,rows=[]){
|
|
|
134
134
|
}
|
|
135
135
|
// One form per check: the package manager's script form when there is one,
|
|
136
136
|
// since that is what the project's own instructions usually name.
|
|
137
|
-
const blockForms=(host,config,relative)=>host!=='claude'?[]:hookForms(config,relative).map(row=>({kind:row.kind,
|
|
137
|
+
const blockForms=(host,config,relative)=>host!=='claude'?[]:hookForms(config,relative).filter(row=>!row.part).map(row=>({kind:row.kind,
|
|
138
138
|
form:row.forms.find(form=>/^(?:npm run|pnpm|yarn) /.test(form))??row.forms.find(form=>!form.startsWith('redue '))??row.forms[0]}));
|
|
139
139
|
const priorBlocks=(host,relative)=>[false,true].map(legacy=>`\n\n<!-- REDUE:${host}:begin -->\n`+
|
|
140
140
|
`Before deciding whether configured verification needs repeating, use REDUE.\n`+
|
|
@@ -166,7 +166,7 @@ function knownBlock(host,relative,block){
|
|
|
166
166
|
function skillFor(relative,host,config){
|
|
167
167
|
// Double quotes work in POSIX shells, cmd and PowerShell for ordinary names.
|
|
168
168
|
const redue=redueFor(relative)??`redue --config ${JSON.stringify(relative)}`,status=`${redue} status --sync --json`;
|
|
169
|
-
const rows=host==='claude'?hookForms(config,relative):[];
|
|
169
|
+
const rows=host==='claude'?hookForms(config,relative).filter(row=>!row.part):[];
|
|
170
170
|
const label=row=>row.kind==='test'?(row.check==='test'?'Unit tests':`Unit tests \`${row.check}\``):
|
|
171
171
|
row.check==='typecheck'?'Typecheck':`Typecheck \`${row.check}\``;
|
|
172
172
|
const commands=rows.map(row=>`- ${label(row)}: `+
|