@shipispec/tsfix 0.5.0 → 0.6.1
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 +109 -1
- package/README.md +65 -11
- package/dist/cli.js +387 -74
- package/dist/index.d.ts +101 -1
- package/dist/index.js +363 -46
- package/dist/types/index.d.ts +101 -1
- package/dist/types/libraryMigrations.d.ts +57 -0
- package/dist/types/mendAgent.d.ts +14 -1
- package/dist/types/runMendLoop.d.ts +10 -3
- package/dist/types/tsLanguageServiceFixer.d.ts +7 -0
- package/package.json +3 -1
package/dist/types/index.d.ts
CHANGED
|
@@ -82,6 +82,12 @@ export interface ValidationLoopOptions {
|
|
|
82
82
|
dryRun?: boolean;
|
|
83
83
|
/** Default: a no-op logger. Pass your own to capture layer events. */
|
|
84
84
|
logger?: Logger;
|
|
85
|
+
/**
|
|
86
|
+
* Per-error telemetry callback for Layer 1 (LSP fixer). Fires once per
|
|
87
|
+
* fixable error with `{layer: 1, errorCode, fixed, latencyMs, ts}`. Optional;
|
|
88
|
+
* undefined callback costs nothing. See `LayerEvent`.
|
|
89
|
+
*/
|
|
90
|
+
onLayerEvent?: (event: LayerEvent) => void;
|
|
85
91
|
}
|
|
86
92
|
export interface ValidationLoopResult {
|
|
87
93
|
passed: boolean;
|
|
@@ -156,6 +162,20 @@ export interface MendContext {
|
|
|
156
162
|
priorTaskExports?: string;
|
|
157
163
|
/** Compact type signatures of installed npm dependencies (helps prevent API hallucination). */
|
|
158
164
|
installedTypes?: string;
|
|
165
|
+
/**
|
|
166
|
+
* Library-migration hints for installed deps whose breaking changes are
|
|
167
|
+
* known to mislead tsc's quick-fix. When non-empty, the Layer 2 prompt
|
|
168
|
+
* leads with these and the `taskDescription` is overridden to name them.
|
|
169
|
+
*
|
|
170
|
+
* Auto-populated by `runMendLoop` from `<workspaceRoot>/package.json` if
|
|
171
|
+
* the caller doesn't set it. Pass `[]` explicitly to opt out.
|
|
172
|
+
*
|
|
173
|
+
* See `libraryMigrations.ts` for the built-in registry.
|
|
174
|
+
*/
|
|
175
|
+
libraryMigrations?: Array<{
|
|
176
|
+
name: string;
|
|
177
|
+
hint: string;
|
|
178
|
+
}>;
|
|
159
179
|
}
|
|
160
180
|
/**
|
|
161
181
|
* Per-layer event for streaming telemetry across the validate → fix → mend
|
|
@@ -189,8 +209,88 @@ export type { TypeContextOptions, TypeContext } from "./typeContext.js";
|
|
|
189
209
|
export { parseEditBlocks, applySingleBlock, applyEditBlocks } from "./applyEditBlock.js";
|
|
190
210
|
export type { EditBlock, ApplyEditBlocksOptions, ApplyResult, SingleBlockResult, } from "./applyEditBlock.js";
|
|
191
211
|
export { mendSingleFile } from "./mendAgent.js";
|
|
192
|
-
export type { MendSingleFileOptions, MendSingleFileResult, LLMCall } from "./mendAgent.js";
|
|
212
|
+
export type { MendSingleFileOptions, MendSingleFileResult, LLMCall, LLMProvider, } from "./mendAgent.js";
|
|
193
213
|
export { runMendLoop } from "./runMendLoop.js";
|
|
194
214
|
export type { RunMendLoopOptions, RunMendLoopResult, MendLoopIteration, StopReason, } from "./runMendLoop.js";
|
|
195
215
|
export { stubAndContinue } from "./stubAndContinue.js";
|
|
196
216
|
export type { StubAndContinueOptions, StubAndContinueResult, AppliedStub, SkippedStub, } from "./stubAndContinue.js";
|
|
217
|
+
export { BUILT_IN_LIBRARY_MIGRATIONS, detectLibraryMigrations, formatLibraryMigrationsBlock, formatLibraryMigrationsTaskDescription, } from "./libraryMigrations.js";
|
|
218
|
+
export type { LibraryMigrationHint } from "./libraryMigrations.js";
|
|
219
|
+
import { type RunMendLoopResult } from "./runMendLoop.js";
|
|
220
|
+
import type { AppliedStub } from "./stubAndContinue.js";
|
|
221
|
+
import type { LLMProvider } from "./mendAgent.js";
|
|
222
|
+
export interface RunFullStackOptions {
|
|
223
|
+
/** Absolute path to the workspace (must contain `tsconfig.json`). */
|
|
224
|
+
workspaceRoot: string;
|
|
225
|
+
/** Files to scope to. If omitted, all `.ts`/`.tsx` files under workspaceRoot. */
|
|
226
|
+
targetFiles?: string[];
|
|
227
|
+
/** Skip Layer 0/1 LSP auto-fixer. Default false. */
|
|
228
|
+
skipLSPFixer?: boolean;
|
|
229
|
+
/**
|
|
230
|
+
* Layer 2 config. If omitted, the loop stops after Layer 0/1 (matches
|
|
231
|
+
* `runValidationLoop` behavior — no LLM calls).
|
|
232
|
+
*/
|
|
233
|
+
llm?: {
|
|
234
|
+
provider: LLMProvider;
|
|
235
|
+
model: string;
|
|
236
|
+
apiKey: string;
|
|
237
|
+
maxIterations?: number;
|
|
238
|
+
};
|
|
239
|
+
/**
|
|
240
|
+
* After Layer 2 (if any), insert `// @ts-expect-error - tsfix: …` above
|
|
241
|
+
* each unresolved error site so tsc exits 0. Opt-in. Default false.
|
|
242
|
+
*/
|
|
243
|
+
stubOnFailure?: boolean;
|
|
244
|
+
/**
|
|
245
|
+
* Run all layers in memory; report counts but don't write. Default false.
|
|
246
|
+
* Layer 2 is auto-skipped under dryRun (it writes patches; would be a
|
|
247
|
+
* no-op).
|
|
248
|
+
*/
|
|
249
|
+
dryRun?: boolean;
|
|
250
|
+
/** Logger. Default no-op. */
|
|
251
|
+
logger?: Logger;
|
|
252
|
+
/** Per-layer telemetry stream. Forwarded to Layer 1, 2, 4. */
|
|
253
|
+
onLayerEvent?: (event: LayerEvent) => void;
|
|
254
|
+
/** @internal — LLM call override. Tests inject a fake; real callers leave it. */
|
|
255
|
+
_callLLM?: import("./mendAgent.js").LLMCall;
|
|
256
|
+
}
|
|
257
|
+
export interface RunFullStackResult {
|
|
258
|
+
/** True if `errorsAfterAllLayers === 0`. */
|
|
259
|
+
passed: boolean;
|
|
260
|
+
/** Errors detected before any fix attempt. */
|
|
261
|
+
errorsBefore: number;
|
|
262
|
+
/** Errors remaining after Layer 0/1 (before Layer 2 + 4). */
|
|
263
|
+
errorsAfterLayer1: number;
|
|
264
|
+
/** Errors remaining after every layer that ran. */
|
|
265
|
+
errorsAfterAllLayers: number;
|
|
266
|
+
/** Per-layer sub-results. `layer2` is null when `llm` was omitted; `layer4` is null when `stubOnFailure` was false (or had no candidates). */
|
|
267
|
+
layer1: ValidationLoopResult["lspFixer"];
|
|
268
|
+
layer2: RunMendLoopResult | null;
|
|
269
|
+
layer4: {
|
|
270
|
+
stubsApplied: AppliedStub[];
|
|
271
|
+
} | null;
|
|
272
|
+
/** USD spent on Layer 2. 0 when Layer 2 didn't run. */
|
|
273
|
+
totalCostUsd: number;
|
|
274
|
+
/** Wall-clock total across all layers. */
|
|
275
|
+
totalLatencyMs: number;
|
|
276
|
+
/** Remaining diagnostics, grouped by TS code (e.g. `{TS2339: 3}`). */
|
|
277
|
+
remainingByCode: Record<string, number>;
|
|
278
|
+
/** Remaining diagnostics, grouped by file path (relative to workspaceRoot). */
|
|
279
|
+
remainingByFile: Record<string, number>;
|
|
280
|
+
}
|
|
281
|
+
/**
|
|
282
|
+
* Run the full tsfix stack (Layer 0/1 → Layer 2 → Layer 4) end-to-end.
|
|
283
|
+
*
|
|
284
|
+
* @example
|
|
285
|
+
* ```ts
|
|
286
|
+
* const result = await runFullStack({
|
|
287
|
+
* workspaceRoot: "/path/to/project",
|
|
288
|
+
* llm: { provider: "anthropic", model: "claude-haiku-4-5", apiKey: KEY },
|
|
289
|
+
* stubOnFailure: true,
|
|
290
|
+
* onLayerEvent: (e) => console.log(e),
|
|
291
|
+
* });
|
|
292
|
+
* if (!result.passed) { ... }
|
|
293
|
+
* console.log(`Spent $${result.totalCostUsd.toFixed(4)}`);
|
|
294
|
+
* ```
|
|
295
|
+
*/
|
|
296
|
+
export declare function runFullStack(opts: RunFullStackOptions): Promise<RunFullStackResult>;
|
|
@@ -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 {};
|
|
@@ -16,10 +16,17 @@
|
|
|
16
16
|
*/
|
|
17
17
|
import type { MendContext } from "./index.js";
|
|
18
18
|
import { type ApplyResult, type EditBlock } from "./applyEditBlock.js";
|
|
19
|
+
/**
|
|
20
|
+
* Provider identifier. Each corresponds to one `@ai-sdk/<provider>` adapter
|
|
21
|
+
* in the Vercel AI SDK. Adding a provider requires (1) the corresponding
|
|
22
|
+
* `@ai-sdk/X` dep in package.json, (2) a case in `buildLanguageModel`, and
|
|
23
|
+
* (3) the appropriate API-key env-var name documented in the CLI.
|
|
24
|
+
*/
|
|
25
|
+
export type LLMProvider = "anthropic" | "openai" | "google";
|
|
19
26
|
export interface MendSingleFileOptions {
|
|
20
27
|
context: MendContext;
|
|
21
28
|
llm: {
|
|
22
|
-
provider:
|
|
29
|
+
provider: LLMProvider;
|
|
23
30
|
model: string;
|
|
24
31
|
apiKey: string;
|
|
25
32
|
};
|
|
@@ -39,6 +46,12 @@ export interface MendSingleFileResult {
|
|
|
39
46
|
export type LLMCall = (params: {
|
|
40
47
|
systemBlock: string;
|
|
41
48
|
userBlock: string;
|
|
49
|
+
/**
|
|
50
|
+
* Provider name. Optional for backward compatibility — when omitted,
|
|
51
|
+
* the default impl treats it as "anthropic" (matches v0.5.0 behavior).
|
|
52
|
+
* Test injection points can ignore this field.
|
|
53
|
+
*/
|
|
54
|
+
provider?: LLMProvider;
|
|
42
55
|
model: string;
|
|
43
56
|
apiKey: string;
|
|
44
57
|
}) => Promise<{
|
|
@@ -22,13 +22,13 @@
|
|
|
22
22
|
* returns. We can't iterate without writing to disk because re-validation
|
|
23
23
|
* needs the actual file changes.
|
|
24
24
|
*/
|
|
25
|
-
import type { Diagnostic, MendContext } from "./index.js";
|
|
26
|
-
import { type LLMCall } from "./mendAgent.js";
|
|
25
|
+
import type { Diagnostic, LayerEvent, MendContext } from "./index.js";
|
|
26
|
+
import { type LLMCall, type LLMProvider } from "./mendAgent.js";
|
|
27
27
|
import { type AppliedStub } from "./stubAndContinue.js";
|
|
28
28
|
export interface RunMendLoopOptions {
|
|
29
29
|
context: MendContext;
|
|
30
30
|
llm: {
|
|
31
|
-
provider:
|
|
31
|
+
provider: LLMProvider;
|
|
32
32
|
model: string;
|
|
33
33
|
apiKey: string;
|
|
34
34
|
};
|
|
@@ -43,6 +43,13 @@ export interface RunMendLoopOptions {
|
|
|
43
43
|
* Default false. Ignored when `dryRun: true`.
|
|
44
44
|
*/
|
|
45
45
|
stubOnFailure?: boolean;
|
|
46
|
+
/**
|
|
47
|
+
* Per-iteration / per-stub telemetry callback. Layer 2 emits one event per
|
|
48
|
+
* iteration with the dominant error code (`fixed: true` if the iteration
|
|
49
|
+
* cleared all errors); Layer 4 emits one event per stubbed `(line, code)`
|
|
50
|
+
* pair. Both forwarded to the same callback. Optional.
|
|
51
|
+
*/
|
|
52
|
+
onLayerEvent?: (event: LayerEvent) => void;
|
|
46
53
|
/** @internal — LLM call override for tests. */
|
|
47
54
|
_callLLM?: LLMCall;
|
|
48
55
|
}
|
|
@@ -51,6 +51,13 @@ export interface LSPFixerOptions {
|
|
|
51
51
|
* before letting it modify a workspace.
|
|
52
52
|
*/
|
|
53
53
|
dryRun?: boolean;
|
|
54
|
+
/**
|
|
55
|
+
* Per-error telemetry callback. One event per `(errorCode, fix-attempt)`
|
|
56
|
+
* with `fixed: true` when the fix landed and `fixed: false` when the LSP
|
|
57
|
+
* abstained (no safe candidate). Events fire even on dry runs.
|
|
58
|
+
* Optional — undefined callback costs nothing.
|
|
59
|
+
*/
|
|
60
|
+
onLayerEvent?: (event: import("./index.js").LayerEvent) => void;
|
|
54
61
|
}
|
|
55
62
|
export interface LSPFixerResult {
|
|
56
63
|
/** Number of fixes successfully applied across all iterations. */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shipispec/tsfix",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.1",
|
|
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",
|
|
@@ -65,6 +65,8 @@
|
|
|
65
65
|
},
|
|
66
66
|
"dependencies": {
|
|
67
67
|
"@ai-sdk/anthropic": "^3.0.44",
|
|
68
|
+
"@ai-sdk/google": "^3.0.75",
|
|
69
|
+
"@ai-sdk/openai": "^3.0.64",
|
|
68
70
|
"ai": "^6.0.86"
|
|
69
71
|
},
|
|
70
72
|
"devDependencies": {
|