@wrongstack/plugins 0.293.0 → 0.295.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (211) hide show
  1. package/README.md +7 -71
  2. package/dist/accessibility-auditor/index.d.ts +1 -1
  3. package/dist/accessibility-auditor/index.d.ts.map +1 -1
  4. package/dist/accessibility-auditor.js +50 -33
  5. package/dist/agent-handoff/index.d.ts +1 -1
  6. package/dist/agent-handoff/index.d.ts.map +1 -1
  7. package/dist/agent-handoff.js +30 -1
  8. package/dist/api-compatibility-gate/index.d.ts +1 -1
  9. package/dist/api-compatibility-gate/index.d.ts.map +1 -1
  10. package/dist/api-compatibility-gate.js +21 -16
  11. package/dist/audit/index.d.ts +386 -0
  12. package/dist/audit/index.d.ts.map +1 -0
  13. package/dist/audit.d.ts +1 -0
  14. package/dist/audit.js +454 -0
  15. package/dist/auto-doc/index.d.ts +1 -1
  16. package/dist/auto-doc/index.d.ts.map +1 -1
  17. package/dist/auto-doc.js +3 -1
  18. package/dist/auto-escalate/index.d.ts +1 -1
  19. package/dist/auto-escalate/index.d.ts.map +1 -1
  20. package/dist/auto-i18n-extractor/index.d.ts +1 -1
  21. package/dist/auto-i18n-extractor/index.d.ts.map +1 -1
  22. package/dist/auto-i18n-extractor.js +7 -1
  23. package/dist/branch-guard/index.d.ts +1 -1
  24. package/dist/branch-guard/index.d.ts.map +1 -1
  25. package/dist/branch-guard.js +111 -1
  26. package/dist/catalog.d.ts +5 -30
  27. package/dist/catalog.d.ts.map +1 -1
  28. package/dist/changelog-writer/index.d.ts +1 -1
  29. package/dist/changelog-writer/index.d.ts.map +1 -1
  30. package/dist/changelog-writer.js +137 -3
  31. package/dist/checkpoint/index.d.ts +3 -1
  32. package/dist/checkpoint/index.d.ts.map +1 -1
  33. package/dist/checkpoint.js +41 -16
  34. package/dist/code-metrics/index.d.ts +1 -1
  35. package/dist/code-metrics/index.d.ts.map +1 -1
  36. package/dist/code-metrics.js +34 -29
  37. package/dist/commit-validator/index.d.ts +1 -1
  38. package/dist/commit-validator/index.d.ts.map +1 -1
  39. package/dist/commit-validator.js +17 -7
  40. package/dist/config-validator/index.d.ts +1 -1
  41. package/dist/config-validator/index.d.ts.map +1 -1
  42. package/dist/context-pins/index.d.ts +1 -1
  43. package/dist/context-pins/index.d.ts.map +1 -1
  44. package/dist/context-pins.js +6 -5
  45. package/dist/cost-tracker/index.d.ts +1 -1
  46. package/dist/cost-tracker/index.d.ts.map +1 -1
  47. package/dist/cost-tracker.js +109 -2
  48. package/dist/cron/index.d.ts +1 -1
  49. package/dist/cron/index.d.ts.map +1 -1
  50. package/dist/cron.js +1 -0
  51. package/dist/dead-code-detector/index.d.ts +1 -1
  52. package/dist/dead-code-detector/index.d.ts.map +1 -1
  53. package/dist/dead-code-detector.js +31 -18
  54. package/dist/dep-guard/index.d.ts +1 -1
  55. package/dist/dep-guard/index.d.ts.map +1 -1
  56. package/dist/dependency-vulnerability-gate/index.d.ts +1 -1
  57. package/dist/dependency-vulnerability-gate/index.d.ts.map +1 -1
  58. package/dist/dependency-vulnerability-gate.js +67 -20
  59. package/dist/diff-summary/index.d.ts +1 -1
  60. package/dist/diff-summary/index.d.ts.map +1 -1
  61. package/dist/diff-summary.js +128 -30
  62. package/dist/doc-sync-guard/index.d.ts +1 -1
  63. package/dist/doc-sync-guard/index.d.ts.map +1 -1
  64. package/dist/doc-sync-guard.js +7 -1
  65. package/dist/duplicate-code-detector/index.d.ts +26 -1
  66. package/dist/duplicate-code-detector/index.d.ts.map +1 -1
  67. package/dist/duplicate-code-detector.js +280 -69
  68. package/dist/error-lens/index.d.ts +1 -1
  69. package/dist/error-lens/index.d.ts.map +1 -1
  70. package/dist/factories/index.d.ts +4 -0
  71. package/dist/factories/index.d.ts.map +1 -0
  72. package/dist/factories.d.ts +1 -0
  73. package/dist/factories.js +74 -0
  74. package/dist/feature-flag-tracker/index.d.ts +1 -1
  75. package/dist/feature-flag-tracker/index.d.ts.map +1 -1
  76. package/dist/feature-flag-tracker.js +59 -33
  77. package/dist/file-watcher/index.d.ts +1 -1
  78. package/dist/file-watcher/index.d.ts.map +1 -1
  79. package/dist/file-watcher.js +6 -7
  80. package/dist/format-on-save/index.d.ts +3 -1
  81. package/dist/format-on-save/index.d.ts.map +1 -1
  82. package/dist/format-on-save.js +289 -59
  83. package/dist/generated-plugin-exports.d.ts +65 -0
  84. package/dist/generated-plugin-exports.d.ts.map +1 -0
  85. package/dist/git-autocommit/index.d.ts +1 -1
  86. package/dist/git-autocommit/index.d.ts.map +1 -1
  87. package/dist/import-organizer/index.d.ts +1 -1
  88. package/dist/import-organizer/index.d.ts.map +1 -1
  89. package/dist/import-organizer.js +141 -20
  90. package/dist/index.d.ts +4 -183
  91. package/dist/index.d.ts.map +1 -1
  92. package/dist/index.js +18298 -17279
  93. package/dist/injection-shield/index.d.ts +1 -1
  94. package/dist/injection-shield/index.d.ts.map +1 -1
  95. package/dist/interface-contract-guard/index.d.ts +1 -1
  96. package/dist/interface-contract-guard/index.d.ts.map +1 -1
  97. package/dist/interface-contract-guard.js +87 -53
  98. package/dist/knowledge-graph/index.d.ts +1 -1
  99. package/dist/knowledge-graph/index.d.ts.map +1 -1
  100. package/dist/knowledge-graph.js +7 -6
  101. package/dist/license-audit-gate/index.d.ts +1 -1
  102. package/dist/license-audit-gate/index.d.ts.map +1 -1
  103. package/dist/lint-gate/index.d.ts +1 -1
  104. package/dist/lint-gate/index.d.ts.map +1 -1
  105. package/dist/lint-gate.js +112 -1
  106. package/dist/llm-cache/index.d.ts +5 -1
  107. package/dist/llm-cache/index.d.ts.map +1 -1
  108. package/dist/llm-cache.js +16 -8
  109. package/dist/loop-breaker/index.d.ts +1 -1
  110. package/dist/loop-breaker/index.d.ts.map +1 -1
  111. package/dist/loop-breaker.js +25 -9
  112. package/dist/manifest/index.d.ts +16 -0
  113. package/dist/manifest/index.d.ts.map +1 -0
  114. package/dist/manifest.d.ts +1 -0
  115. package/dist/manifest.js +152 -0
  116. package/dist/migration-planner/index.d.ts +1 -1
  117. package/dist/migration-planner/index.d.ts.map +1 -1
  118. package/dist/migration-planner.js +5 -1
  119. package/dist/model-router/index.d.ts +1 -1
  120. package/dist/model-router/index.d.ts.map +1 -1
  121. package/dist/notify-hub/index.d.ts +1 -1
  122. package/dist/notify-hub/index.d.ts.map +1 -1
  123. package/dist/notify-hub/webhook-channel.d.ts +1 -1
  124. package/dist/notify-hub/webhook-channel.d.ts.map +1 -1
  125. package/dist/notify-hub.js +41 -4
  126. package/dist/path-guard/index.d.ts +1 -1
  127. package/dist/path-guard/index.d.ts.map +1 -1
  128. package/dist/performance-regression-gate/index.d.ts +1 -1
  129. package/dist/performance-regression-gate/index.d.ts.map +1 -1
  130. package/dist/plugin-stack-observer/index.d.ts +1 -1
  131. package/dist/plugin-stack-observer/index.d.ts.map +1 -1
  132. package/dist/pr-drafter/index.d.ts +1 -1
  133. package/dist/pr-drafter/index.d.ts.map +1 -1
  134. package/dist/pr-drafter.js +28 -29
  135. package/dist/process-guard/index.d.ts +1 -1
  136. package/dist/process-guard/index.d.ts.map +1 -1
  137. package/dist/prompt-firewall/index.d.ts +1 -1
  138. package/dist/prompt-firewall/index.d.ts.map +1 -1
  139. package/dist/prompt-firewall.js +140 -11
  140. package/dist/refactor-suggester/index.d.ts +1 -1
  141. package/dist/refactor-suggester/index.d.ts.map +1 -1
  142. package/dist/refactor-suggester.js +148 -33
  143. package/dist/release-notes-generator/index.d.ts +1 -1
  144. package/dist/release-notes-generator/index.d.ts.map +1 -1
  145. package/dist/release-notes-generator.js +29 -22
  146. package/dist/runtime/bounded-map.d.ts +86 -0
  147. package/dist/runtime/bounded-map.d.ts.map +1 -0
  148. package/dist/runtime/credential-patterns.d.ts +42 -0
  149. package/dist/runtime/credential-patterns.d.ts.map +1 -0
  150. package/dist/runtime/index.d.ts +18 -0
  151. package/dist/runtime/index.d.ts.map +1 -1
  152. package/dist/runtime/llm.d.ts +1 -1
  153. package/dist/runtime/llm.d.ts.map +1 -1
  154. package/dist/runtime/local-bin.d.ts +120 -0
  155. package/dist/runtime/local-bin.d.ts.map +1 -0
  156. package/dist/runtime/safe-json.d.ts +25 -0
  157. package/dist/runtime/safe-json.d.ts.map +1 -0
  158. package/dist/runtime.js +332 -22
  159. package/dist/schema-evolution-guard/index.d.ts +1 -1
  160. package/dist/schema-evolution-guard/index.d.ts.map +1 -1
  161. package/dist/schema-evolution-guard.js +7 -1
  162. package/dist/secret-scanner/index.d.ts +1 -1
  163. package/dist/secret-scanner/index.d.ts.map +1 -1
  164. package/dist/secret-scanner.js +181 -48
  165. package/dist/security-hotspot-scanner/index.d.ts +1 -1
  166. package/dist/security-hotspot-scanner/index.d.ts.map +1 -1
  167. package/dist/security-hotspot-scanner.js +158 -20
  168. package/dist/semantic-search-indexer/index.d.ts +1 -1
  169. package/dist/semantic-search-indexer/index.d.ts.map +1 -1
  170. package/dist/semantic-search-indexer.js +7 -1
  171. package/dist/semver-bump/index.d.ts +1 -1
  172. package/dist/semver-bump/index.d.ts.map +1 -1
  173. package/dist/semver-bump.js +97 -43
  174. package/dist/session-recap/index.d.ts +1 -1
  175. package/dist/session-recap/index.d.ts.map +1 -1
  176. package/dist/shell-check/index.d.ts +1 -1
  177. package/dist/shell-check/index.d.ts.map +1 -1
  178. package/dist/smart-rename/index.d.ts +1 -1
  179. package/dist/smart-rename/index.d.ts.map +1 -1
  180. package/dist/spec-linker/index.d.ts +1 -1
  181. package/dist/spec-linker/index.d.ts.map +1 -1
  182. package/dist/spec-linker.js +222 -20772
  183. package/dist/template-engine/index.d.ts +1 -1
  184. package/dist/template-engine/index.d.ts.map +1 -1
  185. package/dist/template-engine.js +34 -8
  186. package/dist/test-coverage-gate/index.d.ts +1 -1
  187. package/dist/test-coverage-gate/index.d.ts.map +1 -1
  188. package/dist/test-coverage-gate.js +34 -14
  189. package/dist/test-flake-detector/index.d.ts +1 -1
  190. package/dist/test-flake-detector/index.d.ts.map +1 -1
  191. package/dist/test-flake-detector.js +31 -20
  192. package/dist/test-generator/index.d.ts +1 -1
  193. package/dist/test-generator/index.d.ts.map +1 -1
  194. package/dist/test-runner-gate/index.d.ts +1 -1
  195. package/dist/test-runner-gate/index.d.ts.map +1 -1
  196. package/dist/test-runner-gate.js +132 -9
  197. package/dist/todo-listener/index.d.ts +1 -32
  198. package/dist/todo-listener/index.d.ts.map +1 -1
  199. package/dist/todo-listener.js +30 -1
  200. package/dist/todo-tracker/index.d.ts +1 -1
  201. package/dist/todo-tracker/index.d.ts.map +1 -1
  202. package/dist/todo-tracker.js +3 -4
  203. package/dist/token-budget/index.d.ts +1 -1
  204. package/dist/token-budget/index.d.ts.map +1 -1
  205. package/dist/token-throttle/index.d.ts +1 -1
  206. package/dist/token-throttle/index.d.ts.map +1 -1
  207. package/dist/token-throttle.js +17 -2
  208. package/dist/type-gate/index.d.ts +1 -1
  209. package/dist/type-gate/index.d.ts.map +1 -1
  210. package/dist/type-gate.js +106 -19
  211. package/package.json +80 -68
package/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # @wrongstack/plugins
2
2
 
3
3
  First-party plugin collection for [WrongStack](https://github.com/WrongStack/WrongStack).
4
- Sixty-three focused, single-purpose plugins ship in this package. Passive,
4
+ Sixty-four focused, single-purpose plugins ship in this package. Passive,
5
5
  broadly applicable diagnostics and bounded safety checks load automatically;
6
6
  plugins with automatic mutation, policy enforcement, network egress, background
7
7
  work, or provider-call semantic changes are opt-in.
@@ -15,79 +15,15 @@ exports a default `Plugin` object. The host's plugin loader
15
15
  `ToolRegistry` and may also register **hooks** (e.g.
16
16
  `secret-scanner` registers `PreToolUse` + `PostToolUse` hooks).
17
17
 
18
- Plugins are loaded lazily by `packages/cli/src/wiring/plugins.ts`
19
- under the `BUILTIN_PLUGIN_FACTORIES` array. The canonical default is
20
- `PLUGIN_AUDIT_ENTRIES.defaultState`: list an inactive plugin with
18
+ Official plugins are loaded lazily through factories generated from the typed
19
+ manifest. The canonical default is each manifest entry's `audit.defaultState`:
20
+ list an inactive plugin with
21
21
  `{ name: '<plugin>', enabled: true }` to opt in, or list an active plugin with
22
22
  `enabled: false` to opt out.
23
23
 
24
24
  ## Plugin catalog
25
25
 
26
- | # | Plugin | Tools | Hooks | Notes |
27
- |---|---|---|---|---|
28
- | 1 | [`auto-doc`](./src/auto-doc) | `auto_doc` | — | JSDoc/TSDoc generation with `dry_run` preview |
29
- | 2 | [`git-autocommit`](./src/git-autocommit) | `git_autocommit` | — | AI-written conventional commits; warns on simultaneous worktrees |
30
- | 3 | [`shell-check`](./src/shell-check) | `shellcheck` | — | Runs `shellcheck` on files or directories |
31
- | 4 | [`cost-tracker`](./src/cost-tracker) | `cost_summary`, `cost_reset`, `cost_export` | — | Tracks per-model token usage and estimated USD cost |
32
- | 5 | [`file-watcher`](./src/file-watcher) | `watch_start`, `watch_stop`, `watch_list` | — | Watches project files and emits debounced change events |
33
- | 6 | [`cron`](./src/cron) | `cron_schedule`, `cron_list`, `cron_cancel` | — | Schedules recurring in-session actions |
34
- | 7 | [`template-engine`](./src/template-engine) | `template_expand`, `template_render`, `template_create`, `template_list` | — | Expands templates with variables, conditionals, and loops |
35
- | 8 | [`semver-bump`](./src/semver-bump) | `semver_bump`, `semver_current`, `semver_changelog` | — | Infers version bumps and changelogs from conventional commits |
36
- | 9 | [`secret-scanner`](./src/secret-scanner) | `secret_scanner_status`, `secret_scanner_test` | `PreToolUse` + `PostToolUse` | Blocks/redacts input secrets and warns on output leaks |
37
- | 10 | [`todo-tracker`](./src/todo-tracker) | `todo_tracker_*` | — | Persistent project-scoped backlog that survives sessions |
38
- | 11 | [`token-budget`](./src/token-budget) | `token_budget_status` | `Stop` + `PostToolUse` | Tracks token use and can warn/stop at configured limits |
39
- | 12 | [`lint-gate`](./src/lint-gate) | `lint_gate_status` | `PreToolUse` | Lints would-be write/edit content before mutation |
40
- | 13 | [`branch-guard`](./src/branch-guard) | `branch_guard_status` | `PreToolUse` | Opt-in protected-branch policy for commits, pushes, and merges |
41
- | 14 | [`diff-summary`](./src/diff-summary) | `diff_summary_status` | `PostToolUse` | Injects compact git diff context after write/edit |
42
- | 15 | [`commit-validator`](./src/commit-validator) | `commit_validator_status` | `PreToolUse` | Opt-in conventional-commit policy |
43
- | 16 | [`format-on-save`](./src/format-on-save) | `format_on_save_status` | `PostToolUse` | Runs formatter after write/edit |
44
- | 17 | [`test-runner-gate`](./src/test-runner-gate) | `test_gate_status` | `PostToolUse` | Runs relevant tests after source edits |
45
- | 18 | [`import-organizer`](./src/import-organizer) | `import_organizer_status` | `PostToolUse` | Organizes imports and applies safe fixes after write/edit |
46
- | 19 | [`todo-listener`](./src/todo-listener) | `todo_listener_status` | `PostToolUse` | Broadcasts todo status changes to the project mailbox |
47
- | 20 | [`session-recap`](./src/session-recap) | `session_recap_status` | `Stop` | Posts a compact session recap to the project mailbox |
48
- | 21 | [`spec-linker`](./src/spec-linker) | `spec_linker_status` | `PreToolUse` + `PostToolUse` | Detects unlinked plugin references in markdown docs |
49
- | 22 | [`loop-breaker`](./src/loop-breaker) | `loop_breaker_status` | `PreToolUse` | Detects repeated tool-call loops and oscillations |
50
- | 23 | [`path-guard`](./src/path-guard) | `path_guard_status` | `PreToolUse` | Opt-in protected-path policy for writes and destructive commands |
51
- | 24 | [`context-pins`](./src/context-pins) | `pin_add`, `pin_remove`, `pin_list` | System prompt contributor | Persists short pinned facts across compaction/session boundaries |
52
- | 25 | [`checkpoint`](./src/checkpoint) | `checkpoint_create`, `checkpoint_list`, `checkpoint_restore` | `PreToolUse` | Opt-in in-memory snapshots of pre-edit file contents |
53
- | 26 | [`error-lens`](./src/error-lens) | `error_lens_history` | `PostToolUse` | Summarizes failed tool output and repeated failure patterns |
54
- | 27 | [`dep-guard`](./src/dep-guard) | `dep_guard_status` | `PreToolUse` | Supervises dependency installs for deny-list and supply-chain warnings |
55
- | 28 | [`config-validator`](./src/config-validator) | `config_validator_status` | `PostToolUse` | Validates edited JSON/JSONC/YAML/TOML config files |
56
- | 29 | [`notify-hub`](./src/notify-hub) | `notify_send`, `notify_hub_status` | `Stop` + event subscriptions | Sends session/tool notifications to a configured webhook |
57
- | 30 | [`changelog-writer`](./src/changelog-writer) | `changelog_add`, `changelog_preview`, `changelog_write` | — | Collects and writes Keep-a-Changelog entries |
58
- | 31 | [`injection-shield`](./src/injection-shield) | `injection_shield_status` | `PostToolUse` | Warns when tool output contains prompt-injection patterns |
59
- | 32 | [`llm-cache`](./src/llm-cache) | `llm_cache_status`, `llm_cache_clear` | Provider runner wrapper | Opt-in cache for deterministic provider requests |
60
- | 33 | [`model-router`](./src/model-router) | `model_router_status` | Provider runner wrapper | Opt-in routing of provider calls by declarative rules |
61
- | 34 | [`prompt-firewall`](./src/prompt-firewall) | `prompt_firewall_status` | Provider runner wrapper | Opt-in provider-wire prompt/secret scanning and redaction/blocking |
62
- | 35 | [`auto-escalate`](./src/auto-escalate) | `auto_escalate_status` | `onError` extension | Opt-in retry/escalation ladder for transient provider errors |
63
- | 36 | [`token-throttle`](./src/token-throttle) | `token_throttle_status` | Provider runner wrapper | Opt-in rolling-window token throttling for provider calls |
64
- | 37 | [`type-gate`](./src/type-gate) | `type_gate_status` | `PostToolUse` | Runs `tsc --noEmit` after `write`/`edit` to catch type regressions |
65
- | 38 | [`test-coverage-gate`](./src/test-coverage-gate) | `coverage_gate_status` | `PostToolUse` | Detects test-coverage regressions after source-file edits |
66
- | 39 | [`pr-drafter`](./src/pr-drafter) | `pr_draft` | `Stop` + event subscriptions | Drafts a pull-request description from session commits/files/diff |
67
- | 40 | [`agent-handoff`](./src/agent-handoff) | `handoff_note`, `handoff_status` | `subagent.done` event listener | Opt-in mailbox publication of subagent results |
68
- | 41 | [`knowledge-graph`](./src/knowledge-graph) | `kg_add_fact`, `kg_query`, `kg_remove_fact`, `kg_status` | System prompt contributor | Persistent structured (subject, relation, object) project facts |
69
- | 42 | [`dead-code-detector`](./src/dead-code-detector) | `dead_code_scan` | `PostToolUse` | Regex-based scan for exported identifiers that appear unused |
70
- | 43 | [`dependency-vulnerability-gate`](./src/dependency-vulnerability-gate) | `dependency_audit_status` | `PostToolUse` | Opt-in blocking `npm/pnpm audit` after installs |
71
- | 44 | [`migration-planner`](./src/migration-planner) | `migration_plan`, `migration_status` | `PostToolUse` | Evidence-backed checklists + optional `api.llm` risk/verification analysis |
72
- | 45 | [`semantic-search-indexer`](./src/semantic-search-indexer) | `semantic_search`, `semantic_index_status` | — | Lightweight keyword index over project source files |
73
- | 46 | [`auto-i18n-extractor`](./src/auto-i18n-extractor) | `i18n_extract`, `i18n_status` | `PostToolUse` | Detects hardcoded user-facing strings and suggests i18n keys |
74
- | 47 | [`doc-sync-guard`](./src/doc-sync-guard) | `doc_sync_status` | `PostToolUse` | Warns when README/docs edits omit recently changed source files |
75
- | 48 | [`api-compatibility-gate`](./src/api-compatibility-gate) | `api_compat_status` | `PostToolUse` | Detects removed exports in entry-point files as breaking changes |
76
- | 49 | [`performance-regression-gate`](./src/performance-regression-gate) | `perf_regression_status` | — | Compares benchmark results and reports regressions |
77
- | 50 | [`test-flake-detector`](./src/test-flake-detector) | `flake_detect`, `flake_status` | — | Runs a test command N times to identify flaky tests |
78
- | 51 | [`schema-evolution-guard`](./src/schema-evolution-guard) | `schema_evolution_status` | `PostToolUse` | Detects destructive patterns in DB/API schema files |
79
- | 52 | [`license-audit-gate`](./src/license-audit-gate) | `license_audit_status` | `PostToolUse` | Audits newly added dependency licenses against an allowlist |
80
- | 53 | [`accessibility-auditor`](./src/accessibility-auditor) | `a11y_audit`, `a11y_status` | `PostToolUse` | Audits UI files for missing alt, labels, button text, duplicate ids |
81
- | 54 | [`security-hotspot-scanner`](./src/security-hotspot-scanner) | `security_hotspot_scan`, `security_hotspot_status` | `PostToolUse` | Scans source for security anti-patterns after writes/edits |
82
- | 55 | [`duplicate-code-detector`](./src/duplicate-code-detector) | `detect_duplicate_code`, `duplicate_code_status` | `PostToolUse` | Detects duplicate/similar code blocks using normalized-line fingerprints |
83
- | 56 | [`code-metrics`](./src/code-metrics) | `measure_code_metrics`, `metrics_status` | `PostToolUse` | Computes LOC, comment ratio, function count, and approximate cyclomatic complexity |
84
- | 57 | [`refactor-suggester`](./src/refactor-suggester) | `suggest_refactors`, `refactor_status` | `PostToolUse` | Flags long functions, deep nesting, many parameters, magic numbers, console.log |
85
- | 58 | [`test-generator`](./src/test-generator) | `generate_unit_tests` | — | Framework-correct skeletons + optional behavior-focused `api.llm` tests |
86
- | 59 | [`release-notes-generator`](./src/release-notes-generator) | `generate_release_notes` | — | Traceable notes + optional hash-preserving `api.llm` polish |
87
- | 60 | [`smart-rename`](./src/smart-rename) | `smart_rename` | — | Whole-word symbol rename with preview and optional apply |
88
- | 61 | [`feature-flag-tracker`](./src/feature-flag-tracker) | `scan_feature_flags`, `feature_flag_status` | `PostToolUse` | Scans source files for feature-flag usage and reports flag inventory |
89
- | 62 | [`interface-contract-guard`](./src/interface-contract-guard) | `check_interface_contracts`, `interface_contract_status` | `PostToolUse` | Warns when TypeScript interfaces change or lack implementers |
90
- | 63 | [`plugin-stack-observer`](./src/plugin-stack-observer) | `plugin_stack_status` | Event listener + optional system prompt contributor | Reports active provider-runner wrappers in registration order |
26
+ Plugin identity, import paths, versions, API ranges, and descriptions are generated from the typed manifest in [`PLUGIN_CATALOG.md`](./PLUGIN_CATALOG.md). Run `pnpm plugins:manifest:write` after changing the manifest.
91
27
 
92
28
  ### Removed plugins (use built-in tools instead)
93
29
 
@@ -702,7 +638,7 @@ context. Increase `includeTranscriptTail` for more history.
702
638
  Two hooks working together:
703
639
 
704
640
  **PostToolUse** (always active when the plugin is enabled):
705
- scans markdown files for *unlinked* references to one of the 63
641
+ scans markdown files for *unlinked* references to one of the manifest-listed
706
642
  known plugins and surfaces them to the LLM via
707
643
  `additionalContext`. The plugin does NOT modify the file — it
708
644
  only injects a low-noise context block listing the unlinked
@@ -726,7 +662,7 @@ the PostToolUse context tells the LLM what to fix.
726
662
 
727
663
  **Detection rules** (both hooks):
728
664
  - Source matches `config.fileGlobs` (default: `**/*.md`, `**/*.mdx`)
729
- - The reference matches one of the 63 known plugin names
665
+ - The reference matches one of the manifest-listed plugin names
730
666
  (case-insensitive)
731
667
  - It is NOT already wrapped in a markdown link `[name](...)` or
732
668
  inline code `` `name` ``
@@ -24,7 +24,7 @@
24
24
  *
25
25
  * @public
26
26
  */
27
- import type { Plugin } from '@wrongstack/core';
27
+ import type { Plugin } from '@wrongstack/core/types';
28
28
  export type A11yRule = 'missing-alt' | 'missing-input-label' | 'low-contrast-placeholder' | 'missing-button-text' | 'duplicate-id';
29
29
  export interface A11yFinding {
30
30
  file: string;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/accessibility-auditor/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAIH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAS/C,MAAM,MAAM,QAAQ,GAChB,aAAa,GACb,qBAAqB,GACrB,0BAA0B,GAC1B,qBAAqB,GACrB,cAAc,CAAC;AAEnB,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,QAAQ,CAAC;IACf,QAAQ,EAAE,OAAO,GAAG,SAAS,CAAC;IAC9B,OAAO,EAAE,MAAM,CAAC;CACjB;AAoPD,QAAA,MAAM,MAAM,EAAE,MA0Nb,CAAC;eAEa,MAAM"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/accessibility-auditor/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAIH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AASrD,MAAM,MAAM,QAAQ,GAChB,aAAa,GACb,qBAAqB,GACrB,0BAA0B,GAC1B,qBAAqB,GACrB,cAAc,CAAC;AAEnB,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,QAAQ,CAAC;IACf,QAAQ,EAAE,OAAO,GAAG,SAAS,CAAC;IAC9B,OAAO,EAAE,MAAM,CAAC;CACjB;AAwQD,QAAA,MAAM,MAAM,EAAE,MA8Nb,CAAC;eAEa,MAAM"}
@@ -1,10 +1,14 @@
1
1
  // src/accessibility-auditor/index.ts
2
- import { readFileSync } from "node:fs";
2
+ import { readFile } from "node:fs/promises";
3
3
  import { isAbsolute as isAbsolute2, relative as relative2, resolve as resolve2 } from "node:path";
4
4
 
5
5
  // src/runtime/index.ts
6
- import { existsSync, readdirSync, statSync } from "node:fs";
7
6
  import { basename, extname, isAbsolute, relative, resolve } from "node:path";
7
+
8
+ // src/runtime/local-bin.ts
9
+ import { buildWin32CmdShimInvocation, resolveWin32Command } from "@wrongstack/tools/win32";
10
+
11
+ // src/runtime/index.ts
8
12
  var MAX_BUFFER_BYTES = 16 * 1024 * 1024;
9
13
  function hasLeadingDash(arg) {
10
14
  return arg.length > 0 && arg.startsWith("-");
@@ -17,44 +21,45 @@ function withinProjectPath(projectRoot, candidate) {
17
21
  return rel === "" || !rel.startsWith("..") && !isAbsolute(rel);
18
22
  }
19
23
  function withinProject(p) {
20
- return withinProjectPath(process.cwd(), p) || relative(process.cwd(), p) === ".";
24
+ const cwd = process.cwd();
25
+ return withinProjectPath(cwd, p) || relative(cwd, p) === ".";
21
26
  }
22
27
  var DEFAULT_EXCLUDE_DIRS = ["node_modules", "dist", ".git", "coverage"];
23
- function collectSourceFiles(root, opts) {
28
+ async function collectSourceFilesAsync(root, opts) {
29
+ const { readdir, stat } = await import("node:fs/promises");
24
30
  const files = [];
25
- if (!existsSync(root)) return files;
26
- const s = statSync(root);
27
- if (s.isFile()) {
28
- if (matchesExtension(root, opts.extensions)) files.push(root);
31
+ try {
32
+ const s = await stat(root);
33
+ if (s.isFile()) {
34
+ if (matchesExtension(root, opts.extensions)) files.push(root);
35
+ return files;
36
+ }
37
+ if (!s.isDirectory()) return files;
38
+ } catch {
29
39
  return files;
30
40
  }
31
- if (!s.isDirectory()) return files;
32
41
  const exclude = opts.excludeDirs ?? DEFAULT_EXCLUDE_DIRS;
33
- function walk(dir, depth) {
42
+ const excludeSet = new Set(exclude);
43
+ async function walk(dir, depth) {
34
44
  if (opts.maxDepth !== void 0 && depth > opts.maxDepth) return;
35
45
  let entries;
36
46
  try {
37
- entries = readdirSync(dir);
47
+ entries = await readdir(dir, { withFileTypes: true });
38
48
  } catch {
39
49
  return;
40
50
  }
51
+ entries.sort((a, b) => a.name.localeCompare(b.name));
41
52
  for (const entry of entries) {
42
- if (exclude.includes(entry)) continue;
43
- const full = resolve(dir, entry);
44
- let st;
45
- try {
46
- st = statSync(full);
47
- } catch {
48
- continue;
49
- }
50
- if (st.isDirectory()) {
51
- walk(full, depth + 1);
52
- } else if (st.isFile() && matchesExtension(full, opts.extensions)) {
53
+ if (excludeSet.has(entry.name)) continue;
54
+ const full = resolve(dir, entry.name);
55
+ if (entry.isDirectory()) {
56
+ await walk(full, depth + 1);
57
+ } else if (entry.isFile() && matchesExtension(full, opts.extensions)) {
53
58
  files.push(full);
54
59
  }
55
60
  }
56
61
  }
57
- walk(root, 0);
62
+ await walk(root, 0);
58
63
  return files;
59
64
  }
60
65
  function matchesExtension(p, exts) {
@@ -109,10 +114,10 @@ function hasMeaningfulAlt(tag) {
109
114
  const valMatch = tag.match(/\balt\s*=\s*["']?([^"'\s>]*)["']?/i);
110
115
  return valMatch ? valMatch[1].trim().length > 0 : false;
111
116
  }
112
- function auditFile(filePath, projectRoot) {
117
+ async function auditFile(filePath, projectRoot) {
113
118
  let content;
114
119
  try {
115
- content = readFileSync(filePath, "utf-8");
120
+ content = await readFile(filePath, "utf-8");
116
121
  } catch {
117
122
  return [];
118
123
  }
@@ -198,21 +203,29 @@ function auditFile(filePath, projectRoot) {
198
203
  }
199
204
  return findings;
200
205
  }
201
- function auditPath(rawPath, cfg) {
206
+ async function auditPath(rawPath, cfg) {
202
207
  const root = process.cwd();
203
208
  const resolved = isAbsolute2(rawPath) ? resolve2(rawPath) : resolve2(root, rawPath);
204
209
  const exts = normalizeExtensions(cfg.includeExtensions);
205
- const files = collectSourceFiles(resolved, { extensions: exts });
210
+ const files = await collectSourceFilesAsync(resolved, { extensions: exts });
206
211
  const findings = [];
212
+ let scannedFiles = 0;
213
+ let truncated = false;
207
214
  for (const file of files) {
208
- const fileFindings = auditFile(file, root);
215
+ const fileFindings = await auditFile(file, root);
216
+ scannedFiles += 1;
209
217
  findings.push(...fileFindings);
210
- if (findings.length >= cfg.maxFindings) break;
218
+ if (findings.length >= cfg.maxFindings) {
219
+ truncated = scannedFiles < files.length;
220
+ break;
221
+ }
211
222
  }
212
223
  return {
213
224
  path: relative2(root, resolved),
214
225
  findings: findings.slice(0, cfg.maxFindings),
215
- fileCount: files.length
226
+ fileCount: files.length,
227
+ scannedFiles,
228
+ truncated
216
229
  };
217
230
  }
218
231
  function formatSummary(result) {
@@ -274,7 +287,7 @@ var plugin = {
274
287
  state.lastResult = null;
275
288
  state.hookUnregister = null;
276
289
  const cfg = readConfig(api.config.extensions?.["accessibility-auditor"]);
277
- const hook = (input) => {
290
+ const hook = async (input) => {
278
291
  if (!cfg.enabled || !cfg.onWriteEdit) return;
279
292
  if (input.toolResult?.isError) return;
280
293
  const inp = input.toolInput ?? {};
@@ -284,7 +297,7 @@ var plugin = {
284
297
  const exts = normalizeExtensions(cfg.includeExtensions);
285
298
  if (!matchesExtension(sourcePath, exts)) return;
286
299
  state.hookInvocationCount += 1;
287
- const result = auditPath(sourcePath, cfg);
300
+ const result = await auditPath(sourcePath, cfg);
288
301
  state.auditCount += 1;
289
302
  state.fileCount += result.fileCount;
290
303
  state.findingCount += result.findings.length;
@@ -328,7 +341,7 @@ var plugin = {
328
341
  return { ok: false, error: "path must be inside the project" };
329
342
  }
330
343
  state.auditCount += 1;
331
- const result = auditPath(rawPath, cfg);
344
+ const result = await auditPath(rawPath, cfg);
332
345
  state.fileCount += result.fileCount;
333
346
  state.findingCount += result.findings.length;
334
347
  state.lastResult = {
@@ -341,6 +354,10 @@ var plugin = {
341
354
  ok: true,
342
355
  path: result.path,
343
356
  fileCount: result.fileCount,
357
+ scannedFiles: result.scannedFiles,
358
+ // Say so when the cap stopped the walk early: a partial scan
359
+ // that reports few findings must not read as a clean result.
360
+ truncated: result.truncated,
344
361
  findingCount: result.findings.length,
345
362
  findings: result.findings
346
363
  };
@@ -29,7 +29,7 @@
29
29
  *
30
30
  * @public
31
31
  */
32
- import type { Plugin } from '@wrongstack/core';
32
+ import type { Plugin } from '@wrongstack/core/types';
33
33
  declare const plugin: Plugin;
34
34
  export default plugin;
35
35
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/agent-handoff/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAEH,OAAO,KAAK,EAAE,MAAM,EAA6B,MAAM,kBAAkB,CAAC;AAwK1E,QAAA,MAAM,MAAM,EAAE,MAsNb,CAAC;eAEa,MAAM"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/agent-handoff/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAGH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AA2KrD,QAAA,MAAM,MAAM,EAAE,MAsNb,CAAC;eAEa,MAAM"}
@@ -1,3 +1,32 @@
1
+ // src/runtime/local-bin.ts
2
+ import { buildWin32CmdShimInvocation, resolveWin32Command } from "@wrongstack/tools/win32";
3
+
4
+ // src/runtime/safe-json.ts
5
+ var UNSERIALIZABLE = "[unserializable]";
6
+ function safeJsonStringify(value, indent) {
7
+ try {
8
+ const stack = [];
9
+ const out = JSON.stringify(
10
+ value,
11
+ function replacer(_key, val) {
12
+ if (typeof val === "bigint") return `${val.toString()}n`;
13
+ if (val === null || typeof val !== "object") return val;
14
+ while (stack.length > 0 && stack[stack.length - 1] !== this) stack.pop();
15
+ if (stack.includes(val)) return "[circular]";
16
+ stack.push(val);
17
+ return val;
18
+ },
19
+ indent
20
+ );
21
+ return out ?? String(value);
22
+ } catch {
23
+ return UNSERIALIZABLE;
24
+ }
25
+ }
26
+
27
+ // src/runtime/index.ts
28
+ var MAX_BUFFER_BYTES = 16 * 1024 * 1024;
29
+
1
30
  // src/agent-handoff/index.ts
2
31
  var API_VERSION = "^0.1.10";
3
32
  var state = {
@@ -55,7 +84,7 @@ function buildBody(payload, cfg) {
55
84
  if (cfg.includeResult && payload.result !== void 0) {
56
85
  lines.push("## Result");
57
86
  lines.push("```json");
58
- lines.push(JSON.stringify(payload.result, null, 2));
87
+ lines.push(safeJsonStringify(payload.result, 2));
59
88
  lines.push("```");
60
89
  lines.push("");
61
90
  }
@@ -30,7 +30,7 @@
30
30
  *
31
31
  * @public
32
32
  */
33
- import type { Plugin } from '@wrongstack/core';
33
+ import type { Plugin } from '@wrongstack/core/types';
34
34
  declare const plugin: Plugin;
35
35
  export default plugin;
36
36
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/api-compatibility-gate/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAKH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAkR/C,QAAA,MAAM,MAAM,EAAE,MA6Mb,CAAC;eAEa,MAAM"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/api-compatibility-gate/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAMH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAsRrD,QAAA,MAAM,MAAM,EAAE,MA6Mb,CAAC;eAEa,MAAM"}
@@ -1,6 +1,7 @@
1
1
  // src/api-compatibility-gate/index.ts
2
- import { execFileSync } from "node:child_process";
2
+ import { execFile } from "node:child_process";
3
3
  import { existsSync, readFileSync } from "node:fs";
4
+ import { readFile } from "node:fs/promises";
4
5
  import { basename, dirname, extname, isAbsolute, relative, resolve } from "node:path";
5
6
  var API_VERSION = "^0.1.10";
6
7
  var state = {
@@ -155,21 +156,25 @@ function extractExports(source) {
155
156
  function readGitVersion(filePath) {
156
157
  const resolved = resolveToProjectRoot(filePath);
157
158
  const relPath = normalizePath(relative(process.cwd(), resolved));
158
- try {
159
- const out = execFileSync("git", ["show", `HEAD:${relPath}`], {
160
- encoding: "utf-8",
161
- cwd: process.cwd(),
162
- stdio: ["pipe", "pipe", "pipe"]
163
- });
164
- return out;
165
- } catch {
166
- return null;
167
- }
159
+ return new Promise((resolveVersion) => {
160
+ execFile(
161
+ "git",
162
+ ["show", `HEAD:${relPath}`],
163
+ {
164
+ encoding: "utf8",
165
+ cwd: process.cwd(),
166
+ windowsHide: true,
167
+ timeout: 5e3,
168
+ maxBuffer: 5 * 1024 * 1024
169
+ },
170
+ (error, stdout) => resolveVersion(error ? null : stdout)
171
+ );
172
+ });
168
173
  }
169
- function readCurrentFile(filePath) {
174
+ async function readCurrentFile(filePath) {
170
175
  try {
171
176
  const resolved = resolveToProjectRoot(filePath);
172
- return readFileSync(resolved, "utf-8");
177
+ return await readFile(resolved, "utf8");
173
178
  } catch {
174
179
  return null;
175
180
  }
@@ -227,7 +232,7 @@ var plugin = {
227
232
  state.hookUnregister = null;
228
233
  }
229
234
  const cfg = readConfig(api.config.extensions?.["api-compatibility-gate"]);
230
- const hook = (input) => {
235
+ const hook = async (input) => {
231
236
  if (!cfg.enabled) return;
232
237
  if (input.toolResult?.isError) return;
233
238
  const inp = input.toolInput ?? {};
@@ -242,12 +247,12 @@ var plugin = {
242
247
  state.skippedCount += 1;
243
248
  return;
244
249
  }
245
- const before = readGitVersion(filePath);
250
+ const before = await readGitVersion(filePath);
246
251
  if (before === null) {
247
252
  state.skippedCount += 1;
248
253
  return;
249
254
  }
250
- const after = readCurrentFile(filePath);
255
+ const after = await readCurrentFile(filePath);
251
256
  if (after === null) {
252
257
  state.errorCount += 1;
253
258
  return;