@shipispec/tsfix 0.4.0 → 0.6.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 +126 -1
- package/README.md +65 -11
- package/dist/cli.js +999 -28
- package/dist/index.d.ts +24 -0
- package/dist/index.js +397 -34719
- package/dist/types/index.d.ts +24 -0
- package/dist/types/libraryMigrations.d.ts +57 -0
- package/dist/types/runMendLoop.d.ts +16 -1
- package/dist/types/stubAndContinue.d.ts +68 -0
- package/package.json +2 -3
package/dist/types/index.d.ts
CHANGED
|
@@ -44,6 +44,12 @@
|
|
|
44
44
|
* - `mendSingleFile` — single-LLM-call repair via Vercel AI SDK
|
|
45
45
|
* - `runMendLoop` — bounded retry with no-progress / regression detection
|
|
46
46
|
* - `parseEditBlocks` / `applyEditBlocks` — Aider-style SEARCH/REPLACE applier
|
|
47
|
+
*
|
|
48
|
+
* ## Layer 4 escape hatch (v0.5.0+)
|
|
49
|
+
*
|
|
50
|
+
* - `stubAndContinue` — insert `// @ts-expect-error - tsfix: ...` above
|
|
51
|
+
* unresolved error sites so the workspace compiles. Opt-in: set
|
|
52
|
+
* `stubOnFailure: true` on `runMendLoop`, or call directly.
|
|
47
53
|
*/
|
|
48
54
|
export { runInProcessTsc, isInProcessTscEnabled, resetInProcessTscCache } from "./validatorInProcess.js";
|
|
49
55
|
export type { InProcessTscOptions, InProcessTscResult } from "./validatorInProcess.js";
|
|
@@ -150,6 +156,20 @@ export interface MendContext {
|
|
|
150
156
|
priorTaskExports?: string;
|
|
151
157
|
/** Compact type signatures of installed npm dependencies (helps prevent API hallucination). */
|
|
152
158
|
installedTypes?: string;
|
|
159
|
+
/**
|
|
160
|
+
* Library-migration hints for installed deps whose breaking changes are
|
|
161
|
+
* known to mislead tsc's quick-fix. When non-empty, the Layer 2 prompt
|
|
162
|
+
* leads with these and the `taskDescription` is overridden to name them.
|
|
163
|
+
*
|
|
164
|
+
* Auto-populated by `runMendLoop` from `<workspaceRoot>/package.json` if
|
|
165
|
+
* the caller doesn't set it. Pass `[]` explicitly to opt out.
|
|
166
|
+
*
|
|
167
|
+
* See `libraryMigrations.ts` for the built-in registry.
|
|
168
|
+
*/
|
|
169
|
+
libraryMigrations?: Array<{
|
|
170
|
+
name: string;
|
|
171
|
+
hint: string;
|
|
172
|
+
}>;
|
|
153
173
|
}
|
|
154
174
|
/**
|
|
155
175
|
* Per-layer event for streaming telemetry across the validate → fix → mend
|
|
@@ -186,3 +206,7 @@ export { mendSingleFile } from "./mendAgent.js";
|
|
|
186
206
|
export type { MendSingleFileOptions, MendSingleFileResult, LLMCall } from "./mendAgent.js";
|
|
187
207
|
export { runMendLoop } from "./runMendLoop.js";
|
|
188
208
|
export type { RunMendLoopOptions, RunMendLoopResult, MendLoopIteration, StopReason, } from "./runMendLoop.js";
|
|
209
|
+
export { stubAndContinue } from "./stubAndContinue.js";
|
|
210
|
+
export type { StubAndContinueOptions, StubAndContinueResult, AppliedStub, SkippedStub, } from "./stubAndContinue.js";
|
|
211
|
+
export { BUILT_IN_LIBRARY_MIGRATIONS, detectLibraryMigrations, formatLibraryMigrationsBlock, formatLibraryMigrationsTaskDescription, } from "./libraryMigrations.js";
|
|
212
|
+
export type { LibraryMigrationHint } from "./libraryMigrations.js";
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Library breaking-change registry for Layer 2.
|
|
3
|
+
*
|
|
4
|
+
* When an installed dependency has a known migration whose correct fix
|
|
5
|
+
* differs from tsc's own quick-fix suggestion, this module injects a hint
|
|
6
|
+
* into the mend prompt so the LLM doesn't blindly follow tsc.
|
|
7
|
+
*
|
|
8
|
+
* Empirically grounded — each entry corresponds to a concrete bench case
|
|
9
|
+
* where, without the hint, both haiku-4-5 and sonnet-4-5 score 0/3
|
|
10
|
+
* functional+secure and follow tsc's misleading quick-fix; with the hint,
|
|
11
|
+
* both score 3/3 functional+secure on the same fixture.
|
|
12
|
+
*
|
|
13
|
+
* Scope: library MIGRATIONS where tsc's quick-fix is misleading or where
|
|
14
|
+
* the correct fix requires syntax not present in the source. NOT for
|
|
15
|
+
* general TS errors — those are tsfix's deterministic Layer 0/1 surface.
|
|
16
|
+
*/
|
|
17
|
+
export interface LibraryMigrationHint {
|
|
18
|
+
/** Display name, e.g., `"vite-plugin-svgr@^4.0.0"`. Populated by detect. */
|
|
19
|
+
name: string;
|
|
20
|
+
/** Instructional text injected into the Layer 2 system prompt. */
|
|
21
|
+
hint: string;
|
|
22
|
+
}
|
|
23
|
+
interface RegistryEntry {
|
|
24
|
+
match: {
|
|
25
|
+
name: string;
|
|
26
|
+
minMajor?: number;
|
|
27
|
+
maxMajor?: number;
|
|
28
|
+
};
|
|
29
|
+
hint: string;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Built-in registry. Keep entries SMALL — only patterns where we have
|
|
33
|
+
* empirical evidence that the model picks the wrong fix without the hint.
|
|
34
|
+
* Don't lard this up with general advice; that belongs in the system prompt.
|
|
35
|
+
*/
|
|
36
|
+
export declare const BUILT_IN_LIBRARY_MIGRATIONS: RegistryEntry[];
|
|
37
|
+
/**
|
|
38
|
+
* Read the workspace's package.json, walk dependencies + devDependencies,
|
|
39
|
+
* return the registry entries whose match rule fires.
|
|
40
|
+
*
|
|
41
|
+
* Best-effort: returns `[]` on any failure (missing package.json, parse
|
|
42
|
+
* error, etc.). Never throws.
|
|
43
|
+
*/
|
|
44
|
+
export declare function detectLibraryMigrations(workspaceRoot: string, registry?: RegistryEntry[]): LibraryMigrationHint[];
|
|
45
|
+
/**
|
|
46
|
+
* Format an array of hints into a prompt block. Empty input → empty string,
|
|
47
|
+
* caller can short-circuit.
|
|
48
|
+
*/
|
|
49
|
+
export declare function formatLibraryMigrationsBlock(hints: LibraryMigrationHint[]): string;
|
|
50
|
+
/**
|
|
51
|
+
* Build the one-line task description from a list of hints. Empty input
|
|
52
|
+
* → undefined. We've found that putting library names in the
|
|
53
|
+
* `taskDescription` (the prompt's headline framing) is dramatically more
|
|
54
|
+
* effective than burying the same content in the body.
|
|
55
|
+
*/
|
|
56
|
+
export declare function formatLibraryMigrationsTaskDescription(hints: LibraryMigrationHint[]): string | undefined;
|
|
57
|
+
export {};
|
|
@@ -24,6 +24,7 @@
|
|
|
24
24
|
*/
|
|
25
25
|
import type { Diagnostic, MendContext } from "./index.js";
|
|
26
26
|
import { type LLMCall } from "./mendAgent.js";
|
|
27
|
+
import { type AppliedStub } from "./stubAndContinue.js";
|
|
27
28
|
export interface RunMendLoopOptions {
|
|
28
29
|
context: MendContext;
|
|
29
30
|
llm: {
|
|
@@ -35,6 +36,13 @@ export interface RunMendLoopOptions {
|
|
|
35
36
|
maxIterations?: number;
|
|
36
37
|
/** Single dry-run pass — call LLM, parse, but don't write to disk. Default false. */
|
|
37
38
|
dryRun?: boolean;
|
|
39
|
+
/**
|
|
40
|
+
* When the loop exits with leftover errors (stopReason !== "fixed"),
|
|
41
|
+
* apply Layer 4 stub-and-continue: insert `// @ts-expect-error - tsfix: ...`
|
|
42
|
+
* comments above each unresolved error site so tsc exits 0. Opt-in.
|
|
43
|
+
* Default false. Ignored when `dryRun: true`.
|
|
44
|
+
*/
|
|
45
|
+
stubOnFailure?: boolean;
|
|
38
46
|
/** @internal — LLM call override for tests. */
|
|
39
47
|
_callLLM?: LLMCall;
|
|
40
48
|
}
|
|
@@ -50,7 +58,7 @@ export interface MendLoopIteration {
|
|
|
50
58
|
/** Raw LLM response for this iteration — useful for debugging failed patches. */
|
|
51
59
|
rawResponse: string;
|
|
52
60
|
}
|
|
53
|
-
export type StopReason = "noErrors" | "fixed" | "noProgress" | "regressed" | "maxIterations";
|
|
61
|
+
export type StopReason = "noErrors" | "fixed" | "noProgress" | "regressed" | "maxIterations" | "stubbed";
|
|
54
62
|
export interface RunMendLoopResult {
|
|
55
63
|
iterations: MendLoopIteration[];
|
|
56
64
|
diagnosticsBefore: Diagnostic[];
|
|
@@ -60,5 +68,12 @@ export interface RunMendLoopResult {
|
|
|
60
68
|
totalInputTokens: number;
|
|
61
69
|
totalOutputTokens: number;
|
|
62
70
|
totalLatencyMs: number;
|
|
71
|
+
/**
|
|
72
|
+
* Layer 4 stubs applied after the LLM loop terminated with leftover
|
|
73
|
+
* errors. Present only when `stubOnFailure: true` was set. Empty array
|
|
74
|
+
* means stubOnFailure ran but nothing was eligible (e.g. all errors
|
|
75
|
+
* were in .d.ts files).
|
|
76
|
+
*/
|
|
77
|
+
stubs?: AppliedStub[];
|
|
63
78
|
}
|
|
64
79
|
export declare function runMendLoop(opts: RunMendLoopOptions): Promise<RunMendLoopResult>;
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Layer 4 — stub-and-continue escape hatch.
|
|
3
|
+
*
|
|
4
|
+
* When Layer 0/1 abstains and Layer 2's `runMendLoop` returns with leftover
|
|
5
|
+
* diagnostics (stopReason `noProgress`, `regressed`, or `maxIterations`),
|
|
6
|
+
* Layer 4 inserts a `// @ts-expect-error` directive immediately above each
|
|
7
|
+
* unresolved error site so `tsc --noEmit` exits 0. Caller's pipeline
|
|
8
|
+
* unblocks; the developer reviews the directive at leisure.
|
|
9
|
+
*
|
|
10
|
+
* Why `@ts-expect-error` and not `@ts-ignore`:
|
|
11
|
+
* `@ts-expect-error` errors out if the next line has NO error — meaning
|
|
12
|
+
* stale directives self-destruct as soon as the underlying issue is
|
|
13
|
+
* fixed by other means. `@ts-ignore` is permissive and rots silently.
|
|
14
|
+
*
|
|
15
|
+
* Trust posture: Layer 4 is opt-in. The CLI default never reaches it.
|
|
16
|
+
* `runMendLoop` only invokes it when `stubOnFailure: true` is set.
|
|
17
|
+
*
|
|
18
|
+
* Idempotency: re-running on a workspace that already has stubs above the
|
|
19
|
+
* same error lines is a no-op. We detect the existing directive on the
|
|
20
|
+
* line above and skip.
|
|
21
|
+
*/
|
|
22
|
+
import type { Diagnostic } from "./index.js";
|
|
23
|
+
export interface StubAndContinueOptions {
|
|
24
|
+
/** Absolute path to the workspace (used for resolving / skipping node_modules). */
|
|
25
|
+
workspaceRoot: string;
|
|
26
|
+
/** Unresolved diagnostics (errors only — warnings/suggestions ignored). */
|
|
27
|
+
diagnostics: Diagnostic[];
|
|
28
|
+
/** Report what would be stubbed without writing. Default false. */
|
|
29
|
+
dryRun?: boolean;
|
|
30
|
+
logger?: {
|
|
31
|
+
info(msg: string): void;
|
|
32
|
+
warn(msg: string): void;
|
|
33
|
+
error(msg: string): void;
|
|
34
|
+
};
|
|
35
|
+
/** Override the comment marker (default: "tsfix"). */
|
|
36
|
+
stubMarker?: string;
|
|
37
|
+
/** Cap on message length included in the comment. Default 120. */
|
|
38
|
+
maxMessageLength?: number;
|
|
39
|
+
}
|
|
40
|
+
export interface AppliedStub {
|
|
41
|
+
/** Absolute path of the file edited. */
|
|
42
|
+
file: string;
|
|
43
|
+
/**
|
|
44
|
+
* 1-based line number tsc originally reported the error on (pre-stub).
|
|
45
|
+
* In the file *after* stubbing, the error code lives at `errorLine + 1`
|
|
46
|
+
* and the `@ts-expect-error` comment lives at `errorLine`.
|
|
47
|
+
*/
|
|
48
|
+
errorLine: number;
|
|
49
|
+
/** All TS codes on the error line, deduplicated and sorted. */
|
|
50
|
+
codes: string[];
|
|
51
|
+
/** The comment text actually written (without leading whitespace). */
|
|
52
|
+
commentText: string;
|
|
53
|
+
}
|
|
54
|
+
export interface SkippedStub {
|
|
55
|
+
file: string;
|
|
56
|
+
line: number;
|
|
57
|
+
codes: string[];
|
|
58
|
+
reason: "node_modules" | "declaration_file" | "file_not_found" | "already_stubbed" | "file_too_short";
|
|
59
|
+
}
|
|
60
|
+
export interface StubAndContinueResult {
|
|
61
|
+
stubsApplied: AppliedStub[];
|
|
62
|
+
skipped: SkippedStub[];
|
|
63
|
+
filesEdited: string[];
|
|
64
|
+
diagnosticsBefore: number;
|
|
65
|
+
/** Diagnostics still on disk after stubs were applied (excludes the stubbed sites). */
|
|
66
|
+
diagnosticsAfter: number;
|
|
67
|
+
}
|
|
68
|
+
export declare function stubAndContinue(opts: StubAndContinueOptions): StubAndContinueResult;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shipispec/tsfix",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "TypeScript error-recovery for LLM-generated code. Layer 0/1 deterministic auto-fix via the TS Language Service + Layer 2 LLM mend (Vercel AI SDK + Anthropic) in one package.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"typescript",
|
|
@@ -52,8 +52,7 @@
|
|
|
52
52
|
"build": "node scripts/build.mjs",
|
|
53
53
|
"matrix": "node scripts/run-matrix.mjs",
|
|
54
54
|
"capture": "node scripts/capture-fixture.mjs",
|
|
55
|
-
"
|
|
56
|
-
"generate-fixtures": "node scripts/generate-fixtures.mjs",
|
|
55
|
+
"generate-fixtures": "tsx scripts/generate-fixtures.mjs",
|
|
57
56
|
"prepack": "npm run build",
|
|
58
57
|
"setup-fixtures": "node -e \"require('fs').existsSync('fixtures/_shared/node_modules')||require('child_process').execSync('npm install --prefix fixtures/_shared',{stdio:'inherit'})\"",
|
|
59
58
|
"prebenchmark": "npm run setup-fixtures",
|