@wrongstack/plugins 0.283.1 → 0.284.1
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/branch-guard.js +32 -21
- package/dist/checkpoint.js +23 -3
- package/dist/commit-validator.js +6 -1
- package/dist/dep-guard.js +6 -1
- package/dist/index.js +385 -204
- package/dist/injection-shield.d.ts +1 -1
- package/dist/injection-shield.js +2 -1
- package/dist/lint-gate.js +105 -69
- package/dist/loop-breaker.js +36 -13
- package/dist/path-guard.js +6 -1
- package/dist/secret-scanner.js +34 -19
- package/dist/semantic-search-indexer.js +94 -37
- package/dist/spec-linker.js +17 -11
- package/package.json +3 -3
package/dist/branch-guard.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { execFile } from 'child_process';
|
|
2
2
|
|
|
3
3
|
// src/branch-guard/index.ts
|
|
4
4
|
var API_VERSION = "^0.1.10";
|
|
@@ -52,29 +52,34 @@ function hasDisabledPluginEntry(raw) {
|
|
|
52
52
|
return name === "branch-guard" || name === "@wrongstack/plugins/branch-guard";
|
|
53
53
|
});
|
|
54
54
|
}
|
|
55
|
-
function
|
|
55
|
+
function runGit(args, cwd, signal) {
|
|
56
|
+
return new Promise((resolve, reject) => {
|
|
57
|
+
execFile(
|
|
58
|
+
"git",
|
|
59
|
+
args,
|
|
60
|
+
{ encoding: "utf-8", timeout: 3e3, cwd, windowsHide: true, signal },
|
|
61
|
+
(error, stdout) => {
|
|
62
|
+
if (error) reject(error);
|
|
63
|
+
else resolve(stdout);
|
|
64
|
+
}
|
|
65
|
+
);
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
async function getCurrentBranch(cwd, signal) {
|
|
56
69
|
try {
|
|
57
|
-
const branch =
|
|
58
|
-
encoding: "utf-8",
|
|
59
|
-
timeout: 3e3,
|
|
60
|
-
cwd,
|
|
61
|
-
stdio: ["pipe", "pipe", "pipe"]
|
|
62
|
-
}).trim();
|
|
70
|
+
const branch = (await runGit(["branch", "--show-current"], cwd, signal)).trim();
|
|
63
71
|
return branch || null;
|
|
64
|
-
} catch {
|
|
72
|
+
} catch (err) {
|
|
73
|
+
if (signal.aborted) throw err;
|
|
65
74
|
return null;
|
|
66
75
|
}
|
|
67
76
|
}
|
|
68
|
-
function detectUncommittedChanges(cwd) {
|
|
77
|
+
async function detectUncommittedChanges(cwd, signal) {
|
|
69
78
|
try {
|
|
70
|
-
const output =
|
|
71
|
-
encoding: "utf-8",
|
|
72
|
-
timeout: 3e3,
|
|
73
|
-
cwd,
|
|
74
|
-
stdio: ["pipe", "pipe", "pipe"]
|
|
75
|
-
}).trim();
|
|
79
|
+
const output = (await runGit(["status", "--porcelain"], cwd, signal)).trim();
|
|
76
80
|
return output.length > 0;
|
|
77
|
-
} catch {
|
|
81
|
+
} catch (err) {
|
|
82
|
+
if (signal.aborted) throw err;
|
|
78
83
|
return false;
|
|
79
84
|
}
|
|
80
85
|
}
|
|
@@ -158,7 +163,7 @@ var plugin = {
|
|
|
158
163
|
cfg = readHostConfig(next);
|
|
159
164
|
});
|
|
160
165
|
const cwd = typeof process.cwd === "function" ? process.cwd() : void 0;
|
|
161
|
-
const hook = (input) => {
|
|
166
|
+
const hook = async (input, runtime = { signal: new AbortController().signal }) => {
|
|
162
167
|
const toolName = input.toolName ?? "";
|
|
163
168
|
const inp = input.toolInput ?? {};
|
|
164
169
|
state.invocationCount += 1;
|
|
@@ -176,13 +181,13 @@ var plugin = {
|
|
|
176
181
|
}
|
|
177
182
|
if (!gitOp) return;
|
|
178
183
|
if (!shouldBlock(gitOp.type, cfg)) return;
|
|
179
|
-
const branch = getCurrentBranch(cwd);
|
|
184
|
+
const branch = await getCurrentBranch(cwd, runtime.signal);
|
|
180
185
|
if (!branch) return;
|
|
181
186
|
const protectedSet = new Set(cfg.branches);
|
|
182
187
|
if (!protectedSet.has(branch)) return;
|
|
183
188
|
const when = (/* @__PURE__ */ new Date()).toISOString();
|
|
184
189
|
const opVerb = gitOp.type === "commit" ? "committing to" : gitOp.type === "push" ? "pushing from" : "merging into";
|
|
185
|
-
const hasUncommitted = detectUncommittedChanges(cwd);
|
|
190
|
+
const hasUncommitted = await detectUncommittedChanges(cwd, runtime.signal);
|
|
186
191
|
const retryStep = toolName === "git_autocommit" ? "retry git_autocommit" : `git ${gitOp.type} ...`;
|
|
187
192
|
const suggestionParts = [];
|
|
188
193
|
if (hasUncommitted) {
|
|
@@ -215,7 +220,13 @@ var plugin = {
|
|
|
215
220
|
\u26A0\uFE0F branch-guard: you are ${opVerb} protected branch '${branch}'. ` + (hasUncommitted ? `You have uncommitted changes \u2014 consider \`git stash\` before switching branches. ` : "") + `Use a feature branch instead. Protected: ${cfg.branches.join(", ")}.`
|
|
216
221
|
};
|
|
217
222
|
};
|
|
218
|
-
state.hookUnregister = api.registerHook("PreToolUse", "bash|git|git_autocommit", hook
|
|
223
|
+
state.hookUnregister = api.registerHook("PreToolUse", "bash|git|git_autocommit", hook, {
|
|
224
|
+
name: "branch-guard",
|
|
225
|
+
stage: "validate",
|
|
226
|
+
timeoutMs: 7e3,
|
|
227
|
+
failurePolicy: "closed",
|
|
228
|
+
policy: true
|
|
229
|
+
});
|
|
219
230
|
api.tools.register({
|
|
220
231
|
name: "branch_guard_status",
|
|
221
232
|
description: "Reports branch-guard state: protected branches, mode, and per-session invocation/block/warn counters.",
|
package/dist/checkpoint.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { mkdirSync, writeFileSync, statSync, readFileSync } from 'fs';
|
|
2
|
+
import { stat, readFile } from 'fs/promises';
|
|
2
3
|
import { dirname, resolve, isAbsolute, relative } from 'path';
|
|
3
4
|
|
|
4
5
|
// src/checkpoint/index.ts
|
|
@@ -51,6 +52,19 @@ function captureFile(path, maxBytes) {
|
|
|
51
52
|
return { path, content: null, bytes: 0 };
|
|
52
53
|
}
|
|
53
54
|
}
|
|
55
|
+
async function captureFileForHook(path, maxBytes, signal) {
|
|
56
|
+
try {
|
|
57
|
+
signal.throwIfAborted();
|
|
58
|
+
const st = await stat(path);
|
|
59
|
+
if (st.size > maxBytes) return "too-large";
|
|
60
|
+
const content = await readFile(path, "utf-8");
|
|
61
|
+
signal.throwIfAborted();
|
|
62
|
+
return { path, content, bytes: st.size };
|
|
63
|
+
} catch (err) {
|
|
64
|
+
if (signal.aborted) throw err;
|
|
65
|
+
return { path, content: null, bytes: 0 };
|
|
66
|
+
}
|
|
67
|
+
}
|
|
54
68
|
function pushSnapshot(snapshot, maxSnapshots) {
|
|
55
69
|
state.snapshots.push(snapshot);
|
|
56
70
|
if (state.snapshots.length > maxSnapshots) {
|
|
@@ -103,13 +117,13 @@ var plugin = {
|
|
|
103
117
|
}
|
|
104
118
|
const cfg = readConfig(api.config.extensions?.["checkpoint"]);
|
|
105
119
|
if (cfg.enabled && cfg.autoCapture) {
|
|
106
|
-
const hook = (input) => {
|
|
120
|
+
const hook = async (input, runtime = { signal: new AbortController().signal }) => {
|
|
107
121
|
const ti = input.toolInput ?? {};
|
|
108
122
|
const raw = ti["path"] ?? ti["file_path"] ?? ti["filePath"];
|
|
109
123
|
if (typeof raw !== "string" || raw.length === 0) return;
|
|
110
124
|
const safePath = resolveProjectPath(raw);
|
|
111
125
|
if (!safePath) return;
|
|
112
|
-
const captured =
|
|
126
|
+
const captured = await captureFileForHook(safePath, cfg.maxFileBytes, runtime.signal);
|
|
113
127
|
if (captured === "too-large") {
|
|
114
128
|
state.skippedLarge += 1;
|
|
115
129
|
return;
|
|
@@ -136,7 +150,13 @@ var plugin = {
|
|
|
136
150
|
when: (/* @__PURE__ */ new Date()).toISOString()
|
|
137
151
|
});
|
|
138
152
|
};
|
|
139
|
-
state.hookUnregister = api.registerHook("PreToolUse", "write|edit", hook
|
|
153
|
+
state.hookUnregister = api.registerHook("PreToolUse", "write|edit", hook, {
|
|
154
|
+
name: "checkpoint-guard",
|
|
155
|
+
stage: "validate",
|
|
156
|
+
// Checkpointing is recovery automation, not an enforcement boundary.
|
|
157
|
+
// A transient read failure must not stall normal/YOLO writes.
|
|
158
|
+
failurePolicy: "open"
|
|
159
|
+
});
|
|
140
160
|
}
|
|
141
161
|
api.tools.register({
|
|
142
162
|
name: "checkpoint_create",
|
package/dist/commit-validator.js
CHANGED
|
@@ -292,7 +292,12 @@ Suggested rewrite (${suggest.model}):
|
|
|
292
292
|
additionalContext: baseContext
|
|
293
293
|
};
|
|
294
294
|
};
|
|
295
|
-
state.hookUnregister = api.registerHook("PreToolUse", "bash|git_autocommit", hook
|
|
295
|
+
state.hookUnregister = api.registerHook("PreToolUse", "bash|git_autocommit", hook, {
|
|
296
|
+
name: "commit-validator",
|
|
297
|
+
stage: "validate",
|
|
298
|
+
failurePolicy: "closed",
|
|
299
|
+
policy: true
|
|
300
|
+
});
|
|
296
301
|
api.tools.register({
|
|
297
302
|
name: "commit_validator_status",
|
|
298
303
|
description: "Reports commit-validator state: mode, allowedTypes, maxSubjectLength, and per-session valid/invalid counters.",
|
package/dist/dep-guard.js
CHANGED
|
@@ -276,7 +276,12 @@ ${notes.map((n) => ` - ${n}`).join("\n")}`
|
|
|
276
276
|
additionalContext: `dep-guard: this command adds ${packages.length} dependenc${packages.length === 1 ? "y" : "ies"}: ${packages.map((p) => p.name).join(", ")}. Confirm each is intentional.`
|
|
277
277
|
};
|
|
278
278
|
};
|
|
279
|
-
state.hookUnregister = api.registerHook("PreToolUse", "bash|exec", hook
|
|
279
|
+
state.hookUnregister = api.registerHook("PreToolUse", "bash|exec", hook, {
|
|
280
|
+
name: "dep-guard",
|
|
281
|
+
stage: "validate",
|
|
282
|
+
failurePolicy: "closed",
|
|
283
|
+
policy: true
|
|
284
|
+
});
|
|
280
285
|
api.tools.register({
|
|
281
286
|
name: "dep_guard_status",
|
|
282
287
|
description: "Reports dep-guard state: deny/allow lists, mode, and counters (installs seen, blocks, warns).",
|