@wrongstack/plugins 0.286.0 → 0.289.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.
Files changed (74) hide show
  1. package/README.md +20 -11
  2. package/dist/accessibility-auditor/index.d.ts.map +1 -1
  3. package/dist/accessibility-auditor.js +67 -55
  4. package/dist/api-compatibility-gate.js +1 -1
  5. package/dist/auto-doc.js +1 -1
  6. package/dist/auto-i18n-extractor/index.d.ts.map +1 -1
  7. package/dist/auto-i18n-extractor.js +20 -12
  8. package/dist/changelog-writer.js +1 -1
  9. package/dist/code-metrics/index.d.ts.map +1 -1
  10. package/dist/code-metrics.js +63 -50
  11. package/dist/commit-validator.js +1 -1
  12. package/dist/config-validator.js +1 -1
  13. package/dist/cost-tracker/index.d.ts.map +1 -1
  14. package/dist/cost-tracker.js +4 -2
  15. package/dist/cron/index.d.ts.map +1 -1
  16. package/dist/cron.js +29 -0
  17. package/dist/dead-code-detector/index.d.ts.map +1 -1
  18. package/dist/dead-code-detector.js +28 -19
  19. package/dist/dep-guard.js +1 -1
  20. package/dist/diff-summary/index.d.ts.map +1 -1
  21. package/dist/diff-summary.js +19 -11
  22. package/dist/doc-sync-guard/index.d.ts.map +1 -1
  23. package/dist/doc-sync-guard.js +25 -16
  24. package/dist/duplicate-code-detector/index.d.ts.map +1 -1
  25. package/dist/duplicate-code-detector.js +70 -57
  26. package/dist/error-lens.js +1 -1
  27. package/dist/feature-flag-tracker/index.d.ts.map +1 -1
  28. package/dist/feature-flag-tracker.js +69 -56
  29. package/dist/file-watcher/index.d.ts.map +1 -1
  30. package/dist/file-watcher.js +21 -4
  31. package/dist/format-on-save/index.d.ts.map +1 -1
  32. package/dist/format-on-save.js +19 -11
  33. package/dist/git-autocommit.js +1 -1
  34. package/dist/import-organizer/index.d.ts.map +1 -1
  35. package/dist/import-organizer.js +23 -14
  36. package/dist/index.d.ts +4 -4
  37. package/dist/index.js +1167 -1048
  38. package/dist/interface-contract-guard/index.d.ts.map +1 -1
  39. package/dist/interface-contract-guard.js +64 -51
  40. package/dist/lint-gate/index.d.ts.map +1 -1
  41. package/dist/lint-gate.js +61 -30
  42. package/dist/migration-planner/index.d.ts +12 -2
  43. package/dist/migration-planner/index.d.ts.map +1 -1
  44. package/dist/migration-planner.js +210 -28
  45. package/dist/pr-drafter.js +1 -1
  46. package/dist/prompt-firewall/index.d.ts +14 -6
  47. package/dist/prompt-firewall/index.d.ts.map +1 -1
  48. package/dist/prompt-firewall.js +7 -6
  49. package/dist/refactor-suggester/index.d.ts.map +1 -1
  50. package/dist/refactor-suggester.js +69 -56
  51. package/dist/release-notes-generator/index.d.ts +3 -1
  52. package/dist/release-notes-generator/index.d.ts.map +1 -1
  53. package/dist/release-notes-generator.js +152 -12
  54. package/dist/runtime/index.d.ts +36 -0
  55. package/dist/runtime/index.d.ts.map +1 -1
  56. package/dist/runtime/llm.d.ts +33 -0
  57. package/dist/runtime/llm.d.ts.map +1 -0
  58. package/dist/runtime.js +119 -25
  59. package/dist/schema-evolution-guard/index.d.ts.map +1 -1
  60. package/dist/schema-evolution-guard.js +22 -13
  61. package/dist/secret-scanner/index.d.ts.map +1 -1
  62. package/dist/secret-scanner.js +17 -6
  63. package/dist/security-hotspot-scanner/index.d.ts.map +1 -1
  64. package/dist/security-hotspot-scanner.js +24 -15
  65. package/dist/session-recap.js +1 -1
  66. package/dist/spec-linker.js +1167 -1048
  67. package/dist/test-generator/index.d.ts +5 -3
  68. package/dist/test-generator/index.d.ts.map +1 -1
  69. package/dist/test-generator.js +169 -26
  70. package/dist/test-runner-gate/index.d.ts.map +1 -1
  71. package/dist/test-runner-gate.js +65 -43
  72. package/dist/type-gate/index.d.ts.map +1 -1
  73. package/dist/type-gate.js +23 -35
  74. package/package.json +3 -3
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/interface-contract-guard/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAIH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAQ/C,MAAM,WAAW,wBAAwB;IACvC,aAAa,EAAE,MAAM,CAAC;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB;AAwLD,QAAA,MAAM,MAAM,EAAE,MAuMb,CAAC;eAEa,MAAM"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/interface-contract-guard/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAIH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAS/C,MAAM,WAAW,wBAAwB;IACvC,aAAa,EAAE,MAAM,CAAC;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB;AAqID,QAAA,MAAM,MAAM,EAAE,MAuMb,CAAC;eAEa,MAAM"}
@@ -1,55 +1,37 @@
1
1
  // src/interface-contract-guard/index.ts
2
- import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
3
- import { extname, isAbsolute, relative, resolve } from "node:path";
4
- var API_VERSION = "^0.1.10";
5
- var state = {
6
- scanCount: 0,
7
- findingCount: 0,
8
- hookInvocationCount: 0,
9
- warningCount: 0,
10
- errorCount: 0,
11
- hookUnregister: null
12
- };
13
- var DEFAULTS = {
14
- enabled: false,
15
- extensions: [".ts", ".tsx"],
16
- maxFindings: 50
17
- };
18
- function readConfig(raw) {
19
- if (!raw || typeof raw !== "object") return { ...DEFAULTS };
20
- const r = raw;
21
- return {
22
- enabled: r["enabled"] !== false,
23
- extensions: Array.isArray(r["extensions"]) ? r["extensions"].filter((x) => typeof x === "string") : DEFAULTS.extensions,
24
- maxFindings: typeof r["maxFindings"] === "number" && r["maxFindings"] >= 1 && r["maxFindings"] <= 500 ? r["maxFindings"] : DEFAULTS.maxFindings
25
- };
2
+ import { readFileSync } from "node:fs";
3
+ import { isAbsolute as isAbsolute2, relative as relative2, resolve as resolve2 } from "node:path";
4
+
5
+ // src/runtime/index.ts
6
+ import { existsSync, readdirSync, statSync } from "node:fs";
7
+ import { basename, extname, isAbsolute, relative, resolve } from "node:path";
8
+ var MAX_BUFFER_BYTES = 16 * 1024 * 1024;
9
+ function hasLeadingDash(arg) {
10
+ return arg.length > 0 && arg.startsWith("-");
26
11
  }
27
- function withinProject(p) {
28
- if (typeof p !== "string" || p.length === 0 || p.length > 4096) return false;
29
- const root = process.cwd();
30
- const resolved = isAbsolute(p) ? resolve(p) : resolve(root, p);
31
- const rel = relative(root, resolved);
32
- if (rel === "" || rel === ".") return true;
33
- if (rel.startsWith("..")) return false;
34
- if (isAbsolute(rel)) return false;
35
- return true;
36
- }
37
- function normalizeExtensions(exts) {
38
- return exts.map((e) => e.startsWith(".") ? e.toLowerCase() : `.${e.toLowerCase()}`);
12
+ function withinProjectPath(projectRoot, candidate) {
13
+ if (candidate.length === 0 || candidate.length > 4096) return false;
14
+ if (hasLeadingDash(candidate)) return false;
15
+ const resolved = isAbsolute(candidate) ? resolve(candidate) : resolve(projectRoot, candidate);
16
+ const rel = relative(projectRoot, resolved);
17
+ return rel === "" || !rel.startsWith("..") && !isAbsolute(rel);
39
18
  }
40
- function matchesExtension(p, exts) {
41
- return exts.includes(extname(p).toLowerCase());
19
+ function withinProject(p) {
20
+ return withinProjectPath(process.cwd(), p) || relative(process.cwd(), p) === ".";
42
21
  }
43
- function collectSourceFiles(root, exts) {
22
+ var DEFAULT_EXCLUDE_DIRS = ["node_modules", "dist", ".git", "coverage"];
23
+ function collectSourceFiles(root, opts) {
44
24
  const files = [];
45
25
  if (!existsSync(root)) return files;
46
26
  const s = statSync(root);
47
27
  if (s.isFile()) {
48
- if (matchesExtension(root, exts)) files.push(root);
28
+ if (matchesExtension(root, opts.extensions)) files.push(root);
49
29
  return files;
50
30
  }
51
31
  if (!s.isDirectory()) return files;
52
- function walk(dir) {
32
+ const exclude = opts.excludeDirs ?? DEFAULT_EXCLUDE_DIRS;
33
+ function walk(dir, depth) {
34
+ if (opts.maxDepth !== void 0 && depth > opts.maxDepth) return;
53
35
  let entries;
54
36
  try {
55
37
  entries = readdirSync(dir);
@@ -57,7 +39,7 @@ function collectSourceFiles(root, exts) {
57
39
  return;
58
40
  }
59
41
  for (const entry of entries) {
60
- if (entry === "node_modules" || entry === "dist" || entry === ".git" || entry === "coverage") continue;
42
+ if (exclude.includes(entry)) continue;
61
43
  const full = resolve(dir, entry);
62
44
  let st;
63
45
  try {
@@ -66,20 +48,51 @@ function collectSourceFiles(root, exts) {
66
48
  continue;
67
49
  }
68
50
  if (st.isDirectory()) {
69
- walk(full);
70
- } else if (st.isFile() && matchesExtension(full, exts)) {
51
+ walk(full, depth + 1);
52
+ } else if (st.isFile() && matchesExtension(full, opts.extensions)) {
71
53
  files.push(full);
72
54
  }
73
55
  }
74
56
  }
75
- walk(root);
57
+ walk(root, 0);
76
58
  return files;
77
59
  }
60
+ function matchesExtension(p, exts) {
61
+ return exts.includes(extname(p).toLowerCase());
62
+ }
63
+
64
+ // src/interface-contract-guard/index.ts
65
+ var API_VERSION = "^0.1.10";
66
+ var state = {
67
+ scanCount: 0,
68
+ findingCount: 0,
69
+ hookInvocationCount: 0,
70
+ warningCount: 0,
71
+ errorCount: 0,
72
+ hookUnregister: null
73
+ };
74
+ var DEFAULTS = {
75
+ enabled: false,
76
+ extensions: [".ts", ".tsx"],
77
+ maxFindings: 50
78
+ };
79
+ function readConfig(raw) {
80
+ if (!raw || typeof raw !== "object") return { ...DEFAULTS };
81
+ const r = raw;
82
+ return {
83
+ enabled: r["enabled"] !== false,
84
+ extensions: Array.isArray(r["extensions"]) ? r["extensions"].filter((x) => typeof x === "string") : DEFAULTS.extensions,
85
+ maxFindings: typeof r["maxFindings"] === "number" && r["maxFindings"] >= 1 && r["maxFindings"] <= 500 ? r["maxFindings"] : DEFAULTS.maxFindings
86
+ };
87
+ }
88
+ function normalizeExtensions(exts) {
89
+ return exts.map((e) => e.startsWith(".") ? e.toLowerCase() : `.${e.toLowerCase()}`);
90
+ }
78
91
  function toPosix(p) {
79
92
  return p.replace(/\\/g, "/");
80
93
  }
81
94
  function relativePath(p) {
82
- return toPosix(relative(process.cwd(), p));
95
+ return toPosix(relative2(process.cwd(), p));
83
96
  }
84
97
  var INTERFACE_RE = /(?:export\s+)?interface\s+([A-Za-z_$][A-Za-z0-9_$]*)/g;
85
98
  function extractInterfaceNames(content) {
@@ -99,9 +112,9 @@ function hasImplementer(name, contents) {
99
112
  }
100
113
  function scanPath(rawPath, cfg) {
101
114
  const root = process.cwd();
102
- const resolved = isAbsolute(rawPath) ? resolve(rawPath) : resolve(root, rawPath);
115
+ const resolved = isAbsolute2(rawPath) ? resolve2(rawPath) : resolve2(root, rawPath);
103
116
  const exts = normalizeExtensions(cfg.extensions);
104
- const files = collectSourceFiles(resolved, exts);
117
+ const files = collectSourceFiles(resolved, { extensions: exts });
105
118
  const contents = [];
106
119
  for (const p of files) {
107
120
  try {
@@ -177,7 +190,7 @@ var plugin = {
177
190
  const exts = normalizeExtensions(cfg.extensions);
178
191
  if (!matchesExtension(sourcePath, exts)) return;
179
192
  state.hookInvocationCount += 1;
180
- const resolved = resolve(process.cwd(), sourcePath);
193
+ const resolved = resolve2(process.cwd(), sourcePath);
181
194
  let content;
182
195
  try {
183
196
  content = readFileSync(resolved, "utf-8");
@@ -195,7 +208,7 @@ var plugin = {
195
208
  If you changed member shapes, search the project for implementers/\`satisfies\`/\`as\` usages and update them accordingly.`
196
209
  };
197
210
  };
198
- state.hookUnregister = api.registerHook("PostToolUse", "write|edit", hook);
211
+ state.hookUnregister = api.registerHook("PostToolUse", "write|edit", hook, { background: true });
199
212
  api.tools.register({
200
213
  name: "check_interface_contracts",
201
214
  description: "Scan TypeScript files for interface declarations that have no visible implementer (implements / as / satisfies).",
@@ -225,7 +238,7 @@ If you changed member shapes, search the project for implementers/\`satisfies\`/
225
238
  state.findingCount += result.findings.length;
226
239
  return {
227
240
  ok: true,
228
- path: relativePath(resolve(process.cwd(), rawPath)),
241
+ path: relativePath(resolve2(process.cwd(), rawPath)),
229
242
  scannedFiles: result.scannedFiles,
230
243
  findings: result.findings
231
244
  };
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/lint-gate/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAMH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AA6R/C,QAAA,MAAM,MAAM,EAAE,MA8Vb,CAAC;eAEa,MAAM"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/lint-gate/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAQH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAwU/C,QAAA,MAAM,MAAM,EAAE,MAwWb,CAAC;eAEa,MAAM"}
package/dist/lint-gate.js CHANGED
@@ -1,8 +1,10 @@
1
1
  // src/lint-gate/index.ts
2
2
  import { execFile } from "node:child_process";
3
+ import { readFileSync } from "node:fs";
3
4
  import { mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
5
+ import { createRequire } from "node:module";
4
6
  import { tmpdir } from "node:os";
5
- import { join } from "node:path";
7
+ import { dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
6
8
  var API_VERSION = "^0.1.10";
7
9
  var state = {
8
10
  /** Total PreToolUse invocations. */
@@ -36,55 +38,74 @@ function readConfig(raw) {
36
38
  fixRules: Array.isArray(r["fixRules"]) ? r["fixRules"].filter((x) => typeof x === "string") : []
37
39
  };
38
40
  }
39
- function executable(command) {
40
- return process.platform === "win32" && command === "npx" ? "npx.cmd" : command;
41
+ var LINTER_PACKAGES = {
42
+ biome: "@biomejs/biome",
43
+ eslint: "eslint"
44
+ };
45
+ function isInside(parent, candidate) {
46
+ const rel = relative(parent, candidate);
47
+ return rel === "" || !rel.startsWith(`..${sep}`) && rel !== ".." && !isAbsolute(rel);
48
+ }
49
+ function resolveLocalLinter(name, cwd) {
50
+ try {
51
+ const packageName = LINTER_PACKAGES[name];
52
+ const requireFromProject = createRequire(resolve(cwd, "package.json"));
53
+ const packagePath = requireFromProject.resolve(`${packageName}/package.json`);
54
+ const packageJson = JSON.parse(readFileSync(packagePath, "utf-8"));
55
+ const relativeBin = typeof packageJson.bin === "string" ? packageJson.bin : packageJson.bin?.[name] ?? Object.values(packageJson.bin ?? {})[0];
56
+ if (!relativeBin || isAbsolute(relativeBin)) return null;
57
+ const packageDir = dirname(packagePath);
58
+ const entry = resolve(packageDir, relativeBin);
59
+ if (!isInside(packageDir, entry)) return null;
60
+ return {
61
+ cmd: process.execPath,
62
+ args: name === "biome" ? [entry, "check", "--reporter=json"] : [entry, "--format=json"],
63
+ name
64
+ };
65
+ } catch {
66
+ return null;
67
+ }
41
68
  }
42
- function runCommand(command, args, timeoutMs, signal) {
43
- return new Promise((resolve) => {
69
+ function runCommand(command, args, timeoutMs, cwd, signal) {
70
+ return new Promise((resolveResult) => {
44
71
  try {
45
72
  execFile(
46
- executable(command),
73
+ command,
47
74
  args,
48
75
  {
49
76
  encoding: "utf-8",
50
77
  timeout: timeoutMs,
51
- cwd: process.cwd(),
78
+ cwd,
52
79
  windowsHide: true,
80
+ shell: false,
53
81
  maxBuffer: 2 * 1024 * 1024,
54
82
  ...signal ? { signal } : {}
55
83
  },
56
- (error, stdout) => resolve({ stdout, error })
84
+ (error, stdout) => resolveResult({ stdout, error })
57
85
  );
58
86
  } catch (err) {
59
- resolve({ stdout: "", error: err instanceof Error ? err : new Error(String(err)) });
87
+ resolveResult({ stdout: "", error: err instanceof Error ? err : new Error(String(err)) });
60
88
  }
61
89
  });
62
90
  }
63
- async function detectLinter(requested) {
64
- const tryBiome = requested === "biome" || requested === "auto";
65
- const tryEslint = requested === "eslint" || requested === "auto";
66
- if (tryBiome) {
67
- const probe = await runCommand("npx", ["biome", "--version"], 5e3);
68
- if (!probe.error) {
69
- return { cmd: "npx", args: ["biome", "check", "--reporter=json"], name: "biome" };
70
- }
71
- }
72
- if (tryEslint) {
73
- const probe = await runCommand("npx", ["eslint", "--version"], 5e3);
74
- if (!probe.error) {
75
- return { cmd: "npx", args: ["eslint", "--format=json"], name: "eslint" };
76
- }
91
+ async function detectLinter(requested, cwd) {
92
+ const candidates = requested === "auto" ? ["biome", "eslint"] : [requested];
93
+ for (const name of candidates) {
94
+ const linter = resolveLocalLinter(name, cwd);
95
+ if (!linter) continue;
96
+ const probe = await runCommand(linter.cmd, [linter.args[0], "--version"], 5e3, cwd);
97
+ if (!probe.error) return linter;
77
98
  }
78
99
  return null;
79
100
  }
80
- async function lintContent(content, filePath, linter, timeoutMs, signal) {
101
+ async function lintContent(content, filePath, linter, timeoutMs, cwd, signal) {
81
102
  const ext = filePath.includes(".") ? filePath.slice(filePath.lastIndexOf(".")) : ".ts";
82
103
  const tmpDir = await mkdtemp(join(tmpdir(), "lint-gate-"));
83
104
  const tmpFile = join(tmpDir, `input${ext}`);
84
105
  try {
85
106
  await writeFile(tmpFile, content, "utf-8");
86
107
  const fullArgs = [...linter.args, tmpFile];
87
- const result = await runCommand(linter.cmd, fullArgs, timeoutMs, signal);
108
+ const result = await runCommand(linter.cmd, fullArgs, timeoutMs, cwd, signal);
88
109
  if (signal.aborted) throw signal.reason;
89
110
  if (result.error && !result.stdout) return null;
90
111
  return parseLinterOutput(result.stdout, linter.name);
@@ -95,14 +116,14 @@ async function lintContent(content, filePath, linter, timeoutMs, signal) {
95
116
  await rm(tmpDir, { recursive: true, force: true }).catch(() => void 0);
96
117
  }
97
118
  }
98
- async function lintAndFix(content, filePath, linter, timeoutMs, signal) {
119
+ async function lintAndFix(content, filePath, linter, timeoutMs, cwd, signal) {
99
120
  const ext = filePath.includes(".") ? filePath.slice(filePath.lastIndexOf(".")) : ".ts";
100
121
  const tmpDir = await mkdtemp(join(tmpdir(), "lint-gate-fix-"));
101
122
  const tmpFile = join(tmpDir, `input${ext}`);
102
123
  try {
103
124
  await writeFile(tmpFile, content, "utf-8");
104
- const fixArgs = linter.name === "biome" ? ["biome", "check", "--write", tmpFile] : ["eslint", "--fix", tmpFile];
105
- await runCommand(linter.cmd, fixArgs, timeoutMs, signal);
125
+ const fixArgs = linter.name === "biome" ? [linter.args[0], "check", "--write", tmpFile] : [linter.args[0], "--fix", tmpFile];
126
+ await runCommand(linter.cmd, fixArgs, timeoutMs, cwd, signal);
106
127
  if (signal.aborted) throw signal.reason;
107
128
  return await readFile(tmpFile, "utf-8");
108
129
  } catch {
@@ -203,7 +224,8 @@ var plugin = {
203
224
  state.hookUnregister = null;
204
225
  state.lastResult = null;
205
226
  const cfg = readConfig(api.config.extensions?.["lint-gate"]);
206
- const linterReady = detectLinter(cfg.linter).then((linter) => {
227
+ const cwd = resolve(api.config.cwd ?? process.cwd());
228
+ const linterReady = detectLinter(cfg.linter, cwd).then((linter) => {
207
229
  if (!linter) {
208
230
  api.log.warn("lint-gate: no linter found (biome or eslint) \u2014 hook will be a no-op", {
209
231
  requested: cfg.linter
@@ -240,7 +262,14 @@ var plugin = {
240
262
  } else {
241
263
  return;
242
264
  }
243
- const issues = await lintContent(content, filePath, linter, cfg.timeoutMs, runtime.signal);
265
+ const issues = await lintContent(
266
+ content,
267
+ filePath,
268
+ linter,
269
+ cfg.timeoutMs,
270
+ cwd,
271
+ runtime.signal
272
+ );
244
273
  if (issues === null) {
245
274
  state.linterErrorCount += 1;
246
275
  return;
@@ -280,6 +309,7 @@ ${summary}${truncated}`
280
309
  filePath,
281
310
  linter,
282
311
  cfg.timeoutMs,
312
+ cwd,
283
313
  runtime.signal
284
314
  );
285
315
  if (fixedContent !== content) {
@@ -318,6 +348,7 @@ ${remainingSummary}` : "")
318
348
  filePath,
319
349
  linter,
320
350
  cfg.timeoutMs,
351
+ cwd,
321
352
  runtime.signal
322
353
  );
323
354
  if (fixedNewStr !== newStr) {
@@ -4,7 +4,8 @@
4
4
  * The plugin reads a package CHANGELOG (project root or node_modules) and
5
5
  * produces a migration checklist with breaking changes and recommended steps
6
6
  * between two versions. If no changelog is available it returns a generic
7
- * migration guide.
7
+ * migration guide. Optional `api.llm` analysis stays separate from the
8
+ * deterministic facts and falls back to them on any invalid response.
8
9
  *
9
10
  * Tools registered:
10
11
  * - migration_plan — produce a migration checklist for a package/version range
@@ -16,13 +17,22 @@
16
17
  * {
17
18
  * "enabled": true,
18
19
  * "changelogPaths": ["CHANGELOG.md"],
19
- * "maxChars": 100_000
20
+ * "maxChars": 100_000,
21
+ * "useLlm": false,
22
+ * "maxLlmChars": 20_000
20
23
  * }
21
24
  * ```
22
25
  *
23
26
  * @public
24
27
  */
25
28
  import type { Plugin } from '@wrongstack/core';
29
+ export interface MigrationAiAnalysis {
30
+ summary: string;
31
+ riskLevel: 'low' | 'medium' | 'high' | 'unknown';
32
+ risks: string[];
33
+ additionalSteps: string[];
34
+ verificationSteps: string[];
35
+ }
26
36
  declare const plugin: Plugin;
27
37
  export default plugin;
28
38
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/migration-planner/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAIH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAmP/C,QAAA,MAAM,MAAM,EAAE,MAgOb,CAAC;eAEa,MAAM"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/migration-planner/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAGH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAwP/C,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,SAAS,CAAC;IACjD,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,eAAe,EAAE,MAAM,EAAE,CAAC;IAC1B,iBAAiB,EAAE,MAAM,EAAE,CAAC;CAC7B;AAwED,QAAA,MAAM,MAAM,EAAE,MA+Sb,CAAC;eAEa,MAAM"}