@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 +104 -0
- package/README.md +144 -86
- package/dist/cli.js +724 -0
- package/dist/index.d.ts +176 -0
- package/dist/index.js +576 -0
- package/dist/types/index.d.ts +176 -0
- package/dist/types/tsLanguageServiceFixer.d.ts +124 -0
- package/dist/types/validatorInProcess.d.ts +64 -0
- package/package.json +19 -16
- package/bin/tsfix.mjs +0 -49
- package/cli/run-stack.ts +0 -195
- package/src/index.ts +0 -202
- package/src/tsLanguageServiceFixer.ts +0 -486
- package/src/validatorInProcess.ts +0 -276
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
|
-
#
|
|
1
|
+
# tsfix
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
> Headless TypeScript error recovery — auto-resolve `TS2304`, `TS2305`, `TS2551`, `TS2552`, `TS2724` before they reach a human.
|
|
4
4
|
|
|
5
|
-
|
|
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
|
-
##
|
|
9
|
+
## Before / after
|
|
14
10
|
|
|
15
|
-
|
|
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
|
-
|
|
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
|
-
|
|
22
|
+
$ tsc --noEmit
|
|
23
|
+
$ # 0 errors
|
|
24
|
+
```
|
|
31
25
|
|
|
32
|
-
|
|
26
|
+
## 30-second cold start
|
|
33
27
|
|
|
28
|
+
```bash
|
|
29
|
+
cd your-broken-project
|
|
30
|
+
npx @shipispec/tsfix --workspace .
|
|
34
31
|
```
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
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
|
-
|
|
79
|
+
By design, tsfix only applies fixes that are **deterministic** and **non-structural**. It will refuse to:
|
|
72
80
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
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
|
-
##
|
|
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
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
>
|
|
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
|
-
|
|
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
|
-
|
|
109
|
-
|
|
151
|
+
error: this workspace has no TypeScript installed.
|
|
152
|
+
run: npm install --save-dev typescript
|
|
110
153
|
```
|
|
111
154
|
|
|
112
|
-
|
|
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
|
-
|
|
168
|
+
## License
|
|
115
169
|
|
|
116
|
-
|
|
170
|
+
MIT.
|
|
117
171
|
|
|
118
|
-
|
|
172
|
+
## See also
|
|
119
173
|
|
|
120
|
-
|
|
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.
|