@wrongstack/plugins 0.285.0 → 0.287.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 (67) 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 +30 -26
  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 +21 -14
  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 +34 -27
  11. package/dist/commit-validator.js +1 -1
  12. package/dist/config-validator.js +1 -1
  13. package/dist/cron/index.d.ts.map +1 -1
  14. package/dist/cron.js +29 -0
  15. package/dist/dead-code-detector/index.d.ts.map +1 -1
  16. package/dist/dead-code-detector.js +30 -22
  17. package/dist/dep-guard.js +1 -1
  18. package/dist/diff-summary/index.d.ts.map +1 -1
  19. package/dist/diff-summary.js +19 -11
  20. package/dist/doc-sync-guard/index.d.ts.map +1 -1
  21. package/dist/doc-sync-guard.js +24 -15
  22. package/dist/duplicate-code-detector/index.d.ts.map +1 -1
  23. package/dist/duplicate-code-detector.js +27 -18
  24. package/dist/error-lens.js +1 -1
  25. package/dist/feature-flag-tracker/index.d.ts.map +1 -1
  26. package/dist/feature-flag-tracker.js +27 -19
  27. package/dist/file-watcher/index.d.ts.map +1 -1
  28. package/dist/file-watcher.js +15 -2
  29. package/dist/format-on-save/index.d.ts.map +1 -1
  30. package/dist/format-on-save.js +19 -11
  31. package/dist/git-autocommit.js +1 -1
  32. package/dist/import-organizer/index.d.ts.map +1 -1
  33. package/dist/import-organizer.js +22 -13
  34. package/dist/index.d.ts +4 -4
  35. package/dist/index.js +1007 -759
  36. package/dist/interface-contract-guard/index.d.ts.map +1 -1
  37. package/dist/interface-contract-guard.js +27 -19
  38. package/dist/license-audit-gate/index.d.ts.map +1 -1
  39. package/dist/license-audit-gate.js +1 -2
  40. package/dist/migration-planner/index.d.ts +12 -2
  41. package/dist/migration-planner/index.d.ts.map +1 -1
  42. package/dist/migration-planner.js +210 -28
  43. package/dist/pr-drafter.js +1 -1
  44. package/dist/refactor-suggester/index.d.ts.map +1 -1
  45. package/dist/refactor-suggester.js +37 -29
  46. package/dist/release-notes-generator/index.d.ts +3 -1
  47. package/dist/release-notes-generator/index.d.ts.map +1 -1
  48. package/dist/release-notes-generator.js +152 -12
  49. package/dist/runtime/index.d.ts +12 -0
  50. package/dist/runtime/index.d.ts.map +1 -1
  51. package/dist/runtime/llm.d.ts +33 -0
  52. package/dist/runtime/llm.d.ts.map +1 -0
  53. package/dist/runtime.js +57 -2
  54. package/dist/schema-evolution-guard/index.d.ts.map +1 -1
  55. package/dist/schema-evolution-guard.js +21 -12
  56. package/dist/security-hotspot-scanner/index.d.ts.map +1 -1
  57. package/dist/security-hotspot-scanner.js +25 -16
  58. package/dist/session-recap.js +1 -1
  59. package/dist/spec-linker.js +1007 -759
  60. package/dist/test-generator/index.d.ts +5 -3
  61. package/dist/test-generator/index.d.ts.map +1 -1
  62. package/dist/test-generator.js +183 -41
  63. package/dist/test-runner-gate/index.d.ts.map +1 -1
  64. package/dist/test-runner-gate.js +65 -43
  65. package/dist/type-gate/index.d.ts.map +1 -1
  66. package/dist/type-gate.js +5 -13
  67. package/package.json +3 -3
@@ -1,6 +1,25 @@
1
1
  // src/dead-code-detector/index.ts
2
2
  import { readdirSync, readFileSync, statSync } from "node:fs";
3
- import { extname, isAbsolute, join, relative, resolve } from "node:path";
3
+ import { extname, join, relative as relative2, resolve as resolve2 } from "node:path";
4
+
5
+ // src/runtime/index.ts
6
+ import { basename, isAbsolute, relative, resolve } from "node:path";
7
+ var MAX_BUFFER_BYTES = 16 * 1024 * 1024;
8
+ function hasLeadingDash(arg) {
9
+ return arg.length > 0 && arg.startsWith("-");
10
+ }
11
+ function withinProjectPath(projectRoot, candidate) {
12
+ if (candidate.length === 0 || candidate.length > 4096) return false;
13
+ if (hasLeadingDash(candidate)) return false;
14
+ const resolved = isAbsolute(candidate) ? resolve(candidate) : resolve(projectRoot, candidate);
15
+ const rel = relative(projectRoot, resolved);
16
+ return rel === "" || !rel.startsWith("..") && !isAbsolute(rel);
17
+ }
18
+ function withinProject(p) {
19
+ return withinProjectPath(process.cwd(), p) || relative(process.cwd(), p) === ".";
20
+ }
21
+
22
+ // src/dead-code-detector/index.ts
4
23
  var API_VERSION = "^0.1.10";
5
24
  var state = {
6
25
  scanCount: 0,
@@ -51,7 +70,7 @@ function gatherFiles(root, depth, cfg) {
51
70
  }
52
71
  }
53
72
  }
54
- walk(resolve(root), depth);
73
+ walk(resolve2(root), depth);
55
74
  return files;
56
75
  }
57
76
  function stripNoise(content) {
@@ -73,9 +92,8 @@ var DECLARATION_EXPORT_RE = /export\s+(?:async\s+|abstract\s+)?(?:const|let|var|
73
92
  var NAMED_EXPORT_RE = /export\s*\{([^}]+)\}/g;
74
93
  function extractExports(content, filePath) {
75
94
  const exports = [];
76
- let match;
77
95
  DECLARATION_EXPORT_RE.lastIndex = 0;
78
- while ((match = DECLARATION_EXPORT_RE.exec(content)) !== null) {
96
+ for (const match of content.matchAll(DECLARATION_EXPORT_RE)) {
79
97
  exports.push({
80
98
  identifier: match[1],
81
99
  file: filePath,
@@ -84,7 +102,7 @@ function extractExports(content, filePath) {
84
102
  });
85
103
  }
86
104
  NAMED_EXPORT_RE.lastIndex = 0;
87
- while ((match = NAMED_EXPORT_RE.exec(content)) !== null) {
105
+ for (const match of content.matchAll(NAMED_EXPORT_RE)) {
88
106
  const line = lineNumber(content, match.index);
89
107
  const names = match[1].split(",").map((s) => s.trim()).filter(Boolean);
90
108
  for (const name of names) {
@@ -135,22 +153,12 @@ function scan(root, depth, cfg) {
135
153
  }
136
154
  return { findings: findUnusedSymbols(files), scannedFiles: files.length };
137
155
  }
138
- function withinProject(p) {
139
- if (typeof p !== "string" || p.length === 0 || p.length > 4096) return false;
140
- const root = process.cwd();
141
- const resolved = isAbsolute(p) ? resolve(p) : resolve(root, p);
142
- const rel = relative(root, resolved);
143
- if (rel === "" || rel === ".") return true;
144
- if (rel.startsWith("..")) return false;
145
- if (isAbsolute(rel)) return false;
146
- return true;
147
- }
148
156
  function resolveScanRoot(rawPath) {
149
- const resolved = resolve(process.cwd(), rawPath);
157
+ const resolved = resolve2(process.cwd(), rawPath);
150
158
  try {
151
159
  const stats = statSync(resolved);
152
160
  if (!stats.isDirectory()) {
153
- return resolve(resolved, "..");
161
+ return resolve2(resolved, "..");
154
162
  }
155
163
  } catch {
156
164
  }
@@ -160,7 +168,7 @@ function toPosix(p) {
160
168
  return p.replace(/\\/g, "/");
161
169
  }
162
170
  function relativePath(p) {
163
- return toPosix(relative(process.cwd(), p));
171
+ return toPosix(relative2(process.cwd(), p));
164
172
  }
165
173
  var plugin = {
166
174
  name: "dead-code-detector",
@@ -227,12 +235,12 @@ var plugin = {
227
235
  let result;
228
236
  try {
229
237
  result = scan(scanRoot, 1, cfg);
230
- } catch (err) {
238
+ } catch {
231
239
  state.errorCount += 1;
232
240
  return;
233
241
  }
234
- const changedFile = resolve(process.cwd(), sourcePath);
235
- const relevant = result.findings.filter((f) => resolve(f.file) === changedFile);
242
+ const changedFile = resolve2(process.cwd(), sourcePath);
243
+ const relevant = result.findings.filter((f) => resolve2(f.file) === changedFile);
236
244
  if (relevant.length === 0) {
237
245
  state.missCount += 1;
238
246
  return;
@@ -248,7 +256,7 @@ ${list}
248
256
  Consider removing the export if it is not part of the public API.`;
249
257
  return { additionalContext: message };
250
258
  };
251
- state.hookUnregister = api.registerHook("PostToolUse", "write|edit", hook);
259
+ state.hookUnregister = api.registerHook("PostToolUse", "write|edit", hook, { background: true });
252
260
  api.tools.register({
253
261
  name: "dead_code_scan",
254
262
  description: "Scan TypeScript/JavaScript source files for exported symbols that appear unused anywhere in the project.",
package/dist/dep-guard.js CHANGED
@@ -138,7 +138,7 @@ var plugin = {
138
138
  version: "0.1.0",
139
139
  description: "Supervises dependency installs: blocks deny-listed packages, flags typosquat lookalikes, and optionally warns on unpinned versions",
140
140
  apiVersion: "^0.1.10",
141
- capabilities: { tools: true, hooks: true },
141
+ capabilities: { tools: true, hooks: true, llm: true },
142
142
  defaultConfig: { ...DEFAULTS },
143
143
  configSchema: {
144
144
  type: "object",
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/diff-summary/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAIH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAgQ/C,QAAA,MAAM,MAAM,EAAE,MA4Pb,CAAC;eAEa,MAAM"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/diff-summary/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAGH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAwP/C,QAAA,MAAM,MAAM,EAAE,MA4Pb,CAAC;eAEa,MAAM"}
@@ -1,17 +1,25 @@
1
1
  // src/diff-summary/index.ts
2
2
  import { execFileSync } from "node:child_process";
3
- import { isAbsolute, relative, resolve } from "node:path";
4
- var API_VERSION = "^0.1.10";
3
+
4
+ // src/runtime/index.ts
5
+ import { basename, isAbsolute, relative, resolve } from "node:path";
6
+ var MAX_BUFFER_BYTES = 16 * 1024 * 1024;
7
+ function hasLeadingDash(arg) {
8
+ return arg.length > 0 && arg.startsWith("-");
9
+ }
10
+ function withinProjectPath(projectRoot, candidate) {
11
+ if (candidate.length === 0 || candidate.length > 4096) return false;
12
+ if (hasLeadingDash(candidate)) return false;
13
+ const resolved = isAbsolute(candidate) ? resolve(candidate) : resolve(projectRoot, candidate);
14
+ const rel = relative(projectRoot, resolved);
15
+ return rel === "" || !rel.startsWith("..") && !isAbsolute(rel);
16
+ }
5
17
  function withinProject(p) {
6
- if (typeof p !== "string" || p.length === 0 || p.length > 4096) return false;
7
- const root = process.cwd();
8
- const resolved = isAbsolute(p) ? resolve(p) : resolve(root, p);
9
- const rel = relative(root, resolved);
10
- if (rel === "" || rel === ".") return true;
11
- if (rel.startsWith("..")) return false;
12
- if (isAbsolute(rel)) return false;
13
- return true;
18
+ return withinProjectPath(process.cwd(), p) || relative(process.cwd(), p) === ".";
14
19
  }
20
+
21
+ // src/diff-summary/index.ts
22
+ var API_VERSION = "^0.1.10";
15
23
  var state = {
16
24
  invocationCount: 0,
17
25
  /** Times a diff was successfully generated and injected. */
@@ -243,7 +251,7 @@ var plugin = {
243
251
  additionalContext: header + summary
244
252
  };
245
253
  };
246
- state.hookUnregister = api.registerHook("PostToolUse", "write|edit", hook);
254
+ state.hookUnregister = api.registerHook("PostToolUse", "write|edit", hook, { background: true });
247
255
  api.tools.register({
248
256
  name: "diff_summary_status",
249
257
  description: "Reports diff-summary state: mode, maxLines, and per-session invocation/injected/fallback counters.",
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/doc-sync-guard/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAGH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAkJ/C,QAAA,MAAM,MAAM,EAAE,MAuJb,CAAC;eAEa,MAAM"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/doc-sync-guard/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAGH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AA0I/C,QAAA,MAAM,MAAM,EAAE,MAuJb,CAAC;eAEa,MAAM"}
@@ -1,5 +1,24 @@
1
1
  // src/doc-sync-guard/index.ts
2
- import { isAbsolute, relative, resolve, basename, extname } from "node:path";
2
+ import { basename as basename2, extname } from "node:path";
3
+
4
+ // src/runtime/index.ts
5
+ import { basename, isAbsolute, relative, resolve } from "node:path";
6
+ var MAX_BUFFER_BYTES = 16 * 1024 * 1024;
7
+ function hasLeadingDash(arg) {
8
+ return arg.length > 0 && arg.startsWith("-");
9
+ }
10
+ function withinProjectPath(projectRoot, candidate) {
11
+ if (candidate.length === 0 || candidate.length > 4096) return false;
12
+ if (hasLeadingDash(candidate)) return false;
13
+ const resolved = isAbsolute(candidate) ? resolve(candidate) : resolve(projectRoot, candidate);
14
+ const rel = relative(projectRoot, resolved);
15
+ return rel === "" || !rel.startsWith("..") && !isAbsolute(rel);
16
+ }
17
+ function withinProject(p) {
18
+ return withinProjectPath(process.cwd(), p) || relative(process.cwd(), p) === ".";
19
+ }
20
+
21
+ // src/doc-sync-guard/index.ts
3
22
  var API_VERSION = "^0.1.10";
4
23
  var state = {
5
24
  changedFiles: [],
@@ -24,16 +43,6 @@ function readConfig(raw) {
24
43
  maxTrackedFiles: typeof r["maxTrackedFiles"] === "number" && r["maxTrackedFiles"] >= 1 && r["maxTrackedFiles"] <= 200 ? r["maxTrackedFiles"] : DEFAULTS.maxTrackedFiles
25
44
  };
26
45
  }
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
46
  function normalizeSlashes(p) {
38
47
  return p.replace(/\\/g, "/");
39
48
  }
@@ -45,14 +54,14 @@ function isPublicSource(p, extensions) {
45
54
  if (!isSourceFile(p, extensions)) return false;
46
55
  const norm = normalizeSlashes(p);
47
56
  if (norm.includes("/node_modules/") || norm.startsWith("node_modules/")) return false;
48
- const base = basename(norm).toLowerCase();
57
+ const base = basename2(norm).toLowerCase();
49
58
  if (/\.(test|spec)\./.test(base)) return false;
50
59
  if (base.startsWith("_")) return false;
51
60
  return true;
52
61
  }
53
62
  function isDocFile(p, docNames) {
54
63
  const norm = normalizeSlashes(p);
55
- const base = basename(norm);
64
+ const base = basename2(norm);
56
65
  const lowerBase = base.toLowerCase();
57
66
  if (docNames.some((name) => lowerBase === name.toLowerCase())) return true;
58
67
  if (norm.includes("/docs/") || norm.startsWith("docs/")) return true;
@@ -70,7 +79,7 @@ function extractDocContent(toolInput) {
70
79
  return void 0;
71
80
  }
72
81
  function referenceTokens(p) {
73
- const base = basename(p);
82
+ const base = basename2(p);
74
83
  const withoutExt = base.replace(/\.[^.]+$/, "");
75
84
  const tokens = [base];
76
85
  if (withoutExt && withoutExt !== base) tokens.push(withoutExt);
@@ -155,7 +164,7 @@ Consider mentioning these changes in the documentation.`;
155
164
  return { additionalContext: message };
156
165
  }
157
166
  };
158
- state.hookUnregister = api.registerHook("PostToolUse", "write|edit", hook);
167
+ state.hookUnregister = api.registerHook("PostToolUse", "write|edit", hook, { background: true });
159
168
  api.tools.register({
160
169
  name: "doc_sync_status",
161
170
  description: "Reports doc-sync-guard state: tracked changed files, doc-write count, and warning count.",
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/duplicate-code-detector/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAIH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAsP/C,QAAA,MAAM,MAAM,EAAE,MA8Qb,CAAC;eAEa,MAAM"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/duplicate-code-detector/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAIH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AA8O/C,QAAA,MAAM,MAAM,EAAE,MA8Qb,CAAC;eAEa,MAAM"}
@@ -1,6 +1,25 @@
1
1
  // src/duplicate-code-detector/index.ts
2
2
  import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
3
- import { extname, isAbsolute, relative, resolve } from "node:path";
3
+ import { extname, isAbsolute as isAbsolute2, relative as relative2, resolve as resolve2 } from "node:path";
4
+
5
+ // src/runtime/index.ts
6
+ import { basename, isAbsolute, relative, resolve } from "node:path";
7
+ var MAX_BUFFER_BYTES = 16 * 1024 * 1024;
8
+ function hasLeadingDash(arg) {
9
+ return arg.length > 0 && arg.startsWith("-");
10
+ }
11
+ function withinProjectPath(projectRoot, candidate) {
12
+ if (candidate.length === 0 || candidate.length > 4096) return false;
13
+ if (hasLeadingDash(candidate)) return false;
14
+ const resolved = isAbsolute(candidate) ? resolve(candidate) : resolve(projectRoot, candidate);
15
+ const rel = relative(projectRoot, resolved);
16
+ return rel === "" || !rel.startsWith("..") && !isAbsolute(rel);
17
+ }
18
+ function withinProject(p) {
19
+ return withinProjectPath(process.cwd(), p) || relative(process.cwd(), p) === ".";
20
+ }
21
+
22
+ // src/duplicate-code-detector/index.ts
4
23
  var API_VERSION = "^0.1.10";
5
24
  var state = {
6
25
  scanCount: 0,
@@ -31,16 +50,6 @@ function readConfig(raw) {
31
50
  maxFindings: typeof r["maxFindings"] === "number" && r["maxFindings"] >= 1 && r["maxFindings"] <= 500 ? r["maxFindings"] : DEFAULTS.maxFindings
32
51
  };
33
52
  }
34
- function withinProject(p) {
35
- if (typeof p !== "string" || p.length === 0 || p.length > 4096) return false;
36
- const root = process.cwd();
37
- const resolved = isAbsolute(p) ? resolve(p) : resolve(root, p);
38
- const rel = relative(root, resolved);
39
- if (rel === "" || rel === ".") return true;
40
- if (rel.startsWith("..")) return false;
41
- if (isAbsolute(rel)) return false;
42
- return true;
43
- }
44
53
  function matchesExtension(p, exts) {
45
54
  const ext = extname(p).toLowerCase();
46
55
  return exts.includes(ext);
@@ -62,7 +71,7 @@ function collectSourceFiles(root, cfg) {
62
71
  return;
63
72
  }
64
73
  for (const entry of entries) {
65
- const full = resolve(dir, entry);
74
+ const full = resolve2(dir, entry);
66
75
  let st;
67
76
  try {
68
77
  st = statSync(full);
@@ -83,7 +92,7 @@ function toPosix(p) {
83
92
  return p.replace(/\\/g, "/");
84
93
  }
85
94
  function relativePath(p) {
86
- return toPosix(relative(process.cwd(), p));
95
+ return toPosix(relative2(process.cwd(), p));
87
96
  }
88
97
  function removeInlineComments(line) {
89
98
  return line.replace(/\/\/.*$/, "").replace(/\/\*[\s\S]*?\*\//, "");
@@ -141,7 +150,7 @@ function findDuplicates(files, minLines, maxFindings) {
141
150
  }
142
151
  function scanPath(rawPath, cfg) {
143
152
  const root = process.cwd();
144
- const resolved = isAbsolute(rawPath) ? resolve(rawPath) : resolve(root, rawPath);
153
+ const resolved = isAbsolute2(rawPath) ? resolve2(rawPath) : resolve2(root, rawPath);
145
154
  const filePaths = collectSourceFiles(resolved, cfg);
146
155
  const files = /* @__PURE__ */ new Map();
147
156
  for (const p of filePaths) {
@@ -226,7 +235,7 @@ var plugin = {
226
235
  const now = Date.now();
227
236
  const lastWarning = state.lastHookWarning.get(sourcePath);
228
237
  if (lastWarning !== void 0 && now - lastWarning < 6e4) return;
229
- const changedFile = resolve(process.cwd(), sourcePath);
238
+ const changedFile = resolve2(process.cwd(), sourcePath);
230
239
  let content;
231
240
  try {
232
241
  content = readFileSync(changedFile, "utf-8");
@@ -236,7 +245,7 @@ var plugin = {
236
245
  }
237
246
  const changedWindows = extractWindows(changedFile, content, cfg.minLines);
238
247
  if (changedWindows.length === 0) return;
239
- const projectRoot = resolve(process.cwd());
248
+ const projectRoot = resolve2(process.cwd());
240
249
  let otherFiles;
241
250
  try {
242
251
  const filePaths = collectSourceFiles(projectRoot, cfg).filter((p) => p !== changedFile);
@@ -272,7 +281,7 @@ var plugin = {
272
281
  contextAs: "separate"
273
282
  };
274
283
  };
275
- state.hookUnregister = api.registerHook("PostToolUse", "write|edit", hook);
284
+ state.hookUnregister = api.registerHook("PostToolUse", "write|edit", hook, { background: true });
276
285
  api.tools.register({
277
286
  name: "detect_duplicate_code",
278
287
  description: "Scan source files for duplicated code blocks. Uses normalized-line fingerprinting to find identical multi-line blocks across files.",
@@ -302,7 +311,7 @@ var plugin = {
302
311
  state.findingCount += result.findings.length;
303
312
  return {
304
313
  ok: true,
305
- path: relativePath(resolve(process.cwd(), rawPath)),
314
+ path: relativePath(resolve2(process.cwd(), rawPath)),
306
315
  scannedFiles: result.scannedFiles,
307
316
  minLines: cfg.minLines,
308
317
  findings: result.findings
@@ -85,7 +85,7 @@ var plugin = {
85
85
  version: "0.1.0",
86
86
  description: "Distills failed command output into a compact digest (error line + project stack frames) and flags repeated failures",
87
87
  apiVersion: "^0.1.10",
88
- capabilities: { tools: true, hooks: true },
88
+ capabilities: { tools: true, hooks: true, llm: true },
89
89
  defaultConfig: { ...DEFAULTS },
90
90
  configSchema: {
91
91
  type: "object",
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/feature-flag-tracker/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAIH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAQ/C,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;CACjB;AA8LD,QAAA,MAAM,MAAM,EAAE,MA+Mb,CAAC;eAEa,MAAM"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/feature-flag-tracker/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAIH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAS/C,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;CACjB;AAoLD,QAAA,MAAM,MAAM,EAAE,MA+Mb,CAAC;eAEa,MAAM"}
@@ -1,6 +1,25 @@
1
1
  // src/feature-flag-tracker/index.ts
2
2
  import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
3
- import { extname, isAbsolute, relative, resolve } from "node:path";
3
+ import { extname, isAbsolute as isAbsolute2, relative as relative2, resolve as resolve2 } from "node:path";
4
+
5
+ // src/runtime/index.ts
6
+ import { basename, isAbsolute, relative, resolve } from "node:path";
7
+ var MAX_BUFFER_BYTES = 16 * 1024 * 1024;
8
+ function hasLeadingDash(arg) {
9
+ return arg.length > 0 && arg.startsWith("-");
10
+ }
11
+ function withinProjectPath(projectRoot, candidate) {
12
+ if (candidate.length === 0 || candidate.length > 4096) return false;
13
+ if (hasLeadingDash(candidate)) return false;
14
+ const resolved = isAbsolute(candidate) ? resolve(candidate) : resolve(projectRoot, candidate);
15
+ const rel = relative(projectRoot, resolved);
16
+ return rel === "" || !rel.startsWith("..") && !isAbsolute(rel);
17
+ }
18
+ function withinProject(p) {
19
+ return withinProjectPath(process.cwd(), p) || relative(process.cwd(), p) === ".";
20
+ }
21
+
22
+ // src/feature-flag-tracker/index.ts
4
23
  var API_VERSION = "^0.1.10";
5
24
  var state = {
6
25
  scanCount: 0,
@@ -32,16 +51,6 @@ function readConfig(raw) {
32
51
  maxFindings: typeof r["maxFindings"] === "number" && r["maxFindings"] >= 1 && r["maxFindings"] <= 500 ? r["maxFindings"] : DEFAULTS.maxFindings
33
52
  };
34
53
  }
35
- function withinProject(p) {
36
- if (typeof p !== "string" || p.length === 0 || p.length > 4096) return false;
37
- const root = process.cwd();
38
- const resolved = isAbsolute(p) ? resolve(p) : resolve(root, p);
39
- const rel = relative(root, resolved);
40
- if (rel === "" || rel === ".") return true;
41
- if (rel.startsWith("..")) return false;
42
- if (isAbsolute(rel)) return false;
43
- return true;
44
- }
45
54
  function normalizeExtensions(exts) {
46
55
  return exts.map((e) => e.startsWith(".") ? e.toLowerCase() : `.${e.toLowerCase()}`);
47
56
  }
@@ -66,7 +75,7 @@ function collectSourceFiles(root, exts) {
66
75
  }
67
76
  for (const entry of entries) {
68
77
  if (entry === "node_modules" || entry === "dist" || entry === ".git" || entry === "coverage") continue;
69
- const full = resolve(dir, entry);
78
+ const full = resolve2(dir, entry);
70
79
  let st;
71
80
  try {
72
81
  st = statSync(full);
@@ -87,7 +96,7 @@ function toPosix(p) {
87
96
  return p.replace(/\\/g, "/");
88
97
  }
89
98
  function relativePath(p) {
90
- return toPosix(relative(process.cwd(), p));
99
+ return toPosix(relative2(process.cwd(), p));
91
100
  }
92
101
  function compilePatterns(patterns) {
93
102
  const sources = [...DEFAULT_PATTERNS, ...patterns];
@@ -98,8 +107,7 @@ function scanFile(filePath, content, patterns, maxFindings) {
98
107
  const lines = content.split(/\r?\n/);
99
108
  for (const re of patterns) {
100
109
  re.lastIndex = 0;
101
- let m;
102
- while ((m = re.exec(content)) !== null) {
110
+ for (const m of content.matchAll(re)) {
103
111
  const flag = m[1] ?? m[0];
104
112
  const lineNo = content.slice(0, m.index).split(/\r?\n/).length;
105
113
  const context = (lines[lineNo - 1] ?? "").trim();
@@ -118,7 +126,7 @@ function scanFile(filePath, content, patterns, maxFindings) {
118
126
  }
119
127
  function scanPath(rawPath, cfg) {
120
128
  const root = process.cwd();
121
- const resolved = isAbsolute(rawPath) ? resolve(rawPath) : resolve(root, rawPath);
129
+ const resolved = isAbsolute2(rawPath) ? resolve2(rawPath) : resolve2(root, rawPath);
122
130
  const exts = normalizeExtensions(cfg.extensions);
123
131
  const files = collectSourceFiles(resolved, exts);
124
132
  const patterns = compilePatterns(cfg.patterns);
@@ -189,7 +197,7 @@ var plugin = {
189
197
  const exts = normalizeExtensions(cfg.extensions);
190
198
  if (!matchesExtension(sourcePath, exts)) return;
191
199
  state.hookInvocationCount += 1;
192
- const resolved = resolve(process.cwd(), sourcePath);
200
+ const resolved = resolve2(process.cwd(), sourcePath);
193
201
  let content;
194
202
  try {
195
203
  content = readFileSync(resolved, "utf-8");
@@ -208,7 +216,7 @@ var plugin = {
208
216
  Make sure flag behavior is intentional and consider updating flag inventory/docs.`
209
217
  };
210
218
  };
211
- state.hookUnregister = api.registerHook("PostToolUse", "write|edit", hook);
219
+ state.hookUnregister = api.registerHook("PostToolUse", "write|edit", hook, { background: true });
212
220
  api.tools.register({
213
221
  name: "scan_feature_flags",
214
222
  description: "Scan source files for feature-flag-like expressions (isFeatureEnabled, featureFlags.*, useFeatureFlag, flags.*, plus custom patterns).",
@@ -238,7 +246,7 @@ Make sure flag behavior is intentional and consider updating flag inventory/docs
238
246
  state.flagCount += result.usages.length;
239
247
  return {
240
248
  ok: true,
241
- path: relativePath(resolve(process.cwd(), rawPath)),
249
+ path: relativePath(resolve2(process.cwd(), rawPath)),
242
250
  scannedFiles: result.scannedFiles,
243
251
  usages: result.usages
244
252
  };
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/file-watcher/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AA2D/C,QAAA,MAAM,MAAM,EAAE,MAsWb,CAAC;eAEa,MAAM"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/file-watcher/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AA2D/C,QAAA,MAAM,MAAM,EAAE,MAqXb,CAAC;eAEa,MAAM"}
@@ -294,6 +294,7 @@ var plugin = {
294
294
  api.log.info("file-watcher plugin loaded", { version: "0.1.0" });
295
295
  },
296
296
  teardown(api) {
297
+ const closed = watches.size;
297
298
  for (const handle of watches.values()) {
298
299
  for (const w of handle.watchers) {
299
300
  try {
@@ -306,9 +307,21 @@ var plugin = {
306
307
  for (const t of debounceTimers.values()) clearTimeout(t);
307
308
  debounceTimers.clear();
308
309
  api.log.info("file-watcher: teardown complete", {
309
- closed: 0
310
- // recorded for log symmetry; actual count cleared above
310
+ closed
311
311
  });
312
+ },
313
+ async health() {
314
+ const watcherCount = Array.from(watches.values()).reduce(
315
+ (sum, handle) => sum + handle.watchers.length,
316
+ 0
317
+ );
318
+ return {
319
+ ok: true,
320
+ message: `file-watcher: ${watches.size} active watch group(s), ${watcherCount} filesystem watcher(s)`,
321
+ activeWatchGroups: watches.size,
322
+ filesystemWatchers: watcherCount,
323
+ pendingDebounces: debounceTimers.size
324
+ };
312
325
  }
313
326
  };
314
327
  var file_watcher_default = plugin;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/format-on-save/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAKH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAmO/C,QAAA,MAAM,MAAM,EAAE,MA2Ob,CAAC;eAEa,MAAM"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/format-on-save/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAIH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AA2N/C,QAAA,MAAM,MAAM,EAAE,MA2Ob,CAAC;eAEa,MAAM"}
@@ -1,18 +1,26 @@
1
1
  // src/format-on-save/index.ts
2
2
  import { execFileSync, execSync } from "node:child_process";
3
3
  import { existsSync, statSync } from "node:fs";
4
- import { isAbsolute, relative, resolve } from "node:path";
5
- var API_VERSION = "^0.1.10";
4
+
5
+ // src/runtime/index.ts
6
+ import { basename, isAbsolute, relative, resolve } from "node:path";
7
+ var MAX_BUFFER_BYTES = 16 * 1024 * 1024;
8
+ function hasLeadingDash(arg) {
9
+ return arg.length > 0 && arg.startsWith("-");
10
+ }
11
+ function withinProjectPath(projectRoot, candidate) {
12
+ if (candidate.length === 0 || candidate.length > 4096) return false;
13
+ if (hasLeadingDash(candidate)) return false;
14
+ const resolved = isAbsolute(candidate) ? resolve(candidate) : resolve(projectRoot, candidate);
15
+ const rel = relative(projectRoot, resolved);
16
+ return rel === "" || !rel.startsWith("..") && !isAbsolute(rel);
17
+ }
6
18
  function withinProject(p) {
7
- if (typeof p !== "string" || p.length === 0 || p.length > 4096) return false;
8
- const root = process.cwd();
9
- const resolved = isAbsolute(p) ? resolve(p) : resolve(root, p);
10
- const rel = relative(root, resolved);
11
- if (rel === "" || rel === ".") return true;
12
- if (rel.startsWith("..")) return false;
13
- if (isAbsolute(rel)) return false;
14
- return true;
19
+ return withinProjectPath(process.cwd(), p) || relative(process.cwd(), p) === ".";
15
20
  }
21
+
22
+ // src/format-on-save/index.ts
23
+ var API_VERSION = "^0.1.10";
16
24
  var state = {
17
25
  invocationCount: 0,
18
26
  /** Times formatting was applied (file changed). */
@@ -219,7 +227,7 @@ var plugin = {
219
227
  state.cleanCount += 1;
220
228
  return;
221
229
  };
222
- state.hookUnregister = api.registerHook("PostToolUse", "write|edit", hook);
230
+ state.hookUnregister = api.registerHook("PostToolUse", "write|edit", hook, { background: true });
223
231
  state.patternUnregister = api.onPattern(
224
232
  "import-organizer:done",
225
233
  (_eventName, payload) => {
@@ -160,7 +160,7 @@ var plugin = {
160
160
  version: "0.2.0",
161
161
  description: "AI-powered git staging and conventional commit message generation",
162
162
  apiVersion: API_VERSION,
163
- capabilities: { tools: true },
163
+ capabilities: { tools: true, llm: true },
164
164
  defaultConfig: {
165
165
  conventionalCommits: true,
166
166
  autoStage: false,
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/import-organizer/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6CG;AAKH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AA4R/C,QAAA,MAAM,MAAM,EAAE,MA8Nb,CAAC;eAEa,MAAM"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/import-organizer/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6CG;AAKH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAoR/C,QAAA,MAAM,MAAM,EAAE,MA8Nb,CAAC;eAEa,MAAM"}
@@ -1,17 +1,26 @@
1
1
  // src/import-organizer/index.ts
2
2
  import { spawn } from "node:child_process";
3
3
  import { existsSync, statSync } from "node:fs";
4
+ import { basename as basename2, isAbsolute as isAbsolute2 } from "node:path";
5
+
6
+ // src/runtime/index.ts
4
7
  import { basename, 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("-");
11
+ }
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);
18
+ }
5
19
  function withinProject(p) {
6
- if (typeof p !== "string" || p.length === 0 || p.length > 4096) return false;
7
- const root = process.cwd();
8
- const resolved = isAbsolute(p) ? resolve(p) : resolve(root, p);
9
- const rel = relative(root, resolved);
10
- if (rel === "" || rel === ".") return true;
11
- if (rel.startsWith("..")) return false;
12
- if (isAbsolute(rel)) return false;
13
- return true;
20
+ return withinProjectPath(process.cwd(), p) || relative(process.cwd(), p) === ".";
14
21
  }
22
+
23
+ // src/import-organizer/index.ts
15
24
  var ALLOWED_FIRST_TOKENS = /* @__PURE__ */ new Set([
16
25
  "npx",
17
26
  "pnpm",
@@ -31,13 +40,13 @@ function resolveAllowedCommand(command) {
31
40
  if (ALLOWED_FIRST_TOKENS.has(head)) {
32
41
  return { cmd: head, args: tokens.slice(1) };
33
42
  }
34
- if (isAbsolute(head)) {
43
+ if (isAbsolute2(head)) {
35
44
  if (!withinProject(head)) return null;
36
- const base = basename(head);
45
+ const base = basename2(head);
37
46
  if (ALLOWED_FIRST_TOKENS.has(base)) return { cmd: head, args: tokens.slice(1) };
38
47
  }
39
- if (!isAbsolute(head) && withinProject(head)) {
40
- const base = basename(head);
48
+ if (!isAbsolute2(head) && withinProject(head)) {
49
+ const base = basename2(head);
41
50
  if (ALLOWED_FIRST_TOKENS.has(base)) return { cmd: head, args: tokens.slice(1) };
42
51
  }
43
52
  return null;
@@ -256,7 +265,7 @@ ${result.stderr.trim()}`
256
265
  }
257
266
  return;
258
267
  };
259
- state.hookUnregister = api.registerHook("PostToolUse", "write|edit", hook);
268
+ state.hookUnregister = api.registerHook("PostToolUse", "write|edit", hook, { background: true });
260
269
  api.tools.register({
261
270
  name: "import_organizer_status",
262
271
  description: "Reports import-organizer state: linter availability, config, and per-session organized/clean/error counters.",