@wrongstack/plugins 1.0.8 → 1.0.11
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 +1553 -1086
- 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/loop-breaker.js +11 -4
- 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 +19 -12
- 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/template-engine.js
CHANGED
|
@@ -15,6 +15,7 @@ var __reExport = (target, mod, secondTarget) => (__copyProps(target, mod, "defau
|
|
|
15
15
|
// src/template-engine/index.ts
|
|
16
16
|
import { readFile, writeFile } from "node:fs/promises";
|
|
17
17
|
import { isAbsolute } from "node:path";
|
|
18
|
+
import { ToolValidationError } from "@wrongstack/core/types";
|
|
18
19
|
|
|
19
20
|
// src/runtime/index.ts
|
|
20
21
|
var runtime_exports = {};
|
|
@@ -183,24 +184,26 @@ var plugin = {
|
|
|
183
184
|
const output_path = typeof rawOutputPath === "string" && rawOutputPath.trim().length > 0 ? rawOutputPath.trim() : void 0;
|
|
184
185
|
const raw = input["raw"] ?? false;
|
|
185
186
|
if (!template || typeof template !== "string") {
|
|
186
|
-
|
|
187
|
+
throw new ToolValidationError({
|
|
188
|
+
message: "template is required and must be a string",
|
|
189
|
+
field: "template"
|
|
190
|
+
});
|
|
187
191
|
}
|
|
188
192
|
if (!variables || typeof variables !== "object") {
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
result = raw ? renderTemplateRaw(template, variables) : renderTemplate(template, variables, autoEscapeHtml);
|
|
194
|
-
} catch (err) {
|
|
195
|
-
return { ok: false, error: String(err) };
|
|
193
|
+
throw new ToolValidationError({
|
|
194
|
+
message: "variables is required and must be an object",
|
|
195
|
+
field: "variables"
|
|
196
|
+
});
|
|
196
197
|
}
|
|
198
|
+
const result = raw ? renderTemplateRaw(template, variables) : renderTemplate(template, variables, autoEscapeHtml);
|
|
197
199
|
if (output_path) {
|
|
198
200
|
const pathError = validateWritableTemplateTarget("output_path", output_path);
|
|
199
|
-
if (pathError)
|
|
201
|
+
if (pathError)
|
|
202
|
+
throw new ToolValidationError({ message: pathError, field: "output_path" });
|
|
200
203
|
try {
|
|
201
204
|
await writeFile(output_path, result, "utf-8");
|
|
202
205
|
} catch (err) {
|
|
203
|
-
|
|
206
|
+
throw new Error(`Could not write ${output_path}: ${String(err)}`, { cause: err });
|
|
204
207
|
}
|
|
205
208
|
return {
|
|
206
209
|
ok: true,
|
|
@@ -251,32 +254,36 @@ var plugin = {
|
|
|
251
254
|
const output_path = typeof rawOutputPath === "string" && rawOutputPath.trim().length > 0 ? rawOutputPath.trim() : void 0;
|
|
252
255
|
const raw = input["raw"] ?? false;
|
|
253
256
|
if (!template_path || typeof template_path !== "string") {
|
|
254
|
-
|
|
257
|
+
throw new ToolValidationError({
|
|
258
|
+
message: "template_path is required and must be a string",
|
|
259
|
+
field: "template_path"
|
|
260
|
+
});
|
|
255
261
|
}
|
|
256
262
|
const templatePathError = validateRelativeTemplatePath("template_path", template_path);
|
|
257
|
-
if (templatePathError)
|
|
263
|
+
if (templatePathError) {
|
|
264
|
+
throw new ToolValidationError({ message: templatePathError, field: "template_path" });
|
|
265
|
+
}
|
|
258
266
|
if (!variables || typeof variables !== "object") {
|
|
259
|
-
|
|
267
|
+
throw new ToolValidationError({
|
|
268
|
+
message: "variables is required and must be an object",
|
|
269
|
+
field: "variables"
|
|
270
|
+
});
|
|
260
271
|
}
|
|
261
272
|
let content;
|
|
262
273
|
try {
|
|
263
274
|
content = await readFile(template_path, "utf-8");
|
|
264
275
|
} catch (err) {
|
|
265
|
-
|
|
266
|
-
}
|
|
267
|
-
let result;
|
|
268
|
-
try {
|
|
269
|
-
result = raw ? renderTemplateRaw(content, variables) : renderTemplate(content, variables, autoEscapeHtml);
|
|
270
|
-
} catch (err) {
|
|
271
|
-
return { ok: false, error: `Template rendering failed: ${err}` };
|
|
276
|
+
throw new Error(`Could not read template file: ${String(err)}`, { cause: err });
|
|
272
277
|
}
|
|
278
|
+
const result = raw ? renderTemplateRaw(content, variables) : renderTemplate(content, variables, autoEscapeHtml);
|
|
273
279
|
if (output_path) {
|
|
274
280
|
const pathError = validateWritableTemplateTarget("output_path", output_path);
|
|
275
|
-
if (pathError)
|
|
281
|
+
if (pathError)
|
|
282
|
+
throw new ToolValidationError({ message: pathError, field: "output_path" });
|
|
276
283
|
try {
|
|
277
284
|
await writeFile(output_path, result, "utf-8");
|
|
278
285
|
} catch (err) {
|
|
279
|
-
|
|
286
|
+
throw new Error(`Could not write ${output_path}: ${String(err)}`, { cause: err });
|
|
280
287
|
}
|
|
281
288
|
return {
|
|
282
289
|
ok: true,
|
|
@@ -327,30 +334,39 @@ var plugin = {
|
|
|
327
334
|
const rawDesc = input["description"] ?? input["desc"] ?? input["summary"];
|
|
328
335
|
const description = typeof rawDesc === "string" ? rawDesc : void 0;
|
|
329
336
|
if (!name || typeof name !== "string" || name.trim() === "") {
|
|
330
|
-
|
|
337
|
+
throw new ToolValidationError({
|
|
338
|
+
message: "name is required and must be a non-empty string",
|
|
339
|
+
field: "name"
|
|
340
|
+
});
|
|
331
341
|
}
|
|
332
342
|
if (!content || typeof content !== "string") {
|
|
333
|
-
|
|
343
|
+
throw new ToolValidationError({
|
|
344
|
+
message: "content is required and must be a string",
|
|
345
|
+
field: "content"
|
|
346
|
+
});
|
|
334
347
|
}
|
|
335
348
|
if (name.length > MAX_TEMPLATE_NAME_CHARS) {
|
|
336
|
-
|
|
349
|
+
throw new ToolValidationError({
|
|
350
|
+
message: `name exceeds ${MAX_TEMPLATE_NAME_CHARS} characters`,
|
|
351
|
+
field: "name"
|
|
352
|
+
});
|
|
337
353
|
}
|
|
338
354
|
if (content.length > MAX_TEMPLATE_CONTENT_CHARS) {
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
};
|
|
355
|
+
throw new ToolValidationError({
|
|
356
|
+
message: `content exceeds ${MAX_TEMPLATE_CONTENT_CHARS} characters`,
|
|
357
|
+
field: "content"
|
|
358
|
+
});
|
|
343
359
|
}
|
|
344
360
|
if (description && description.length > MAX_TEMPLATE_DESCRIPTION_CHARS) {
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
};
|
|
361
|
+
throw new ToolValidationError({
|
|
362
|
+
message: `description exceeds ${MAX_TEMPLATE_DESCRIPTION_CHARS} characters`,
|
|
363
|
+
field: "description"
|
|
364
|
+
});
|
|
349
365
|
}
|
|
350
366
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
351
367
|
const existing = templates.get(name);
|
|
352
368
|
if (!existing && templates.size >= MAX_TEMPLATES) {
|
|
353
|
-
|
|
369
|
+
throw new Error(`template limit reached (${MAX_TEMPLATES})`);
|
|
354
370
|
}
|
|
355
371
|
const tmpl = {
|
|
356
372
|
name,
|
|
@@ -363,10 +379,7 @@ var plugin = {
|
|
|
363
379
|
for (const stored of templates.values()) retainedChars += templateChars(stored);
|
|
364
380
|
const nextChars = retainedChars - (existing ? templateChars(existing) : 0) + templateChars(tmpl);
|
|
365
381
|
if (nextChars > MAX_TOTAL_TEMPLATE_CHARS) {
|
|
366
|
-
|
|
367
|
-
ok: false,
|
|
368
|
-
error: `template store exceeds ${MAX_TOTAL_TEMPLATE_CHARS} retained characters`
|
|
369
|
-
};
|
|
382
|
+
throw new Error(`template store exceeds ${MAX_TOTAL_TEMPLATE_CHARS} retained characters`);
|
|
370
383
|
}
|
|
371
384
|
templates.set(name, tmpl);
|
|
372
385
|
api.metrics.gauge("template_count", templates.size);
|
|
@@ -17,6 +17,7 @@ import { execFile } from "node:child_process";
|
|
|
17
17
|
import { readFileSync } from "node:fs";
|
|
18
18
|
import { createRequire } from "node:module";
|
|
19
19
|
import { dirname, isAbsolute, relative, resolve } from "node:path";
|
|
20
|
+
import { ToolValidationError } from "@wrongstack/core/types";
|
|
20
21
|
|
|
21
22
|
// src/runtime/index.ts
|
|
22
23
|
var runtime_exports = {};
|
|
@@ -274,7 +275,7 @@ var plugin = {
|
|
|
274
275
|
mutating: false,
|
|
275
276
|
async execute(input) {
|
|
276
277
|
if (!cfg.enabled) {
|
|
277
|
-
|
|
278
|
+
throw new Error("test-flake-detector is disabled");
|
|
278
279
|
}
|
|
279
280
|
const raw = input;
|
|
280
281
|
const rawPattern = input.testPattern ?? raw["pattern"] ?? raw["path"] ?? raw["file"] ?? raw["filePath"] ?? raw["TargetFile"] ?? raw["targetFile"];
|
|
@@ -285,10 +286,10 @@ var plugin = {
|
|
|
285
286
|
const requestedRuns = typeof rawRuns === "number" && rawRuns >= 1 ? Math.min(Math.floor(rawRuns), cfg.maxRuns) : 5;
|
|
286
287
|
const command = resolveTestCommand(commandString, testPattern);
|
|
287
288
|
if (!command) {
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
};
|
|
289
|
+
throw new ToolValidationError({
|
|
290
|
+
message: "Unsupported test command or unsafe testPattern. Use vitest, jest, or mocha through a supported package runner, and keep patterns inside the project.",
|
|
291
|
+
field: "command"
|
|
292
|
+
});
|
|
292
293
|
}
|
|
293
294
|
state.invocationCount += 1;
|
|
294
295
|
const start = Date.now();
|
|
@@ -316,6 +317,11 @@ var plugin = {
|
|
|
316
317
|
}
|
|
317
318
|
}
|
|
318
319
|
const all = Array.from(records.values());
|
|
320
|
+
if (all.length === 0 && runErrors.length === requestedRuns) {
|
|
321
|
+
throw new Error(
|
|
322
|
+
`test command produced no test results in ${requestedRuns} run(s): ${runErrors.slice(0, 3).join("; ")}`
|
|
323
|
+
);
|
|
324
|
+
}
|
|
319
325
|
const flakyTests = all.filter((r) => r.passCount > 0 && r.failCount > 0);
|
|
320
326
|
const alwaysFailing = all.filter((r) => r.passCount === 0 && r.failCount > 0);
|
|
321
327
|
const alwaysPassing = all.filter((r) => r.passCount > 0 && r.failCount === 0);
|
package/dist/test-generator.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// src/test-generator/index.ts
|
|
2
2
|
import { readFileSync } from "node:fs";
|
|
3
3
|
import { isAbsolute, relative, resolve } from "node:path";
|
|
4
|
+
import { ToolValidationError } from "@wrongstack/core/types";
|
|
4
5
|
|
|
5
6
|
// src/runtime/llm.ts
|
|
6
7
|
import {
|
|
@@ -299,7 +300,7 @@ var plugin = {
|
|
|
299
300
|
category: "Development",
|
|
300
301
|
mutating: false,
|
|
301
302
|
async execute(input, _ctx, execOpts) {
|
|
302
|
-
if (!cfg.enabled)
|
|
303
|
+
if (!cfg.enabled) throw new Error("test-generator is disabled");
|
|
303
304
|
execOpts?.signal?.throwIfAborted();
|
|
304
305
|
const inp = input ?? {};
|
|
305
306
|
const rawFramework = inp["framework"];
|
|
@@ -310,16 +311,19 @@ var plugin = {
|
|
|
310
311
|
};
|
|
311
312
|
const rawPath = inp["path"] ?? inp["filePath"] ?? inp["file_path"] ?? inp["TargetFile"] ?? inp["targetFile"] ?? inp["file"];
|
|
312
313
|
if (!rawPath || typeof rawPath !== "string") {
|
|
313
|
-
|
|
314
|
+
throw new ToolValidationError({ message: "path is required", field: "path" });
|
|
314
315
|
}
|
|
315
316
|
if (!withinProject(rawPath)) {
|
|
316
|
-
|
|
317
|
+
throw new ToolValidationError({
|
|
318
|
+
message: "path is outside the project root",
|
|
319
|
+
field: "path"
|
|
320
|
+
});
|
|
317
321
|
}
|
|
318
322
|
if (!SOURCE_EXTENSIONS.some((ext) => rawPath.toLowerCase().endsWith(ext))) {
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
};
|
|
323
|
+
throw new ToolValidationError({
|
|
324
|
+
message: `test generation only reads source files (${SOURCE_EXTENSIONS.join(", ")}); refusing "${rawPath}"`,
|
|
325
|
+
field: "path"
|
|
326
|
+
});
|
|
323
327
|
}
|
|
324
328
|
const resolved = resolve(process.cwd(), rawPath);
|
|
325
329
|
state.generateCount += 1;
|
|
@@ -328,7 +332,7 @@ var plugin = {
|
|
|
328
332
|
result = generateForFile(resolved, effectiveCfg);
|
|
329
333
|
} catch (err) {
|
|
330
334
|
state.errorCount += 1;
|
|
331
|
-
|
|
335
|
+
throw new Error(`Could not read ${rawPath}: ${String(err)}`, { cause: err });
|
|
332
336
|
}
|
|
333
337
|
state.exportCount += result.exports.length;
|
|
334
338
|
const raw = input ?? {};
|
|
@@ -26,8 +26,75 @@
|
|
|
26
26
|
* into the session's ctx.todos via the
|
|
27
27
|
* built-in `todo` tool
|
|
28
28
|
* - todo_tracker_status : Counters + last update timestamp
|
|
29
|
+
*
|
|
30
|
+
* Durability contract (the file is the source of truth, memory is a cache):
|
|
31
|
+
* - A file that cannot be parsed or validated is QUARANTINED (renamed to
|
|
32
|
+
* `<file>.corrupt-<timestamp>`), never silently overwritten. If the
|
|
33
|
+
* rename fails the store goes read-only.
|
|
34
|
+
* - A file written by a newer format version is never written to.
|
|
35
|
+
* - Every mutation runs under the shared file lock, re-reads the file,
|
|
36
|
+
* applies the change to a clone, writes atomically, and only then
|
|
37
|
+
* updates memory. Two sessions on one project therefore merge instead of
|
|
38
|
+
* erasing each other, and a failed write leaves memory and disk unchanged.
|
|
39
|
+
* - Read tools re-read the file so another process's changes are visible.
|
|
40
|
+
*/
|
|
41
|
+
import { type Plugin } from '@wrongstack/core/types';
|
|
42
|
+
type Status = 'pending' | 'in_progress' | 'completed' | 'dropped';
|
|
43
|
+
type Priority = 'low' | 'normal' | 'high';
|
|
44
|
+
interface TrackedItem {
|
|
45
|
+
id: string;
|
|
46
|
+
content: string;
|
|
47
|
+
status: Status;
|
|
48
|
+
priority: Priority;
|
|
49
|
+
tags: string[];
|
|
50
|
+
createdAt: string;
|
|
51
|
+
updatedAt: string;
|
|
52
|
+
completedAt: string | null;
|
|
53
|
+
/** Session that first created the item (if known). */
|
|
54
|
+
sourceSessionId: string | null;
|
|
55
|
+
/** Optional free-form note attached to the item. */
|
|
56
|
+
notes: string | null;
|
|
57
|
+
}
|
|
58
|
+
interface TodoTrackerFile {
|
|
59
|
+
version: 1;
|
|
60
|
+
projectSlug: string;
|
|
61
|
+
updatedAt: string;
|
|
62
|
+
items: TrackedItem[];
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Filesystem/persistence seam. Production uses the real primitives; tests
|
|
66
|
+
* inject faults here instead of module-mocking `@wrongstack/core/utils`
|
|
67
|
+
* (which is flaky under parallel vitest — see context-pins-extra.test.ts).
|
|
29
68
|
*/
|
|
30
|
-
|
|
69
|
+
export interface TodoTrackerDeps {
|
|
70
|
+
readFile(path: string): Promise<string>;
|
|
71
|
+
rename(from: string, to: string): Promise<void>;
|
|
72
|
+
atomicWrite(path: string, content: string, opts?: {
|
|
73
|
+
mode?: number;
|
|
74
|
+
}): Promise<void>;
|
|
75
|
+
withFileLock<T>(path: string, fn: () => Promise<T>): Promise<T>;
|
|
76
|
+
ensureDir(dir: string): Promise<void>;
|
|
77
|
+
}
|
|
78
|
+
/** Basename without its extension; dotfiles like `.todos` keep their name. */
|
|
79
|
+
export declare function deriveProjectSlug(filePath: string): string;
|
|
80
|
+
type LoadResult = {
|
|
81
|
+
kind: 'missing';
|
|
82
|
+
} | {
|
|
83
|
+
kind: 'ok';
|
|
84
|
+
file: TodoTrackerFile;
|
|
85
|
+
} | {
|
|
86
|
+
kind: 'corrupt';
|
|
87
|
+
reason: string;
|
|
88
|
+
} | {
|
|
89
|
+
kind: 'unsupportedVersion';
|
|
90
|
+
version: unknown;
|
|
91
|
+
} | {
|
|
92
|
+
kind: 'invalidItems';
|
|
93
|
+
reason: string;
|
|
94
|
+
};
|
|
95
|
+
/** Pure classification of raw file bytes. Exported for tests. */
|
|
96
|
+
export declare function parseTrackerFile(rawText: string): LoadResult;
|
|
97
|
+
export declare function createTodoTrackerPlugin(overrides?: Partial<TodoTrackerDeps>): Plugin;
|
|
31
98
|
declare const plugin: Plugin;
|
|
32
99
|
export default plugin;
|
|
33
100
|
//# sourceMappingURL=index.d.ts.map
|