@shipispec/tsfix 0.1.0 → 0.3.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 ADDED
@@ -0,0 +1,104 @@
1
+ # Changelog
2
+
3
+ All notable changes to `@shipispec/tsfix` are documented here. Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/); versioning follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
4
+
5
+ ## [Unreleased]
6
+
7
+ ## [0.3.0] - 2026-05-07
8
+
9
+ Phase 2 contract release. **Establishes the public types `MendContext`, `LayerEvent`, and `Diagnostic` so a downstream LLM-mend package (e.g. `@shipispec/tsmend`) can consume tsfix's output without redefining the shape.** No behavior changes; purely additive types. Also collapses several dev-only improvements that landed since v0.2.0 into a single release.
10
+
11
+ ### Added
12
+ - **`MendContext` interface** — public type defining the input contract for a Layer 2–4 LLM-mend agent. Required fields: `workspaceRoot`, `diagnostics`, `erroredFiles`. Optional fields: `taskDescription`, `featureSpecText`, `acceptanceCriteria`, `siblingTasks`, `priorTaskExports`, `installedTypes`.
13
+ - **`LayerEvent` interface** — per-layer event shape for streaming telemetry. Designed for an `onLayerEvent` callback in a future minor release; the type is published now so downstream callers can construct events themselves.
14
+ - **`Diagnostic` type alias** — public re-export of `InProcessTscResult["diagnostics"][number]`. Convenience for consumers building `MendContext`.
15
+ - **Project-shape matrix** (`scripts/run-matrix.mjs`, `npm run matrix`) — pre-publish gate that builds the local tarball and exercises it cold against 6 distinct project shapes: `monorepo-refs` (project references — pinned as a documented limitation), `next-app` (App Router, `paths` alias, `jsx: preserve`), `plain-ts-bundler` (esnext + bundler), `plain-ts-commonjs` (legacy CJS + ES2015 + node10), `plain-ts-nodenext` (nodenext resolution), `react-vite` (TSX + `jsx: react-jsx`). 6/6 pass. Dev-only — not shipped in the tarball.
16
+ - **Capture script** (`scripts/capture-fixture.mjs`, `npm run capture`) — Phase 3b tooling for snapshotting real broken workspaces into `fixtures/real-<name>/`. Awaits first real failure to produce fixtures.
17
+ - **GitHub Actions CI** (`.github/workflows/test.yml`) — runs check-types, vitest, benchmark, and the matrix on every PR + main push.
18
+
19
+ ### Changed
20
+ - **Repository moved.** `tsc-defense-stack/` was extracted from the `spectoship-meta` monorepo into its own repository at <https://github.com/owgreen-dev/tsfix>. All `repository.url`, `homepage`, `bugs.url` fields point at the new repo. Internal git history pre-2026-05-06 lives in the original monorepo; the CHANGELOG narrates v0.1.0–v0.2.0 in detail.
21
+ - **Public README rewritten** for an OSS audience — tagline, before/after, 30-second cold start, four-layer model, library API, trust model, contributing protocol. Previous internal-orientation README preserved at `docs/internal-orientation.md`.
22
+
23
+ ### Engines
24
+ - Node `>=20.9.0` (unchanged)
25
+ - TypeScript `>=5.0.0` peer (unchanged)
26
+
27
+ ## [0.2.0] - 2026-05-04
28
+
29
+ Phase 1a complete. **Plain Node consumers can now `import` the package without a TypeScript loader, and `npx @shipispec/tsfix` works cold.** Folds in everything that was queued for v0.1.1 (which was never published — its commit is now part of v0.2.0).
30
+
31
+ ### Added
32
+ - **esbuild bundle** in `dist/`. Three artifacts: `dist/index.js` (library, ESM bundle), `dist/cli.js` (CLI, ESM with shebang, executable), `dist/index.d.ts` (public type declarations from `tsc --emitDeclarationOnly`). Per-file `.d.ts` files in `dist/types/` for callers wanting subpath types.
33
+ - `npm run build` → `node scripts/build.mjs`. Also runs as `prepublishOnly` so `npm publish` always ships fresh `dist/`.
34
+ - **`--dry-run` flag** on the CLI and `dryRun` option on `runValidationLoop` and `runLSPFixerPass`. Runs the full LSP fix loop in memory; reports what *would* be edited; no disk writes. Resolves the documented footgun where pointing tsfix at a fixture irreversibly mutated it. (Audit M-E4.)
35
+ - **Trust model section** in README: `tsfix` loads `typescript` from your workspace's `node_modules`. Only run on workspaces you trust. (Audit M-S1.)
36
+ - **Troubleshooting section** in README covering the most likely user errors (`ERR_MODULE_NOT_FOUND` for typescript; missing `tsconfig.json`).
37
+ - **Dev-vs-consumer guidance** in README. (Audit M-E3.)
38
+ - **3 dryRun unit tests** in `src/dryRun.test.ts`.
39
+
40
+ ### Changed
41
+ - **Package shape**: `main`/`types`/`exports` now point at `dist/`, not `src/`. `bin.tsfix` points at `dist/cli.js` directly. `files` array ships `dist/` only (no more `src/`, `cli/`, `bin/` in tarball). Tarball: 10 files, 16.8 KB packed.
42
+ - **Removed `bin/tsfix.mjs` wrapper** — replaced by the bundled `dist/cli.js`. The wrapper was a Phase 0c bridge that depended on local `tsx`; the bundle drops that dependency.
43
+ - **Dropped `./validation` and `./lsp-fixer` subpath exports** — unused; the only consumer (spectoship2 shims) imports from the main entry. Easy to re-add if needed.
44
+ - README CLI section rewritten to reflect Phase 0c standalone install (no longer references the old `cd spectoship2 && tsx ../tsc-defense-stack/...` flow).
45
+
46
+ ### Fixed
47
+ - **Plain Node `import { runValidationLoop } from "@shipispec/tsfix"` now works.** Was previously blocked by Node 22+ refusing to type-strip `.ts` files in `node_modules` (audit H-E1). Verified end-to-end via `npm install` from tarball + `node use-as-library.mjs`.
48
+ - **`npx @shipispec/tsfix --workspace ./project` now works cold.** Was previously blocked by the bin wrapper requiring `tsx` from the package's own `node_modules` (audit M-E2).
49
+ - `bin/tsfix.mjs` error message no longer references the old `tsc-defense` name (n/a in 0.2.0 — wrapper deleted entirely; audit M-E1).
50
+ - `cli/run-stack.ts` no longer ships with executable permission bits (the bundled `dist/cli.js` does, which is correct since it's the actual entry; audit L-S2).
51
+
52
+ ### Engines
53
+ - Node `>=20.9.0` (unchanged)
54
+ - TypeScript `>=5.0.0` peer (unchanged; npm 7+ auto-installs)
55
+
56
+ ### Outstanding from audit (deferred)
57
+ - L-S1 (npm `--provenance` for supply-chain attestation) — Phase 1b CI publish.
58
+
59
+ ## [0.1.1] - 2026-05-04
60
+
61
+ Patch release addressing the medium-severity findings from the post-publish audit (`docs/audit-2026-05-04.md`). No API breaks; consumers can upgrade with `npm install @shipispec/tsfix@latest`.
62
+
63
+ ### Added
64
+ - **`--dry-run` flag** on the CLI and a corresponding `dryRun` option on `runValidationLoop` and `runLSPFixerPass`. Runs the full LSP fix loop in memory and reports what *would* be edited, but does not write to disk. Resolves the documented footgun where running `tsfix` against a fixture directory irreversibly mutated the broken code. (Audit M-E4.)
65
+ - **Trust model section** in README: explicit disclosure that `tsfix` loads `typescript` from the workspace's `node_modules`, with the standard "only run on workspaces you trust" warning. (Audit M-S1.)
66
+ - **Dev-vs-consumer guidance** in README: clarifies that `npm scripts` shipped in the published `package.json` (benchmark/test/setup-fixtures) are for contributors only — consumer-side `node_modules/@shipispec/tsfix/` doesn't have `tsx`/`vitest`/`fixtures/`. (Audit M-E3.)
67
+
68
+ ### Fixed
69
+ - `bin/tsfix.mjs` error message no longer references the old `tsc-defense` name; now describes the correct `tsfix` flow when `tsx` cannot be resolved. (Audit M-E1.)
70
+ - `cli/run-stack.ts` no longer ships with executable permission bits (was `-rwxr-xr-x`, now `-rw-r--r--`). The file is loaded by `tsx`, never run directly. (Audit L-S2.)
71
+
72
+ ### Changed
73
+ - README CLI section rewritten to reflect Phase 0c standalone install (no longer references the old monorepo `cd spectoship2 && tsx ../tsc-defense-stack/...` flow).
74
+
75
+ ## [0.1.0] - 2026-05-04
76
+
77
+ Initial public release. **Layers 0–1 only** (deterministic detection + auto-fix). LLM-driven mend layers stay in `spectoship2/` until v0.2.
78
+
79
+ ### Added
80
+ - **`runValidationLoop(opts)`** — full deterministic loop (validate → auto-fix → re-validate). Recommended entry point.
81
+ - **`runInProcessTsc(opts)`** — in-process `tsc --noEmit` returning structured diagnostics. No subprocess spawn, no Node 23 startup-pause issue. Workspace lib-path override (uses the workspace's `node_modules/typescript` so globals resolve under esbuild bundling).
82
+ - **`runLSPFixerPass(opts)`** — Layer 0 deterministic auto-fixer using `ts.LanguageService.getCodeFixesAtPosition`. Strictly opt-in by error code and fix name:
83
+ - `SAFE_FIXABLE_CODES`: `TS2304`, `TS2305`, `TS2551`, `TS2552`, `TS2724`
84
+ - `SAFE_FIX_NAMES`: `import`, `fixImport`, `spelling`, `fixSpelling`
85
+ - 5-iteration cap with signature-set progress check (stops when the `(file, start, code)` set repeats)
86
+ - Multi-fix equivalence check abstains when candidate fixes produce different edits
87
+ - **`discoverTsFiles(workspaceRoot)`** — file-discovery helper. Includes `.ts`/`.tsx`; excludes `.d.ts` and `node_modules`/`.next`/`dist`/`build`/`out`/`coverage`/`.git`.
88
+ - **CLI** (`tsfix --workspace <path>`, after `npm link`). Flags: `--json`, `--no-lsp`, `--verbose`, `--files <comma-list>`, `--help`. Exit 0 = clean, 1 = errors remain, 2 = bad args.
89
+ - **MIT license**, no runtime deps except peer `typescript >=5.0.0`.
90
+
91
+ ### Known limitations
92
+ - **`npx @shipispec/tsfix ./project`** does not work for cold-start. The bin wrapper requires `tsx` to be resolvable from the package's own `node_modules`. Use `npm install @shipispec/tsfix && npm link` for now. Phase 1a (esbuild bundle) addresses this.
93
+ - **`export { X } from "./mod"`** — TS LanguageService returns zero code-fixes for typos in this syntactic position. Documented in `fixtures/synthetic-cross-file-typo-ts2305/`.
94
+ - **Footgun:** the CLI mutates files in place with no snapshot/restore. Don't point it at the package's own `fixtures/` directories during dev — use `npm run benchmark` instead, which snapshots and restores.
95
+
96
+ ### Engines
97
+ - Node `>=20.9.0` (matches VS Code Extension Host runtime)
98
+ - TypeScript `>=5.0.0` (peer dep, must be installed in the consuming workspace)
99
+
100
+ [Unreleased]: https://github.com/owgreen-dev/tsfix/compare/v0.3.0...HEAD
101
+ [0.3.0]: https://github.com/owgreen-dev/tsfix/compare/v0.2.0...v0.3.0
102
+ [0.2.0]: https://github.com/owgreen-dev/tsfix/compare/v0.1.1...v0.2.0
103
+ [0.1.1]: https://github.com/owgreen-dev/tsfix/compare/v0.1.0...v0.1.1
104
+ [0.1.0]: https://github.com/owgreen-dev/tsfix/releases/tag/v0.1.0
package/README.md CHANGED
@@ -1,120 +1,178 @@
1
- # TSC Defense Stack — `@shipispec/tsfix`
1
+ # tsfix
2
2
 
3
- Standalone npm package implementing **Layers 0–1** of the TypeScript error-recovery stack: in-process tsc validation + deterministic LSP auto-fix. Layers 2–4 (LLM mend) currently live in `spectoship2/src/pipeline/` and will move to a sister package `@shipispec/tsmend` per the roadmap.
3
+ > Headless TypeScript error recovery auto-resolve `TS2304`, `TS2305`, `TS2551`, `TS2552`, `TS2724` before they reach a human.
4
4
 
5
- Read first:
6
- - `STATUS.md` — what's working, what's planned, current gaps
7
- - `ARCHITECTURE.md` — why the package is shaped the way it is
8
- - `tsc-defense-roadmap.md` — phased plan with open decisions
9
- - `CLAUDE.md` — working principles (small allowlist, fixture-pinned trust model)
5
+ `@shipispec/tsfix` borrows the same TypeScript Language Service that powers VS Code's "Quick Fix" lightbulb and runs it as a CLI. Point it at a workspace, it fixes typos, missing imports, and did-you-mean errors deterministically — no LLM, no calls home, no config.
10
6
 
11
- ---
7
+ Built for the case where you've just generated a few hundred files of TypeScript with an LLM and `tsc --noEmit` is screaming at you.
12
8
 
13
- ## Source-of-truth map
9
+ ## Before / after
14
10
 
15
- This package owns its TypeScript-error handling code outright. The shims in `spectoship2/` re-export from here, not the reverse.
11
+ ```
12
+ $ tsc --noEmit
13
+ src/api.ts:5:2 - error TS2552: Cannot find name 'consol'. Did you mean 'console'?
14
+ src/api.ts:8:5 - error TS2305: Module '"react"' has no exported member 'ueState'.
15
+ src/api.ts:12:14 - error TS2551: Property 'lenght' does not exist on type 'string[]'. Did you mean 'length'?
16
16
 
17
- | Path | Role |
18
- |---|---|
19
- | `src/index.ts` | Public API (`runValidationLoop`, `runInProcessTsc`, `runLSPFixerPass`, `discoverTsFiles`) |
20
- | `src/validatorInProcess.ts` | In-process tsc with lib-path workaround (Layer 0) |
21
- | `src/tsLanguageServiceFixer.ts` | LSP auto-fixer using `getCodeFixesAtPosition` (Layer 1) |
22
- | `cli/run-stack.ts` | CLI: `tsx cli/run-stack.ts --workspace <path>` |
23
- | `benchmark/run-benchmark.ts` | Fixture harness (auto-discovers `fixtures/*/`) |
24
- | `fixtures/` | 14 hand-authored synthetic fixtures across 3 tiers |
25
- | `spectoship2/src/pipeline/validatorInProcess.ts` | **Re-export shim** → `@shipispec/tsfix` |
26
- | `spectoship2/src/pipeline/tsLanguageServiceFixer.ts` | **Re-export shim** → `@shipispec/tsfix` |
17
+ Found 3 errors in 1 file.
27
18
 
28
- ---
19
+ $ npx @shipispec/tsfix --workspace .
20
+ [ts-lsp-fixer] applied 3 fixes across 1 file
29
21
 
30
- ## How the layers fit together
22
+ $ tsc --noEmit
23
+ $ # 0 errors
24
+ ```
31
25
 
32
- Per `ARCHITECTURE.md`, a TSC error has up to four chances to die before reaching a user. Layers -1 (prevention) and 2-4 (mend) live outside this package.
26
+ ## 30-second cold start
33
27
 
28
+ ```bash
29
+ cd your-broken-project
30
+ npx @shipispec/tsfix --workspace .
34
31
  ```
35
- ┌─────────────────────────────────────────────────┐
36
- Layer -1: PREVENTION (in spectoship2/, not here)│
37
- │ packageGotchas, installedExports, priorExports│
38
- │ codeGenPrompts (rules injected into prompt) │
39
- └────────────────────┬────────────────────────────┘
40
- files written to disk
41
-
42
- ┌────── @shipispec/tsfix ───────┴──────────────────────────┐
43
- │ │
44
- │ ┌─────────────────────────────────────────────┐ │
45
- │ │ Layer 0: src/validatorInProcess.ts │ │
46
- │ │ in-process tsc → structured diagnostics │ │
47
- │ │ workspace lib-path override │ │
48
- │ └─────────────────────┬───────────────────────┘ │
49
- │ │ if errors │
50
- │ ▼ │
51
- │ ┌─────────────────────────────────────────────┐ │
52
- │ │ Layer 1: src/tsLanguageServiceFixer.ts │ │
53
- │ │ getCodeFixesAtPosition (5 SAFE codes) │ │
54
- │ │ signature-set progress check, max 5 iters │ │
55
- │ └─────────────────────┬───────────────────────┘ │
56
- │ │ re-validate; if errors remain │
57
- └─────────────────────────┼─────────────────────────────────────────┘
58
-
59
- ┌─────────────────────────────────┐
60
- │ Layers 2-4: LLM MEND │
61
- │ mendAgent / mendArchitect / │
62
- │ multiFileMend / repairAgent │
63
- │ (in spectoship2/, not here; │
64
- │ moves to @shipispec/tsmend│
65
- │ in v0.2 per roadmap) │
66
- └─────────────────────────────────┘
32
+
33
+ No config file. Exit code conventions:
34
+
35
+ | Code | Meaning |
36
+ |---|---|
37
+ | 0 | Workspace is clean |
38
+ | 1 | Errors remain (printed to stderr) |
39
+ | 2 | Bad arguments / harness error |
40
+
41
+ Preview what *would* change without writing to disk:
42
+
43
+ ```bash
44
+ npx @shipispec/tsfix --workspace . --dry-run
67
45
  ```
68
46
 
69
- ---
47
+ Machine-readable output for piping into other tools:
48
+
49
+ ```bash
50
+ npx @shipispec/tsfix --workspace . --json
51
+ ```
52
+
53
+ ### All flags
54
+
55
+ | Flag | Meaning |
56
+ |---|---|
57
+ | `--workspace <path>` | Required. Directory containing your `tsconfig.json`. |
58
+ | `--dry-run` | Run the fixer in memory, report counts, write nothing. |
59
+ | `--no-lsp` | Validate only — skip auto-fix. |
60
+ | `--files <a.ts,b.ts>` | Restrict fixing to a comma-separated list. |
61
+ | `--json` | Machine-readable output. |
62
+ | `--verbose` | Per-fix logging. |
63
+ | `--help` | Print usage. |
64
+
65
+ ## What it fixes
66
+
67
+ | TS code | Meaning | What tsfix does |
68
+ |---|---|---|
69
+ | `TS2304` | Cannot find name | Auto-imports |
70
+ | `TS2305` | Module has no exported member | Did-you-mean rename |
71
+ | `TS2551` | Property does not exist on T, did you mean Y | Spelling fix |
72
+ | `TS2552` | Cannot find name, did you mean Y | Spelling fix |
73
+ | `TS2724` | Module member did-you-mean | Import rename |
74
+
75
+ Against a 14-fixture benchmark spanning typos, did-you-mean cases, multi-file ripples, and 4 API-drift scenarios: **14/14 fixtures pass and 14/25 errors are auto-fixed (56%).** The remaining errors are intentionally outside Layer 0's scope (see below).
76
+
77
+ ## What it does *not* fix
70
78
 
71
- ## What to read first
79
+ By design, tsfix only applies fixes that are **deterministic** and **non-structural**. It will refuse to:
72
80
 
73
- 1. **`STATUS.md`** current state, fixture catalog, recent fixes
74
- 2. **`ARCHITECTURE.md`** why the package is shaped the way it is (12 sections)
75
- 3. **`tsc-defense-roadmap.md`** phased plan with open decisions
76
- 4. **`src/index.ts`** public API entry point (`runValidationLoop`)
77
- 5. **`src/tsLanguageServiceFixer.ts`** — Layer 1 fixer; understand `SAFE_FIXABLE_CODES`, the signature-set progress check, and the iteration loop
78
- 6. **`src/validatorInProcess.ts`** — in-process tsc with the lib-path workaround that makes the package work inside the VS Code Extension Host
81
+ - Add or remove function declarations
82
+ - Insert type annotations or change types
83
+ - Modify control flow (`await` insertions, async propagation)
84
+ - Rewrite JSX trees
85
+ - Add object-literal stub properties
79
86
 
80
- ---
87
+ The internal allowlist is two-layered: error codes (`SAFE_FIXABLE_CODES`) and Quick Fix names (`SAFE_FIX_NAMES = ['import', 'fixImport', 'spelling', 'fixSpelling']`). When the language service offers anything outside that allowlist, tsfix abstains and surfaces the error in the result so a higher layer (LLM, human) can pick it up.
81
88
 
82
- ## Standalone harness
89
+ ## The four-layer model
90
+
91
+ tsfix is **Layer 0–1** of a larger error-recovery stack. The other layers are LLM-driven and live elsewhere (in your own code, or in companion packages):
83
92
 
84
93
  ```
85
- cli/run-stack.ts # CLI: run stack on any workspace
86
- benchmark/run-benchmark.ts # benchmark across all fixtures
87
- fixtures/ # 14 hand-authored synthetic workspaces
88
- _shared/ # shared node_modules symlink target
89
- clean-baseline/ # regression check (must stay green)
90
- synthetic-*/ # 9 LSP-behavior fixtures (positive + negative)
91
- api-drift-*/ # 4 version-drift fixtures (Zod 3 vs 4, React 18 vs 19, etc.)
94
+ Layer 0 Prevention (prompt rules, exported-API injection — your problem)
95
+ Layer 1 tsfix (this package: deterministic auto-fix)
96
+ ─────────────────────────────────────────────────────────────────────────
97
+ Layer 2 Single-file LLM mend (architect + editor split)
98
+ Layer 3 — Multi-file LLM mend (blast-radius search/replace)
99
+ Layer 4 — Stub-and-continue (escape hatch)
92
100
  ```
93
101
 
94
- ### Run the CLI
102
+ The bet: roughly half of TypeScript errors in LLM output are deterministically fixable. By catching them in Layer 1, you dodge the LLM tax (latency, cost, nondeterminism) on the easy half.
95
103
 
104
+ ## Library API
105
+
106
+ ```typescript
107
+ import { runValidationLoop } from '@shipispec/tsfix';
108
+
109
+ const result = runValidationLoop({
110
+ workspaceRoot: '/path/to/your/project',
111
+ // Optional:
112
+ // targetFiles: ['src/api.ts'],
113
+ // dryRun: true,
114
+ // logger: { info: console.log, warn: console.warn, error: console.error },
115
+ });
116
+
117
+ result.errorsBefore; // number
118
+ result.errorsAfter; // number
119
+ result.lspFixer.fixesApplied; // number
120
+ result.lspFixer.filesEdited; // string[]
121
+ result.passed; // boolean — true if errorsAfter === 0
96
122
  ```
97
- cd /Users/ogg/Documents/microservices/Meta/spectoship2
98
- ./node_modules/.bin/tsx ../tsc-defense-stack/cli/run-stack.ts --workspace <path>
99
- ```
100
123
 
101
- Flags: `--json`, `--no-lsp`, `--verbose`, `--files <comma-list>`. Exit 0 = clean, 1 = errors remain, 2 = bad args.
124
+ Other exports:
125
+
126
+ - `runInProcessTsc(opts)` — validation only, no fixer. Returns structured diagnostics.
127
+ - `runLSPFixerPass(opts)` — Layer 0 fixer alone, no validation loop wrapper.
128
+ - `discoverTsFiles(workspaceRoot)` — file-walking helper. Skips `node_modules`, `.next`, `dist`, `build`, `out`, `coverage`, `.git`.
129
+
130
+ ## Trust model
131
+
132
+ tsfix loads `typescript` from your workspace's `node_modules` — it does **not** bundle its own. This is intentional: it ensures the fixer behaves identically to the `tsc` your project actually compiles with.
102
133
 
103
- > Note: per Phase 0c of `tsc-defense-roadmap.md`, this package will gain its own local `node_modules` so commands run from inside the package directory. Today it shares `spectoship2/node_modules`.
134
+ > **Run tsfix only on workspaces you trust.** Loading `typescript` from an attacker-controlled `node_modules` is equivalent to running `node_modules/.bin/tsc` against it.
104
135
 
105
- ### Run the benchmark
136
+ Other surface:
137
+
138
+ - No network calls.
139
+ - No telemetry.
140
+ - No background processes.
141
+ - No config files written or modified outside `--workspace`.
142
+
143
+ ## Engines
144
+
145
+ - Node `>=20.9.0`
146
+ - TypeScript `>=5.0.0` (peer dep — must be installed in your workspace)
147
+
148
+ If your workspace has no `node_modules/typescript`, tsfix will fail with a clear error:
106
149
 
107
150
  ```
108
- cd /Users/ogg/Documents/microservices/Meta/spectoship2
109
- ./node_modules/.bin/tsx ../tsc-defense-stack/benchmark/run-benchmark.ts
151
+ error: this workspace has no TypeScript installed.
152
+ run: npm install --save-dev typescript
110
153
  ```
111
154
 
112
- `--fixture <name>` to run a single fixture in isolation.
155
+ ## Contributing
156
+
157
+ The contract for adding a fix:
158
+
159
+ 1. **Probe** — write a tiny test workspace with the exact error you want fixable. Drop it under `fixtures/<descriptive-name>/` with an `expected.json` declaring `errorsBefore`, `errorsAfterMax`, `lspFixesAppliedMin/Max`, and `mustPass`.
160
+ 2. **Verify** — run `npm run benchmark -- --fixture <name>` and inspect what the language service offers (the `fix.fixName` field).
161
+ 3. **Allowlist change** — if `fixName` is unsafe (`fixMissingFunctionDeclaration`, `addMissingPropertyAndOptional`, etc.), document why we don't trust it. Otherwise, add the error code to `SAFE_FIXABLE_CODES` and the fix name to `SAFE_FIX_NAMES` in `src/tsLanguageServiceFixer.ts`.
162
+ 4. **Lock it in** — confirm all existing fixtures still pass (`npm run benchmark`). Open a PR.
163
+
164
+ Each new code/fix-name pair gets its own fixture. We don't trust the language service blindly — we trust it under specific, pinned conditions.
165
+
166
+ `npm run matrix` runs the same package against 6 distinct project shapes (Next.js, Vite + React, plain `nodenext`, plain `bundler`, plain CommonJS, monorepo with project references). It builds the local tarball and exercises it cold; pre-publish gate.
113
167
 
114
- ### Current baseline
168
+ ## License
115
169
 
116
- **14/14 synthetic fixtures pass. LSP fixer auto-resolves 14/25 errors (56%).** The remaining errors are intentional non-fixes — TS7006 implicit-any, TS2741 missing prop, API-drift errors that need the mend layer. See `STATUS.md` § Fixture catalog for the full list.
170
+ MIT.
117
171
 
118
- ### Capturing real-failure fixtures
172
+ ## See also
119
173
 
120
- Phase 3b in the roadmap. When a real spec-pipeline run produces a TSC error Layer 0-1 doesn't fix, snapshot the broken `.ts(x)` files into `fixtures/real-<timestamp>-<hash>/` with an `expected.json`. The fixture set then grows from production failures, not just synthetic ones.
174
+ - `CHANGELOG.md` release notes per version.
175
+ - `ARCHITECTURE.md` — internal design rationale (the four-layer model, the workspace lib-path workaround).
176
+ - `STATUS.md` — current snapshot, gaps, and roadmap state.
177
+ - `tsc-defense-roadmap.md` — phased plan.
178
+ - `docs/internal-orientation.md` — the original SpecToShip-context README, kept for contributors who want the design history.