@wrongstack/plugins 1.0.8 → 1.0.10
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/dist/accessibility-auditor/index.d.ts +1 -1
- package/dist/accessibility-auditor.js +15 -3
- package/dist/agent-handoff.js +6 -6
- package/dist/auto-doc/index.d.ts +1 -1
- package/dist/auto-doc.js +31 -20
- package/dist/auto-i18n-extractor/index.d.ts +1 -1
- package/dist/auto-i18n-extractor.js +12 -8
- package/dist/branch-guard.js +3 -3
- package/dist/changelog-writer/index.d.ts +1 -1
- package/dist/changelog-writer.js +19 -10
- package/dist/checkpoint/index.d.ts +1 -1
- package/dist/checkpoint.js +38 -24
- package/dist/code-metrics/index.d.ts +1 -1
- package/dist/code-metrics.js +12 -4
- package/dist/commit-validator.js +5 -5
- package/dist/context-pins/index.d.ts +1 -1
- package/dist/context-pins.js +12 -9
- package/dist/cost-tracker.js +21 -9
- package/dist/cron/index.d.ts +1 -1
- package/dist/cron.js +18 -5
- package/dist/dead-code-detector/index.d.ts +1 -1
- package/dist/dead-code-detector.js +14 -12
- package/dist/duplicate-code-detector/index.d.ts +1 -1
- package/dist/duplicate-code-detector.js +11 -3
- package/dist/feature-flag-tracker/index.d.ts +1 -1
- package/dist/feature-flag-tracker.js +12 -4
- package/dist/file-watcher/index.d.ts +1 -1
- package/dist/file-watcher.js +37 -38
- package/dist/git-autocommit/index.d.ts +1 -1
- package/dist/git-autocommit.js +44 -39
- package/dist/gitignore-guard/index.d.ts +1 -1
- package/dist/gitignore-guard.js +12 -6
- package/dist/index.js +1534 -1080
- package/dist/interface-contract-guard/index.d.ts +1 -1
- package/dist/interface-contract-guard.js +12 -4
- package/dist/knowledge-graph/index.d.ts +1 -1
- package/dist/knowledge-graph.js +11 -9
- package/dist/migration-planner/index.d.ts +1 -1
- package/dist/migration-planner.js +6 -2
- package/dist/notify-hub/index.d.ts +1 -1
- package/dist/notify-hub.js +12 -11
- package/dist/performance-regression-gate/index.d.ts +1 -1
- package/dist/performance-regression-gate.js +33 -13
- package/dist/pr-drafter/index.d.ts +10 -1
- package/dist/pr-drafter.js +57 -26
- package/dist/refactor-suggester/index.d.ts +1 -1
- package/dist/refactor-suggester.js +17 -4
- package/dist/release-notes-generator.js +2 -2
- package/dist/secret-scanner/index.d.ts +1 -1
- package/dist/secret-scanner.js +11 -3
- package/dist/security-hotspot-scanner/index.d.ts +1 -1
- package/dist/security-hotspot-scanner.js +6 -2
- package/dist/semantic-search-indexer/index.d.ts +1 -1
- package/dist/semantic-search-indexer.js +19 -3
- package/dist/semver-bump/index.d.ts +1 -1
- package/dist/semver-bump.js +57 -37
- package/dist/session-recap.js +4 -2
- package/dist/shell-check/index.d.ts +1 -1
- package/dist/shell-check.js +30 -39
- package/dist/smart-rename/index.d.ts +1 -1
- package/dist/smart-rename.js +23 -10
- package/dist/template-engine/index.d.ts +1 -1
- package/dist/template-engine.js +51 -38
- package/dist/test-flake-detector/index.d.ts +1 -1
- package/dist/test-flake-detector.js +11 -5
- package/dist/test-generator/index.d.ts +1 -1
- package/dist/test-generator.js +12 -8
- package/dist/todo-tracker/index.d.ts +68 -1
- package/dist/todo-tracker.js +579 -395
- package/dist/token-budget.js +7 -4
- package/dist/token-throttle.js +6 -3
- package/package.json +7 -7
package/dist/cost-tracker.js
CHANGED
|
@@ -64,6 +64,9 @@ function estimateCost(model, freshTokens, completionTokens, cachedTokens = 0) {
|
|
|
64
64
|
const outputCost = completionTokens / 1e6 * pricing.output;
|
|
65
65
|
return inputCost + outputCost;
|
|
66
66
|
}
|
|
67
|
+
function toFiniteNumber(value) {
|
|
68
|
+
return Number.isFinite(value) ? value : 0;
|
|
69
|
+
}
|
|
67
70
|
var plugin = {
|
|
68
71
|
name: "cost-tracker",
|
|
69
72
|
version: "0.1.0",
|
|
@@ -137,11 +140,12 @@ var plugin = {
|
|
|
137
140
|
const input = v["input"];
|
|
138
141
|
const output = v["output"];
|
|
139
142
|
if (typeof input !== "number" || typeof output !== "number") continue;
|
|
143
|
+
if (!Number.isFinite(input) || !Number.isFinite(output)) continue;
|
|
140
144
|
const cacheRead = v["cacheRead"];
|
|
141
145
|
pricingOverrides[model.toLowerCase()] = {
|
|
142
146
|
input,
|
|
143
147
|
output,
|
|
144
|
-
...typeof cacheRead === "number" ? { cacheRead } : {}
|
|
148
|
+
...typeof cacheRead === "number" && Number.isFinite(cacheRead) ? { cacheRead } : {}
|
|
145
149
|
};
|
|
146
150
|
}
|
|
147
151
|
}
|
|
@@ -154,11 +158,11 @@ var plugin = {
|
|
|
154
158
|
if (!providerModels) continue;
|
|
155
159
|
for (const [modelId, model] of Object.entries(providerModels)) {
|
|
156
160
|
const cost = model?.cost;
|
|
157
|
-
if (cost && typeof cost.input === "number" && typeof cost.output === "number") {
|
|
161
|
+
if (cost && typeof cost.input === "number" && typeof cost.output === "number" && Number.isFinite(cost.input) && Number.isFinite(cost.output)) {
|
|
158
162
|
bundledFromRegistry[modelId.toLowerCase()] = {
|
|
159
163
|
input: cost.input,
|
|
160
164
|
output: cost.output,
|
|
161
|
-
...typeof cost.cache_read === "number" ? { cacheRead: cost.cache_read } : {}
|
|
165
|
+
...typeof cost.cache_read === "number" && Number.isFinite(cost.cache_read) ? { cacheRead: cost.cache_read } : {}
|
|
162
166
|
};
|
|
163
167
|
hydrated += 1;
|
|
164
168
|
}
|
|
@@ -186,14 +190,22 @@ var plugin = {
|
|
|
186
190
|
const usage = payload.usage;
|
|
187
191
|
const model = payload.ctx?.model ?? "unknown";
|
|
188
192
|
const u = usage ?? {};
|
|
189
|
-
const cachedTokens =
|
|
190
|
-
|
|
191
|
-
|
|
193
|
+
const cachedTokens = toFiniteNumber(
|
|
194
|
+
Number(u["cacheRead"] ?? u["cache_read_input_tokens"] ?? u["cached_prompt_tokens"] ?? 0)
|
|
195
|
+
);
|
|
196
|
+
const rawInput = toFiniteNumber(
|
|
197
|
+
Number(u["input"] ?? u["prompt_tokens"] ?? u["inputTokens"] ?? u["promptTokens"] ?? 0)
|
|
198
|
+
);
|
|
199
|
+
const rawCacheWrite = toFiniteNumber(
|
|
200
|
+
Number(u["cacheWrite"] ?? u["cache_creation_input_tokens"] ?? 0)
|
|
201
|
+
);
|
|
192
202
|
const freshTokens = rawInput + rawCacheWrite;
|
|
193
203
|
const promptTokens = freshTokens + cachedTokens;
|
|
194
|
-
const completionTokens =
|
|
195
|
-
|
|
196
|
-
|
|
204
|
+
const completionTokens = toFiniteNumber(
|
|
205
|
+
Number(
|
|
206
|
+
u["output"] ?? u["completion_tokens"] ?? u["outputTokens"] ?? u["completionTokens"] ?? 0
|
|
207
|
+
)
|
|
208
|
+
);
|
|
197
209
|
const totalTokens = promptTokens + completionTokens;
|
|
198
210
|
const costUsd = estimateCost(model, freshTokens, completionTokens, cachedTokens);
|
|
199
211
|
const record = {
|
package/dist/cron/index.d.ts
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* - cron_list: List all scheduled jobs
|
|
7
7
|
* - cron_cancel: Cancel a scheduled job
|
|
8
8
|
*/
|
|
9
|
-
import type
|
|
9
|
+
import { type Plugin } from '@wrongstack/core/types';
|
|
10
10
|
declare const plugin: Plugin;
|
|
11
11
|
export default plugin;
|
|
12
12
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/cron.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
// src/cron/index.ts
|
|
2
|
+
import { ToolValidationError } from "@wrongstack/core/types";
|
|
2
3
|
var COORDINATION_CRON_CAPABILITY = "coordination.cron";
|
|
3
4
|
var API_VERSION = "^0.1.10";
|
|
4
5
|
var state = {
|
|
@@ -167,16 +168,28 @@ var plugin = {
|
|
|
167
168
|
const action = input["action"] ?? input["task"] ?? input["command"] ?? input["run"];
|
|
168
169
|
const enabled = input["enabled"] ?? true;
|
|
169
170
|
if (!name || typeof name !== "string" || name.trim() === "") {
|
|
170
|
-
|
|
171
|
+
throw new ToolValidationError({
|
|
172
|
+
message: "name is required and must be a non-empty string",
|
|
173
|
+
field: "name"
|
|
174
|
+
});
|
|
171
175
|
}
|
|
172
176
|
if (Number.isNaN(intervalMs) || rawInterval === void 0 || rawInterval === null) {
|
|
173
|
-
|
|
177
|
+
throw new ToolValidationError({
|
|
178
|
+
message: "intervalMs must be a number >= 1000",
|
|
179
|
+
field: "intervalMs"
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
if (typeof action !== "string" || action.trim() === "") {
|
|
183
|
+
throw new ToolValidationError({
|
|
184
|
+
message: "action is required and must be a non-empty string",
|
|
185
|
+
field: "action"
|
|
186
|
+
});
|
|
174
187
|
}
|
|
175
188
|
if (state.jobs.has(name)) {
|
|
176
|
-
|
|
189
|
+
throw new Error(`Cron job '${name}' already exists. Use cron_cancel first.`);
|
|
177
190
|
}
|
|
178
191
|
if (state.jobs.size >= maxConcurrent) {
|
|
179
|
-
|
|
192
|
+
throw new Error(`Maximum concurrent jobs (${maxConcurrent}) reached.`);
|
|
180
193
|
}
|
|
181
194
|
const job = {
|
|
182
195
|
name,
|
|
@@ -242,7 +255,7 @@ var plugin = {
|
|
|
242
255
|
async execute(input) {
|
|
243
256
|
const name = input["name"] ?? input["jobName"] ?? input["job_name"] ?? input["job"] ?? input["id"];
|
|
244
257
|
if (!name || typeof name !== "string" || !state.jobs.has(name)) {
|
|
245
|
-
|
|
258
|
+
throw new Error(`No cron job named '${name}'`);
|
|
246
259
|
}
|
|
247
260
|
cancelJob(name);
|
|
248
261
|
api.metrics.gauge("cron_active_jobs", state.jobs.size);
|
|
@@ -15,6 +15,7 @@ var __reExport = (target, mod, secondTarget) => (__copyProps(target, mod, "defau
|
|
|
15
15
|
// src/dead-code-detector/index.ts
|
|
16
16
|
import { readdir, readFile, stat } from "node:fs/promises";
|
|
17
17
|
import { extname, join, relative, resolve } from "node:path";
|
|
18
|
+
import { ToolValidationError } from "@wrongstack/core/types";
|
|
18
19
|
|
|
19
20
|
// src/runtime/index.ts
|
|
20
21
|
var runtime_exports = {};
|
|
@@ -168,14 +169,8 @@ async function scan(root, depth, cfg) {
|
|
|
168
169
|
}
|
|
169
170
|
async function resolveScanRoot(rawPath) {
|
|
170
171
|
const resolved = resolve(process.cwd(), rawPath);
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
if (!stats.isDirectory()) {
|
|
174
|
-
return resolve(resolved, "..");
|
|
175
|
-
}
|
|
176
|
-
} catch {
|
|
177
|
-
}
|
|
178
|
-
return resolved;
|
|
172
|
+
const stats = await stat(resolved);
|
|
173
|
+
return stats.isDirectory() ? resolved : resolve(resolved, "..");
|
|
179
174
|
}
|
|
180
175
|
function toPosix(p) {
|
|
181
176
|
return p.replace(/\\/g, "/");
|
|
@@ -294,22 +289,29 @@ Consider removing the export if it is not part of the public API.`;
|
|
|
294
289
|
category: "Diagnostics",
|
|
295
290
|
mutating: false,
|
|
296
291
|
async execute(input) {
|
|
297
|
-
if (!cfg.enabled)
|
|
292
|
+
if (!cfg.enabled) throw new Error("dead-code-detector is disabled");
|
|
298
293
|
const raw = input ?? {};
|
|
299
294
|
const rawPath = (typeof input.path === "string" && input.path.trim().length > 0 ? input.path.trim() : void 0) ?? (typeof raw["directory"] === "string" ? raw["directory"] : void 0) ?? (typeof raw["dir"] === "string" ? raw["dir"] : void 0) ?? (typeof raw["SearchDirectory"] === "string" ? raw["SearchDirectory"] : void 0) ?? (typeof raw["filePath"] === "string" ? raw["filePath"] : void 0) ?? (typeof raw["TargetFile"] === "string" ? raw["TargetFile"] : void 0) ?? (typeof raw["targetFile"] === "string" ? raw["targetFile"] : void 0) ?? (typeof raw["file_path"] === "string" ? raw["file_path"] : void 0) ?? (typeof raw["file"] === "string" ? raw["file"] : void 0) ?? ".";
|
|
300
295
|
const rawDepth = typeof input.depth === "number" ? input.depth : cfg.defaultDepth;
|
|
301
296
|
const depth = Math.max(0, Math.min(Math.floor(rawDepth), cfg.maxDepth));
|
|
302
297
|
if (!(0, runtime_exports.withinProject)(rawPath)) {
|
|
303
|
-
|
|
298
|
+
throw new ToolValidationError({
|
|
299
|
+
message: "scan path is outside the project root",
|
|
300
|
+
field: "path"
|
|
301
|
+
});
|
|
304
302
|
}
|
|
305
303
|
state.scanCount += 1;
|
|
306
|
-
const scanRoot = await resolveScanRoot(rawPath);
|
|
307
304
|
let result;
|
|
305
|
+
let scanRoot;
|
|
308
306
|
try {
|
|
307
|
+
scanRoot = await resolveScanRoot(rawPath);
|
|
309
308
|
result = await scan(scanRoot, depth, cfg);
|
|
310
309
|
} catch (err) {
|
|
311
310
|
state.errorCount += 1;
|
|
312
|
-
|
|
311
|
+
throw new Error(
|
|
312
|
+
`dead_code_scan failed for ${rawPath}: ${err instanceof Error ? err.message : String(err)}`,
|
|
313
|
+
{ cause: err }
|
|
314
|
+
);
|
|
313
315
|
}
|
|
314
316
|
return {
|
|
315
317
|
ok: true,
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
*
|
|
26
26
|
* @public
|
|
27
27
|
*/
|
|
28
|
-
import type
|
|
28
|
+
import { type Plugin } from '@wrongstack/core/types';
|
|
29
29
|
/**
|
|
30
30
|
* Tunable budgets that bound the hook fingerprint index. Keeping the defaults
|
|
31
31
|
* together makes the file, fingerprint, and byte limits explicit.
|
|
@@ -15,6 +15,7 @@ var __reExport = (target, mod, secondTarget) => (__copyProps(target, mod, "defau
|
|
|
15
15
|
// src/duplicate-code-detector/index.ts
|
|
16
16
|
import { readFile, realpath, stat } from "node:fs/promises";
|
|
17
17
|
import { extname, isAbsolute, relative, resolve, sep } from "node:path";
|
|
18
|
+
import { ToolValidationError } from "@wrongstack/core/types";
|
|
18
19
|
|
|
19
20
|
// src/runtime/index.ts
|
|
20
21
|
var runtime_exports = {};
|
|
@@ -442,19 +443,26 @@ var plugin = {
|
|
|
442
443
|
category: "Diagnostics",
|
|
443
444
|
mutating: false,
|
|
444
445
|
async execute(input) {
|
|
445
|
-
if (!cfg.enabled)
|
|
446
|
+
if (!cfg.enabled) throw new Error("duplicate-code-detector is disabled");
|
|
446
447
|
const raw = input;
|
|
447
448
|
const rawPath = (typeof raw["path"] === "string" ? raw["path"] : void 0) ?? (typeof raw["directory"] === "string" ? raw["directory"] : void 0) ?? (typeof raw["dir"] === "string" ? raw["dir"] : void 0) ?? (typeof raw["SearchDirectory"] === "string" ? raw["SearchDirectory"] : void 0) ?? (typeof raw["filePath"] === "string" ? raw["filePath"] : void 0) ?? (typeof raw["file_path"] === "string" ? raw["file_path"] : void 0) ?? (typeof raw["TargetFile"] === "string" ? raw["TargetFile"] : void 0) ?? (typeof raw["targetFile"] === "string" ? raw["targetFile"] : void 0) ?? (typeof raw["file"] === "string" ? raw["file"] : void 0) ?? ".";
|
|
448
449
|
if (!(0, runtime_exports.withinProject)(rawPath)) {
|
|
449
|
-
|
|
450
|
+
throw new ToolValidationError({
|
|
451
|
+
message: "scan path is outside the project root",
|
|
452
|
+
field: "path"
|
|
453
|
+
});
|
|
450
454
|
}
|
|
451
455
|
state.scanCount += 1;
|
|
452
456
|
let result;
|
|
453
457
|
try {
|
|
458
|
+
await stat(resolve(process.cwd(), rawPath));
|
|
454
459
|
result = await scanPath(rawPath, cfg);
|
|
455
460
|
} catch (err) {
|
|
456
461
|
state.errorCount += 1;
|
|
457
|
-
|
|
462
|
+
throw new Error(
|
|
463
|
+
`detect_duplicate_code failed for ${rawPath}: ${err instanceof Error ? err.message : String(err)}`,
|
|
464
|
+
{ cause: err }
|
|
465
|
+
);
|
|
458
466
|
}
|
|
459
467
|
state.findingCount += result.findings.length;
|
|
460
468
|
return {
|
|
@@ -13,8 +13,9 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
13
13
|
var __reExport = (target, mod, secondTarget) => (__copyProps(target, mod, "default"), secondTarget && __copyProps(secondTarget, mod, "default"));
|
|
14
14
|
|
|
15
15
|
// src/feature-flag-tracker/index.ts
|
|
16
|
-
import { readFile } from "node:fs/promises";
|
|
16
|
+
import { readFile, stat } from "node:fs/promises";
|
|
17
17
|
import { isAbsolute, relative, resolve } from "node:path";
|
|
18
|
+
import { ToolValidationError } from "@wrongstack/core/types";
|
|
18
19
|
|
|
19
20
|
// src/runtime/index.ts
|
|
20
21
|
var runtime_exports = {};
|
|
@@ -246,19 +247,26 @@ Make sure flag behavior is intentional and consider updating flag inventory/docs
|
|
|
246
247
|
category: "Diagnostics",
|
|
247
248
|
mutating: false,
|
|
248
249
|
async execute(input) {
|
|
249
|
-
if (!cfg.enabled)
|
|
250
|
+
if (!cfg.enabled) throw new Error("feature-flag-tracker is disabled");
|
|
250
251
|
const raw = input ?? {};
|
|
251
252
|
const rawPath = (typeof input.path === "string" && input.path.trim().length > 0 ? input.path.trim() : void 0) ?? (typeof raw["directory"] === "string" ? raw["directory"] : void 0) ?? (typeof raw["SearchDirectory"] === "string" ? raw["SearchDirectory"] : void 0) ?? (typeof raw["dir"] === "string" ? raw["dir"] : void 0) ?? (typeof raw["TargetFile"] === "string" ? raw["TargetFile"] : void 0) ?? (typeof raw["targetFile"] === "string" ? raw["targetFile"] : void 0) ?? (typeof raw["filePath"] === "string" ? raw["filePath"] : void 0) ?? (typeof raw["file_path"] === "string" ? raw["file_path"] : void 0) ?? (typeof raw["file"] === "string" ? raw["file"] : void 0) ?? ".";
|
|
252
253
|
if (!(0, runtime_exports.withinProject)(rawPath)) {
|
|
253
|
-
|
|
254
|
+
throw new ToolValidationError({
|
|
255
|
+
message: "path is outside the project root",
|
|
256
|
+
field: "path"
|
|
257
|
+
});
|
|
254
258
|
}
|
|
255
259
|
state.scanCount += 1;
|
|
256
260
|
let result;
|
|
257
261
|
try {
|
|
262
|
+
await stat(resolve(process.cwd(), rawPath));
|
|
258
263
|
result = await scanPath(rawPath, cfg);
|
|
259
264
|
} catch (err) {
|
|
260
265
|
state.errorCount += 1;
|
|
261
|
-
|
|
266
|
+
throw new Error(
|
|
267
|
+
`scan_feature_flags failed for ${rawPath}: ${err instanceof Error ? err.message : String(err)}`,
|
|
268
|
+
{ cause: err }
|
|
269
|
+
);
|
|
262
270
|
}
|
|
263
271
|
state.flagCount += result.usages.length;
|
|
264
272
|
return {
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* - watch_stop: Stop a watch by ID
|
|
7
7
|
* - watch_list: List all active watches
|
|
8
8
|
*/
|
|
9
|
-
import type
|
|
9
|
+
import { type Plugin } from '@wrongstack/core/types';
|
|
10
10
|
declare const plugin: Plugin;
|
|
11
11
|
export default plugin;
|
|
12
12
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/file-watcher.js
CHANGED
|
@@ -15,6 +15,7 @@ var __reExport = (target, mod, secondTarget) => (__copyProps(target, mod, "defau
|
|
|
15
15
|
// src/file-watcher/index.ts
|
|
16
16
|
import { watch as fsWatch } from "node:fs";
|
|
17
17
|
import { join } from "node:path";
|
|
18
|
+
import { ToolValidationError } from "@wrongstack/core/types";
|
|
18
19
|
|
|
19
20
|
// src/runtime/index.ts
|
|
20
21
|
var runtime_exports = {};
|
|
@@ -209,11 +210,10 @@ var plugin = {
|
|
|
209
210
|
let rawPaths;
|
|
210
211
|
if (explicitPaths !== void 0) {
|
|
211
212
|
if (!Array.isArray(explicitPaths)) {
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
};
|
|
213
|
+
throw new ToolValidationError({
|
|
214
|
+
message: "paths must be an array of file/directory paths",
|
|
215
|
+
field: "paths"
|
|
216
|
+
});
|
|
217
217
|
}
|
|
218
218
|
rawPaths = explicitPaths;
|
|
219
219
|
} else {
|
|
@@ -221,55 +221,46 @@ var plugin = {
|
|
|
221
221
|
rawPaths = Array.isArray(fallback) ? fallback : typeof fallback === "string" && fallback.trim().length > 0 ? [fallback.trim()] : void 0;
|
|
222
222
|
}
|
|
223
223
|
if (!rawPaths || !Array.isArray(rawPaths)) {
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
};
|
|
224
|
+
throw new ToolValidationError({
|
|
225
|
+
message: "paths must be an array of file/directory paths",
|
|
226
|
+
field: "paths"
|
|
227
|
+
});
|
|
229
228
|
}
|
|
230
229
|
const paths = [...new Set(rawPaths)];
|
|
231
230
|
if (paths.length === 0) {
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
};
|
|
231
|
+
throw new ToolValidationError({
|
|
232
|
+
message: "paths array is empty \u2014 provide at least one path",
|
|
233
|
+
field: "paths"
|
|
234
|
+
});
|
|
237
235
|
}
|
|
238
236
|
if (paths.length > MAX_PATHS_PER_WATCH) {
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
};
|
|
237
|
+
throw new ToolValidationError({
|
|
238
|
+
message: `a watch may contain at most ${MAX_PATHS_PER_WATCH} unique paths`,
|
|
239
|
+
field: "paths"
|
|
240
|
+
});
|
|
244
241
|
}
|
|
245
242
|
if (watches.size >= MAX_WATCH_GROUPS) {
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
watch_id: null
|
|
250
|
-
};
|
|
243
|
+
throw new Error(
|
|
244
|
+
`active watch group limit reached (${MAX_WATCH_GROUPS}); stop a watch with watch_stop first`
|
|
245
|
+
);
|
|
251
246
|
}
|
|
252
247
|
const activeFilesystemWatchers = [...watches.values()].reduce(
|
|
253
248
|
(total, handle2) => total + handle2.watchers.length,
|
|
254
249
|
0
|
|
255
250
|
);
|
|
256
251
|
if (activeFilesystemWatchers + paths.length > MAX_FILESYSTEM_WATCHERS) {
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
watch_id: null
|
|
261
|
-
};
|
|
252
|
+
throw new Error(
|
|
253
|
+
`filesystem watcher limit reached (${MAX_FILESYSTEM_WATCHERS}); stop a watch with watch_stop first`
|
|
254
|
+
);
|
|
262
255
|
}
|
|
263
256
|
const events = input["events"] ?? ["change", "add", "delete"];
|
|
264
257
|
const recursive = input["recursive"] ?? true;
|
|
265
258
|
const bad = paths.find((p) => !(0, runtime_exports.withinProject)(p));
|
|
266
259
|
if (bad !== void 0) {
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
rejectedOutsideProject: true
|
|
272
|
-
};
|
|
260
|
+
throw new ToolValidationError({
|
|
261
|
+
message: `path is outside the project root: ${bad}`,
|
|
262
|
+
field: "paths"
|
|
263
|
+
});
|
|
273
264
|
}
|
|
274
265
|
const id = nextId();
|
|
275
266
|
const handle = {
|
|
@@ -281,8 +272,15 @@ var plugin = {
|
|
|
281
272
|
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
282
273
|
};
|
|
283
274
|
const watchedPaths = [];
|
|
275
|
+
const failedPaths = [];
|
|
284
276
|
for (const p of paths) {
|
|
285
277
|
if (safeWatchDir(p, recursive, handle)) watchedPaths.push(p);
|
|
278
|
+
else failedPaths.push(p);
|
|
279
|
+
}
|
|
280
|
+
if (watchedPaths.length === 0) {
|
|
281
|
+
throw new Error(
|
|
282
|
+
`could not watch any of the requested paths: ${failedPaths.join(", ")} (missing path or OS watcher limit)`
|
|
283
|
+
);
|
|
286
284
|
}
|
|
287
285
|
handle.paths = watchedPaths;
|
|
288
286
|
watches.set(id, handle);
|
|
@@ -291,9 +289,10 @@ var plugin = {
|
|
|
291
289
|
ok: true,
|
|
292
290
|
watch_id: id,
|
|
293
291
|
paths: watchedPaths,
|
|
292
|
+
...failedPaths.length > 0 ? { failedPaths } : {},
|
|
294
293
|
events,
|
|
295
294
|
recursive,
|
|
296
|
-
message: `Started watching ${watchedPaths.length} path(s). Use watch_stop to cancel.`
|
|
295
|
+
message: `Started watching ${watchedPaths.length} path(s). Use watch_stop to cancel.` + (failedPaths.length > 0 ? ` Could not watch: ${failedPaths.join(", ")}.` : "")
|
|
297
296
|
};
|
|
298
297
|
}
|
|
299
298
|
});
|
|
@@ -315,7 +314,7 @@ var plugin = {
|
|
|
315
314
|
const watch_id = typeof rawId === "string" ? rawId.trim() : "";
|
|
316
315
|
const handle = watches.get(watch_id);
|
|
317
316
|
if (!handle) {
|
|
318
|
-
|
|
317
|
+
throw new Error(`No active watch with ID: ${watch_id}`);
|
|
319
318
|
}
|
|
320
319
|
for (const w of handle.watchers) {
|
|
321
320
|
try {
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
* - For staging: use `git_autocommit` with `files` or `paths` (it stages automatically), or `bash` with `git add`.
|
|
25
25
|
* - For status: use the built-in `git` tool with `command: "status"` or `command: "diff"`.
|
|
26
26
|
*/
|
|
27
|
-
import type
|
|
27
|
+
import { type Plugin } from '@wrongstack/core/types';
|
|
28
28
|
/**
|
|
29
29
|
* Parse one `git status --porcelain` line into the path to stage.
|
|
30
30
|
*
|
package/dist/git-autocommit.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import { execFile } from "node:child_process";
|
|
3
3
|
import { existsSync } from "node:fs";
|
|
4
4
|
import { resolve } from "node:path";
|
|
5
|
+
import { ToolValidationError } from "@wrongstack/core/types";
|
|
5
6
|
var API_VERSION = "^0.1.10";
|
|
6
7
|
var commitCount = { value: 0 };
|
|
7
8
|
var lastCommit = { hash: null, at: null };
|
|
@@ -382,7 +383,10 @@ var plugin = {
|
|
|
382
383
|
const rawFiles = input["files"] ?? input["fileList"] ?? input["file_list"];
|
|
383
384
|
if (rawFiles !== void 0) {
|
|
384
385
|
if (!Array.isArray(rawFiles)) {
|
|
385
|
-
|
|
386
|
+
throw new ToolValidationError({
|
|
387
|
+
message: "files must be an array of file paths",
|
|
388
|
+
field: "files"
|
|
389
|
+
});
|
|
386
390
|
}
|
|
387
391
|
files = rawFiles;
|
|
388
392
|
} else if (typeof (input["file"] ?? input["file_path"]) === "string" && String(input["file"] ?? input["file_path"]).trim().length > 0) {
|
|
@@ -396,20 +400,26 @@ var plugin = {
|
|
|
396
400
|
const rawPaths = input["paths"] ?? input["pathList"] ?? input["path_list"];
|
|
397
401
|
if (rawPaths !== void 0) {
|
|
398
402
|
if (!Array.isArray(rawPaths)) {
|
|
399
|
-
|
|
403
|
+
throw new ToolValidationError({
|
|
404
|
+
message: "paths must be an array of pathspec patterns",
|
|
405
|
+
field: "paths"
|
|
406
|
+
});
|
|
400
407
|
}
|
|
401
408
|
pathspecs = rawPaths.filter((p) => typeof p === "string" && p.length > 0);
|
|
402
409
|
if (pathspecs.length === 0) {
|
|
403
|
-
|
|
410
|
+
throw new ToolValidationError({
|
|
411
|
+
message: "paths must contain at least one non-empty pattern",
|
|
412
|
+
field: "paths"
|
|
413
|
+
});
|
|
404
414
|
}
|
|
405
415
|
} else if (typeof input["path"] === "string" && input["path"].trim().length > 0) {
|
|
406
416
|
pathspecs = [input["path"].trim()];
|
|
407
417
|
}
|
|
408
418
|
if (rawPaths !== void 0 && files && files.length > 0) {
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
};
|
|
419
|
+
throw new ToolValidationError({
|
|
420
|
+
message: "Pass either files (exact paths) or paths (pathspec globs), not both \u2014 the other would be silently ignored.",
|
|
421
|
+
field: "paths"
|
|
422
|
+
});
|
|
413
423
|
}
|
|
414
424
|
let commitScope;
|
|
415
425
|
let staged = [];
|
|
@@ -417,10 +427,10 @@ var plugin = {
|
|
|
417
427
|
try {
|
|
418
428
|
await stageFiles(pathspecs);
|
|
419
429
|
} catch (err) {
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
430
|
+
throw new Error(
|
|
431
|
+
`Failed to stage files matching paths: ${err instanceof Error ? err.message : String(err)}`,
|
|
432
|
+
{ cause: err }
|
|
433
|
+
);
|
|
424
434
|
}
|
|
425
435
|
try {
|
|
426
436
|
staged = await getScopedStagedFiles(pathspecs);
|
|
@@ -428,10 +438,9 @@ var plugin = {
|
|
|
428
438
|
staged = [];
|
|
429
439
|
}
|
|
430
440
|
if (staged.length === 0) {
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
};
|
|
441
|
+
throw new Error(
|
|
442
|
+
"No changed files match the given paths \u2014 refusing to commit anything else."
|
|
443
|
+
);
|
|
435
444
|
}
|
|
436
445
|
commitScope = staged;
|
|
437
446
|
try {
|
|
@@ -443,10 +452,10 @@ var plugin = {
|
|
|
443
452
|
try {
|
|
444
453
|
commitScope = await stageFiles(files);
|
|
445
454
|
} catch (err) {
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
455
|
+
throw new Error(
|
|
456
|
+
`Failed to stage files: ${err instanceof Error ? err.message : String(err)}`,
|
|
457
|
+
{ cause: err }
|
|
458
|
+
);
|
|
450
459
|
}
|
|
451
460
|
try {
|
|
452
461
|
staged = await getStagedFiles();
|
|
@@ -511,17 +520,16 @@ var plugin = {
|
|
|
511
520
|
message: `Would create: ${summary || "update code"}`
|
|
512
521
|
};
|
|
513
522
|
}
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
};
|
|
523
|
+
throw new ToolValidationError({
|
|
524
|
+
message: "type is required and must be a valid conventional commit type",
|
|
525
|
+
field: "type"
|
|
526
|
+
});
|
|
518
527
|
}
|
|
519
528
|
const msg = generateCommitMessage(type, scope, summary || "update code", body);
|
|
520
529
|
if (staged.length === 0) {
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
};
|
|
530
|
+
throw new Error(
|
|
531
|
+
'Nothing staged. Pass files (exact paths) or paths (pathspec globs) to scope this commit, stage with git add beforehand, or set extensions["git-autocommit"].autoStage=true to allow staging every changed file (legacy whole-tree behavior).'
|
|
532
|
+
);
|
|
525
533
|
}
|
|
526
534
|
let scopeWarning = null;
|
|
527
535
|
if (commitScope) {
|
|
@@ -563,20 +571,19 @@ ${stagedDiff}
|
|
|
563
571
|
if (drifted.length > 0) {
|
|
564
572
|
const preview = drifted.slice(0, 10).join(", ");
|
|
565
573
|
const suffix = drifted.length > 10 ? ` and ${drifted.length - 10} more` : "";
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
};
|
|
574
|
+
throw new Error(
|
|
575
|
+
`Working tree changed after staging for: ${preview}${suffix}. A scoped commit takes working-tree content, so committing now could include changes that were never staged or previewed. Re-run the tool to re-stage the current content.`
|
|
576
|
+
);
|
|
570
577
|
}
|
|
571
578
|
}
|
|
572
579
|
let hash = "";
|
|
573
580
|
try {
|
|
574
581
|
hash = await commitWithMessage(msg, void 0, commitScope);
|
|
575
582
|
} catch (err) {
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
583
|
+
throw new Error(
|
|
584
|
+
`Failed to commit: ${err instanceof Error ? err.message : String(err)}`,
|
|
585
|
+
{ cause: err }
|
|
586
|
+
);
|
|
580
587
|
}
|
|
581
588
|
api.log.info("git-autocommit: created commit", { hash, type, scope });
|
|
582
589
|
commitCount.value += 1;
|
|
@@ -615,10 +622,8 @@ ${stagedDiff}
|
|
|
615
622
|
\`\`\``
|
|
616
623
|
};
|
|
617
624
|
} catch (err) {
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
error: `Uncaught error in git_autocommit: ${err instanceof Error ? err.message : String(err)}`
|
|
621
|
-
};
|
|
625
|
+
if (err instanceof Error) throw err;
|
|
626
|
+
throw new Error(`Uncaught error in git_autocommit: ${String(err)}`, { cause: err });
|
|
622
627
|
}
|
|
623
628
|
}
|
|
624
629
|
});
|
|
@@ -52,7 +52,7 @@
|
|
|
52
52
|
*
|
|
53
53
|
* @public
|
|
54
54
|
*/
|
|
55
|
-
import type
|
|
55
|
+
import { type Plugin } from '@wrongstack/core/types';
|
|
56
56
|
/**
|
|
57
57
|
* Curated default list of build-artifact-looking names. High precision on
|
|
58
58
|
* purpose: this hook fires on every write/edit, so the defaults avoid
|