@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.
- package/README.md +20 -11
- package/dist/accessibility-auditor/index.d.ts.map +1 -1
- package/dist/accessibility-auditor.js +30 -26
- package/dist/api-compatibility-gate.js +1 -1
- package/dist/auto-doc.js +1 -1
- package/dist/auto-i18n-extractor/index.d.ts.map +1 -1
- package/dist/auto-i18n-extractor.js +21 -14
- package/dist/changelog-writer.js +1 -1
- package/dist/code-metrics/index.d.ts.map +1 -1
- package/dist/code-metrics.js +34 -27
- package/dist/commit-validator.js +1 -1
- package/dist/config-validator.js +1 -1
- package/dist/cron/index.d.ts.map +1 -1
- package/dist/cron.js +29 -0
- package/dist/dead-code-detector/index.d.ts.map +1 -1
- package/dist/dead-code-detector.js +30 -22
- package/dist/dep-guard.js +1 -1
- package/dist/diff-summary/index.d.ts.map +1 -1
- package/dist/diff-summary.js +19 -11
- package/dist/doc-sync-guard/index.d.ts.map +1 -1
- package/dist/doc-sync-guard.js +24 -15
- package/dist/duplicate-code-detector/index.d.ts.map +1 -1
- package/dist/duplicate-code-detector.js +27 -18
- package/dist/error-lens.js +1 -1
- package/dist/feature-flag-tracker/index.d.ts.map +1 -1
- package/dist/feature-flag-tracker.js +27 -19
- package/dist/file-watcher/index.d.ts.map +1 -1
- package/dist/file-watcher.js +15 -2
- package/dist/format-on-save/index.d.ts.map +1 -1
- package/dist/format-on-save.js +19 -11
- package/dist/git-autocommit.js +1 -1
- package/dist/import-organizer/index.d.ts.map +1 -1
- package/dist/import-organizer.js +22 -13
- package/dist/index.d.ts +4 -4
- package/dist/index.js +1007 -759
- package/dist/interface-contract-guard/index.d.ts.map +1 -1
- package/dist/interface-contract-guard.js +27 -19
- package/dist/license-audit-gate/index.d.ts.map +1 -1
- package/dist/license-audit-gate.js +1 -2
- package/dist/migration-planner/index.d.ts +12 -2
- package/dist/migration-planner/index.d.ts.map +1 -1
- package/dist/migration-planner.js +210 -28
- package/dist/pr-drafter.js +1 -1
- package/dist/refactor-suggester/index.d.ts.map +1 -1
- package/dist/refactor-suggester.js +37 -29
- package/dist/release-notes-generator/index.d.ts +3 -1
- package/dist/release-notes-generator/index.d.ts.map +1 -1
- package/dist/release-notes-generator.js +152 -12
- package/dist/runtime/index.d.ts +12 -0
- package/dist/runtime/index.d.ts.map +1 -1
- package/dist/runtime/llm.d.ts +33 -0
- package/dist/runtime/llm.d.ts.map +1 -0
- package/dist/runtime.js +57 -2
- package/dist/schema-evolution-guard/index.d.ts.map +1 -1
- package/dist/schema-evolution-guard.js +21 -12
- package/dist/security-hotspot-scanner/index.d.ts.map +1 -1
- package/dist/security-hotspot-scanner.js +25 -16
- package/dist/session-recap.js +1 -1
- package/dist/spec-linker.js +1007 -759
- package/dist/test-generator/index.d.ts +5 -3
- package/dist/test-generator/index.d.ts.map +1 -1
- package/dist/test-generator.js +183 -41
- package/dist/test-runner-gate/index.d.ts.map +1 -1
- package/dist/test-runner-gate.js +65 -43
- package/dist/type-gate/index.d.ts.map +1 -1
- package/dist/type-gate.js +5 -13
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -68,7 +68,7 @@ under the `BUILTIN_PLUGIN_FACTORIES` array. The canonical default is
|
|
|
68
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
69
|
| 42 | [`dead-code-detector`](./src/dead-code-detector) | `dead_code_scan` | `PostToolUse` | Regex-based scan for exported identifiers that appear unused |
|
|
70
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` |
|
|
71
|
+
| 44 | [`migration-planner`](./src/migration-planner) | `migration_plan`, `migration_status` | `PostToolUse` | Evidence-backed checklists + optional `api.llm` risk/verification analysis |
|
|
72
72
|
| 45 | [`semantic-search-indexer`](./src/semantic-search-indexer) | `semantic_search`, `semantic_index_status` | — | Lightweight keyword index over project source files |
|
|
73
73
|
| 46 | [`auto-i18n-extractor`](./src/auto-i18n-extractor) | `i18n_extract`, `i18n_status` | `PostToolUse` | Detects hardcoded user-facing strings and suggests i18n keys |
|
|
74
74
|
| 47 | [`doc-sync-guard`](./src/doc-sync-guard) | `doc_sync_status` | `PostToolUse` | Warns when README/docs edits omit recently changed source files |
|
|
@@ -82,11 +82,12 @@ under the `BUILTIN_PLUGIN_FACTORIES` array. The canonical default is
|
|
|
82
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
83
|
| 56 | [`code-metrics`](./src/code-metrics) | `measure_code_metrics`, `metrics_status` | `PostToolUse` | Computes LOC, comment ratio, function count, and approximate cyclomatic complexity |
|
|
84
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` | — |
|
|
86
|
-
| 59 | [`release-notes-generator`](./src/release-notes-generator) | `generate_release_notes` | — |
|
|
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
87
|
| 60 | [`smart-rename`](./src/smart-rename) | `smart_rename` | — | Whole-word symbol rename with preview and optional apply |
|
|
88
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
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 |
|
|
90
91
|
|
|
91
92
|
### Removed plugins (use built-in tools instead)
|
|
92
93
|
|
|
@@ -97,7 +98,7 @@ under the `BUILTIN_PLUGIN_FACTORIES` array. The canonical default is
|
|
|
97
98
|
|
|
98
99
|
If a user lists either name in `config.plugins`, the loader emits a
|
|
99
100
|
one-shot `log.warn` and skips loading. See
|
|
100
|
-
[`DEPRECATED_PLUGIN_NAMES`](
|
|
101
|
+
[`DEPRECATED_PLUGIN_NAMES`](../cli/src/wiring/plugins.ts)
|
|
101
102
|
in `packages/cli/src/wiring/plugins.ts` for the canonical list and
|
|
102
103
|
migration hints.
|
|
103
104
|
|
|
@@ -701,7 +702,7 @@ context. Increase `includeTranscriptTail` for more history.
|
|
|
701
702
|
Two hooks working together:
|
|
702
703
|
|
|
703
704
|
**PostToolUse** (always active when the plugin is enabled):
|
|
704
|
-
scans markdown files for *unlinked* references to one of the
|
|
705
|
+
scans markdown files for *unlinked* references to one of the 63
|
|
705
706
|
known plugins and surfaces them to the LLM via
|
|
706
707
|
`additionalContext`. The plugin does NOT modify the file — it
|
|
707
708
|
only injects a low-noise context block listing the unlinked
|
|
@@ -1043,18 +1044,26 @@ There are two surfaces for plugin configuration:
|
|
|
1043
1044
|
{
|
|
1044
1045
|
"plugins": [
|
|
1045
1046
|
{ "name": "auto-doc", "enabled": true },
|
|
1046
|
-
{
|
|
1047
|
+
{
|
|
1048
|
+
"name": "git-autocommit",
|
|
1049
|
+
"enabled": true,
|
|
1050
|
+
"options": { "conventionalCommits": true }
|
|
1051
|
+
}
|
|
1047
1052
|
]
|
|
1048
1053
|
}
|
|
1049
1054
|
```
|
|
1050
|
-
2. **Options** — `
|
|
1051
|
-
plugin
|
|
1052
|
-
|
|
1055
|
+
2. **Options** — set an entry's `options` field or use
|
|
1056
|
+
`config.extensions["<plugin-name>"]`; the CLI merges both before `setup()`.
|
|
1057
|
+
If both are present, `config.extensions` wins for duplicate keys.
|
|
1053
1058
|
|
|
1054
1059
|
```jsonc
|
|
1055
1060
|
{
|
|
1056
|
-
"plugins":
|
|
1057
|
-
"auto-doc"
|
|
1061
|
+
"plugins": [
|
|
1062
|
+
{ "name": "auto-doc", "enabled": true },
|
|
1063
|
+
{ "name": "git-autocommit", "enabled": true },
|
|
1064
|
+
{ "name": "secret-scanner", "enabled": true }
|
|
1065
|
+
],
|
|
1066
|
+
"extensions": {
|
|
1058
1067
|
"git-autocommit": { "conventionalCommits": true },
|
|
1059
1068
|
"secret-scanner": { "mode": "block", "matcher": "bash|write|edit" }
|
|
1060
1069
|
}
|
|
@@ -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;
|
|
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;AA+RD,QAAA,MAAM,MAAM,EAAE,MA0Nb,CAAC;eAEa,MAAM"}
|
|
@@ -1,6 +1,25 @@
|
|
|
1
1
|
// src/accessibility-auditor/index.ts
|
|
2
2
|
import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
|
|
3
|
-
import { isAbsolute, relative, resolve } from "node:path";
|
|
3
|
+
import { 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/accessibility-auditor/index.ts
|
|
4
23
|
var API_VERSION = "^0.1.10";
|
|
5
24
|
var state = {
|
|
6
25
|
auditCount: 0,
|
|
@@ -28,16 +47,6 @@ function readConfig(raw) {
|
|
|
28
47
|
onWriteEdit: r["onWriteEdit"] !== false
|
|
29
48
|
};
|
|
30
49
|
}
|
|
31
|
-
function withinProject(p) {
|
|
32
|
-
if (typeof p !== "string" || p.length === 0 || p.length > 4096) return false;
|
|
33
|
-
const root = process.cwd();
|
|
34
|
-
const resolved = isAbsolute(p) ? resolve(p) : resolve(root, p);
|
|
35
|
-
const rel = relative(root, resolved);
|
|
36
|
-
if (rel === "" || rel === ".") return true;
|
|
37
|
-
if (rel.startsWith("..")) return false;
|
|
38
|
-
if (isAbsolute(rel)) return false;
|
|
39
|
-
return true;
|
|
40
|
-
}
|
|
41
50
|
function normalizeExtensions(exts) {
|
|
42
51
|
return exts.map((e) => e.startsWith(".") ? e.toLowerCase() : `.${e.toLowerCase()}`);
|
|
43
52
|
}
|
|
@@ -63,7 +72,7 @@ function collectUiFiles(root, exts) {
|
|
|
63
72
|
return;
|
|
64
73
|
}
|
|
65
74
|
for (const entry of entries) {
|
|
66
|
-
const full =
|
|
75
|
+
const full = resolve2(dir, entry);
|
|
67
76
|
let st;
|
|
68
77
|
try {
|
|
69
78
|
st = statSync(full);
|
|
@@ -109,7 +118,7 @@ function auditFile(filePath, projectRoot) {
|
|
|
109
118
|
const idsByValue = /* @__PURE__ */ new Map();
|
|
110
119
|
function add(line, rule, severity, message) {
|
|
111
120
|
findings.push({
|
|
112
|
-
file:
|
|
121
|
+
file: relative2(projectRoot, filePath),
|
|
113
122
|
line,
|
|
114
123
|
rule,
|
|
115
124
|
severity,
|
|
@@ -119,24 +128,21 @@ function auditFile(filePath, projectRoot) {
|
|
|
119
128
|
for (let i = 0; i < lines.length; i++) {
|
|
120
129
|
const line = lines[i];
|
|
121
130
|
const lineNo = i + 1;
|
|
122
|
-
|
|
123
|
-
while ((idMatch = ATTR_ID.exec(line)) !== null) {
|
|
131
|
+
for (const idMatch of line.matchAll(ATTR_ID)) {
|
|
124
132
|
const id = idMatch[1];
|
|
125
133
|
const list = idsByValue.get(id) ?? [];
|
|
126
134
|
list.push(lineNo);
|
|
127
135
|
idsByValue.set(id, list);
|
|
128
136
|
}
|
|
129
137
|
ATTR_ID.lastIndex = 0;
|
|
130
|
-
|
|
131
|
-
while ((imgMatch = TAG_IMG.exec(line)) !== null) {
|
|
138
|
+
for (const imgMatch of line.matchAll(TAG_IMG)) {
|
|
132
139
|
const tag = imgMatch[0];
|
|
133
140
|
if (!hasMeaningfulAlt(tag)) {
|
|
134
141
|
add(lineNo, "missing-alt", "error", "<img> is missing meaningful alt text");
|
|
135
142
|
}
|
|
136
143
|
}
|
|
137
144
|
TAG_IMG.lastIndex = 0;
|
|
138
|
-
|
|
139
|
-
while ((inputMatch = TAG_INPUT.exec(line)) !== null) {
|
|
145
|
+
for (const inputMatch of line.matchAll(TAG_INPUT)) {
|
|
140
146
|
const tag = inputMatch[0];
|
|
141
147
|
const typeMatch = tag.match(/\btype\s*=\s*["']?([^"'\s>]*)["']?/i);
|
|
142
148
|
const type = typeMatch ? typeMatch[1].toLowerCase() : "text";
|
|
@@ -161,8 +167,7 @@ function auditFile(filePath, projectRoot) {
|
|
|
161
167
|
}
|
|
162
168
|
}
|
|
163
169
|
TAG_INPUT.lastIndex = 0;
|
|
164
|
-
|
|
165
|
-
while ((buttonMatch = TAG_BUTTON.exec(line)) !== null) {
|
|
170
|
+
for (const buttonMatch of line.matchAll(TAG_BUTTON)) {
|
|
166
171
|
const tag = buttonMatch[0];
|
|
167
172
|
const inner = buttonMatch[1] ?? "";
|
|
168
173
|
const hasText = inner.replace(/\s+/g, "").length > 0;
|
|
@@ -173,8 +178,7 @@ function auditFile(filePath, projectRoot) {
|
|
|
173
178
|
}
|
|
174
179
|
}
|
|
175
180
|
TAG_BUTTON.lastIndex = 0;
|
|
176
|
-
|
|
177
|
-
while ((inputButtonMatch = INPUT_BUTTON.exec(line)) !== null) {
|
|
181
|
+
for (const inputButtonMatch of line.matchAll(INPUT_BUTTON)) {
|
|
178
182
|
const tag = inputButtonMatch[0];
|
|
179
183
|
if (!ATTR_VALUE.test(tag) && !ATTR_ARIA_LABEL.test(tag) && !ATTR_TITLE.test(tag)) {
|
|
180
184
|
add(lineNo, "missing-button-text", "error", `<input type="${inputButtonMatch[1]}"> is missing value/aria-label/title`);
|
|
@@ -193,7 +197,7 @@ function auditFile(filePath, projectRoot) {
|
|
|
193
197
|
}
|
|
194
198
|
function auditPath(rawPath, cfg) {
|
|
195
199
|
const root = process.cwd();
|
|
196
|
-
const resolved =
|
|
200
|
+
const resolved = isAbsolute2(rawPath) ? resolve2(rawPath) : resolve2(root, rawPath);
|
|
197
201
|
const exts = normalizeExtensions(cfg.includeExtensions);
|
|
198
202
|
const files = collectUiFiles(resolved, exts);
|
|
199
203
|
const findings = [];
|
|
@@ -203,7 +207,7 @@ function auditPath(rawPath, cfg) {
|
|
|
203
207
|
if (findings.length >= cfg.maxFindings) break;
|
|
204
208
|
}
|
|
205
209
|
return {
|
|
206
|
-
path:
|
|
210
|
+
path: relative2(root, resolved),
|
|
207
211
|
findings: findings.slice(0, cfg.maxFindings),
|
|
208
212
|
fileCount: files.length
|
|
209
213
|
};
|
|
@@ -294,7 +298,7 @@ var plugin = {
|
|
|
294
298
|
}
|
|
295
299
|
return { additionalContext: summary };
|
|
296
300
|
};
|
|
297
|
-
state.hookUnregister = api.registerHook("PostToolUse", "write|edit", hook);
|
|
301
|
+
state.hookUnregister = api.registerHook("PostToolUse", "write|edit", hook, { background: true });
|
|
298
302
|
api.tools.register({
|
|
299
303
|
name: "a11y_audit",
|
|
300
304
|
description: "Audit a file or directory for accessibility issues. Scans .tsx/.jsx/.html/.vue files for missing alt text, missing labels, low-contrast placeholders, missing button text, and duplicate ids.",
|
|
@@ -279,7 +279,7 @@ var plugin = {
|
|
|
279
279
|
}
|
|
280
280
|
return { additionalContext: message };
|
|
281
281
|
};
|
|
282
|
-
state.hookUnregister = api.registerHook("PostToolUse", "write|edit", hook);
|
|
282
|
+
state.hookUnregister = api.registerHook("PostToolUse", "write|edit", hook, { background: true });
|
|
283
283
|
api.tools.register({
|
|
284
284
|
name: "api_compat_status",
|
|
285
285
|
description: "Reports api-compatibility-gate state: severity, entry-point patterns, and per-session counters.",
|
package/dist/auto-doc.js
CHANGED
|
@@ -241,7 +241,7 @@ var plugin = {
|
|
|
241
241
|
version: "0.2.0",
|
|
242
242
|
description: "Auto-generates JSDoc/TSDoc comments for functions, classes, types, and interfaces",
|
|
243
243
|
apiVersion: AUTO_DOC_API_VERSION,
|
|
244
|
-
capabilities: { tools: true, pipelines: ["toolCall"] },
|
|
244
|
+
capabilities: { tools: true, pipelines: ["toolCall"], llm: true },
|
|
245
245
|
defaultConfig: {
|
|
246
246
|
style: "tsdoc",
|
|
247
247
|
includeTypes: false,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/auto-i18n-extractor/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/auto-i18n-extractor/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAGH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAwK/C,QAAA,MAAM,MAAM,EAAE,MAsQb,CAAC;eAEa,MAAM"}
|
|
@@ -1,6 +1,24 @@
|
|
|
1
1
|
// src/auto-i18n-extractor/index.ts
|
|
2
2
|
import { readFileSync } from "node:fs";
|
|
3
|
-
|
|
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/auto-i18n-extractor/index.ts
|
|
4
22
|
var API_VERSION = "^0.1.10";
|
|
5
23
|
var state = {
|
|
6
24
|
filesScanned: 0,
|
|
@@ -41,16 +59,6 @@ function readConfig(raw) {
|
|
|
41
59
|
excludeAttributes: Array.isArray(r["excludeAttributes"]) ? r["excludeAttributes"].filter((x) => typeof x === "string") : DEFAULTS.excludeAttributes
|
|
42
60
|
};
|
|
43
61
|
}
|
|
44
|
-
function withinProject(p) {
|
|
45
|
-
if (typeof p !== "string" || p.length === 0 || p.length > 4096) return false;
|
|
46
|
-
const root = process.cwd();
|
|
47
|
-
const resolved = isAbsolute(p) ? resolve(p) : resolve(root, p);
|
|
48
|
-
const rel = relative(root, resolved);
|
|
49
|
-
if (rel === "" || rel === ".") return true;
|
|
50
|
-
if (rel.startsWith("..")) return false;
|
|
51
|
-
if (isAbsolute(rel)) return false;
|
|
52
|
-
return true;
|
|
53
|
-
}
|
|
54
62
|
function fileExtension(p) {
|
|
55
63
|
const dot = p.lastIndexOf(".");
|
|
56
64
|
return dot > 0 ? p.slice(dot).toLowerCase() : "";
|
|
@@ -83,8 +91,7 @@ function extractStrings(content, cfg) {
|
|
|
83
91
|
for (let i = 0; i < lines.length; i += 1) {
|
|
84
92
|
const line = lines[i];
|
|
85
93
|
stringRegex.lastIndex = 0;
|
|
86
|
-
|
|
87
|
-
while ((match = stringRegex.exec(line)) !== null) {
|
|
94
|
+
for (const match of line.matchAll(stringRegex)) {
|
|
88
95
|
const value = match[2] ?? "";
|
|
89
96
|
const quoteIndex = match.index + (match[1]?.length ?? 1);
|
|
90
97
|
if (!looksLikeUserText(value, cfg.minLength)) continue;
|
|
@@ -202,7 +209,7 @@ var plugin = {
|
|
|
202
209
|
${lines.join("\n")}${more}`;
|
|
203
210
|
return { additionalContext: context };
|
|
204
211
|
};
|
|
205
|
-
state.hookUnregister = api.registerHook("PostToolUse", "write|edit", hook);
|
|
212
|
+
state.hookUnregister = api.registerHook("PostToolUse", "write|edit", hook, { background: true });
|
|
206
213
|
api.tools.register({
|
|
207
214
|
name: "i18n_extract",
|
|
208
215
|
description: "Scan a UI source file (.tsx/.jsx/.vue) for hardcoded user-facing string literals and return suggested i18n keys.",
|
package/dist/changelog-writer.js
CHANGED
|
@@ -138,7 +138,7 @@ var plugin = {
|
|
|
138
138
|
version: "0.1.0",
|
|
139
139
|
description: "Collects session work (commits, edits, manual notes) and writes Keep-a-Changelog entries under [Unreleased] on demand",
|
|
140
140
|
apiVersion: "^0.1.10",
|
|
141
|
-
capabilities: { tools: true },
|
|
141
|
+
capabilities: { tools: 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/code-metrics/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAIH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/code-metrics/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAIH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAoP/C,QAAA,MAAM,MAAM,EAAE,MAgMb,CAAC;eAEa,MAAM"}
|
package/dist/code-metrics.js
CHANGED
|
@@ -1,6 +1,25 @@
|
|
|
1
1
|
// src/code-metrics/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/code-metrics/index.ts
|
|
4
23
|
var API_VERSION = "^0.1.10";
|
|
5
24
|
var state = {
|
|
6
25
|
measureCount: 0,
|
|
@@ -23,16 +42,6 @@ function readConfig(raw) {
|
|
|
23
42
|
maxFiles: typeof r["maxFiles"] === "number" && r["maxFiles"] >= 1 && r["maxFiles"] <= 500 ? r["maxFiles"] : DEFAULTS.maxFiles
|
|
24
43
|
};
|
|
25
44
|
}
|
|
26
|
-
function withinProject(p) {
|
|
27
|
-
if (typeof p !== "string" || p.length === 0 || p.length > 4096) return false;
|
|
28
|
-
const root = process.cwd();
|
|
29
|
-
const resolved = isAbsolute(p) ? resolve(p) : resolve(root, p);
|
|
30
|
-
const rel = relative(root, resolved);
|
|
31
|
-
if (rel === "" || rel === ".") return true;
|
|
32
|
-
if (rel.startsWith("..")) return false;
|
|
33
|
-
if (isAbsolute(rel)) return false;
|
|
34
|
-
return true;
|
|
35
|
-
}
|
|
36
45
|
function normalizeExtensions(exts) {
|
|
37
46
|
return exts.map((e) => e.startsWith(".") ? e.toLowerCase() : `.${e.toLowerCase()}`);
|
|
38
47
|
}
|
|
@@ -57,7 +66,7 @@ function collectSourceFiles(root, exts) {
|
|
|
57
66
|
}
|
|
58
67
|
for (const entry of entries) {
|
|
59
68
|
if (entry === "node_modules" || entry === "dist" || entry === ".git" || entry === "coverage") continue;
|
|
60
|
-
const full =
|
|
69
|
+
const full = resolve2(dir, entry);
|
|
61
70
|
let st;
|
|
62
71
|
try {
|
|
63
72
|
st = statSync(full);
|
|
@@ -78,40 +87,38 @@ function toPosix(p) {
|
|
|
78
87
|
return p.replace(/\\/g, "/");
|
|
79
88
|
}
|
|
80
89
|
function relativePath(p) {
|
|
81
|
-
return toPosix(
|
|
90
|
+
return toPosix(relative2(process.cwd(), p));
|
|
82
91
|
}
|
|
83
92
|
function countFunctions(content) {
|
|
84
93
|
let count = 0;
|
|
85
94
|
const declRe = /\bfunction\s+[A-Za-z_$][A-Za-z0-9_$]*\s*\(/g;
|
|
86
95
|
const methodRe = /\b[A-Za-z_$][A-Za-z0-9_$]*\s*\([^)]*\)\s*\{/g;
|
|
87
96
|
const arrowRe = /=>\s*(?:\{|\(|[^\s;{}])/g;
|
|
88
|
-
let m;
|
|
89
97
|
declRe.lastIndex = 0;
|
|
90
|
-
|
|
98
|
+
for (const _match of content.matchAll(declRe)) count++;
|
|
91
99
|
methodRe.lastIndex = 0;
|
|
92
|
-
|
|
93
|
-
const prefix = content.slice(0,
|
|
100
|
+
for (const match of content.matchAll(methodRe)) {
|
|
101
|
+
const prefix = content.slice(0, match.index).trimEnd();
|
|
94
102
|
if (/\bfunction\s*$/.test(prefix)) continue;
|
|
95
103
|
count++;
|
|
96
104
|
}
|
|
97
105
|
arrowRe.lastIndex = 0;
|
|
98
|
-
|
|
106
|
+
for (const _match of content.matchAll(arrowRe)) count++;
|
|
99
107
|
return count;
|
|
100
108
|
}
|
|
101
109
|
function countComplexity(content) {
|
|
102
110
|
const controlRe = /\b(if|else\s+if|for|while|switch|catch)\b/g;
|
|
103
111
|
const operatorRe = /[?&|]/g;
|
|
104
112
|
let complexity = 0;
|
|
105
|
-
let m;
|
|
106
113
|
controlRe.lastIndex = 0;
|
|
107
|
-
|
|
114
|
+
for (const _match of content.matchAll(controlRe)) complexity++;
|
|
108
115
|
operatorRe.lastIndex = 0;
|
|
109
|
-
|
|
110
|
-
const ch =
|
|
116
|
+
for (const match of content.matchAll(operatorRe)) {
|
|
117
|
+
const ch = match[0];
|
|
111
118
|
if (ch === "?") {
|
|
112
119
|
complexity++;
|
|
113
120
|
} else {
|
|
114
|
-
const next = content[
|
|
121
|
+
const next = content[match.index + 1];
|
|
115
122
|
if (next === ch) complexity++;
|
|
116
123
|
}
|
|
117
124
|
}
|
|
@@ -157,7 +164,7 @@ function analyzeFile(filePath, content) {
|
|
|
157
164
|
}
|
|
158
165
|
function measurePath(rawPath, cfg) {
|
|
159
166
|
const root = process.cwd();
|
|
160
|
-
const resolved =
|
|
167
|
+
const resolved = isAbsolute2(rawPath) ? resolve2(rawPath) : resolve2(root, rawPath);
|
|
161
168
|
const exts = normalizeExtensions(cfg.extensions);
|
|
162
169
|
const allFiles = collectSourceFiles(resolved, exts);
|
|
163
170
|
const files = allFiles.slice(0, cfg.maxFiles);
|
|
@@ -220,7 +227,7 @@ var plugin = {
|
|
|
220
227
|
const exts = normalizeExtensions(cfg.extensions);
|
|
221
228
|
if (!matchesExtension(sourcePath, exts)) return;
|
|
222
229
|
state.hookInvocationCount += 1;
|
|
223
|
-
const resolved =
|
|
230
|
+
const resolved = resolve2(process.cwd(), sourcePath);
|
|
224
231
|
let content;
|
|
225
232
|
try {
|
|
226
233
|
content = readFileSync(resolved, "utf-8");
|
|
@@ -234,7 +241,7 @@ var plugin = {
|
|
|
234
241
|
\u{1F4CA} code-metrics: ${relativePath(resolved)} \u2014 ${metrics.lines} lines (${metrics.codeLines} code, ${metrics.commentLines} comments, ${metrics.blankLines} blank), ${metrics.functionCount} function(s), complexity ${metrics.complexity}.`
|
|
235
242
|
};
|
|
236
243
|
};
|
|
237
|
-
state.hookUnregister = api.registerHook("PostToolUse", "write|edit", hook);
|
|
244
|
+
state.hookUnregister = api.registerHook("PostToolUse", "write|edit", hook, { background: true });
|
|
238
245
|
api.tools.register({
|
|
239
246
|
name: "measure_code_metrics",
|
|
240
247
|
description: "Measure lines, comments, blank lines, function count, and cyclomatic-complexity-like score for a source file or directory.",
|
|
@@ -264,7 +271,7 @@ var plugin = {
|
|
|
264
271
|
state.fileCount += result.files.length;
|
|
265
272
|
return {
|
|
266
273
|
ok: true,
|
|
267
|
-
path: relativePath(
|
|
274
|
+
path: relativePath(resolve2(process.cwd(), rawPath)),
|
|
268
275
|
files: result.files,
|
|
269
276
|
totalFiles: result.totalFiles,
|
|
270
277
|
capped: result.totalFiles > cfg.maxFiles
|
package/dist/commit-validator.js
CHANGED
|
@@ -130,7 +130,7 @@ var plugin = {
|
|
|
130
130
|
version: "0.1.0",
|
|
131
131
|
description: "PreToolUse hook that validates conventional-commit format before git_autocommit or bash git commit runs",
|
|
132
132
|
apiVersion: API_VERSION,
|
|
133
|
-
capabilities: { tools: true, hooks: true },
|
|
133
|
+
capabilities: { tools: true, hooks: true, llm: true },
|
|
134
134
|
defaultConfig: { ...DEFAULTS },
|
|
135
135
|
configSchema: {
|
|
136
136
|
type: "object",
|
package/dist/config-validator.js
CHANGED
|
@@ -282,7 +282,7 @@ var plugin = {
|
|
|
282
282
|
\u2026 and ${problems.length - 10} more` : "") + "\nFix these before moving on \u2014 downstream tools will fail on this file."
|
|
283
283
|
};
|
|
284
284
|
};
|
|
285
|
-
state.hookUnregister = api.registerHook("PostToolUse", "write|edit", hook);
|
|
285
|
+
state.hookUnregister = api.registerHook("PostToolUse", "write|edit", hook, { background: true });
|
|
286
286
|
api.tools.register({
|
|
287
287
|
name: "config_validator_status",
|
|
288
288
|
description: "Reports config-validator state: watched extensions and counters (files checked, problems found).",
|
package/dist/cron/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cron/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cron/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AA+F/C,QAAA,MAAM,MAAM,EAAE,MAqRb,CAAC;eAEa,MAAM"}
|
package/dist/cron.js
CHANGED
|
@@ -11,6 +11,19 @@ function formatNextRun(intervalMs) {
|
|
|
11
11
|
const ms = Number.isNaN(intervalMs) || intervalMs <= 0 ? 6e4 : intervalMs;
|
|
12
12
|
return new Date(Date.now() + ms).toISOString();
|
|
13
13
|
}
|
|
14
|
+
function buildSnapshot(s, maxConcurrent) {
|
|
15
|
+
const jobs = Array.from(s.jobs.values()).map((j) => ({
|
|
16
|
+
name: j.name,
|
|
17
|
+
intervalMs: j.intervalMs,
|
|
18
|
+
action: j.action,
|
|
19
|
+
enabled: j.enabled,
|
|
20
|
+
lastRun: j.lastRun,
|
|
21
|
+
nextRun: j.nextRun,
|
|
22
|
+
runCount: j.runCount,
|
|
23
|
+
overdue: new Date(j.nextRun).getTime() < Date.now()
|
|
24
|
+
}));
|
|
25
|
+
return { count: jobs.length, maxConcurrent, jobs };
|
|
26
|
+
}
|
|
14
27
|
function clearCronResources() {
|
|
15
28
|
for (const timer of state.timers.values()) {
|
|
16
29
|
clearTimeout(timer);
|
|
@@ -64,6 +77,7 @@ var plugin = {
|
|
|
64
77
|
runCount: job.runCount,
|
|
65
78
|
ts: (/* @__PURE__ */ new Date()).toISOString()
|
|
66
79
|
});
|
|
80
|
+
api.emitCustom("cron:state_snapshot", buildSnapshot(state, maxConcurrent));
|
|
67
81
|
api.metrics.counter("cron_job_fired", 1, { job: name });
|
|
68
82
|
api.metrics.histogram("cron_job_interval_ms", job.intervalMs, { job: name });
|
|
69
83
|
scheduleNextRun(name);
|
|
@@ -165,6 +179,7 @@ var plugin = {
|
|
|
165
179
|
state.jobs.set(name, job);
|
|
166
180
|
scheduleNextRun(name);
|
|
167
181
|
api.metrics.gauge("cron_active_jobs", state.jobs.size);
|
|
182
|
+
api.emitCustom("cron:state_snapshot", buildSnapshot(state, maxConcurrent));
|
|
168
183
|
return {
|
|
169
184
|
ok: true,
|
|
170
185
|
name,
|
|
@@ -220,6 +235,7 @@ var plugin = {
|
|
|
220
235
|
}
|
|
221
236
|
cancelJob(name);
|
|
222
237
|
api.metrics.gauge("cron_active_jobs", state.jobs.size);
|
|
238
|
+
api.emitCustom("cron:state_snapshot", buildSnapshot(state, maxConcurrent));
|
|
223
239
|
return {
|
|
224
240
|
ok: true,
|
|
225
241
|
name,
|
|
@@ -232,6 +248,19 @@ var plugin = {
|
|
|
232
248
|
teardown(api) {
|
|
233
249
|
clearCronResources();
|
|
234
250
|
api.log.info("cron plugin unloaded");
|
|
251
|
+
},
|
|
252
|
+
async health() {
|
|
253
|
+
const jobs = Array.from(state.jobs.values());
|
|
254
|
+
const overdue = jobs.filter(
|
|
255
|
+
(job) => job.enabled && new Date(job.nextRun).getTime() < Date.now()
|
|
256
|
+
).length;
|
|
257
|
+
return {
|
|
258
|
+
ok: overdue === 0,
|
|
259
|
+
message: overdue > 0 ? `cron: ${overdue} overdue job(s) out of ${jobs.length}` : `cron: ${jobs.length} active job(s)`,
|
|
260
|
+
activeJobs: jobs.length,
|
|
261
|
+
overdueJobs: overdue,
|
|
262
|
+
totalRuns: jobs.reduce((sum, job) => sum + job.runCount, 0)
|
|
263
|
+
};
|
|
235
264
|
}
|
|
236
265
|
};
|
|
237
266
|
var cron_default = plugin;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/dead-code-detector/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAIH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/dead-code-detector/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAIH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAiQ/C,QAAA,MAAM,MAAM,EAAE,MAkOb,CAAC;eAEa,MAAM"}
|