@wrongstack/plugins 0.299.0 → 0.301.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 +3 -1
- package/dist/audit/index.d.ts +1 -1
- package/dist/audit.js +1 -1
- package/dist/auto-doc.js +1 -0
- package/dist/changelog-writer.js +5 -1
- package/dist/commit-validator.js +1 -0
- package/dist/config-validator.js +32 -2
- package/dist/dep-guard.js +18 -7
- package/dist/error-lens.js +5 -1
- package/dist/git-autocommit.js +2 -5
- package/dist/index.js +1420 -70
- package/dist/migration-planner.js +48 -5
- package/dist/path-guard/index.d.ts +14 -12
- package/dist/path-guard.js +1226 -38
- package/dist/plugin-audit-catalog.js +9 -16
- package/dist/pr-drafter.js +13 -3
- package/dist/release-notes-generator.js +1 -0
- package/dist/runtime/index.d.ts +1 -1
- package/dist/runtime/llm.d.ts +12 -1
- package/dist/runtime.js +40 -0
- package/dist/session-recap.js +5 -1
- package/dist/shell-check.js +1 -0
- package/dist/smart-rename.js +5 -1
- package/dist/template-engine.js +51 -6
- package/dist/test-generator.js +31 -0
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -725,7 +725,9 @@ warning depending on `mode`.
|
|
|
725
725
|
|
|
726
726
|
Protects sensitive paths from accidental writes/edits and destructive shell
|
|
727
727
|
commands. Defaults cover lockfiles, `.env`, `.git`, and migration-like paths;
|
|
728
|
-
use `mode: "warn"` for advisory-only enforcement.
|
|
728
|
+
use `mode: "warn"` for advisory-only enforcement. Writer globs are blocked when
|
|
729
|
+
their unresolved scope overlaps a protected path; narrow the target or add an
|
|
730
|
+
`allow` glob to exempt an intentional scope.
|
|
729
731
|
|
|
730
732
|
```jsonc
|
|
731
733
|
{
|
package/dist/audit/index.d.ts
CHANGED
|
@@ -265,7 +265,7 @@ export declare const OFFICIAL_PLUGIN_AUDIT_ENTRIES: readonly [{
|
|
|
265
265
|
}, {
|
|
266
266
|
readonly name: 'migration-planner';
|
|
267
267
|
readonly risk: 'low';
|
|
268
|
-
readonly summary: 'Builds evidence-backed migration checklists with optional
|
|
268
|
+
readonly summary: 'Builds evidence-backed migration checklists with optional Council-reviewed risk analysis';
|
|
269
269
|
readonly defaultState: 'inactive';
|
|
270
270
|
readonly canDisable: true;
|
|
271
271
|
}, {
|
package/dist/audit.js
CHANGED
|
@@ -311,7 +311,7 @@ var OFFICIAL_PLUGIN_AUDIT_ENTRIES = [
|
|
|
311
311
|
{
|
|
312
312
|
name: "migration-planner",
|
|
313
313
|
risk: "low",
|
|
314
|
-
summary: "Builds evidence-backed migration checklists with optional
|
|
314
|
+
summary: "Builds evidence-backed migration checklists with optional Council-reviewed risk analysis",
|
|
315
315
|
defaultState: "inactive",
|
|
316
316
|
canDisable: true
|
|
317
317
|
},
|
package/dist/auto-doc.js
CHANGED
|
@@ -106,6 +106,7 @@ async function generateDocCommentLlm(entity, snippet, includeTypes, api) {
|
|
|
106
106
|
`Write documentation for this ${entity.kind} named "${entity.name}". Respond with ONLY a JSON object of the form {"summary": string, "params": {"<name>": string}, "returns": string}. summary is one concise sentence. params has one entry per parameter` + (paramList.length > 0 ? ` (${paramList.join(", ")})` : " (may be empty)") + ". returns describes the return value (empty string if none). No prose outside the JSON.\n\n```\n" + snippet + "\n```",
|
|
107
107
|
{
|
|
108
108
|
system: "You are a precise API documentation writer. Output only JSON.",
|
|
109
|
+
role: "document",
|
|
109
110
|
maxTokens: 400,
|
|
110
111
|
responseFormat: "json"
|
|
111
112
|
}
|
package/dist/changelog-writer.js
CHANGED
|
@@ -320,7 +320,11 @@ var plugin = {
|
|
|
320
320
|
try {
|
|
321
321
|
const result = await api.llm.complete(
|
|
322
322
|
'Rewrite this Keep-a-Changelog block into concise, user-facing release-notes wording. Keep the EXACT markdown structure: the same "### Section" headings, one "- " bullet per entry, no entries added or removed. Output ONLY the markdown block.\n\n' + block,
|
|
323
|
-
{
|
|
323
|
+
{
|
|
324
|
+
system: "You are a precise technical release-notes editor.",
|
|
325
|
+
role: "document",
|
|
326
|
+
maxTokens: 1500
|
|
327
|
+
}
|
|
324
328
|
);
|
|
325
329
|
const text = result.text.trim();
|
|
326
330
|
if (!text.startsWith("###")) return block;
|
package/dist/commit-validator.js
CHANGED
|
@@ -300,6 +300,7 @@ Errors: ${parsed.errors.join("; ")}
|
|
|
300
300
|
Reply with ONE corrected conventional-commit subject line (and optional body) and nothing else.`,
|
|
301
301
|
{
|
|
302
302
|
system: "You rewrite commit subjects to follow the conventional-commits format. Reply tersely, no preamble, no quotes.",
|
|
303
|
+
role: "reviewer",
|
|
303
304
|
maxTokens: 120
|
|
304
305
|
}
|
|
305
306
|
);
|
package/dist/config-validator.js
CHANGED
|
@@ -1,5 +1,30 @@
|
|
|
1
1
|
// src/config-validator/index.ts
|
|
2
2
|
import { readFileSync, statSync } from "node:fs";
|
|
3
|
+
|
|
4
|
+
// src/runtime/index.ts
|
|
5
|
+
import { basename, extname, isAbsolute, relative, resolve } from "node:path";
|
|
6
|
+
|
|
7
|
+
// src/runtime/local-bin.ts
|
|
8
|
+
import { buildWin32CmdShimInvocation, resolveWin32Command } from "@wrongstack/tools/win32";
|
|
9
|
+
|
|
10
|
+
// src/runtime/index.ts
|
|
11
|
+
var MAX_BUFFER_BYTES = 16 * 1024 * 1024;
|
|
12
|
+
function hasLeadingDash(arg) {
|
|
13
|
+
return arg.length > 0 && arg.startsWith("-");
|
|
14
|
+
}
|
|
15
|
+
function withinProjectPath(projectRoot, candidate) {
|
|
16
|
+
if (candidate.length === 0 || candidate.length > 4096) return false;
|
|
17
|
+
if (hasLeadingDash(candidate)) return false;
|
|
18
|
+
const resolved = isAbsolute(candidate) ? resolve(candidate) : resolve(projectRoot, candidate);
|
|
19
|
+
const rel = relative(projectRoot, resolved);
|
|
20
|
+
return rel === "" || !rel.startsWith("..") && !isAbsolute(rel);
|
|
21
|
+
}
|
|
22
|
+
function withinProject(p) {
|
|
23
|
+
const cwd = process.cwd();
|
|
24
|
+
return withinProjectPath(cwd, p) || relative(cwd, p) === ".";
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// src/config-validator/index.ts
|
|
3
28
|
var state = {
|
|
4
29
|
invocations: 0,
|
|
5
30
|
filesChecked: 0,
|
|
@@ -79,6 +104,9 @@ function positionToLineCol(text, pos) {
|
|
|
79
104
|
const col = pos - upTo.lastIndexOf("\n");
|
|
80
105
|
return { line, col };
|
|
81
106
|
}
|
|
107
|
+
function redactParseSnippet(message) {
|
|
108
|
+
return message.replace(/"[\s\S]{0,200}?"(?= is not valid JSON)/g, '"\u2026"');
|
|
109
|
+
}
|
|
82
110
|
function validateJson(text, isJsonc, fileName) {
|
|
83
111
|
const source = isJsonc ? stripJsonc(text) : text;
|
|
84
112
|
try {
|
|
@@ -112,10 +140,10 @@ function validateJson(text, isJsonc, fileName) {
|
|
|
112
140
|
const idx = source.indexOf(snippetMatch[1]);
|
|
113
141
|
if (idx >= 0) {
|
|
114
142
|
const { line } = positionToLineCol(source, idx);
|
|
115
|
-
return [`JSON parse error near line ${line}
|
|
143
|
+
return [`JSON parse error near line ${line}`];
|
|
116
144
|
}
|
|
117
145
|
}
|
|
118
|
-
return [`JSON parse error: ${message.split("\n")[0]}`];
|
|
146
|
+
return [`JSON parse error: ${redactParseSnippet(message.split("\n")[0] ?? "")}`];
|
|
119
147
|
}
|
|
120
148
|
}
|
|
121
149
|
function validateYaml(text) {
|
|
@@ -252,10 +280,12 @@ var plugin = {
|
|
|
252
280
|
const cfg = readConfig(api.config.extensions?.["config-validator"]);
|
|
253
281
|
const hook = (input) => {
|
|
254
282
|
if (!cfg.enabled) return;
|
|
283
|
+
if (input.toolResult?.isError) return;
|
|
255
284
|
state.invocations += 1;
|
|
256
285
|
const ti = input.toolInput ?? {};
|
|
257
286
|
const raw = ti["path"] ?? ti["file_path"] ?? ti["filePath"];
|
|
258
287
|
if (typeof raw !== "string" || raw.length === 0) return;
|
|
288
|
+
if (!withinProject(raw)) return;
|
|
259
289
|
const lower = raw.toLowerCase();
|
|
260
290
|
if (!cfg.extensions.some((ext) => lower.endsWith(ext))) return;
|
|
261
291
|
let text;
|
package/dist/dep-guard.js
CHANGED
|
@@ -175,7 +175,7 @@ var plugin = {
|
|
|
175
175
|
confirmTyposquatsWithLlm: {
|
|
176
176
|
type: "boolean",
|
|
177
177
|
default: false,
|
|
178
|
-
description: "Ask the
|
|
178
|
+
description: "Ask the risk-review Council to assess a flagged typosquat, with One Shot fallback. Appended to the warn context, never escalates to a block. Off by default."
|
|
179
179
|
}
|
|
180
180
|
}
|
|
181
181
|
},
|
|
@@ -234,14 +234,25 @@ var plugin = {
|
|
|
234
234
|
const baseNote = `"${pkg.name}" is one edit away from the well-known package "${lookalike}" \u2014 possible typosquat. Verify the name before installing.`;
|
|
235
235
|
if (cfg.confirmTyposquatsWithLlm && api.llm) {
|
|
236
236
|
try {
|
|
237
|
-
const
|
|
238
|
-
|
|
237
|
+
const question = `Classify whether npm package "${pkg.name}" is likely a typo or typosquat of "${lookalike}".`;
|
|
238
|
+
const council = api.llm.council ? await api.llm.council(question, {
|
|
239
|
+
context: "The only supplied evidence is that the names have edit distance 1. Do not invent registry, download, ownership, or provenance facts.",
|
|
240
|
+
profile: "risk-review",
|
|
241
|
+
options: [
|
|
242
|
+
{ id: "typo", label: "Likely typo or typosquat" },
|
|
243
|
+
{ id: "real", label: "Likely distinct real package" },
|
|
244
|
+
{ id: "uncertain", label: "Insufficient evidence" }
|
|
245
|
+
]
|
|
246
|
+
}) : null;
|
|
247
|
+
const councilVerdict = council?.status === "decided" && council.optionId ? `${council.optionId.toUpperCase()}: ${council.reason ?? council.answer ?? "no rationale"}` : null;
|
|
248
|
+
const t = councilVerdict ? councilVerdict : (await api.llm.complete(
|
|
249
|
+
`${question} Reply with ONE sentence starting with "TYPO:", "REAL:", or "UNCERTAIN:".`,
|
|
239
250
|
{
|
|
240
|
-
system: "You are a supply-chain security assistant.
|
|
241
|
-
|
|
251
|
+
system: "You are a supply-chain security assistant. Use only supplied evidence and preserve uncertainty.",
|
|
252
|
+
role: "security-reviewer",
|
|
253
|
+
maxTokens: 100
|
|
242
254
|
}
|
|
243
|
-
);
|
|
244
|
-
const t = verdict.text.trim();
|
|
255
|
+
)).text.trim();
|
|
245
256
|
if (t) {
|
|
246
257
|
state.llmConfirmCount += 1;
|
|
247
258
|
api.metrics.counter("llm_confirm");
|
package/dist/error-lens.js
CHANGED
|
@@ -190,7 +190,11 @@ var plugin = {
|
|
|
190
190
|
${errorLine ?? "(no error line)"}
|
|
191
191
|
` + (frames.length > 0 ? `Top stack frames: ${frames.join(", ")}
|
|
192
192
|
` : "") + "Reply with ONE short sentence suggesting the most likely fix. No preamble.",
|
|
193
|
-
{
|
|
193
|
+
{
|
|
194
|
+
system: "You are a terse debugging assistant.",
|
|
195
|
+
role: "reviewer",
|
|
196
|
+
maxTokens: 100
|
|
197
|
+
}
|
|
194
198
|
);
|
|
195
199
|
const text = hint.text.trim();
|
|
196
200
|
if (text) {
|
package/dist/git-autocommit.js
CHANGED
|
@@ -22,11 +22,7 @@ async function runGit(args, cwd, timeoutMs = DEFAULT_GIT_TIMEOUT_MS) {
|
|
|
22
22
|
(err, stdout) => {
|
|
23
23
|
if (err) {
|
|
24
24
|
const e = err;
|
|
25
|
-
rejectPromise(
|
|
26
|
-
new Error(
|
|
27
|
-
`git command failed: ${e.message ?? e.stderr ?? String(err)}`
|
|
28
|
-
)
|
|
29
|
-
);
|
|
25
|
+
rejectPromise(new Error(`git command failed: ${e.message ?? e.stderr ?? String(err)}`));
|
|
30
26
|
return;
|
|
31
27
|
}
|
|
32
28
|
resolvePromise(stdout.trim());
|
|
@@ -198,6 +194,7 @@ Diff:
|
|
|
198
194
|
${diff}`,
|
|
199
195
|
{
|
|
200
196
|
system: "You are a precise release engineer writing Conventional Commits. Output only JSON.",
|
|
197
|
+
role: "document",
|
|
201
198
|
maxTokens: 400,
|
|
202
199
|
responseFormat: "json"
|
|
203
200
|
}
|