@wrongstack/plugins 0.300.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.
@@ -466,6 +466,13 @@ var HOST_PLUGIN_AUDIT_ENTRIES = [
466
466
  defaultState: "active",
467
467
  canDisable: true
468
468
  },
469
+ {
470
+ name: "wstack-cloud-config-sync",
471
+ risk: "medium",
472
+ summary: "my.wrongstack.com config synchronization over the namespaced sync API.",
473
+ defaultState: "active",
474
+ canDisable: true
475
+ },
469
476
  {
470
477
  name: "wstack-chimera",
471
478
  risk: "medium",
@@ -267,9 +267,15 @@ var plugin = {
267
267
  }
268
268
  }
269
269
  },
270
- permission: "auto",
270
+ // Writes the draft to disk (`writeFile` below), so `mutating: false` was
271
+ // not just a missing capability — it actively LIED, letting even a gate
272
+ // that reads `mutating` correctly through. It also flipped the tool onto
273
+ // the wrong side of `smoke.test.ts`, which skips `mutating: true` tools:
274
+ // this one was exercised by the suite and wrote to the repo during it.
275
+ permission: "confirm",
271
276
  category: "Workflow",
272
- mutating: false,
277
+ mutating: true,
278
+ capabilities: ["fs.write"],
273
279
  async execute(input) {
274
280
  if (!cfg.enabled) return { ok: false, error: "pr-drafter is disabled" };
275
281
  const draft = await buildDraft(cfg, api.llm);
@@ -4,6 +4,7 @@ import { readdir } from "node:fs/promises";
4
4
  import { isAbsolute, join, relative, resolve } from "node:path";
5
5
  var API_VERSION = "^0.1.10";
6
6
  function withinProject(p) {
7
+ if (p.startsWith("-")) return false;
7
8
  const root = process.cwd();
8
9
  const resolved = isAbsolute(p) ? resolve(p) : resolve(root, p);
9
10
  const rel = relative(root, resolved);
@@ -91,9 +91,13 @@ var plugin = {
91
91
  },
92
92
  required: ["path", "oldName", "newName"]
93
93
  },
94
- permission: "auto",
94
+ // Rewrites source files on `apply: true`. `auto` + no declared
95
+ // capability meant no confirmation prompt and no read-only-mode block —
96
+ // `readonly-permission-policy` keys on `capabilities`, not `mutating`.
97
+ permission: "confirm",
95
98
  category: "Development",
96
99
  mutating: true,
100
+ capabilities: ["fs.write"],
97
101
  async execute(input) {
98
102
  if (!cfg.enabled) return { ok: false, error: "smart-rename is disabled" };
99
103
  const rawPath = input.path;
@@ -100,6 +100,33 @@ function validateRelativeTemplatePath(field, value) {
100
100
  }
101
101
  return null;
102
102
  }
103
+ var PROTECTED_WRITE_PREFIXES = [
104
+ ".git/",
105
+ ".husky/",
106
+ ".github/workflows/",
107
+ ".wrongstack/",
108
+ ".claude/",
109
+ "node_modules/"
110
+ ];
111
+ var PROTECTED_WRITE_FILES = /* @__PURE__ */ new Set([
112
+ "package.json",
113
+ "pnpm-workspace.yaml",
114
+ "package-lock.json",
115
+ "pnpm-lock.yaml",
116
+ "yarn.lock",
117
+ ".npmrc",
118
+ ".gitattributes"
119
+ ]);
120
+ function validateWritableTemplateTarget(field, value) {
121
+ const baseError = validateRelativeTemplatePath(field, value);
122
+ if (baseError) return baseError;
123
+ const norm = value.replace(/\\/g, "/").replace(/^\.\//, "").toLowerCase();
124
+ const base = norm.slice(norm.lastIndexOf("/") + 1);
125
+ if (PROTECTED_WRITE_PREFIXES.some((p) => norm.startsWith(p)) || PROTECTED_WRITE_FILES.has(base) || base === ".env" || base.startsWith(".env.")) {
126
+ return `${field} "${value}" is a protected path (VCS hooks, CI workflows, dependency manifests, or agent configuration). Write it with the \`write\` tool instead, which prompts for confirmation.`;
127
+ }
128
+ return null;
129
+ }
103
130
  var plugin = {
104
131
  name: "template-engine",
105
132
  version: "0.1.0",
@@ -145,7 +172,14 @@ var plugin = {
145
172
  },
146
173
  required: ["template", "variables"]
147
174
  },
148
- permission: "auto",
175
+ // Writes caller-supplied content to a caller-supplied path. `auto` +
176
+ // no declared capability meant no confirmation prompt, no read-only-mode
177
+ // block (`readonly-permission-policy` keys on `capabilities`, not
178
+ // `mutating`), and no PreToolUse hook — plugin tools reach the executor
179
+ // through `plugin_manager action:'use'`, which bypasses it.
180
+ permission: "confirm",
181
+ capabilities: ["fs.write"],
182
+ riskTier: "destructive",
149
183
  category: "Project",
150
184
  mutating: true,
151
185
  async execute(input) {
@@ -166,9 +200,13 @@ var plugin = {
166
200
  return { ok: false, error: String(err) };
167
201
  }
168
202
  if (output_path) {
169
- const pathError = validateRelativeTemplatePath("output_path", output_path);
203
+ const pathError = validateWritableTemplateTarget("output_path", output_path);
170
204
  if (pathError) return { ok: false, error: pathError };
171
- await writeFile(output_path, result, "utf-8");
205
+ try {
206
+ await writeFile(output_path, result, "utf-8");
207
+ } catch (err) {
208
+ return { ok: false, error: `Could not write ${output_path}: ${String(err)}` };
209
+ }
172
210
  return {
173
211
  ok: true,
174
212
  output_path,
@@ -204,7 +242,10 @@ var plugin = {
204
242
  },
205
243
  required: ["template_path", "variables"]
206
244
  },
207
- permission: "auto",
245
+ // Same reasoning as `template_expand` above.
246
+ permission: "confirm",
247
+ capabilities: ["fs.write"],
248
+ riskTier: "destructive",
208
249
  mutating: true,
209
250
  async execute(input) {
210
251
  const template_path = input["template_path"];
@@ -232,9 +273,13 @@ var plugin = {
232
273
  return { ok: false, error: `Template rendering failed: ${err}` };
233
274
  }
234
275
  if (output_path) {
235
- const pathError = validateRelativeTemplatePath("output_path", output_path);
276
+ const pathError = validateWritableTemplateTarget("output_path", output_path);
236
277
  if (pathError) return { ok: false, error: pathError };
237
- await writeFile(output_path, result, "utf-8");
278
+ try {
279
+ await writeFile(output_path, result, "utf-8");
280
+ } catch (err) {
281
+ return { ok: false, error: `Could not write ${output_path}: ${String(err)}` };
282
+ }
238
283
  return {
239
284
  ok: true,
240
285
  template_path,
@@ -71,6 +71,30 @@ function readConfig(raw) {
71
71
  maxSourceChars: typeof r["maxSourceChars"] === "number" && r["maxSourceChars"] >= 1e3 && r["maxSourceChars"] <= 1e5 ? r["maxSourceChars"] : DEFAULTS.maxSourceChars
72
72
  };
73
73
  }
74
+ var SOURCE_EXTENSIONS = [
75
+ ".ts",
76
+ ".tsx",
77
+ ".js",
78
+ ".jsx",
79
+ ".mjs",
80
+ ".cjs",
81
+ ".mts",
82
+ ".cts",
83
+ ".py",
84
+ ".go",
85
+ ".rs",
86
+ ".java",
87
+ ".kt",
88
+ ".rb",
89
+ ".php",
90
+ ".cs",
91
+ ".swift",
92
+ ".c",
93
+ ".h",
94
+ ".cc",
95
+ ".cpp",
96
+ ".hpp"
97
+ ];
74
98
  function withinProject(p) {
75
99
  if (typeof p !== "string" || p.length === 0 || p.length > 4096) return false;
76
100
  const root = process.cwd();
@@ -298,6 +322,12 @@ var plugin = {
298
322
  if (!withinProject(rawPath)) {
299
323
  return { ok: false, error: "path is outside the project root" };
300
324
  }
325
+ if (!SOURCE_EXTENSIONS.some((ext) => rawPath.toLowerCase().endsWith(ext))) {
326
+ return {
327
+ ok: false,
328
+ error: `test generation only reads source files (${SOURCE_EXTENSIONS.join(", ")}); refusing "${rawPath}"`
329
+ };
330
+ }
301
331
  const resolved = resolve(process.cwd(), rawPath);
302
332
  state.generateCount += 1;
303
333
  let result;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wrongstack/plugins",
3
- "version": "0.300.0",
3
+ "version": "0.301.0",
4
4
  "description": "Official WrongStack collection of focused plugins for code quality, security, observability, planning, and agent coordination",
5
5
  "license": "MIT",
6
6
  "author": "ECOSTACK TECHNOLOGY OÜ",
@@ -299,8 +299,8 @@
299
299
  "vitest": "^4.1.10"
300
300
  },
301
301
  "dependencies": {
302
- "@wrongstack/core": "0.300.0",
303
- "@wrongstack/tools": "0.300.0"
302
+ "@wrongstack/core": "0.301.0",
303
+ "@wrongstack/tools": "0.301.0"
304
304
  },
305
305
  "scripts": {
306
306
  "build": "node ../../scripts/build-package.mjs",