@wrongstack/plugins 0.317.0 → 0.317.2

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 (72) hide show
  1. package/dist/accessibility-auditor.js +12 -10
  2. package/dist/agent-handoff.js +30 -10
  3. package/dist/auto-doc.js +22 -9
  4. package/dist/auto-escalate.js +17 -7
  5. package/dist/auto-i18n-extractor.js +19 -9
  6. package/dist/branch-guard.js +12 -9
  7. package/dist/changelog-writer/index.d.ts +3 -3
  8. package/dist/changelog-writer.js +59 -14
  9. package/dist/checkpoint.js +18 -7
  10. package/dist/code-metrics.js +17 -9
  11. package/dist/commit-validator.js +18 -11
  12. package/dist/config-validator.js +5 -3
  13. package/dist/context-pins.js +17 -6
  14. package/dist/cost-tracker.js +24 -16
  15. package/dist/cron.js +17 -13
  16. package/dist/dead-code-detector.js +5 -4
  17. package/dist/dep-guard.js +5 -4
  18. package/dist/dependency-vulnerability-gate.js +64 -24
  19. package/dist/diff-summary.js +6 -2
  20. package/dist/doc-sync-guard.js +12 -2
  21. package/dist/duplicate-code-detector.js +87 -15
  22. package/dist/error-lens.js +6 -5
  23. package/dist/feature-flag-tracker.js +43 -9
  24. package/dist/file-watcher.js +26 -3
  25. package/dist/format-on-save.js +9 -5
  26. package/dist/git-autocommit.js +20 -12
  27. package/dist/gitignore-guard.js +7 -4
  28. package/dist/import-organizer.js +9 -5
  29. package/dist/index.js +1675 -745
  30. package/dist/injection-shield/index.d.ts +1 -0
  31. package/dist/injection-shield.js +30 -5
  32. package/dist/interface-contract-guard.js +4 -2
  33. package/dist/knowledge-graph.js +40 -14
  34. package/dist/license-audit-gate.js +61 -8
  35. package/dist/lint-gate.js +13 -5
  36. package/dist/llm-cache.js +20 -6
  37. package/dist/loop-breaker.js +16 -8
  38. package/dist/migration-planner.js +80 -11
  39. package/dist/model-router.js +16 -9
  40. package/dist/notify-hub.js +14 -5
  41. package/dist/path-guard.js +7 -3
  42. package/dist/performance-regression-gate.js +12 -6
  43. package/dist/plugin-stack-observer.js +25 -3
  44. package/dist/pr-drafter.js +16 -7
  45. package/dist/process-guard.js +2 -1
  46. package/dist/prompt-firewall.js +9 -7
  47. package/dist/refactor-suggester.js +37 -8
  48. package/dist/release-notes-generator/index.d.ts +9 -0
  49. package/dist/release-notes-generator.js +50 -12
  50. package/dist/schema-evolution-guard.js +33 -6
  51. package/dist/secret-scanner.js +10 -7
  52. package/dist/security-hotspot-scanner.js +13 -8
  53. package/dist/semantic-search-indexer/index.d.ts +11 -0
  54. package/dist/semantic-search-indexer.js +67 -19
  55. package/dist/semver-bump/index.d.ts +3 -3
  56. package/dist/semver-bump.js +30 -15
  57. package/dist/session-recap/index.d.ts +16 -0
  58. package/dist/session-recap.js +18 -9
  59. package/dist/shell-check.js +27 -6
  60. package/dist/smart-rename.js +18 -9
  61. package/dist/spec-linker.js +70 -19
  62. package/dist/template-engine.js +24 -14
  63. package/dist/test-coverage-gate.js +1 -1
  64. package/dist/test-flake-detector.js +28 -5
  65. package/dist/test-generator/index.d.ts +10 -0
  66. package/dist/test-generator.js +35 -13
  67. package/dist/todo-listener.js +2 -2
  68. package/dist/todo-tracker.js +19 -11
  69. package/dist/token-budget.js +14 -9
  70. package/dist/token-throttle.js +8 -3
  71. package/dist/type-gate.js +9 -5
  72. package/package.json +5 -5
@@ -41,12 +41,16 @@ var DEFAULTS = {
41
41
  function readConfig(raw) {
42
42
  if (!raw || typeof raw !== "object") return { ...DEFAULTS };
43
43
  const r = raw;
44
+ const rawExts = r["includeExtensions"] ?? r["include_extensions"] ?? r["extensions"];
45
+ const rawMax = r["maxFindings"] ?? r["max_findings"] ?? r["limit"];
46
+ const rawSeverity = typeof (r["severity"] ?? r["mode"] ?? r["action"]) === "string" ? String(r["severity"] ?? r["mode"] ?? r["action"]).trim().toLowerCase() : void 0;
47
+ const severity = rawSeverity === "block" ? "block" : DEFAULTS.severity;
44
48
  return {
45
49
  enabled: r["enabled"] !== false,
46
- includeExtensions: Array.isArray(r["includeExtensions"]) ? r["includeExtensions"].filter((x) => typeof x === "string") : DEFAULTS.includeExtensions,
47
- maxFindings: typeof r["maxFindings"] === "number" && r["maxFindings"] >= 1 && r["maxFindings"] <= 500 ? r["maxFindings"] : DEFAULTS.maxFindings,
48
- severity: r["severity"] === "block" ? "block" : DEFAULTS.severity,
49
- onWriteEdit: r["onWriteEdit"] !== false
50
+ includeExtensions: Array.isArray(rawExts) ? rawExts.filter((x) => typeof x === "string") : DEFAULTS.includeExtensions,
51
+ maxFindings: typeof rawMax === "number" && rawMax >= 1 && rawMax <= 500 ? rawMax : DEFAULTS.maxFindings,
52
+ severity,
53
+ onWriteEdit: (r["onWriteEdit"] ?? r["on_write_edit"] ?? r["onSave"]) !== false
50
54
  };
51
55
  }
52
56
  function normalizeExtensions(exts) {
@@ -170,7 +174,7 @@ async function auditFile(filePath, projectRoot) {
170
174
  SINGLE_FILE_LABEL_NOTE
171
175
  );
172
176
  }
173
- if (ATTR_PLACEHOLDER.test(tag)) {
177
+ if (!hasPrimaryLabel && ATTR_PLACEHOLDER.test(tag)) {
174
178
  add(lineNo, "low-contrast-placeholder", "warning", "<input> uses placeholder text (often low contrast and disappears on input)");
175
179
  }
176
180
  }
@@ -300,7 +304,7 @@ var plugin = {
300
304
  if (!cfg.enabled || !cfg.onWriteEdit) return;
301
305
  if (input.toolResult?.isError) return;
302
306
  const inp = input.toolInput ?? {};
303
- const rawPath = inp["path"] ?? inp["filePath"] ?? inp["file_path"];
307
+ const rawPath = inp["path"] ?? inp["TargetFile"] ?? inp["filePath"] ?? inp["targetFile"] ?? inp["file_path"] ?? inp["file"];
304
308
  const sourcePath = typeof rawPath === "string" ? rawPath : void 0;
305
309
  if (!sourcePath) return;
306
310
  if (!(0, runtime_exports.withinProject)(sourcePath)) return;
@@ -343,10 +347,8 @@ var plugin = {
343
347
  mutating: false,
344
348
  async execute(input) {
345
349
  if (!cfg.enabled) return { ok: false, error: "accessibility-auditor is disabled" };
346
- const rawPath = input.path;
347
- if (!rawPath || typeof rawPath !== "string") {
348
- return { ok: false, error: "path is required" };
349
- }
350
+ const raw = input;
351
+ const rawPath = (typeof input.path === "string" && input.path.trim().length > 0 ? input.path.trim() : 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["file_path"] === "string" ? raw["file_path"] : void 0) ?? (typeof raw["targetFile"] === "string" ? raw["targetFile"] : void 0) ?? (typeof raw["file"] === "string" ? raw["file"] : void 0) ?? ".";
350
352
  if (!(0, runtime_exports.withinProject)(rawPath)) {
351
353
  return { ok: false, error: "path must be inside the project" };
352
354
  }
@@ -39,13 +39,16 @@ var DEFAULTS = {
39
39
  function readConfig(raw) {
40
40
  if (!raw || typeof raw !== "object") return { ...DEFAULTS };
41
41
  const r = raw;
42
+ const rawPrefix = r["subjectPrefix"] ?? r["subject_prefix"] ?? r["prefix"];
43
+ const rawMax = r["maxBodyChars"] ?? r["max_body_chars"] ?? r["maxChars"];
44
+ const rawTo = r["to"] ?? r["recipient"] ?? r["target"];
42
45
  return {
43
46
  enabled: r["enabled"] !== false,
44
- subjectPrefix: typeof r["subjectPrefix"] === "string" ? r["subjectPrefix"] : DEFAULTS.subjectPrefix,
45
- includeResult: r["includeResult"] !== false,
46
- includeTodos: r["includeTodos"] !== false,
47
- maxBodyChars: typeof r["maxBodyChars"] === "number" && r["maxBodyChars"] >= 500 ? r["maxBodyChars"] : DEFAULTS.maxBodyChars,
48
- to: typeof r["to"] === "string" ? r["to"] : DEFAULTS.to
47
+ subjectPrefix: typeof rawPrefix === "string" ? rawPrefix : DEFAULTS.subjectPrefix,
48
+ includeResult: (r["includeResult"] ?? r["include_result"]) !== false,
49
+ includeTodos: (r["includeTodos"] ?? r["include_todos"]) !== false,
50
+ maxBodyChars: typeof rawMax === "number" && rawMax >= 500 ? rawMax : DEFAULTS.maxBodyChars,
51
+ to: typeof rawTo === "string" ? rawTo : DEFAULTS.to
49
52
  };
50
53
  }
51
54
  function truncate(s, max) {
@@ -190,7 +193,21 @@ var plugin = {
190
193
  state.skippedCount += 1;
191
194
  return;
192
195
  }
193
- const p = payload ?? {};
196
+ const raw = payload ?? {};
197
+ const asString = (v) => {
198
+ if (v === void 0 || v === null) return void 0;
199
+ return typeof v === "string" ? v : (0, runtime_exports.safeJsonStringify)(v) ?? void 0;
200
+ };
201
+ const rawTodos = raw["todos"];
202
+ const p = {
203
+ agentId: asString(raw["agentId"]),
204
+ agentName: asString(raw["agentName"]),
205
+ task: asString(raw["task"]),
206
+ status: asString(raw["status"]),
207
+ summary: asString(raw["summary"]),
208
+ result: raw["result"] ?? raw["output"] ?? raw["data"] ?? raw["toolResult"],
209
+ todos: Array.isArray(rawTodos) ? rawTodos : void 0
210
+ };
194
211
  sendHandoff(cfg, mailbox, p).catch((err) => {
195
212
  state.errorCount += 1;
196
213
  api.log.warn("agent-handoff: mailbox.send failed", {
@@ -223,14 +240,17 @@ var plugin = {
223
240
  async execute(input) {
224
241
  if (!cfg.enabled) return { ok: false, error: "agent-handoff is disabled" };
225
242
  if (!mailbox) return { ok: false, error: "mailbox not available" };
243
+ const raw = input;
244
+ const summary = (typeof raw["summary"] === "string" ? raw["summary"] : void 0) ?? (typeof raw["note"] === "string" ? raw["note"] : void 0) ?? (typeof raw["message"] === "string" ? raw["message"] : void 0) ?? (typeof raw["content"] === "string" ? raw["content"] : void 0) ?? (typeof raw["text"] === "string" ? raw["text"] : void 0) ?? (typeof raw["body"] === "string" ? raw["body"] : void 0);
245
+ const task = (typeof raw["task"] === "string" ? raw["task"] : void 0) ?? (typeof raw["title"] === "string" ? raw["title"] : void 0) ?? (typeof raw["subject"] === "string" ? raw["subject"] : void 0);
226
246
  const payload = {
227
247
  agentName: "manual",
228
- task: input.task,
229
- summary: input.summary,
248
+ task,
249
+ summary,
230
250
  result: input.result,
231
- todos: Array.isArray(input.todos) ? input.todos : void 0
251
+ todos: Array.isArray(input.todos) ? input.todos : input.todos && typeof input.todos === "object" ? [input.todos] : void 0
232
252
  };
233
- const recipient = input.to ?? cfg.to;
253
+ const recipient = (typeof raw["to"] === "string" ? raw["to"] : void 0) ?? (typeof raw["recipient"] === "string" ? raw["recipient"] : void 0) ?? (typeof raw["target"] === "string" ? raw["target"] : void 0) ?? cfg.to;
234
254
  try {
235
255
  const body = buildBody(payload, cfg);
236
256
  const result = await mailbox.send({
package/dist/auto-doc.js CHANGED
@@ -161,7 +161,8 @@ function injectDocComment(content, entity, doc) {
161
161
  return lines.join("\n");
162
162
  }
163
163
  async function runAutoDoc(input, api) {
164
- if (!input.files || typeof input.files !== "object" || !Array.isArray(input.files)) {
164
+ const rawInput = input;
165
+ if (rawInput["files"] !== void 0 && !Array.isArray(rawInput["files"])) {
165
166
  return {
166
167
  ok: false,
167
168
  error: "input.files must be an array of file paths",
@@ -169,7 +170,17 @@ async function runAutoDoc(input, api) {
169
170
  changes: []
170
171
  };
171
172
  }
172
- if (input.files.length === 0) {
173
+ const rawFiles = rawInput["files"] ?? rawInput["file"] ?? rawInput["path"] ?? rawInput["filePath"] ?? rawInput["TargetFile"] ?? rawInput["targetFile"];
174
+ const files = Array.isArray(rawFiles) ? rawFiles.filter((f) => typeof f === "string" && f.trim().length > 0).map((f) => f.trim()) : typeof rawFiles === "string" && rawFiles.trim().length > 0 ? [rawFiles.trim()] : void 0;
175
+ if (!files || !Array.isArray(files)) {
176
+ return {
177
+ ok: false,
178
+ error: "input.files must be an array of file paths",
179
+ filesProcessed: 0,
180
+ changes: []
181
+ };
182
+ }
183
+ if (files.length === 0) {
173
184
  return {
174
185
  ok: false,
175
186
  error: "input.files is empty \u2014 provide at least one file path",
@@ -179,11 +190,11 @@ async function runAutoDoc(input, api) {
179
190
  }
180
191
  const extConfig = api.config.extensions?.["auto-doc"] ?? {};
181
192
  const includeTypes = extConfig["includeTypes"] ?? false;
182
- const useLlm = (input.use_llm ?? extConfig["useLlm"] ?? false) === true && Boolean(api.llm);
193
+ const useLlm = (input.use_llm ?? rawInput["useLlm"] ?? extConfig["useLlm"] ?? false) === true && Boolean(api.llm);
183
194
  const maxLlmEntities = typeof extConfig["maxLlmEntities"] === "number" && extConfig["maxLlmEntities"] >= 0 ? extConfig["maxLlmEntities"] : 25;
184
195
  const results = [];
185
196
  let llmBudget = maxLlmEntities;
186
- for (const rawFile of input.files) {
197
+ for (const rawFile of files) {
187
198
  const safeFile = resolveProjectPath(rawFile);
188
199
  if (!safeFile) {
189
200
  api.log.warn(`auto-doc: skipped file outside project directory: ${rawFile}`);
@@ -200,8 +211,9 @@ async function runAutoDoc(input, api) {
200
211
  }
201
212
  const entities = parseSource(content);
202
213
  let modified = content;
203
- const beforeCount = results.length;
204
- for (const entity of entities) {
214
+ const reversedEntities = [...entities].reverse();
215
+ const fileChanges = [];
216
+ for (const entity of reversedEntities) {
205
217
  if (!input.force && !needsDocComment(modified, entity)) continue;
206
218
  let doc = null;
207
219
  let source = "template";
@@ -224,9 +236,10 @@ async function runAutoDoc(input, api) {
224
236
  }
225
237
  if (!doc) doc = generateDocComment(entity, includeTypes);
226
238
  modified = injectDocComment(modified, entity, doc);
227
- results.push({ file: safeFile, entity: entity.name, source });
239
+ fileChanges.push({ file: safeFile, entity: entity.name, source });
228
240
  }
229
- const changedThisFile = results.length > beforeCount && modified !== content;
241
+ results.push(...fileChanges.reverse());
242
+ const changedThisFile = fileChanges.length > 0 && modified !== content;
230
243
  if (!input.dry_run && changedThisFile) {
231
244
  writeFileSync(safeFile, modified, "utf-8");
232
245
  api.log.info(`auto-doc: updated ${safeFile}`);
@@ -237,7 +250,7 @@ async function runAutoDoc(input, api) {
237
250
  }
238
251
  return {
239
252
  ok: true,
240
- filesProcessed: input.files.length,
253
+ filesProcessed: files.length,
241
254
  changes: results,
242
255
  llm: useLlm ? { docs: state.llmDocs, fallbacks: state.llmFallbacks } : void 0
243
256
  };
@@ -3,23 +3,33 @@ var DEFAULT_PATTERNS = [
3
3
  "overload",
4
4
  "rate.?limit",
5
5
  "\\b429\\b",
6
- "\\b50[023]\\b",
6
+ "\\b50[0234]\\b",
7
7
  "timeout",
8
8
  "ETIMEDOUT",
9
9
  "ECONNRESET",
10
10
  "ECONNREFUSED",
11
- "temporarily unavailable"
11
+ "temporarily unavailable",
12
+ "resource.?exhausted",
13
+ "capacity",
14
+ "gateway"
12
15
  ];
16
+ var DEFAULTS = {
17
+ enabled: false,
18
+ escalation: [],
19
+ retryablePatterns: DEFAULT_PATTERNS.map((p) => new RegExp(p, "i"))
20
+ };
13
21
  function readConfig(raw) {
14
22
  const base = {
15
- enabled: false,
16
- escalation: [],
17
- retryablePatterns: DEFAULT_PATTERNS.map((p) => new RegExp(p, "i"))
23
+ enabled: DEFAULTS.enabled,
24
+ escalation: [...DEFAULTS.escalation],
25
+ retryablePatterns: [...DEFAULTS.retryablePatterns]
18
26
  };
19
27
  if (!raw || typeof raw !== "object") return base;
20
28
  const r = raw;
21
- const escalation = Array.isArray(r["escalation"]) ? r["escalation"].filter((m) => typeof m === "string" && m.length > 0) : [];
22
- const patterns = Array.isArray(r["retryablePatterns"]) ? r["retryablePatterns"].filter((p) => typeof p === "string" && p.length > 0).flatMap((p) => {
29
+ const rawEscalation = r["escalation"] ?? r["ladder"] ?? r["models"];
30
+ const escalation = Array.isArray(rawEscalation) ? rawEscalation.filter((m) => typeof m === "string" && m.length > 0) : [];
31
+ const rawPatterns = r["retryablePatterns"] ?? r["retryable_patterns"] ?? r["patterns"];
32
+ const patterns = Array.isArray(rawPatterns) ? rawPatterns.filter((p) => typeof p === "string" && p.length > 0).flatMap((p) => {
23
33
  try {
24
34
  return [new RegExp(p, "i")];
25
35
  } catch {
@@ -53,12 +53,16 @@ function readConfig(raw) {
53
53
  if (!raw || typeof raw !== "object") return { ...DEFAULTS };
54
54
  const r = raw;
55
55
  const clamp = (n, min, max, fallback) => typeof n === "number" && Number.isFinite(n) && n >= min && n <= max ? Math.floor(n) : fallback;
56
+ const rawExts = r["fileExtensions"] ?? r["file_extensions"] ?? r["extensions"];
57
+ const rawMin = r["minLength"] ?? r["min_length"] ?? r["min"];
58
+ const rawMax = r["maxContextStrings"] ?? r["max_context_strings"] ?? r["maxStrings"] ?? r["limit"];
59
+ const rawExcl = r["excludeAttributes"] ?? r["exclude_attributes"];
56
60
  return {
57
61
  enabled: r["enabled"] !== false,
58
- fileExtensions: Array.isArray(r["fileExtensions"]) ? r["fileExtensions"].filter((x) => typeof x === "string") : DEFAULTS.fileExtensions,
59
- minLength: clamp(r["minLength"], 1, 500, DEFAULTS.minLength),
60
- maxContextStrings: clamp(r["maxContextStrings"], 1, 100, DEFAULTS.maxContextStrings),
61
- excludeAttributes: Array.isArray(r["excludeAttributes"]) ? r["excludeAttributes"].filter((x) => typeof x === "string") : DEFAULTS.excludeAttributes
62
+ fileExtensions: Array.isArray(rawExts) ? rawExts.filter((x) => typeof x === "string") : DEFAULTS.fileExtensions,
63
+ minLength: clamp(rawMin, 1, 500, DEFAULTS.minLength),
64
+ maxContextStrings: clamp(rawMax, 1, 100, DEFAULTS.maxContextStrings),
65
+ excludeAttributes: Array.isArray(rawExcl) ? rawExcl.filter((x) => typeof x === "string") : DEFAULTS.excludeAttributes
62
66
  };
63
67
  }
64
68
  function fileExtension(p) {
@@ -66,8 +70,11 @@ function fileExtension(p) {
66
70
  return dot > 0 ? p.slice(dot).toLowerCase() : "";
67
71
  }
68
72
  function generateKey(value) {
69
- const base = value.toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "").slice(0, 40);
70
- return base ? `t.${base}` : "t.unknown";
73
+ const base = value.toLowerCase().normalize("NFKD").replace(/[^\p{L}\p{N}]+/gu, "_").replace(/^_+|_+$/g, "").slice(0, 40);
74
+ if (base) return `t.${base}`;
75
+ let hash = 0;
76
+ for (let i = 0; i < value.length; i++) hash = hash * 31 + value.charCodeAt(i) >>> 0;
77
+ return `t.str_${hash.toString(36)}`;
71
78
  }
72
79
  function looksLikeUserText(value, minLength) {
73
80
  if (value.length < minLength) return false;
@@ -180,7 +187,8 @@ var plugin = {
180
187
  if (!cfg.enabled) return;
181
188
  if (input.toolResult?.isError) return;
182
189
  const inp = input.toolInput ?? {};
183
- const sourcePath = inp["path"];
190
+ const rawPath = inp["path"] ?? inp["TargetFile"] ?? inp["filePath"] ?? inp["targetFile"] ?? inp["file_path"] ?? inp["file"];
191
+ const sourcePath = typeof rawPath === "string" && rawPath.trim() ? rawPath.trim() : void 0;
184
192
  if (!sourcePath || typeof sourcePath !== "string") return;
185
193
  if (!(0, runtime_exports.withinProject)(sourcePath)) return;
186
194
  const ext = fileExtension(sourcePath);
@@ -231,8 +239,10 @@ ${lines.join("\n")}${more}`;
231
239
  mutating: false,
232
240
  async execute(input) {
233
241
  if (!cfg.enabled) return { ok: false, error: "auto-i18n-extractor is disabled" };
234
- const filePath = input.path;
235
- if (typeof filePath !== "string" || !filePath) {
242
+ const raw = input;
243
+ const rawPath = (typeof input.path === "string" && input.path.trim().length > 0 ? input.path.trim() : void 0) ?? (typeof raw["TargetFile"] === "string" && raw["TargetFile"].trim().length > 0 ? raw["TargetFile"].trim() : void 0) ?? (typeof raw["targetFile"] === "string" && raw["targetFile"].trim().length > 0 ? raw["targetFile"].trim() : void 0) ?? (typeof raw["filePath"] === "string" && raw["filePath"].trim().length > 0 ? raw["filePath"].trim() : void 0) ?? (typeof raw["file_path"] === "string" && raw["file_path"].trim().length > 0 ? raw["file_path"].trim() : void 0) ?? (typeof raw["file"] === "string" && raw["file"].trim().length > 0 ? raw["file"].trim() : void 0);
244
+ const filePath = typeof rawPath === "string" ? rawPath.trim() : "";
245
+ if (!filePath) {
236
246
  return { ok: false, error: "path is required" };
237
247
  }
238
248
  if (!(0, runtime_exports.withinProject)(filePath)) {
@@ -41,15 +41,17 @@ var DEFAULTS = {
41
41
  function readConfig(raw) {
42
42
  if (!raw || typeof raw !== "object") return { ...DEFAULTS };
43
43
  const r = raw;
44
- const branches = Array.isArray(r["branches"]) ? r["branches"].filter((b) => typeof b === "string") : DEFAULTS.branches;
45
- const mode = r["mode"] === "warn" ? "warn" : r["mode"] === "off" ? "off" : "block";
44
+ const rawBranches = r["branches"] ?? r["protectedBranches"] ?? r["protected_branches"] ?? r["protected"];
45
+ const branches = Array.isArray(rawBranches) ? rawBranches.filter((b) => typeof b === "string") : DEFAULTS.branches;
46
+ const rawMode = typeof (r["mode"] ?? r["action"]) === "string" ? String(r["mode"] ?? r["action"]).trim().toLowerCase() : void 0;
47
+ const mode = rawMode === "warn" ? "warn" : rawMode === "off" ? "off" : "block";
46
48
  return {
47
49
  enabled: r["enabled"] !== false && mode !== "off",
48
50
  branches: branches.length > 0 ? branches : DEFAULTS.branches,
49
51
  mode,
50
- blockCommit: r["blockCommit"] !== false,
51
- blockPush: r["blockPush"] !== false,
52
- blockMerge: r["blockMerge"] !== false
52
+ blockCommit: (r["blockCommit"] ?? r["block_commit"]) !== false,
53
+ blockPush: (r["blockPush"] ?? r["block_push"]) !== false,
54
+ blockMerge: (r["blockMerge"] ?? r["block_merge"]) !== false
53
55
  };
54
56
  }
55
57
  function readHostConfig(raw) {
@@ -200,12 +202,13 @@ var plugin = {
200
202
  if (toolName === "git_autocommit") {
201
203
  if (inp["dry_run"] === true) return;
202
204
  gitOp = { type: "commit", snippet: "git_autocommit" };
203
- } else if (toolName === "bash") {
204
- const command = inp["command"];
205
- if (typeof command !== "string") return;
206
- gitOp = detectGitCommand(command);
207
205
  } else if (toolName === "git") {
208
206
  gitOp = detectStructuredGitCommand(inp);
207
+ } else {
208
+ const rawCmd = inp["command"] ?? inp["CommandLine"] ?? inp["cmd"] ?? inp["script"] ?? inp["input"];
209
+ const command = typeof rawCmd === "string" ? rawCmd : void 0;
210
+ if (typeof command !== "string") return;
211
+ gitOp = detectGitCommand(command);
209
212
  }
210
213
  if (!gitOp) return;
211
214
  if (!shouldBlock(gitOp.type, cfg)) return;
@@ -61,9 +61,9 @@ export declare function commitSubjectFromCommand(command: string): string | null
61
61
  export declare function renderUnreleasedBlock(entries: ChangelogEntry[]): string;
62
62
  /**
63
63
  * Merge a rendered Unreleased block into existing changelog content:
64
- * inserted directly under `## [Unreleased]` (existing unreleased
65
- * content is preserved below the new lines). Creates the standard
66
- * header when the file has no Unreleased heading.
64
+ * inserted directly under `## [Unreleased]`, merging section headings
65
+ * and deduplicating entries. Creates the standard header when the file
66
+ * has no Unreleased heading.
67
67
  */
68
68
  export declare function mergeIntoChangelog(existing: string | null, block: string): string;
69
69
  declare const plugin: Plugin;
@@ -134,13 +134,13 @@ ${block}
134
134
  if (!m || m.index === void 0) {
135
135
  const h1 = /^#\s.*$/m.exec(existing);
136
136
  if (h1 && h1.index !== void 0) {
137
- const insertAt2 = h1.index + h1[0].length;
138
- return `${existing.slice(0, insertAt2)}
137
+ const insertAt = h1.index + h1[0].length;
138
+ return `${existing.slice(0, insertAt)}
139
139
 
140
140
  ## [Unreleased]
141
141
 
142
142
  ${block}
143
- ${existing.slice(insertAt2)}`;
143
+ ${existing.slice(insertAt)}`;
144
144
  }
145
145
  return `## [Unreleased]
146
146
 
@@ -148,11 +148,50 @@ ${block}
148
148
 
149
149
  ${existing}`;
150
150
  }
151
- const insertAt = m.index + m[0].length;
152
- return `${existing.slice(0, insertAt)}
151
+ const startIndex = m.index + m[0].length;
152
+ const nextSectionMatch = /^##\s+\[?[0-9v]/im.exec(existing.slice(startIndex));
153
+ const endIndex = nextSectionMatch ? startIndex + nextSectionMatch.index : existing.length;
154
+ const currentUnreleased = existing.slice(startIndex, endIndex);
155
+ const rest = existing.slice(endIndex);
156
+ const existingEntries = [];
157
+ let currentSection = "Changed";
158
+ for (const line of currentUnreleased.split(/\r?\n/)) {
159
+ const trimmed = line.trim();
160
+ if (trimmed.startsWith("### ")) {
161
+ const secName = trimmed.slice(4).trim();
162
+ if (SECTION_ORDER.includes(secName)) currentSection = secName;
163
+ } else if (trimmed.startsWith("- ")) {
164
+ existingEntries.push({
165
+ section: currentSection,
166
+ text: trimmed.slice(2).trim(),
167
+ origin: "manual",
168
+ when: (/* @__PURE__ */ new Date()).toISOString()
169
+ });
170
+ }
171
+ }
172
+ const newEntries = [];
173
+ let newSec = "Changed";
174
+ for (const line of block.split(/\r?\n/)) {
175
+ const trimmed = line.trim();
176
+ if (trimmed.startsWith("### ")) {
177
+ const secName = trimmed.slice(4).trim();
178
+ if (SECTION_ORDER.includes(secName)) newSec = secName;
179
+ } else if (trimmed.startsWith("- ")) {
180
+ newEntries.push({
181
+ section: newSec,
182
+ text: trimmed.slice(2).trim(),
183
+ origin: "manual",
184
+ when: (/* @__PURE__ */ new Date()).toISOString()
185
+ });
186
+ }
187
+ }
188
+ const combinedBlock = renderUnreleasedBlock([...newEntries, ...existingEntries]);
189
+ const formattedRest = rest.trim() ? `
153
190
 
154
- ${block}
155
- ${existing.slice(insertAt)}`;
191
+ ${rest.trimStart()}` : "\n";
192
+ return `${existing.slice(0, startIndex)}
193
+
194
+ ${combinedBlock}${formattedRest}`;
156
195
  }
157
196
  var plugin = {
158
197
  name: "changelog-writer",
@@ -230,8 +269,8 @@ var plugin = {
230
269
  if (p?.isError) return;
231
270
  const toolName = p?.tool ?? p?.name ?? "";
232
271
  const input = p?.input ?? {};
233
- if (toolName === "write" || toolName === "edit") {
234
- const raw = input["path"] ?? input["file_path"] ?? input["filePath"];
272
+ if (toolName === "write" || toolName === "edit" || toolName === "write_to_file" || toolName === "replace_file_content") {
273
+ const raw = input["path"] ?? input["TargetFile"] ?? input["filePath"] ?? input["targetFile"] ?? input["file_path"] ?? input["file"];
235
274
  if (typeof raw === "string" && raw) state.filesTouched.add(raw);
236
275
  return;
237
276
  }
@@ -248,7 +287,7 @@ var plugin = {
248
287
  return;
249
288
  }
250
289
  if (toolName === "bash" || toolName === "exec") {
251
- const command = typeof input["command"] === "string" ? input["command"] : "";
290
+ const command = typeof input["command"] === "string" ? input["command"] : typeof input["CommandLine"] === "string" ? input["CommandLine"] : typeof input["cmd"] === "string" ? input["cmd"] : "";
252
291
  const subject = command ? commitSubjectFromCommand(command) : null;
253
292
  if (subject) {
254
293
  state.commitsSeen += 1;
@@ -280,7 +319,9 @@ var plugin = {
280
319
  mutating: false,
281
320
  async execute(input) {
282
321
  if (!cfg.enabled) return { ok: false, error: "changelog-writer is disabled" };
283
- const text = String(input.text ?? "").trim();
322
+ const raw = input ?? {};
323
+ const rawText = input.text ?? raw["message"] ?? raw["entry"] ?? raw["content"] ?? raw["description"] ?? raw["desc"] ?? raw["summary"] ?? raw["body"] ?? raw["note"] ?? raw["item"];
324
+ const text = String(rawText ?? "").trim();
284
325
  if (!text) return { ok: false, error: "entry text must not be empty" };
285
326
  const section = SECTION_ORDER.includes(input.section) ? input.section : "Changed";
286
327
  addEntry({ section, text, origin: "manual", when: (/* @__PURE__ */ new Date()).toISOString() });
@@ -303,8 +344,10 @@ var plugin = {
303
344
  category: "Docs",
304
345
  mutating: false,
305
346
  async execute(input) {
347
+ const rawInput = input ?? {};
348
+ const shouldPolish = input.polish === true || rawInput["use_llm"] === true || rawInput["useLlm"] === true || rawInput["ai"] === true || rawInput["use_ai"] === true;
306
349
  const raw = renderUnreleasedBlock(state.entries);
307
- const markdown = raw ? await maybePolish(raw, input.polish === true) : "";
350
+ const markdown = raw ? await maybePolish(raw, shouldPolish) : "";
308
351
  return {
309
352
  ok: true,
310
353
  enabled: cfg.enabled,
@@ -312,7 +355,7 @@ var plugin = {
312
355
  pendingEntries: state.entries.length,
313
356
  filesTouched: [...state.filesTouched].slice(0, 50),
314
357
  commitsSeen: state.commitsSeen,
315
- polished: input.polish === true && markdown !== raw,
358
+ polished: shouldPolish && markdown !== raw,
316
359
  llmAvailable: Boolean(api.llm),
317
360
  markdown: markdown || "(no pending entries)"
318
361
  };
@@ -341,9 +384,11 @@ var plugin = {
341
384
  if (state.entries.length === 0) {
342
385
  return { ok: false, error: "no pending entries \u2014 add some with changelog_add first" };
343
386
  }
387
+ const rawInput = input ?? {};
388
+ const shouldPolish = input.polish === true || rawInput["use_llm"] === true || rawInput["useLlm"] === true || rawInput["ai"] === true || rawInput["use_ai"] === true;
344
389
  const block = await maybePolish(
345
390
  renderUnreleasedBlock(state.entries),
346
- input.polish === true
391
+ shouldPolish
347
392
  );
348
393
  let existing = null;
349
394
  try {
@@ -29,6 +29,7 @@ function readConfig(raw) {
29
29
  };
30
30
  }
31
31
  function resolveProjectPath(rawPath, cwd = process.cwd()) {
32
+ if (typeof rawPath !== "string" || rawPath.length === 0) return null;
32
33
  const root = resolve(cwd);
33
34
  const resolved = isAbsolute(rawPath) ? resolve(rawPath) : resolve(root, rawPath);
34
35
  const rel = relative(root, resolved);
@@ -132,7 +133,7 @@ var plugin = {
132
133
  if (cfg.enabled && cfg.autoCapture) {
133
134
  const hook = async (input, runtime = { signal: new AbortController().signal }) => {
134
135
  const ti = input.toolInput ?? {};
135
- const raw = ti["path"] ?? ti["file_path"] ?? ti["filePath"];
136
+ const raw = ti["path"] ?? ti["file_path"] ?? ti["filePath"] ?? ti["TargetFile"] ?? ti["targetFile"] ?? ti["file"];
136
137
  if (typeof raw !== "string" || raw.length === 0) return;
137
138
  const safePath = resolveProjectPath(raw);
138
139
  if (!safePath) return;
@@ -192,7 +193,14 @@ var plugin = {
192
193
  mutating: false,
193
194
  async execute(input) {
194
195
  if (!cfg.enabled) return { ok: false, error: "checkpoint is disabled" };
195
- const paths = Array.isArray(input.paths) ? input.paths.filter((p) => typeof p === "string" && p.length > 0) : [];
196
+ let paths = [];
197
+ const rawInput = input;
198
+ const raw = rawInput["paths"] ?? rawInput["path"] ?? rawInput["files"] ?? rawInput["file"] ?? rawInput["filePath"] ?? rawInput["file_path"] ?? rawInput["TargetFile"] ?? rawInput["targetFile"];
199
+ if (typeof raw === "string" && raw.trim().length > 0) {
200
+ paths = [raw.trim()];
201
+ } else if (Array.isArray(raw)) {
202
+ paths = raw.filter((p) => typeof p === "string" && p.trim().length > 0);
203
+ }
196
204
  if (paths.length === 0) return { ok: false, error: "paths must not be empty" };
197
205
  const files = [];
198
206
  const rejectedOutsideProject = [];
@@ -295,17 +303,20 @@ var plugin = {
295
303
  mutating: true,
296
304
  async execute(input) {
297
305
  if (!cfg.enabled) return { ok: false, error: "checkpoint is disabled" };
298
- const snapshot = input.id ? state.snapshots.find((s) => s.id === input.id) : state.snapshots[state.snapshots.length - 1];
306
+ const raw = input ?? {};
307
+ const rawId = (typeof input.id === "string" && input.id.trim().length > 0 ? input.id.trim() : void 0) ?? (typeof raw["snapshotId"] === "string" ? raw["snapshotId"] : void 0) ?? (typeof raw["snapshot_id"] === "string" ? raw["snapshot_id"] : void 0);
308
+ const rawPath = (typeof input.path === "string" && input.path.trim().length > 0 ? input.path.trim() : void 0) ?? (typeof raw["filePath"] === "string" ? raw["filePath"] : void 0) ?? (typeof raw["file_path"] === "string" ? raw["file_path"] : void 0) ?? (typeof raw["TargetFile"] === "string" ? raw["TargetFile"] : void 0) ?? (typeof raw["targetFile"] === "string" ? raw["targetFile"] : void 0) ?? (typeof raw["file"] === "string" ? raw["file"] : void 0);
309
+ const snapshot = rawId ? state.snapshots.find((s) => s.id === rawId) : state.snapshots[state.snapshots.length - 1];
299
310
  if (!snapshot) {
300
311
  return {
301
312
  ok: false,
302
- error: input.id ? `no snapshot with id "${input.id}"` : "no snapshots captured yet"
313
+ error: rawId ? `no snapshot with id "${rawId}"` : "no snapshots captured yet"
303
314
  };
304
315
  }
305
- const targetPath = input.path ? resolveProjectPath(input.path) ?? input.path : null;
306
- const targets = targetPath ? snapshot.files.filter((f) => f.path === targetPath || f.path === input.path) : snapshot.files;
316
+ const targetPath = rawPath ? resolveProjectPath(rawPath) ?? rawPath : null;
317
+ const targets = targetPath ? snapshot.files.filter((f) => f.path === targetPath || f.path === rawPath) : snapshot.files;
307
318
  if (targets.length === 0) {
308
- return { ok: false, error: `snapshot ${snapshot.id} has no entry for "${input.path}"` };
319
+ return { ok: false, error: `snapshot ${snapshot.id} has no entry for "${rawPath}"` };
309
320
  }
310
321
  const restored = [];
311
322
  const createdByTool = [];
@@ -38,10 +38,12 @@ var DEFAULTS = {
38
38
  function readConfig(raw) {
39
39
  if (!raw || typeof raw !== "object") return { ...DEFAULTS };
40
40
  const r = raw;
41
+ const rawExts = r["extensions"] ?? r["file_extensions"] ?? r["fileExtensions"];
42
+ const rawMax = r["maxFiles"] ?? r["max_files"] ?? r["limit"];
41
43
  return {
42
44
  enabled: r["enabled"] !== false,
43
- extensions: Array.isArray(r["extensions"]) ? r["extensions"].filter((x) => typeof x === "string") : DEFAULTS.extensions,
44
- maxFiles: typeof r["maxFiles"] === "number" && r["maxFiles"] >= 1 && r["maxFiles"] <= 500 ? r["maxFiles"] : DEFAULTS.maxFiles
45
+ extensions: Array.isArray(rawExts) ? rawExts.filter((x) => typeof x === "string") : DEFAULTS.extensions,
46
+ maxFiles: typeof rawMax === "number" && rawMax >= 1 && rawMax <= 500 ? rawMax : DEFAULTS.maxFiles
45
47
  };
46
48
  }
47
49
  function normalizeExtensions(exts) {
@@ -71,15 +73,19 @@ function countFunctions(content) {
71
73
  return count;
72
74
  }
73
75
  var COMPLEXITY_FORMULA = "control(if|else if|for|while|switch|catch) + (&& || ?? ||= &&= ??=) + ternary ?; optional chaining ?. is not counted";
76
+ function stripNonExecutable(code) {
77
+ return code.replace(/\/\*[\s\S]*?\*\//g, " ").replace(/\/\/[^\n]*/g, " ").replace(/(["'`])(?:\\.|(?!\1)[^\\])*\1/g, " ").replace(/\b[A-Za-z_$][A-Za-z0-9_$]*\s*\?\s*:/g, " ");
78
+ }
74
79
  function countComplexity(content) {
80
+ const code = stripNonExecutable(content);
75
81
  const controlRe = /\b(if|else\s+if|for|while|switch|catch)\b/g;
76
82
  let complexity = 0;
77
83
  controlRe.lastIndex = 0;
78
- for (const _match of content.matchAll(controlRe)) complexity++;
79
- for (let i = 0; i < content.length; i++) {
80
- const c = content[i];
81
- const n1 = content[i + 1];
82
- const n2 = content[i + 2];
84
+ for (const _match of code.matchAll(controlRe)) complexity++;
85
+ for (let i = 0; i < code.length; i++) {
86
+ const c = code[i];
87
+ const n1 = code[i + 1];
88
+ const n2 = code[i + 2];
83
89
  if (c === "?" && n1 === "?" && n2 === "=") {
84
90
  complexity++;
85
91
  i += 2;
@@ -214,7 +220,8 @@ var plugin = {
214
220
  if (!cfg.enabled) return;
215
221
  if (input.toolResult?.isError) return;
216
222
  const inp = input.toolInput ?? {};
217
- const sourcePath = inp["path"];
223
+ const rawSource = inp["path"] ?? inp["TargetFile"] ?? inp["filePath"] ?? inp["targetFile"] ?? inp["file_path"] ?? inp["destination"] ?? inp["file"];
224
+ const sourcePath = typeof rawSource === "string" ? rawSource : void 0;
218
225
  if (!sourcePath || typeof sourcePath !== "string") return;
219
226
  if (!(0, runtime_exports.withinProject)(sourcePath)) return;
220
227
  const exts = normalizeExtensions(cfg.extensions);
@@ -249,7 +256,8 @@ var plugin = {
249
256
  mutating: false,
250
257
  async execute(input) {
251
258
  if (!cfg.enabled) return { ok: false, error: "code-metrics is disabled" };
252
- const rawPath = typeof input.path === "string" ? input.path : ".";
259
+ const raw = input ?? {};
260
+ const rawPath = (typeof input.path === "string" && input.path.trim().length > 0 ? input.path.trim() : 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["filePath"] === "string" ? raw["filePath"] : void 0) ?? (typeof raw["TargetFile"] === "string" ? raw["TargetFile"] : void 0) ?? (typeof raw["targetFile"] === "string" ? raw["targetFile"] : void 0) ?? (typeof raw["file_path"] === "string" ? raw["file_path"] : void 0) ?? (typeof raw["file"] === "string" ? raw["file"] : void 0) ?? ".";
253
261
  if (!(0, runtime_exports.withinProject)(rawPath)) {
254
262
  return { ok: false, error: "path is outside the project root" };
255
263
  }