@wrongstack/plugins 1.0.8 → 1.0.11

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 (73) hide show
  1. package/dist/accessibility-auditor/index.d.ts +1 -1
  2. package/dist/accessibility-auditor.js +15 -3
  3. package/dist/agent-handoff.js +6 -6
  4. package/dist/auto-doc/index.d.ts +1 -1
  5. package/dist/auto-doc.js +31 -20
  6. package/dist/auto-i18n-extractor/index.d.ts +1 -1
  7. package/dist/auto-i18n-extractor.js +12 -8
  8. package/dist/branch-guard.js +3 -3
  9. package/dist/changelog-writer/index.d.ts +1 -1
  10. package/dist/changelog-writer.js +19 -10
  11. package/dist/checkpoint/index.d.ts +1 -1
  12. package/dist/checkpoint.js +38 -24
  13. package/dist/code-metrics/index.d.ts +1 -1
  14. package/dist/code-metrics.js +12 -4
  15. package/dist/commit-validator.js +5 -5
  16. package/dist/context-pins/index.d.ts +1 -1
  17. package/dist/context-pins.js +12 -9
  18. package/dist/cost-tracker.js +21 -9
  19. package/dist/cron/index.d.ts +1 -1
  20. package/dist/cron.js +18 -5
  21. package/dist/dead-code-detector/index.d.ts +1 -1
  22. package/dist/dead-code-detector.js +14 -12
  23. package/dist/duplicate-code-detector/index.d.ts +1 -1
  24. package/dist/duplicate-code-detector.js +11 -3
  25. package/dist/feature-flag-tracker/index.d.ts +1 -1
  26. package/dist/feature-flag-tracker.js +12 -4
  27. package/dist/file-watcher/index.d.ts +1 -1
  28. package/dist/file-watcher.js +37 -38
  29. package/dist/git-autocommit/index.d.ts +1 -1
  30. package/dist/git-autocommit.js +44 -39
  31. package/dist/gitignore-guard/index.d.ts +1 -1
  32. package/dist/gitignore-guard.js +12 -6
  33. package/dist/index.js +1553 -1086
  34. package/dist/interface-contract-guard/index.d.ts +1 -1
  35. package/dist/interface-contract-guard.js +12 -4
  36. package/dist/knowledge-graph/index.d.ts +1 -1
  37. package/dist/knowledge-graph.js +11 -9
  38. package/dist/loop-breaker.js +11 -4
  39. package/dist/migration-planner/index.d.ts +1 -1
  40. package/dist/migration-planner.js +6 -2
  41. package/dist/notify-hub/index.d.ts +1 -1
  42. package/dist/notify-hub.js +19 -12
  43. package/dist/performance-regression-gate/index.d.ts +1 -1
  44. package/dist/performance-regression-gate.js +33 -13
  45. package/dist/pr-drafter/index.d.ts +10 -1
  46. package/dist/pr-drafter.js +57 -26
  47. package/dist/refactor-suggester/index.d.ts +1 -1
  48. package/dist/refactor-suggester.js +17 -4
  49. package/dist/release-notes-generator.js +2 -2
  50. package/dist/secret-scanner/index.d.ts +1 -1
  51. package/dist/secret-scanner.js +11 -3
  52. package/dist/security-hotspot-scanner/index.d.ts +1 -1
  53. package/dist/security-hotspot-scanner.js +6 -2
  54. package/dist/semantic-search-indexer/index.d.ts +1 -1
  55. package/dist/semantic-search-indexer.js +19 -3
  56. package/dist/semver-bump/index.d.ts +1 -1
  57. package/dist/semver-bump.js +57 -37
  58. package/dist/session-recap.js +4 -2
  59. package/dist/shell-check/index.d.ts +1 -1
  60. package/dist/shell-check.js +30 -39
  61. package/dist/smart-rename/index.d.ts +1 -1
  62. package/dist/smart-rename.js +23 -10
  63. package/dist/template-engine/index.d.ts +1 -1
  64. package/dist/template-engine.js +51 -38
  65. package/dist/test-flake-detector/index.d.ts +1 -1
  66. package/dist/test-flake-detector.js +11 -5
  67. package/dist/test-generator/index.d.ts +1 -1
  68. package/dist/test-generator.js +12 -8
  69. package/dist/todo-tracker/index.d.ts +68 -1
  70. package/dist/todo-tracker.js +579 -395
  71. package/dist/token-budget.js +7 -4
  72. package/dist/token-throttle.js +6 -3
  73. package/package.json +7 -7
@@ -12,6 +12,9 @@ var __copyProps = (to, from, except, desc) => {
12
12
  };
13
13
  var __reExport = (target, mod, secondTarget) => (__copyProps(target, mod, "default"), secondTarget && __copyProps(secondTarget, mod, "default"));
14
14
 
15
+ // src/secret-scanner/index.ts
16
+ import { ToolValidationError } from "@wrongstack/core/types";
17
+
15
18
  // src/runtime/credential-patterns.ts
16
19
  import {
17
20
  cloneCredentialPatterns,
@@ -447,9 +450,14 @@ var plugin = {
447
450
  mutating: false,
448
451
  async execute(input) {
449
452
  activateRuntime(runtime);
450
- const rawText = input["text"] ?? input["content"] ?? input["string"] ?? input["input"] ?? input["code"] ?? input["value"] ?? "";
451
- const text = typeof rawText === "string" ? rawText : "";
452
- const matched = findMatches(text);
453
+ const rawText = input["text"] ?? input["content"] ?? input["string"] ?? input["input"] ?? input["code"] ?? input["value"];
454
+ if (typeof rawText !== "string") {
455
+ throw new ToolValidationError({
456
+ message: "text is required and must be a string",
457
+ field: "text"
458
+ });
459
+ }
460
+ const matched = findMatches(rawText);
453
461
  return {
454
462
  ok: true,
455
463
  matched,
@@ -22,7 +22,7 @@
22
22
  *
23
23
  * @public
24
24
  */
25
- import type { Plugin } from '@wrongstack/core/types';
25
+ import { type Plugin } from '@wrongstack/core/types';
26
26
  declare const plugin: Plugin;
27
27
  export default plugin;
28
28
  //# sourceMappingURL=index.d.ts.map
@@ -15,6 +15,7 @@ var __reExport = (target, mod, secondTarget) => (__copyProps(target, mod, "defau
15
15
  // src/security-hotspot-scanner/index.ts
16
16
  import { readdir, readFile, stat } from "node:fs/promises";
17
17
  import { extname, isAbsolute, relative, resolve } from "node:path";
18
+ import { ToolValidationError } from "@wrongstack/core/types";
18
19
 
19
20
  // src/runtime/index.ts
20
21
  var runtime_exports = {};
@@ -335,7 +336,7 @@ Review or remove the risky pattern(s).`;
335
336
  category: "Security",
336
337
  mutating: false,
337
338
  async execute(input) {
338
- if (!cfg.enabled) return { ok: false, error: "security-hotspot-scanner is disabled" };
339
+ if (!cfg.enabled) throw new Error("security-hotspot-scanner is disabled");
339
340
  const raw = input;
340
341
  const targetPath = (typeof input.path === "string" ? input.path : void 0) ?? (typeof raw["directory"] === "string" ? raw["directory"] : void 0) ?? (typeof raw["dir"] === "string" ? raw["dir"] : void 0) ?? (typeof raw["SearchDirectory"] === "string" ? raw["SearchDirectory"] : void 0) ?? (typeof raw["TargetFile"] === "string" ? raw["TargetFile"] : void 0) ?? (typeof raw["filePath"] === "string" ? raw["filePath"] : void 0) ?? (typeof raw["targetFile"] === "string" ? raw["targetFile"] : void 0) ?? ".";
341
342
  const result = await scanPath(targetPath, cfg);
@@ -343,7 +344,10 @@ Review or remove the risky pattern(s).`;
343
344
  state.fileScanCount += result.filesScanned;
344
345
  state.findingCount += result.findings.length;
345
346
  if (!result.scanned) {
346
- return { ok: false, error: result.error, path: targetPath };
347
+ throw new ToolValidationError({
348
+ message: `cannot scan ${targetPath}: ${result.error ?? "unknown error"}`,
349
+ field: "path"
350
+ });
347
351
  }
348
352
  state.lastResult = {
349
353
  path: result.path,
@@ -30,7 +30,7 @@
30
30
  *
31
31
  * @public
32
32
  */
33
- import type { Plugin } from '@wrongstack/core/types';
33
+ import { type Plugin } from '@wrongstack/core/types';
34
34
  interface SemanticSearchConfig {
35
35
  enabled: boolean;
36
36
  includeExtensions: string[];
@@ -1,6 +1,7 @@
1
1
  // src/semantic-search-indexer/index.ts
2
2
  import * as fs from "node:fs/promises";
3
3
  import { isAbsolute, relative, resolve } from "node:path";
4
+ import { ToolValidationError } from "@wrongstack/core/types";
4
5
  import { DEFAULT_WALK_IGNORE_DIRS } from "@wrongstack/core/utils";
5
6
  var API_VERSION = "^0.1.10";
6
7
  var escapeRegex = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
@@ -510,16 +511,31 @@ var plugin = {
510
511
  icon: "search",
511
512
  async execute(input) {
512
513
  if (!cfg.enabled) {
513
- return { ok: false, error: "semantic-search-indexer is disabled" };
514
+ throw new Error("semantic-search-indexer is disabled");
514
515
  }
515
516
  const rawPath = input.path ?? input["directory"] ?? input["dir"] ?? input["SearchDirectory"] ?? input["SearchPath"] ?? input["TargetFile"] ?? input["targetFile"] ?? input["filePath"] ?? input["file"];
516
517
  const resolved = resolveProjectPath(typeof rawPath === "string" ? rawPath : void 0);
517
518
  if (!resolved) {
518
- return { ok: false, error: "path outside project root" };
519
+ throw new ToolValidationError({ message: "path outside project root", field: "path" });
519
520
  }
520
- await ensureIndex(resolved, cfg);
521
521
  const rawQuery = input.query ?? input["q"] ?? input["text"] ?? input["keyword"] ?? input["keywords"] ?? input["search"] ?? "";
522
522
  const query = String(rawQuery);
523
+ if (tokenize(query, cfg.minTokenLength).length === 0) {
524
+ throw new ToolValidationError({
525
+ message: `query must contain at least one keyword of ${cfg.minTokenLength}+ characters`,
526
+ field: "query"
527
+ });
528
+ }
529
+ try {
530
+ await fs.stat(resolved);
531
+ } catch (err) {
532
+ throw new ToolValidationError({
533
+ message: `path does not exist or cannot be read: ${typeof rawPath === "string" ? rawPath : resolved}`,
534
+ field: "path",
535
+ cause: err
536
+ });
537
+ }
538
+ await ensureIndex(resolved, cfg);
523
539
  const limit = typeof input.limit === "number" && input.limit >= 1 ? Math.floor(input.limit) : cfg.defaultLimit;
524
540
  const results = runQuery(query, limit, cfg);
525
541
  const queryTokens = [...new Set(tokenize(query, cfg.minTokenLength))];
@@ -6,7 +6,7 @@
6
6
  * - semver_current: Show the current version from package.json
7
7
  * - semver_changelog: Generate a changelog between two versions
8
8
  */
9
- import type { Plugin } from '@wrongstack/core/types';
9
+ import { type Plugin } from '@wrongstack/core/types';
10
10
  type BumpType = 'major' | 'minor' | 'patch' | 'auto';
11
11
  interface ConventionalCommit {
12
12
  hash: string;
@@ -1,10 +1,26 @@
1
1
  // src/semver-bump/index.ts
2
2
  import { expectDefined } from "@wrongstack/core/utils";
3
3
  import { toErrorMessage } from "@wrongstack/core/utils";
4
+ import { ToolValidationError } from "@wrongstack/core/types";
4
5
  import { execFile } from "node:child_process";
5
6
  import { access, readFile, readdir, writeFile } from "node:fs/promises";
6
7
  import { isAbsolute, join, relative, resolve } from "node:path";
7
8
  var API_VERSION = "^0.1.10";
9
+ function requireProjectRoot(rawCwd) {
10
+ const safeCwd = resolveProjectRoot(rawCwd);
11
+ if (!safeCwd) {
12
+ throw new ToolValidationError({
13
+ message: "cwd must stay within the current project directory",
14
+ field: "cwd"
15
+ });
16
+ }
17
+ return safeCwd;
18
+ }
19
+ function requireGitRef(field, ref) {
20
+ if (ref !== void 0 && (typeof ref !== "string" || ref.startsWith("-"))) {
21
+ throw new ToolValidationError({ message: `${field} is not a valid git ref`, field });
22
+ }
23
+ }
8
24
  function resolveProjectRoot(rawCwd, root = process.cwd()) {
9
25
  if (typeof rawCwd !== "string" || rawCwd.length === 0) return root;
10
26
  const base = resolve(root);
@@ -250,14 +266,10 @@ var plugin = {
250
266
  defaultPart = readDefaultPart(next);
251
267
  });
252
268
  async function performBump(part, dryRun, cwd) {
253
- const safeCwd = resolveProjectRoot(cwd);
254
- if (!safeCwd) {
255
- return { ok: false, error: "cwd must stay within the current project directory" };
256
- }
257
- cwd = safeCwd;
269
+ cwd = requireProjectRoot(cwd);
258
270
  const pkg = await getPackageJson(cwd);
259
271
  if (!pkg) {
260
- return { ok: false, error: "No package.json found" };
272
+ throw new Error("No package.json found");
261
273
  }
262
274
  const currentVersion = pkg.version;
263
275
  let bumpPart = part;
@@ -272,8 +284,7 @@ var plugin = {
272
284
  try {
273
285
  commits = await getRecentCommits(lastTag, cwd);
274
286
  } catch (err) {
275
- const msg = toErrorMessage(err);
276
- return { ok: false, error: `Git error: ${msg}`, bumpPart: "patch" };
287
+ throw new Error(`Git error: ${toErrorMessage(err)}`, { cause: err });
277
288
  }
278
289
  bumpPart = determineBump(commits);
279
290
  } else {
@@ -305,8 +316,7 @@ var plugin = {
305
316
  try {
306
317
  await runCommand(process.execPath, [bumpScript, "set", newVersion], root);
307
318
  } catch (err) {
308
- const msg = toErrorMessage(err);
309
- return { ok: false, error: `bump script failed: ${msg}` };
319
+ throw new Error(`bump script failed: ${toErrorMessage(err)}`, { cause: err });
310
320
  }
311
321
  for (const rel of ["package.json", "package-lock.json", "src/lib/utils.ts", "index.html"]) {
312
322
  const p = join(root, "website", rel);
@@ -323,16 +333,15 @@ var plugin = {
323
333
  try {
324
334
  pkgData = JSON.parse(await readFile(manifest, "utf-8"));
325
335
  } catch (err) {
326
- return {
327
- ok: false,
328
- error: `cannot bump: ${manifest} is not readable as JSON (${toErrorMessage(err)}). No manifests were modified.`
329
- };
336
+ throw new Error(
337
+ `cannot bump: ${manifest} is not readable as JSON (${toErrorMessage(err)}). No manifests were modified.`,
338
+ { cause: err }
339
+ );
330
340
  }
331
341
  if (!pkgData || typeof pkgData !== "object") {
332
- return {
333
- ok: false,
334
- error: `cannot bump: ${manifest} does not contain a JSON object. No manifests were modified.`
335
- };
342
+ throw new Error(
343
+ `cannot bump: ${manifest} does not contain a JSON object. No manifests were modified.`
344
+ );
336
345
  }
337
346
  pkgData.version = newVersion;
338
347
  pending.push({ path: manifest, contents: `${JSON.stringify(pkgData, null, 2)}
@@ -342,16 +351,20 @@ var plugin = {
342
351
  await writeFile(path, contents, "utf-8");
343
352
  }
344
353
  }
354
+ let commitError;
345
355
  try {
346
356
  await runGit(["add", "--", ...changed], cwd);
347
357
  await runGit(["commit", "-m", `chore: bump version to ${newVersion}`], cwd);
348
- } catch {
358
+ } catch (err) {
359
+ commitError = toErrorMessage(err);
349
360
  }
361
+ let tagError;
350
362
  if (autoTag) {
351
363
  try {
352
364
  const msg = tagMessage.replace("{{version}}", newVersion);
353
365
  await runGit(["tag", "-a", `${tagPrefix}${newVersion}`, "-m", msg], cwd);
354
- } catch {
366
+ } catch (err) {
367
+ tagError = toErrorMessage(err);
355
368
  }
356
369
  }
357
370
  api.log.info("semver-bump: bumped", { from: currentVersion, to: newVersion, bump: bumpPart });
@@ -371,13 +384,22 @@ var plugin = {
371
384
  commitCount: commits.length,
372
385
  breakingCount: commits.filter((c) => c.breaking).length
373
386
  };
387
+ const tagged = autoTag && tagError === void 0;
388
+ const warnings = [
389
+ ...commitError ? [`commit failed: ${commitError}`] : [],
390
+ ...tagError ? [`tag failed: ${tagError}`] : []
391
+ ];
374
392
  return {
375
393
  ok: true,
376
394
  currentVersion,
377
395
  newVersion,
378
396
  bump: bumpPart,
379
- tag: `${tagPrefix}${newVersion}`,
380
- message: `Bumped ${currentVersion} \u2192 ${newVersion} (${bumpPart})`
397
+ // Only name the tag when it was actually created.
398
+ tag: tagged ? `${tagPrefix}${newVersion}` : null,
399
+ committed: commitError === void 0,
400
+ tagged,
401
+ ...warnings.length > 0 ? { warnings } : {},
402
+ message: `Bumped ${currentVersion} \u2192 ${newVersion} (${bumpPart})` + (warnings.length > 0 ? ` \u2014 ${warnings.join("; ")}` : "")
381
403
  };
382
404
  }
383
405
  api.tools.register({
@@ -463,8 +485,12 @@ var plugin = {
463
485
  if (!safeCwd) {
464
486
  return { message: "cwd must stay within the current project directory" };
465
487
  }
466
- const result = await performBump(mode, dry, safeCwd);
467
- return { message: String(result["message"] ?? result["error"] ?? JSON.stringify(result)) };
488
+ try {
489
+ const result = await performBump(mode, dry, safeCwd);
490
+ return { message: String(result["message"] ?? JSON.stringify(result)) };
491
+ } catch (err) {
492
+ return { message: toErrorMessage(err) };
493
+ }
468
494
  }
469
495
  });
470
496
  api.tools.register({
@@ -481,11 +507,7 @@ var plugin = {
481
507
  async execute(input) {
482
508
  state.invocationCount += 1;
483
509
  state.perTool["semver_current"] = (state.perTool["semver_current"] ?? 0) + 1;
484
- const cwdInput = input["cwd"];
485
- const safeCwd = resolveProjectRoot(cwdInput);
486
- if (!safeCwd) {
487
- return { ok: false, error: "cwd must stay within the current project directory" };
488
- }
510
+ const safeCwd = requireProjectRoot(input["cwd"]);
489
511
  const pkg = await getPackageJson(safeCwd);
490
512
  const currentVersion = pkg?.version ?? "unknown";
491
513
  let latestTag = null;
@@ -531,22 +553,20 @@ var plugin = {
531
553
  state.perTool["semver_changelog"] = (state.perTool["semver_changelog"] ?? 0) + 1;
532
554
  const from = input["from"];
533
555
  const to = input["to"] ?? "HEAD";
534
- const cwd = input["cwd"];
535
- const safeCwd = resolveProjectRoot(cwd);
536
- if (!safeCwd) {
537
- return { ok: false, error: "cwd must stay within the current project directory" };
538
- }
556
+ requireGitRef("from", from);
557
+ requireGitRef("to", to);
558
+ const safeCwd = requireProjectRoot(input["cwd"]);
539
559
  const format = input["format"] ?? "markdown";
540
- const range = from ? `${from}..${to}` : to;
560
+ const rangeArgs = from ? [`${from}..${to}`] : ["-30", to];
541
561
  let commits;
542
562
  try {
543
563
  const output = await runGit(
544
- ["log", range === to ? "-30" : range, "--format=%H%x1f%s%x1f%b%x1e"],
564
+ ["log", ...rangeArgs, "--format=%H%x1f%s%x1f%b%x1e"],
545
565
  safeCwd
546
566
  );
547
567
  commits = parseGitLogOutput(output);
548
568
  } catch (err) {
549
- return { ok: false, error: `Failed to get git log: ${err}` };
569
+ throw new Error(`Failed to get git log: ${toErrorMessage(err)}`, { cause: err });
550
570
  }
551
571
  if (format === "json") {
552
572
  return {
@@ -238,8 +238,10 @@ var plugin = {
238
238
  const offTool = api.onPattern("tool.*", (eventName, payload) => {
239
239
  touchActivity();
240
240
  const p = payload;
241
- const toolName = p?.tool ?? p?.name ?? eventName;
242
- if (typeof toolName === "string") bumpToolCount(toolName);
241
+ const rawTool = p?.tool;
242
+ const nameOf = (v) => typeof v === "string" ? v : v && typeof v === "object" && typeof v.name === "string" ? v.name : void 0;
243
+ const toolName = nameOf(rawTool) ?? nameOf(p?.name) ?? eventName;
244
+ bumpToolCount(toolName);
243
245
  if (toolName === "git_autocommit" || toolName.startsWith("git ")) {
244
246
  }
245
247
  });
@@ -8,7 +8,7 @@
8
8
  * via the `directory` + `pattern` parameters. Pass `files` for specific
9
9
  * files, or `directory` (optionally with `pattern`) for recursive scanning.
10
10
  */
11
- import type { Plugin } from '@wrongstack/core/types';
11
+ import { type Plugin } from '@wrongstack/core/types';
12
12
  declare const plugin: Plugin;
13
13
  export default plugin;
14
14
  //# sourceMappingURL=index.d.ts.map
@@ -1,4 +1,5 @@
1
1
  // src/shell-check/index.ts
2
+ import { ToolValidationError } from "@wrongstack/core/types";
2
3
  import { execFile } from "node:child_process";
3
4
  import { readdir } from "node:fs/promises";
4
5
  import { isAbsolute, join, relative, resolve } from "node:path";
@@ -72,8 +73,11 @@ async function runShellCheck(files, severity, cwd) {
72
73
  }
73
74
  );
74
75
  });
75
- } catch {
76
- return [];
76
+ } catch (err) {
77
+ throw new Error(
78
+ `shellcheck failed without output: ${err instanceof Error ? err.message : String(err)}`,
79
+ { cause: err }
80
+ );
77
81
  }
78
82
  if (!raw.trim()) return [];
79
83
  try {
@@ -86,22 +90,31 @@ async function runShellCheck(files, severity, cwd) {
86
90
  code: item.code,
87
91
  message: item.message
88
92
  }));
89
- } catch {
90
- return [];
93
+ } catch (err) {
94
+ throw new Error(`shellcheck returned unparseable output: ${raw.trim().slice(0, 500)}`, {
95
+ cause: err
96
+ });
91
97
  }
92
98
  }
93
- async function findShellFiles(dir, pattern) {
99
+ async function findShellFiles(dir, pattern, isRoot = true) {
94
100
  const results = [];
95
101
  let entries;
96
102
  try {
97
103
  entries = await readdir(dir, { withFileTypes: true });
98
- } catch {
104
+ } catch (err) {
105
+ if (isRoot) {
106
+ throw new ToolValidationError({
107
+ message: `directory does not exist or cannot be read: ${dir}`,
108
+ field: "directory",
109
+ cause: err
110
+ });
111
+ }
99
112
  return results;
100
113
  }
101
114
  for (const entry of entries) {
102
115
  const full = join(dir, entry.name);
103
116
  if (entry.isDirectory() && entry.name !== "node_modules" && entry.name !== ".git") {
104
- results.push(...await findShellFiles(full, pattern));
117
+ results.push(...await findShellFiles(full, pattern, false));
105
118
  } else if (entry.isFile() && (entry.name.endsWith(".sh") || entry.name.endsWith(".bash") || entry.name.endsWith(".zsh") || entry.name === "Dockerfile" || entry.name === ".bashrc" || entry.name === ".zshrc")) {
106
119
  if (!pattern || entry.name.includes(pattern)) {
107
120
  results.push(full);
@@ -171,12 +184,8 @@ var plugin = {
171
184
  enum: ["error", "warning", "info", "style"],
172
185
  default: "warning",
173
186
  description: "Minimum severity level to report"
174
- },
175
- fix: {
176
- type: "boolean",
177
- default: false,
178
- description: "Apply safe automatic fixes where possible"
179
187
  }
188
+ // `fix` was declared ("apply safe automatic fixes") but never implemented.
180
189
  }
181
190
  },
182
191
  permission: "auto",
@@ -202,22 +211,16 @@ var plugin = {
202
211
  state.invocationCount += 1;
203
212
  const pathIsSafe = (p) => typeof p === "string" && p.length > 0 && p.length <= MAX_PATH_LEN && withinProject(p);
204
213
  if (!pathIsSafe(directory)) {
205
- return {
206
- ok: false,
207
- error: `directory path is outside the project root: ${directory}`,
208
- issues: [],
209
- filesScanned: 0,
210
- rejectedOutsideProject: true
211
- };
214
+ throw new ToolValidationError({
215
+ message: `directory path is outside the project root: ${directory}`,
216
+ field: "directory"
217
+ });
212
218
  }
213
219
  if (files?.some((f) => !pathIsSafe(f))) {
214
- return {
215
- ok: false,
216
- error: "one or more file paths are outside the project root",
217
- issues: [],
218
- filesScanned: 0,
219
- rejectedOutsideProject: true
220
- };
220
+ throw new ToolValidationError({
221
+ message: "one or more file paths are outside the project root",
222
+ field: "files"
223
+ });
221
224
  }
222
225
  let checkFiles;
223
226
  let scannedDirectories = false;
@@ -243,19 +246,7 @@ var plugin = {
243
246
  mode: scannedDirectories ? "directory" : "files"
244
247
  };
245
248
  }
246
- let issues;
247
- try {
248
- issues = await runShellCheck(checkFiles, severity);
249
- } catch (err) {
250
- const msg = err instanceof Error ? err.message : String(err);
251
- return {
252
- ok: false,
253
- error: msg,
254
- issues: [],
255
- filesScanned: 0,
256
- mode: scannedDirectories ? "directory" : "files"
257
- };
258
- }
249
+ const issues = await runShellCheck(checkFiles, severity);
259
250
  const byFile = {};
260
251
  for (const issue of issues) {
261
252
  if (byFile[issue.file] === void 0) {
@@ -18,7 +18,7 @@
18
18
  *
19
19
  * @public
20
20
  */
21
- import type { Plugin } from '@wrongstack/core/types';
21
+ import { type Plugin } from '@wrongstack/core/types';
22
22
  /** True when `name` is something this tool may safely substitute. */
23
23
  export declare function isIdentifier(name: string): boolean;
24
24
  declare const plugin: Plugin;
@@ -1,6 +1,7 @@
1
1
  // src/smart-rename/index.ts
2
2
  import { readFileSync, writeFileSync } from "node:fs";
3
3
  import { extname, isAbsolute, relative, resolve } from "node:path";
4
+ import { ToolValidationError } from "@wrongstack/core/types";
4
5
  var NEW_API_VERSION = "^0.1.10";
5
6
  var state = {
6
7
  renameCount: 0,
@@ -106,32 +107,44 @@ var plugin = {
106
107
  mutating: true,
107
108
  capabilities: ["fs.write"],
108
109
  async execute(input) {
109
- if (!cfg.enabled) return { ok: false, error: "smart-rename is disabled" };
110
+ if (!cfg.enabled) throw new Error("smart-rename is disabled");
110
111
  const inp = input ?? {};
111
112
  const rawPath = inp["path"] ?? inp["filePath"] ?? inp["file_path"] ?? inp["TargetFile"] ?? inp["targetFile"] ?? inp["file"];
112
113
  const oldName = inp["oldName"] ?? inp["old_name"] ?? inp["from"];
113
114
  const newName = inp["newName"] ?? inp["new_name"] ?? inp["to"];
114
115
  if (!rawPath || typeof rawPath !== "string") {
115
- return { ok: false, error: "path is required" };
116
+ throw new ToolValidationError({ message: "path is required", field: "path" });
116
117
  }
117
118
  if (!oldName || typeof oldName !== "string" || oldName.length === 0) {
118
- return { ok: false, error: "oldName is required" };
119
+ throw new ToolValidationError({ message: "oldName is required", field: "oldName" });
119
120
  }
120
121
  if (!newName || typeof newName !== "string" || newName.length === 0) {
121
- return { ok: false, error: "newName is required" };
122
+ throw new ToolValidationError({ message: "newName is required", field: "newName" });
122
123
  }
123
124
  if (!isIdentifier(oldName)) {
124
- return { ok: false, error: `oldName "${oldName}" is not a valid identifier` };
125
+ throw new ToolValidationError({
126
+ message: `oldName "${oldName}" is not a valid identifier`,
127
+ field: "oldName"
128
+ });
125
129
  }
126
130
  if (!isIdentifier(newName)) {
127
- return { ok: false, error: `newName "${newName}" is not a valid identifier` };
131
+ throw new ToolValidationError({
132
+ message: `newName "${newName}" is not a valid identifier`,
133
+ field: "newName"
134
+ });
128
135
  }
129
136
  if (!withinProject(rawPath)) {
130
- return { ok: false, error: "path is outside the project root" };
137
+ throw new ToolValidationError({
138
+ message: "path is outside the project root",
139
+ field: "path"
140
+ });
131
141
  }
132
142
  const ext = extname(rawPath).toLowerCase();
133
143
  if (!cfg.extensions.includes(ext)) {
134
- return { ok: false, error: `extension ${ext} is not allowed for rename` };
144
+ throw new ToolValidationError({
145
+ message: `extension ${ext} is not allowed for rename`,
146
+ field: "path"
147
+ });
135
148
  }
136
149
  const resolved = resolve(process.cwd(), rawPath);
137
150
  let content;
@@ -139,7 +152,7 @@ var plugin = {
139
152
  content = readFileSync(resolved, "utf-8");
140
153
  } catch (err) {
141
154
  state.errorCount += 1;
142
- return { ok: false, error: String(err) };
155
+ throw new Error(`Could not read ${rawPath}: ${String(err)}`, { cause: err });
143
156
  }
144
157
  const { preview, replacements } = renameInContent(content, oldName, newName);
145
158
  state.renameCount += 1;
@@ -152,7 +165,7 @@ var plugin = {
152
165
  writeFileSync(resolved, preview, "utf-8");
153
166
  } catch (err) {
154
167
  state.errorCount += 1;
155
- return { ok: false, error: String(err) };
168
+ throw new Error(`Could not write ${rawPath}: ${String(err)}`, { cause: err });
156
169
  }
157
170
  }
158
171
  return {
@@ -7,7 +7,7 @@
7
7
  * - template_create: Save a named template to the plugin store
8
8
  * - template_list: List all saved templates
9
9
  */
10
- import type { Plugin } from '@wrongstack/core/types';
10
+ import { type Plugin } from '@wrongstack/core/types';
11
11
  declare const plugin: Plugin;
12
12
  export default plugin;
13
13
  //# sourceMappingURL=index.d.ts.map