@wrongstack/plugins 0.286.0 → 0.289.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 +67 -55
- 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 +20 -12
- package/dist/changelog-writer.js +1 -1
- package/dist/code-metrics/index.d.ts.map +1 -1
- package/dist/code-metrics.js +63 -50
- package/dist/commit-validator.js +1 -1
- package/dist/config-validator.js +1 -1
- package/dist/cost-tracker/index.d.ts.map +1 -1
- package/dist/cost-tracker.js +4 -2
- 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 +28 -19
- 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 +25 -16
- package/dist/duplicate-code-detector/index.d.ts.map +1 -1
- package/dist/duplicate-code-detector.js +70 -57
- package/dist/error-lens.js +1 -1
- package/dist/feature-flag-tracker/index.d.ts.map +1 -1
- package/dist/feature-flag-tracker.js +69 -56
- package/dist/file-watcher/index.d.ts.map +1 -1
- package/dist/file-watcher.js +21 -4
- 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 +23 -14
- package/dist/index.d.ts +4 -4
- package/dist/index.js +1167 -1048
- package/dist/interface-contract-guard/index.d.ts.map +1 -1
- package/dist/interface-contract-guard.js +64 -51
- package/dist/lint-gate/index.d.ts.map +1 -1
- package/dist/lint-gate.js +61 -30
- 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/prompt-firewall/index.d.ts +14 -6
- package/dist/prompt-firewall/index.d.ts.map +1 -1
- package/dist/prompt-firewall.js +7 -6
- package/dist/refactor-suggester/index.d.ts.map +1 -1
- package/dist/refactor-suggester.js +69 -56
- 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 +36 -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 +119 -25
- package/dist/schema-evolution-guard/index.d.ts.map +1 -1
- package/dist/schema-evolution-guard.js +22 -13
- package/dist/secret-scanner/index.d.ts.map +1 -1
- package/dist/secret-scanner.js +17 -6
- package/dist/security-hotspot-scanner/index.d.ts.map +1 -1
- package/dist/security-hotspot-scanner.js +24 -15
- package/dist/session-recap.js +1 -1
- package/dist/spec-linker.js +1167 -1048
- package/dist/test-generator/index.d.ts +5 -3
- package/dist/test-generator/index.d.ts.map +1 -1
- package/dist/test-generator.js +169 -26
- 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 +23 -35
- 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;AAoPD,QAAA,MAAM,MAAM,EAAE,MA0Nb,CAAC;eAEa,MAAM"}
|
|
@@ -1,61 +1,37 @@
|
|
|
1
1
|
// src/accessibility-auditor/index.ts
|
|
2
|
-
import {
|
|
3
|
-
import { isAbsolute, relative, resolve } from "node:path";
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
hookUnregister: null
|
|
12
|
-
};
|
|
13
|
-
var DEFAULTS = {
|
|
14
|
-
enabled: false,
|
|
15
|
-
includeExtensions: [".tsx", ".jsx", ".html", ".vue"],
|
|
16
|
-
maxFindings: 50,
|
|
17
|
-
severity: "warn",
|
|
18
|
-
onWriteEdit: true
|
|
19
|
-
};
|
|
20
|
-
function readConfig(raw) {
|
|
21
|
-
if (!raw || typeof raw !== "object") return { ...DEFAULTS };
|
|
22
|
-
const r = raw;
|
|
23
|
-
return {
|
|
24
|
-
enabled: r["enabled"] !== false,
|
|
25
|
-
includeExtensions: Array.isArray(r["includeExtensions"]) ? r["includeExtensions"].filter((x) => typeof x === "string") : DEFAULTS.includeExtensions,
|
|
26
|
-
maxFindings: typeof r["maxFindings"] === "number" && r["maxFindings"] >= 1 && r["maxFindings"] <= 500 ? r["maxFindings"] : DEFAULTS.maxFindings,
|
|
27
|
-
severity: r["severity"] === "block" ? "block" : DEFAULTS.severity,
|
|
28
|
-
onWriteEdit: r["onWriteEdit"] !== false
|
|
29
|
-
};
|
|
30
|
-
}
|
|
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;
|
|
2
|
+
import { readFileSync } from "node:fs";
|
|
3
|
+
import { isAbsolute as isAbsolute2, relative as relative2, resolve as resolve2 } from "node:path";
|
|
4
|
+
|
|
5
|
+
// src/runtime/index.ts
|
|
6
|
+
import { existsSync, readdirSync, statSync } from "node:fs";
|
|
7
|
+
import { basename, extname, isAbsolute, relative, resolve } from "node:path";
|
|
8
|
+
var MAX_BUFFER_BYTES = 16 * 1024 * 1024;
|
|
9
|
+
function hasLeadingDash(arg) {
|
|
10
|
+
return arg.length > 0 && arg.startsWith("-");
|
|
40
11
|
}
|
|
41
|
-
function
|
|
42
|
-
|
|
12
|
+
function withinProjectPath(projectRoot, candidate) {
|
|
13
|
+
if (candidate.length === 0 || candidate.length > 4096) return false;
|
|
14
|
+
if (hasLeadingDash(candidate)) return false;
|
|
15
|
+
const resolved = isAbsolute(candidate) ? resolve(candidate) : resolve(projectRoot, candidate);
|
|
16
|
+
const rel = relative(projectRoot, resolved);
|
|
17
|
+
return rel === "" || !rel.startsWith("..") && !isAbsolute(rel);
|
|
43
18
|
}
|
|
44
|
-
function
|
|
45
|
-
|
|
46
|
-
if (dot === -1) return false;
|
|
47
|
-
return exts.includes(p.slice(dot).toLowerCase());
|
|
19
|
+
function withinProject(p) {
|
|
20
|
+
return withinProjectPath(process.cwd(), p) || relative(process.cwd(), p) === ".";
|
|
48
21
|
}
|
|
49
|
-
|
|
22
|
+
var DEFAULT_EXCLUDE_DIRS = ["node_modules", "dist", ".git", "coverage"];
|
|
23
|
+
function collectSourceFiles(root, opts) {
|
|
50
24
|
const files = [];
|
|
51
25
|
if (!existsSync(root)) return files;
|
|
52
26
|
const s = statSync(root);
|
|
53
27
|
if (s.isFile()) {
|
|
54
|
-
if (matchesExtension(root,
|
|
28
|
+
if (matchesExtension(root, opts.extensions)) files.push(root);
|
|
55
29
|
return files;
|
|
56
30
|
}
|
|
57
31
|
if (!s.isDirectory()) return files;
|
|
58
|
-
|
|
32
|
+
const exclude = opts.excludeDirs ?? DEFAULT_EXCLUDE_DIRS;
|
|
33
|
+
function walk(dir, depth) {
|
|
34
|
+
if (opts.maxDepth !== void 0 && depth > opts.maxDepth) return;
|
|
59
35
|
let entries;
|
|
60
36
|
try {
|
|
61
37
|
entries = readdirSync(dir);
|
|
@@ -63,6 +39,7 @@ function collectUiFiles(root, exts) {
|
|
|
63
39
|
return;
|
|
64
40
|
}
|
|
65
41
|
for (const entry of entries) {
|
|
42
|
+
if (exclude.includes(entry)) continue;
|
|
66
43
|
const full = resolve(dir, entry);
|
|
67
44
|
let st;
|
|
68
45
|
try {
|
|
@@ -71,15 +48,50 @@ function collectUiFiles(root, exts) {
|
|
|
71
48
|
continue;
|
|
72
49
|
}
|
|
73
50
|
if (st.isDirectory()) {
|
|
74
|
-
walk(full);
|
|
75
|
-
} else if (st.isFile() && matchesExtension(full,
|
|
51
|
+
walk(full, depth + 1);
|
|
52
|
+
} else if (st.isFile() && matchesExtension(full, opts.extensions)) {
|
|
76
53
|
files.push(full);
|
|
77
54
|
}
|
|
78
55
|
}
|
|
79
56
|
}
|
|
80
|
-
walk(root);
|
|
57
|
+
walk(root, 0);
|
|
81
58
|
return files;
|
|
82
59
|
}
|
|
60
|
+
function matchesExtension(p, exts) {
|
|
61
|
+
return exts.includes(extname(p).toLowerCase());
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// src/accessibility-auditor/index.ts
|
|
65
|
+
var API_VERSION = "^0.1.10";
|
|
66
|
+
var state = {
|
|
67
|
+
auditCount: 0,
|
|
68
|
+
fileCount: 0,
|
|
69
|
+
findingCount: 0,
|
|
70
|
+
hookInvocationCount: 0,
|
|
71
|
+
lastResult: null,
|
|
72
|
+
hookUnregister: null
|
|
73
|
+
};
|
|
74
|
+
var DEFAULTS = {
|
|
75
|
+
enabled: false,
|
|
76
|
+
includeExtensions: [".tsx", ".jsx", ".html", ".vue"],
|
|
77
|
+
maxFindings: 50,
|
|
78
|
+
severity: "warn",
|
|
79
|
+
onWriteEdit: true
|
|
80
|
+
};
|
|
81
|
+
function readConfig(raw) {
|
|
82
|
+
if (!raw || typeof raw !== "object") return { ...DEFAULTS };
|
|
83
|
+
const r = raw;
|
|
84
|
+
return {
|
|
85
|
+
enabled: r["enabled"] !== false,
|
|
86
|
+
includeExtensions: Array.isArray(r["includeExtensions"]) ? r["includeExtensions"].filter((x) => typeof x === "string") : DEFAULTS.includeExtensions,
|
|
87
|
+
maxFindings: typeof r["maxFindings"] === "number" && r["maxFindings"] >= 1 && r["maxFindings"] <= 500 ? r["maxFindings"] : DEFAULTS.maxFindings,
|
|
88
|
+
severity: r["severity"] === "block" ? "block" : DEFAULTS.severity,
|
|
89
|
+
onWriteEdit: r["onWriteEdit"] !== false
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
function normalizeExtensions(exts) {
|
|
93
|
+
return exts.map((e) => e.startsWith(".") ? e.toLowerCase() : `.${e.toLowerCase()}`);
|
|
94
|
+
}
|
|
83
95
|
var TAG_IMG = /<img\b[^>]*>/gi;
|
|
84
96
|
var TAG_INPUT = /<input\b[^>]*\/?>/gi;
|
|
85
97
|
var TAG_BUTTON = /<button\b[^>]*>([\s\S]*?)<\/button>/gi;
|
|
@@ -109,7 +121,7 @@ function auditFile(filePath, projectRoot) {
|
|
|
109
121
|
const idsByValue = /* @__PURE__ */ new Map();
|
|
110
122
|
function add(line, rule, severity, message) {
|
|
111
123
|
findings.push({
|
|
112
|
-
file:
|
|
124
|
+
file: relative2(projectRoot, filePath),
|
|
113
125
|
line,
|
|
114
126
|
rule,
|
|
115
127
|
severity,
|
|
@@ -188,9 +200,9 @@ function auditFile(filePath, projectRoot) {
|
|
|
188
200
|
}
|
|
189
201
|
function auditPath(rawPath, cfg) {
|
|
190
202
|
const root = process.cwd();
|
|
191
|
-
const resolved =
|
|
203
|
+
const resolved = isAbsolute2(rawPath) ? resolve2(rawPath) : resolve2(root, rawPath);
|
|
192
204
|
const exts = normalizeExtensions(cfg.includeExtensions);
|
|
193
|
-
const files =
|
|
205
|
+
const files = collectSourceFiles(resolved, { extensions: exts });
|
|
194
206
|
const findings = [];
|
|
195
207
|
for (const file of files) {
|
|
196
208
|
const fileFindings = auditFile(file, root);
|
|
@@ -198,7 +210,7 @@ function auditPath(rawPath, cfg) {
|
|
|
198
210
|
if (findings.length >= cfg.maxFindings) break;
|
|
199
211
|
}
|
|
200
212
|
return {
|
|
201
|
-
path:
|
|
213
|
+
path: relative2(root, resolved),
|
|
202
214
|
findings: findings.slice(0, cfg.maxFindings),
|
|
203
215
|
fileCount: files.length
|
|
204
216
|
};
|
|
@@ -289,7 +301,7 @@ var plugin = {
|
|
|
289
301
|
}
|
|
290
302
|
return { additionalContext: summary };
|
|
291
303
|
};
|
|
292
|
-
state.hookUnregister = api.registerHook("PostToolUse", "write|edit", hook);
|
|
304
|
+
state.hookUnregister = api.registerHook("PostToolUse", "write|edit", hook, { background: true });
|
|
293
305
|
api.tools.register({
|
|
294
306
|
name: "a11y_audit",
|
|
295
307
|
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, extname, 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() : "";
|
|
@@ -201,7 +209,7 @@ var plugin = {
|
|
|
201
209
|
${lines.join("\n")}${more}`;
|
|
202
210
|
return { additionalContext: context };
|
|
203
211
|
};
|
|
204
|
-
state.hookUnregister = api.registerHook("PostToolUse", "write|edit", hook);
|
|
212
|
+
state.hookUnregister = api.registerHook("PostToolUse", "write|edit", hook, { background: true });
|
|
205
213
|
api.tools.register({
|
|
206
214
|
name: "i18n_extract",
|
|
207
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;AA0M/C,QAAA,MAAM,MAAM,EAAE,MAgMb,CAAC;eAEa,MAAM"}
|
package/dist/code-metrics.js
CHANGED
|
@@ -1,54 +1,37 @@
|
|
|
1
1
|
// src/code-metrics/index.ts
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
};
|
|
12
|
-
var DEFAULTS = {
|
|
13
|
-
enabled: false,
|
|
14
|
-
extensions: [".ts", ".tsx", ".js", ".jsx"],
|
|
15
|
-
maxFiles: 50
|
|
16
|
-
};
|
|
17
|
-
function readConfig(raw) {
|
|
18
|
-
if (!raw || typeof raw !== "object") return { ...DEFAULTS };
|
|
19
|
-
const r = raw;
|
|
20
|
-
return {
|
|
21
|
-
enabled: r["enabled"] !== false,
|
|
22
|
-
extensions: Array.isArray(r["extensions"]) ? r["extensions"].filter((x) => typeof x === "string") : DEFAULTS.extensions,
|
|
23
|
-
maxFiles: typeof r["maxFiles"] === "number" && r["maxFiles"] >= 1 && r["maxFiles"] <= 500 ? r["maxFiles"] : DEFAULTS.maxFiles
|
|
24
|
-
};
|
|
25
|
-
}
|
|
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;
|
|
2
|
+
import { readFileSync } from "node:fs";
|
|
3
|
+
import { isAbsolute as isAbsolute2, relative as relative2, resolve as resolve2 } from "node:path";
|
|
4
|
+
|
|
5
|
+
// src/runtime/index.ts
|
|
6
|
+
import { existsSync, readdirSync, statSync } from "node:fs";
|
|
7
|
+
import { basename, extname, isAbsolute, relative, resolve } from "node:path";
|
|
8
|
+
var MAX_BUFFER_BYTES = 16 * 1024 * 1024;
|
|
9
|
+
function hasLeadingDash(arg) {
|
|
10
|
+
return arg.length > 0 && arg.startsWith("-");
|
|
35
11
|
}
|
|
36
|
-
function
|
|
37
|
-
|
|
12
|
+
function withinProjectPath(projectRoot, candidate) {
|
|
13
|
+
if (candidate.length === 0 || candidate.length > 4096) return false;
|
|
14
|
+
if (hasLeadingDash(candidate)) return false;
|
|
15
|
+
const resolved = isAbsolute(candidate) ? resolve(candidate) : resolve(projectRoot, candidate);
|
|
16
|
+
const rel = relative(projectRoot, resolved);
|
|
17
|
+
return rel === "" || !rel.startsWith("..") && !isAbsolute(rel);
|
|
38
18
|
}
|
|
39
|
-
function
|
|
40
|
-
return
|
|
19
|
+
function withinProject(p) {
|
|
20
|
+
return withinProjectPath(process.cwd(), p) || relative(process.cwd(), p) === ".";
|
|
41
21
|
}
|
|
42
|
-
|
|
22
|
+
var DEFAULT_EXCLUDE_DIRS = ["node_modules", "dist", ".git", "coverage"];
|
|
23
|
+
function collectSourceFiles(root, opts) {
|
|
43
24
|
const files = [];
|
|
44
25
|
if (!existsSync(root)) return files;
|
|
45
26
|
const s = statSync(root);
|
|
46
27
|
if (s.isFile()) {
|
|
47
|
-
if (matchesExtension(root,
|
|
28
|
+
if (matchesExtension(root, opts.extensions)) files.push(root);
|
|
48
29
|
return files;
|
|
49
30
|
}
|
|
50
31
|
if (!s.isDirectory()) return files;
|
|
51
|
-
|
|
32
|
+
const exclude = opts.excludeDirs ?? DEFAULT_EXCLUDE_DIRS;
|
|
33
|
+
function walk(dir, depth) {
|
|
34
|
+
if (opts.maxDepth !== void 0 && depth > opts.maxDepth) return;
|
|
52
35
|
let entries;
|
|
53
36
|
try {
|
|
54
37
|
entries = readdirSync(dir);
|
|
@@ -56,7 +39,7 @@ function collectSourceFiles(root, exts) {
|
|
|
56
39
|
return;
|
|
57
40
|
}
|
|
58
41
|
for (const entry of entries) {
|
|
59
|
-
if (
|
|
42
|
+
if (exclude.includes(entry)) continue;
|
|
60
43
|
const full = resolve(dir, entry);
|
|
61
44
|
let st;
|
|
62
45
|
try {
|
|
@@ -65,20 +48,50 @@ function collectSourceFiles(root, exts) {
|
|
|
65
48
|
continue;
|
|
66
49
|
}
|
|
67
50
|
if (st.isDirectory()) {
|
|
68
|
-
walk(full);
|
|
69
|
-
} else if (st.isFile() && matchesExtension(full,
|
|
51
|
+
walk(full, depth + 1);
|
|
52
|
+
} else if (st.isFile() && matchesExtension(full, opts.extensions)) {
|
|
70
53
|
files.push(full);
|
|
71
54
|
}
|
|
72
55
|
}
|
|
73
56
|
}
|
|
74
|
-
walk(root);
|
|
57
|
+
walk(root, 0);
|
|
75
58
|
return files;
|
|
76
59
|
}
|
|
60
|
+
function matchesExtension(p, exts) {
|
|
61
|
+
return exts.includes(extname(p).toLowerCase());
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// src/code-metrics/index.ts
|
|
65
|
+
var API_VERSION = "^0.1.10";
|
|
66
|
+
var state = {
|
|
67
|
+
measureCount: 0,
|
|
68
|
+
fileCount: 0,
|
|
69
|
+
hookInvocationCount: 0,
|
|
70
|
+
errorCount: 0,
|
|
71
|
+
hookUnregister: null
|
|
72
|
+
};
|
|
73
|
+
var DEFAULTS = {
|
|
74
|
+
enabled: false,
|
|
75
|
+
extensions: [".ts", ".tsx", ".js", ".jsx"],
|
|
76
|
+
maxFiles: 50
|
|
77
|
+
};
|
|
78
|
+
function readConfig(raw) {
|
|
79
|
+
if (!raw || typeof raw !== "object") return { ...DEFAULTS };
|
|
80
|
+
const r = raw;
|
|
81
|
+
return {
|
|
82
|
+
enabled: r["enabled"] !== false,
|
|
83
|
+
extensions: Array.isArray(r["extensions"]) ? r["extensions"].filter((x) => typeof x === "string") : DEFAULTS.extensions,
|
|
84
|
+
maxFiles: typeof r["maxFiles"] === "number" && r["maxFiles"] >= 1 && r["maxFiles"] <= 500 ? r["maxFiles"] : DEFAULTS.maxFiles
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
function normalizeExtensions(exts) {
|
|
88
|
+
return exts.map((e) => e.startsWith(".") ? e.toLowerCase() : `.${e.toLowerCase()}`);
|
|
89
|
+
}
|
|
77
90
|
function toPosix(p) {
|
|
78
91
|
return p.replace(/\\/g, "/");
|
|
79
92
|
}
|
|
80
93
|
function relativePath(p) {
|
|
81
|
-
return toPosix(
|
|
94
|
+
return toPosix(relative2(process.cwd(), p));
|
|
82
95
|
}
|
|
83
96
|
function countFunctions(content) {
|
|
84
97
|
let count = 0;
|
|
@@ -155,9 +168,9 @@ function analyzeFile(filePath, content) {
|
|
|
155
168
|
}
|
|
156
169
|
function measurePath(rawPath, cfg) {
|
|
157
170
|
const root = process.cwd();
|
|
158
|
-
const resolved =
|
|
171
|
+
const resolved = isAbsolute2(rawPath) ? resolve2(rawPath) : resolve2(root, rawPath);
|
|
159
172
|
const exts = normalizeExtensions(cfg.extensions);
|
|
160
|
-
const allFiles = collectSourceFiles(resolved, exts);
|
|
173
|
+
const allFiles = collectSourceFiles(resolved, { extensions: exts });
|
|
161
174
|
const files = allFiles.slice(0, cfg.maxFiles);
|
|
162
175
|
const metrics = [];
|
|
163
176
|
for (const p of files) {
|
|
@@ -218,7 +231,7 @@ var plugin = {
|
|
|
218
231
|
const exts = normalizeExtensions(cfg.extensions);
|
|
219
232
|
if (!matchesExtension(sourcePath, exts)) return;
|
|
220
233
|
state.hookInvocationCount += 1;
|
|
221
|
-
const resolved =
|
|
234
|
+
const resolved = resolve2(process.cwd(), sourcePath);
|
|
222
235
|
let content;
|
|
223
236
|
try {
|
|
224
237
|
content = readFileSync(resolved, "utf-8");
|
|
@@ -232,7 +245,7 @@ var plugin = {
|
|
|
232
245
|
\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}.`
|
|
233
246
|
};
|
|
234
247
|
};
|
|
235
|
-
state.hookUnregister = api.registerHook("PostToolUse", "write|edit", hook);
|
|
248
|
+
state.hookUnregister = api.registerHook("PostToolUse", "write|edit", hook, { background: true });
|
|
236
249
|
api.tools.register({
|
|
237
250
|
name: "measure_code_metrics",
|
|
238
251
|
description: "Measure lines, comments, blank lines, function count, and cyclomatic-complexity-like score for a source file or directory.",
|
|
@@ -262,7 +275,7 @@ var plugin = {
|
|
|
262
275
|
state.fileCount += result.files.length;
|
|
263
276
|
return {
|
|
264
277
|
ok: true,
|
|
265
|
-
path: relativePath(
|
|
278
|
+
path: relativePath(resolve2(process.cwd(), rawPath)),
|
|
266
279
|
files: result.files,
|
|
267
280
|
totalFiles: result.totalFiles,
|
|
268
281
|
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).",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cost-tracker/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAK/C;;;;;;;;GAQG;AACH,MAAM,WAAW,YAAY;IAC3B,gDAAgD;IAChD,KAAK,EAAE,MAAM,CAAC;IACd,qDAAqD;IACrD,MAAM,EAAE,MAAM,CAAC;CAChB;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cost-tracker/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAK/C;;;;;;;;GAQG;AACH,MAAM,WAAW,YAAY;IAC3B,gDAAgD;IAChD,KAAK,EAAE,MAAM,CAAC;IACd,qDAAqD;IACrD,MAAM,EAAE,MAAM,CAAC;CAChB;AAyKD,QAAA,MAAM,MAAM,EAAE,MA8Zb,CAAC;eAEa,MAAM"}
|
package/dist/cost-tracker.js
CHANGED
|
@@ -19,11 +19,13 @@ var digestCounters = {
|
|
|
19
19
|
mailboxDigestErrors: 0
|
|
20
20
|
};
|
|
21
21
|
function readCostTrackerConfig(raw) {
|
|
22
|
+
const digestEveryN = raw?.["mailboxDigestEveryN"];
|
|
23
|
+
const digestTo = raw?.["mailboxDigestTo"];
|
|
22
24
|
return {
|
|
23
25
|
budgetLimit: typeof raw?.["budgetLimit"] === "number" ? raw["budgetLimit"] : 0,
|
|
24
26
|
warningThreshold: typeof raw?.["warningThreshold"] === "number" ? raw["warningThreshold"] : 80,
|
|
25
|
-
mailboxDigestEveryN: typeof
|
|
26
|
-
mailboxDigestTo: typeof
|
|
27
|
+
mailboxDigestEveryN: typeof digestEveryN === "number" && digestEveryN >= 0 ? Math.floor(digestEveryN) : 0,
|
|
28
|
+
mailboxDigestTo: typeof digestTo === "string" && digestTo.length > 0 ? digestTo : "cost-tracker"
|
|
27
29
|
};
|
|
28
30
|
}
|
|
29
31
|
function estimateCost(model, promptTokens, completionTokens) {
|
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;
|