@wrongstack/plugins 0.281.0 → 0.281.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/README.md +325 -34
- package/dist/auto-doc.js +22 -7
- package/dist/branch-guard.js +18 -4
- package/dist/changelog-writer.js +12 -1
- package/dist/checkpoint.js +25 -3
- package/dist/context-pins.js +11 -2
- package/dist/git-autocommit.js +5 -3
- package/dist/index.js +232 -67
- package/dist/notify-hub.js +26 -1
- package/dist/semver-bump.js +43 -9
- package/dist/spec-linker.js +24 -3
- package/dist/template-engine.js +44 -29
- package/package.json +3 -3
package/dist/git-autocommit.js
CHANGED
|
@@ -6,13 +6,15 @@ var API_VERSION = "^0.1.10";
|
|
|
6
6
|
var commitCount = { value: 0 };
|
|
7
7
|
var lastCommit = { hash: null, at: null };
|
|
8
8
|
var llmGenerated = { value: 0 };
|
|
9
|
-
|
|
9
|
+
var DEFAULT_GIT_TIMEOUT_MS = 3e4;
|
|
10
|
+
var GIT_COMMIT_TIMEOUT_MS = 5 * 6e4;
|
|
11
|
+
function runGit(args, cwd, timeoutMs = DEFAULT_GIT_TIMEOUT_MS) {
|
|
10
12
|
try {
|
|
11
13
|
return execFileSync("git", args, {
|
|
12
14
|
encoding: "utf-8",
|
|
13
15
|
cwd,
|
|
14
16
|
stdio: ["pipe", "pipe", "pipe"],
|
|
15
|
-
timeout:
|
|
17
|
+
timeout: timeoutMs,
|
|
16
18
|
maxBuffer: 10 * 1024 * 1024,
|
|
17
19
|
windowsHide: true
|
|
18
20
|
}).trim();
|
|
@@ -43,7 +45,7 @@ function stageFiles(files, cwd) {
|
|
|
43
45
|
runGit(["add", ...existing], cwd);
|
|
44
46
|
}
|
|
45
47
|
function commitWithMessage(message, cwd) {
|
|
46
|
-
return runGit(["commit", "-m", message], cwd);
|
|
48
|
+
return runGit(["commit", "-m", message], cwd, GIT_COMMIT_TIMEOUT_MS);
|
|
47
49
|
}
|
|
48
50
|
function getWorktrees(cwd) {
|
|
49
51
|
try {
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
+
import * as path from 'path';
|
|
2
|
+
import { dirname, resolve, isAbsolute, relative, join, basename } from 'path';
|
|
1
3
|
import { execSync, execFileSync, spawn } from 'child_process';
|
|
2
4
|
import { readFileSync, writeFileSync, mkdirSync, existsSync, statSync, watch, readdirSync, mkdtempSync, rmSync } from 'fs';
|
|
3
|
-
import * as path from 'path';
|
|
4
|
-
import { dirname, isAbsolute, join, basename } from 'path';
|
|
5
5
|
import { expectDefined } from '@wrongstack/core';
|
|
6
6
|
import { tmpdir } from 'os';
|
|
7
7
|
import { randomUUID, createHash } from 'crypto';
|
|
@@ -10,6 +10,14 @@ import * as fsp from 'fs/promises';
|
|
|
10
10
|
|
|
11
11
|
// src/auto-doc/index.ts
|
|
12
12
|
var AUTO_DOC_API_VERSION = "^0.1.10";
|
|
13
|
+
function resolveProjectPath(rawPath, cwd = process.cwd()) {
|
|
14
|
+
if (typeof rawPath !== "string" || rawPath.length === 0) return null;
|
|
15
|
+
const root = resolve(cwd);
|
|
16
|
+
const resolved = isAbsolute(rawPath) ? resolve(rawPath) : resolve(root, rawPath);
|
|
17
|
+
const rel = relative(root, resolved);
|
|
18
|
+
if (rel === "" || !rel.startsWith("..") && !isAbsolute(rel)) return resolved;
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
13
21
|
var state = {
|
|
14
22
|
invocationCount: 0,
|
|
15
23
|
/** Doc comments whose prose came from the LLM this session. */
|
|
@@ -180,14 +188,19 @@ async function runAutoDoc(input, api) {
|
|
|
180
188
|
const maxLlmEntities = typeof extConfig["maxLlmEntities"] === "number" && extConfig["maxLlmEntities"] >= 0 ? extConfig["maxLlmEntities"] : 25;
|
|
181
189
|
const results = [];
|
|
182
190
|
let llmBudget = maxLlmEntities;
|
|
183
|
-
for (const
|
|
191
|
+
for (const rawFile of input.files) {
|
|
192
|
+
const safeFile = resolveProjectPath(rawFile);
|
|
193
|
+
if (!safeFile) {
|
|
194
|
+
api.log.warn(`auto-doc: skipped file outside project directory: ${rawFile}`);
|
|
195
|
+
continue;
|
|
196
|
+
}
|
|
184
197
|
try {
|
|
185
198
|
const { readFileSync: readFileSync7, writeFileSync: writeFileSync6 } = await import('fs');
|
|
186
199
|
let content;
|
|
187
200
|
try {
|
|
188
|
-
content = readFileSync7(
|
|
201
|
+
content = readFileSync7(safeFile, "utf-8");
|
|
189
202
|
} catch {
|
|
190
|
-
api.log.warn(`auto-doc: could not read file ${
|
|
203
|
+
api.log.warn(`auto-doc: could not read file ${safeFile}`);
|
|
191
204
|
continue;
|
|
192
205
|
}
|
|
193
206
|
const entities = parseSource(content);
|
|
@@ -215,14 +228,14 @@ async function runAutoDoc(input, api) {
|
|
|
215
228
|
}
|
|
216
229
|
if (!doc) doc = generateDocComment(entity, includeTypes);
|
|
217
230
|
modified = injectDocComment(modified, entity, doc);
|
|
218
|
-
results.push({ file, entity: entity.name, source });
|
|
231
|
+
results.push({ file: safeFile, entity: entity.name, source });
|
|
219
232
|
}
|
|
220
233
|
if (!input.dry_run && results.length > 0) {
|
|
221
|
-
writeFileSync6(
|
|
222
|
-
api.log.info(`auto-doc: updated ${
|
|
234
|
+
writeFileSync6(safeFile, modified, "utf-8");
|
|
235
|
+
api.log.info(`auto-doc: updated ${safeFile}`);
|
|
223
236
|
}
|
|
224
237
|
} catch (err) {
|
|
225
|
-
api.log.error(`auto-doc: error processing ${
|
|
238
|
+
api.log.error(`auto-doc: error processing ${safeFile}: ${err}`);
|
|
226
239
|
}
|
|
227
240
|
}
|
|
228
241
|
return {
|
|
@@ -625,9 +638,21 @@ var plugin3 = {
|
|
|
625
638
|
default: "block",
|
|
626
639
|
description: '"block" refuses the call; "warn" injects context but lets it through.'
|
|
627
640
|
},
|
|
628
|
-
blockCommit: {
|
|
629
|
-
|
|
630
|
-
|
|
641
|
+
blockCommit: {
|
|
642
|
+
type: "boolean",
|
|
643
|
+
default: true,
|
|
644
|
+
description: "Block commits on protected branches."
|
|
645
|
+
},
|
|
646
|
+
blockPush: {
|
|
647
|
+
type: "boolean",
|
|
648
|
+
default: true,
|
|
649
|
+
description: "Block pushes from protected branches."
|
|
650
|
+
},
|
|
651
|
+
blockMerge: {
|
|
652
|
+
type: "boolean",
|
|
653
|
+
default: true,
|
|
654
|
+
description: "Block merges into protected branches."
|
|
655
|
+
}
|
|
631
656
|
}
|
|
632
657
|
},
|
|
633
658
|
setup(api) {
|
|
@@ -645,6 +670,7 @@ var plugin3 = {
|
|
|
645
670
|
state3.invocationCount += 1;
|
|
646
671
|
let gitOp = null;
|
|
647
672
|
if (toolName === "git_autocommit") {
|
|
673
|
+
if (inp["dry_run"] === true) return;
|
|
648
674
|
gitOp = { type: "commit", snippet: "git_autocommit" };
|
|
649
675
|
} else if (toolName === "bash") {
|
|
650
676
|
const command = inp["command"];
|
|
@@ -659,6 +685,7 @@ var plugin3 = {
|
|
|
659
685
|
const when = (/* @__PURE__ */ new Date()).toISOString();
|
|
660
686
|
const opVerb = gitOp.type === "commit" ? "committing to" : gitOp.type === "push" ? "pushing from" : "merging into";
|
|
661
687
|
const hasUncommitted = detectUncommittedChanges(cwd);
|
|
688
|
+
const retryStep = toolName === "git_autocommit" ? "retry git_autocommit" : `git ${gitOp.type} ...`;
|
|
662
689
|
const suggestionParts = [];
|
|
663
690
|
if (hasUncommitted) {
|
|
664
691
|
suggestionParts.push("git stash");
|
|
@@ -667,7 +694,7 @@ var plugin3 = {
|
|
|
667
694
|
if (hasUncommitted) {
|
|
668
695
|
suggestionParts.push("git stash pop");
|
|
669
696
|
}
|
|
670
|
-
suggestionParts.push(
|
|
697
|
+
suggestionParts.push(retryStep);
|
|
671
698
|
const suggestion = suggestionParts.join(" \u2192 ");
|
|
672
699
|
const reason = `branch-guard: refused to ${gitOp.type} on protected branch '${branch}'. You're on a protected branch. Use a feature branch instead.
|
|
673
700
|
` + (hasUncommitted ? `You have uncommitted changes. Safe workflow:
|
|
@@ -769,12 +796,19 @@ var DEFAULTS2 = {
|
|
|
769
796
|
collectCommits: true,
|
|
770
797
|
maxEntries: 200
|
|
771
798
|
};
|
|
799
|
+
function resolveProjectPath2(rawPath, cwd = process.cwd()) {
|
|
800
|
+
const root = resolve(cwd);
|
|
801
|
+
const resolved = isAbsolute(rawPath) ? resolve(rawPath) : resolve(root, rawPath);
|
|
802
|
+
const rel = relative(root, resolved);
|
|
803
|
+
if (rel === "" || !rel.startsWith("..") && !isAbsolute(rel)) return resolved;
|
|
804
|
+
return null;
|
|
805
|
+
}
|
|
772
806
|
function readConfig3(raw) {
|
|
773
807
|
if (!raw || typeof raw !== "object") return { ...DEFAULTS2 };
|
|
774
808
|
const r = raw;
|
|
775
809
|
return {
|
|
776
810
|
enabled: r["enabled"] !== false,
|
|
777
|
-
filePath: typeof r["filePath"] === "string" && r["filePath"].length > 0 ? r["filePath"] : DEFAULTS2.filePath,
|
|
811
|
+
filePath: typeof r["filePath"] === "string" && r["filePath"].length > 0 ? resolveProjectPath2(r["filePath"]) ?? "" : resolveProjectPath2(DEFAULTS2.filePath) ?? DEFAULTS2.filePath,
|
|
778
812
|
collectCommits: r["collectCommits"] !== false,
|
|
779
813
|
maxEntries: typeof r["maxEntries"] === "number" && r["maxEntries"] >= 10 ? r["maxEntries"] : DEFAULTS2.maxEntries
|
|
780
814
|
};
|
|
@@ -1044,6 +1078,9 @@ var plugin4 = {
|
|
|
1044
1078
|
mutating: true,
|
|
1045
1079
|
async execute(input) {
|
|
1046
1080
|
if (!cfg.enabled) return { ok: false, error: "changelog-writer is disabled" };
|
|
1081
|
+
if (!cfg.filePath) {
|
|
1082
|
+
return { ok: false, error: "filePath must stay within the current project directory" };
|
|
1083
|
+
}
|
|
1047
1084
|
if (state4.entries.length === 0) {
|
|
1048
1085
|
return { ok: false, error: "no pending entries \u2014 add some with changelog_add first" };
|
|
1049
1086
|
}
|
|
@@ -1142,6 +1179,13 @@ function readConfig4(raw) {
|
|
|
1142
1179
|
maxFileBytes: typeof r["maxFileBytes"] === "number" && r["maxFileBytes"] >= 1024 ? r["maxFileBytes"] : DEFAULTS3.maxFileBytes
|
|
1143
1180
|
};
|
|
1144
1181
|
}
|
|
1182
|
+
function resolveProjectPath3(rawPath, cwd = process.cwd()) {
|
|
1183
|
+
const root = resolve(cwd);
|
|
1184
|
+
const resolved = isAbsolute(rawPath) ? resolve(rawPath) : resolve(root, rawPath);
|
|
1185
|
+
const rel = relative(root, resolved);
|
|
1186
|
+
if (rel === "" || !rel.startsWith("..") && !isAbsolute(rel)) return resolved;
|
|
1187
|
+
return null;
|
|
1188
|
+
}
|
|
1145
1189
|
function captureFile(path2, maxBytes) {
|
|
1146
1190
|
try {
|
|
1147
1191
|
const st = statSync(path2);
|
|
@@ -1208,7 +1252,9 @@ var plugin5 = {
|
|
|
1208
1252
|
const ti = input.toolInput ?? {};
|
|
1209
1253
|
const raw = ti["path"] ?? ti["file_path"] ?? ti["filePath"];
|
|
1210
1254
|
if (typeof raw !== "string" || raw.length === 0) return;
|
|
1211
|
-
const
|
|
1255
|
+
const safePath = resolveProjectPath3(raw);
|
|
1256
|
+
if (!safePath) return;
|
|
1257
|
+
const captured = captureFile(safePath, cfg.maxFileBytes);
|
|
1212
1258
|
if (captured === "too-large") {
|
|
1213
1259
|
state5.skippedLarge += 1;
|
|
1214
1260
|
return;
|
|
@@ -1250,9 +1296,15 @@ var plugin5 = {
|
|
|
1250
1296
|
const paths = Array.isArray(input.paths) ? input.paths.filter((p) => typeof p === "string" && p.length > 0) : [];
|
|
1251
1297
|
if (paths.length === 0) return { ok: false, error: "paths must not be empty" };
|
|
1252
1298
|
const files = [];
|
|
1299
|
+
const rejectedOutsideProject = [];
|
|
1253
1300
|
let skipped = 0;
|
|
1254
1301
|
for (const p of paths) {
|
|
1255
|
-
const
|
|
1302
|
+
const safePath = resolveProjectPath3(p);
|
|
1303
|
+
if (!safePath) {
|
|
1304
|
+
rejectedOutsideProject.push(p);
|
|
1305
|
+
continue;
|
|
1306
|
+
}
|
|
1307
|
+
const captured = captureFile(safePath, cfg.maxFileBytes);
|
|
1256
1308
|
if (captured === "too-large") {
|
|
1257
1309
|
skipped += 1;
|
|
1258
1310
|
state5.skippedLarge += 1;
|
|
@@ -1260,6 +1312,13 @@ var plugin5 = {
|
|
|
1260
1312
|
}
|
|
1261
1313
|
files.push(captured);
|
|
1262
1314
|
}
|
|
1315
|
+
if (rejectedOutsideProject.length > 0) {
|
|
1316
|
+
return {
|
|
1317
|
+
ok: false,
|
|
1318
|
+
error: "paths must stay within the current project directory",
|
|
1319
|
+
rejectedOutsideProject
|
|
1320
|
+
};
|
|
1321
|
+
}
|
|
1263
1322
|
if (files.length === 0) {
|
|
1264
1323
|
return { ok: false, error: "all files were skipped (too large)" };
|
|
1265
1324
|
}
|
|
@@ -2087,12 +2146,21 @@ var DEFAULTS6 = {
|
|
|
2087
2146
|
maxPins: 20,
|
|
2088
2147
|
maxPinChars: 500
|
|
2089
2148
|
};
|
|
2149
|
+
function resolveProjectPath4(rawPath, cwd = process.cwd()) {
|
|
2150
|
+
if (typeof rawPath !== "string" || rawPath.length === 0) return "";
|
|
2151
|
+
const root = resolve(cwd);
|
|
2152
|
+
const resolved = isAbsolute(rawPath) ? resolve(rawPath) : resolve(root, rawPath);
|
|
2153
|
+
const rel = relative(root, resolved);
|
|
2154
|
+
if (rel === "" || !rel.startsWith("..") && !isAbsolute(rel)) return resolved;
|
|
2155
|
+
return null;
|
|
2156
|
+
}
|
|
2090
2157
|
function readConfig7(raw) {
|
|
2091
2158
|
if (!raw || typeof raw !== "object") return { ...DEFAULTS6 };
|
|
2092
2159
|
const r = raw;
|
|
2160
|
+
const rawPath = typeof r["filePath"] === "string" ? r["filePath"] : DEFAULTS6.filePath;
|
|
2093
2161
|
return {
|
|
2094
2162
|
enabled: r["enabled"] !== false,
|
|
2095
|
-
filePath:
|
|
2163
|
+
filePath: rawPath ? resolveProjectPath4(rawPath) ?? "" : "",
|
|
2096
2164
|
maxPins: typeof r["maxPins"] === "number" && r["maxPins"] >= 1 && r["maxPins"] <= 100 ? r["maxPins"] : DEFAULTS6.maxPins,
|
|
2097
2165
|
maxPinChars: typeof r["maxPinChars"] === "number" && r["maxPinChars"] >= 20 ? r["maxPinChars"] : DEFAULTS6.maxPinChars
|
|
2098
2166
|
};
|
|
@@ -4157,13 +4225,15 @@ var API_VERSION8 = "^0.1.10";
|
|
|
4157
4225
|
var commitCount = { value: 0 };
|
|
4158
4226
|
var lastCommit = { hash: null, at: null };
|
|
4159
4227
|
var llmGenerated = { value: 0 };
|
|
4160
|
-
|
|
4228
|
+
var DEFAULT_GIT_TIMEOUT_MS = 3e4;
|
|
4229
|
+
var GIT_COMMIT_TIMEOUT_MS = 5 * 6e4;
|
|
4230
|
+
function runGit(args, cwd, timeoutMs = DEFAULT_GIT_TIMEOUT_MS) {
|
|
4161
4231
|
try {
|
|
4162
4232
|
return execFileSync("git", args, {
|
|
4163
4233
|
encoding: "utf-8",
|
|
4164
4234
|
cwd,
|
|
4165
4235
|
stdio: ["pipe", "pipe", "pipe"],
|
|
4166
|
-
timeout:
|
|
4236
|
+
timeout: timeoutMs,
|
|
4167
4237
|
maxBuffer: 10 * 1024 * 1024,
|
|
4168
4238
|
windowsHide: true
|
|
4169
4239
|
}).trim();
|
|
@@ -4194,7 +4264,7 @@ function stageFiles(files, cwd) {
|
|
|
4194
4264
|
runGit(["add", ...existing], cwd);
|
|
4195
4265
|
}
|
|
4196
4266
|
function commitWithMessage(message, cwd) {
|
|
4197
|
-
return runGit(["commit", "-m", message], cwd);
|
|
4267
|
+
return runGit(["commit", "-m", message], cwd, GIT_COMMIT_TIMEOUT_MS);
|
|
4198
4268
|
}
|
|
4199
4269
|
function getWorktrees(cwd) {
|
|
4200
4270
|
try {
|
|
@@ -4635,7 +4705,7 @@ function readConfig12(raw) {
|
|
|
4635
4705
|
};
|
|
4636
4706
|
}
|
|
4637
4707
|
function runCommand(command, args, timeoutMs, cwd) {
|
|
4638
|
-
return new Promise((
|
|
4708
|
+
return new Promise((resolve6) => {
|
|
4639
4709
|
let timedOut = false;
|
|
4640
4710
|
const stdoutChunks = [];
|
|
4641
4711
|
const stderrChunks = [];
|
|
@@ -4647,17 +4717,17 @@ function runCommand(command, args, timeoutMs, cwd) {
|
|
|
4647
4717
|
signal: AbortSignal.timeout(timeoutMs)
|
|
4648
4718
|
});
|
|
4649
4719
|
} catch {
|
|
4650
|
-
|
|
4720
|
+
resolve6({ code: 127, stdout: "", stderr: "", timedOut: false });
|
|
4651
4721
|
return;
|
|
4652
4722
|
}
|
|
4653
4723
|
child.stdout?.on("data", (c) => stdoutChunks.push(c));
|
|
4654
4724
|
child.stderr?.on("data", (c) => stderrChunks.push(c));
|
|
4655
4725
|
child.on("error", () => {
|
|
4656
|
-
|
|
4726
|
+
resolve6({ code: 127, stdout: "", stderr: "", timedOut: false });
|
|
4657
4727
|
});
|
|
4658
4728
|
child.on("close", (code) => {
|
|
4659
4729
|
if (timedOut) return;
|
|
4660
|
-
|
|
4730
|
+
resolve6({
|
|
4661
4731
|
code,
|
|
4662
4732
|
stdout: Buffer.concat(stdoutChunks).toString("utf-8"),
|
|
4663
4733
|
stderr: Buffer.concat(stderrChunks).toString("utf-8"),
|
|
@@ -4666,7 +4736,7 @@ function runCommand(command, args, timeoutMs, cwd) {
|
|
|
4666
4736
|
});
|
|
4667
4737
|
child.on("abort", () => {
|
|
4668
4738
|
timedOut = true;
|
|
4669
|
-
|
|
4739
|
+
resolve6({ code: null, stdout: "", stderr: "", timedOut: true });
|
|
4670
4740
|
});
|
|
4671
4741
|
});
|
|
4672
4742
|
}
|
|
@@ -6169,6 +6239,31 @@ var DEFAULTS17 = {
|
|
|
6169
6239
|
timeoutMs: 5e3,
|
|
6170
6240
|
maxConsecutiveFailures: 5
|
|
6171
6241
|
};
|
|
6242
|
+
function isPrivateIPv4(hostname) {
|
|
6243
|
+
const parts = hostname.split(".").map((p) => Number(p));
|
|
6244
|
+
if (parts.length !== 4 || parts.some((p) => !Number.isInteger(p) || p < 0 || p > 255)) {
|
|
6245
|
+
return false;
|
|
6246
|
+
}
|
|
6247
|
+
const [a, b] = parts;
|
|
6248
|
+
return a === 0 || a === 10 || a === 127 || a === 169 && b === 254 || a === 172 && b >= 16 && b <= 31 || a === 192 && b === 168;
|
|
6249
|
+
}
|
|
6250
|
+
function isBlockedHostname(hostname) {
|
|
6251
|
+
const h = hostname.toLowerCase().replace(/^\[|\]$/g, "");
|
|
6252
|
+
return h === "localhost" || h.endsWith(".localhost") || h === "::1" || h === "0:0:0:0:0:0:0:1" || h.startsWith("fc") || h.startsWith("fd") || h.startsWith("fe80:") || isPrivateIPv4(h);
|
|
6253
|
+
}
|
|
6254
|
+
function normalizeWebhookUrl(raw) {
|
|
6255
|
+
if (typeof raw !== "string" || raw.trim().length === 0) return "";
|
|
6256
|
+
try {
|
|
6257
|
+
const url = new URL(raw.trim());
|
|
6258
|
+
if (url.protocol !== "https:" && url.protocol !== "http:") return "";
|
|
6259
|
+
if (url.username || url.password) return "";
|
|
6260
|
+
if (!url.hostname || isBlockedHostname(url.hostname)) return "";
|
|
6261
|
+
url.hash = "";
|
|
6262
|
+
return url.toString();
|
|
6263
|
+
} catch {
|
|
6264
|
+
return "";
|
|
6265
|
+
}
|
|
6266
|
+
}
|
|
6172
6267
|
function readConfig18(raw) {
|
|
6173
6268
|
if (!raw || typeof raw !== "object") return { ...DEFAULTS17, events: [...DEFAULTS17.events] };
|
|
6174
6269
|
const r = raw;
|
|
@@ -6180,7 +6275,7 @@ function readConfig18(raw) {
|
|
|
6180
6275
|
}
|
|
6181
6276
|
return {
|
|
6182
6277
|
enabled: r["enabled"] !== false,
|
|
6183
|
-
webhookUrl:
|
|
6278
|
+
webhookUrl: normalizeWebhookUrl(r["webhookUrl"]),
|
|
6184
6279
|
events: Array.isArray(r["events"]) ? r["events"].filter((e) => KNOWN_EVENTS.includes(e)) : [...DEFAULTS17.events],
|
|
6185
6280
|
headers,
|
|
6186
6281
|
timeoutMs: typeof r["timeoutMs"] === "number" && r["timeoutMs"] >= 500 && r["timeoutMs"] <= 6e4 ? r["timeoutMs"] : DEFAULTS17.timeoutMs,
|
|
@@ -7383,6 +7478,14 @@ var plugin26 = {
|
|
|
7383
7478
|
};
|
|
7384
7479
|
var secret_scanner_default = plugin26;
|
|
7385
7480
|
var API_VERSION11 = "^0.1.10";
|
|
7481
|
+
function resolveProjectRoot(rawCwd, root = process.cwd()) {
|
|
7482
|
+
if (typeof rawCwd !== "string" || rawCwd.length === 0) return root;
|
|
7483
|
+
const base = resolve(root);
|
|
7484
|
+
const resolved = isAbsolute(rawCwd) ? resolve(rawCwd) : resolve(base, rawCwd);
|
|
7485
|
+
const rel = relative(base, resolved);
|
|
7486
|
+
if (rel === "" || !rel.startsWith("..") && !isAbsolute(rel)) return resolved;
|
|
7487
|
+
return null;
|
|
7488
|
+
}
|
|
7386
7489
|
var state24 = {
|
|
7387
7490
|
/** Total invocations across all three tools this session. */
|
|
7388
7491
|
invocationCount: 0,
|
|
@@ -7433,7 +7536,11 @@ function collectManifests(root) {
|
|
|
7433
7536
|
function parseVersion(v) {
|
|
7434
7537
|
const m = v.match(/^v?(\d+)\.(\d+)\.(\d+)/);
|
|
7435
7538
|
if (!m) return [0, 0, 0];
|
|
7436
|
-
return [
|
|
7539
|
+
return [
|
|
7540
|
+
Number.parseInt(expectDefined(m[1]), 10),
|
|
7541
|
+
Number.parseInt(expectDefined(m[2]), 10),
|
|
7542
|
+
Number.parseInt(expectDefined(m[3]), 10)
|
|
7543
|
+
];
|
|
7437
7544
|
}
|
|
7438
7545
|
function bumpVersion(version, part) {
|
|
7439
7546
|
let [major, minor, patch] = parseVersion(version);
|
|
@@ -7569,6 +7676,11 @@ var plugin27 = {
|
|
|
7569
7676
|
defaultPart = readDefaultPart(next);
|
|
7570
7677
|
});
|
|
7571
7678
|
async function performBump(part, dryRun, cwd) {
|
|
7679
|
+
const safeCwd = resolveProjectRoot(cwd);
|
|
7680
|
+
if (!safeCwd) {
|
|
7681
|
+
return { ok: false, error: "cwd must stay within the current project directory" };
|
|
7682
|
+
}
|
|
7683
|
+
cwd = safeCwd;
|
|
7572
7684
|
const pkg = getPackageJson(cwd);
|
|
7573
7685
|
if (!pkg) {
|
|
7574
7686
|
return { ok: false, error: "No package.json found" };
|
|
@@ -7676,7 +7788,12 @@ var plugin27 = {
|
|
|
7676
7788
|
properties: {
|
|
7677
7789
|
cwd: { type: "string", description: "Working directory (defaults to project root)" },
|
|
7678
7790
|
dry_run: { type: "boolean", default: false },
|
|
7679
|
-
part: {
|
|
7791
|
+
part: {
|
|
7792
|
+
type: "string",
|
|
7793
|
+
enum: ["major", "minor", "patch", "auto"],
|
|
7794
|
+
default: defaultPart,
|
|
7795
|
+
description: "Version part to bump. Omitted \u2192 the configured default (/settings semver-part, factory default: patch). Use auto to infer from commits."
|
|
7796
|
+
}
|
|
7680
7797
|
}
|
|
7681
7798
|
},
|
|
7682
7799
|
permission: "confirm",
|
|
@@ -7740,7 +7857,11 @@ var plugin27 = {
|
|
|
7740
7857
|
if (mode !== "patch" && mode !== "minor" && mode !== "major" && mode !== "auto") {
|
|
7741
7858
|
return { message: `Unknown mode "${mode}". Use status, patch, minor, major or auto.` };
|
|
7742
7859
|
}
|
|
7743
|
-
const
|
|
7860
|
+
const safeCwd = resolveProjectRoot(cwd);
|
|
7861
|
+
if (!safeCwd) {
|
|
7862
|
+
return { message: "cwd must stay within the current project directory" };
|
|
7863
|
+
}
|
|
7864
|
+
const result = await performBump(mode, dry, safeCwd);
|
|
7744
7865
|
return { message: String(result["message"] ?? result["error"] ?? JSON.stringify(result)) };
|
|
7745
7866
|
}
|
|
7746
7867
|
});
|
|
@@ -7758,16 +7879,20 @@ var plugin27 = {
|
|
|
7758
7879
|
async execute(input) {
|
|
7759
7880
|
state24.invocationCount += 1;
|
|
7760
7881
|
state24.perTool["semver_current"] = (state24.perTool["semver_current"] ?? 0) + 1;
|
|
7761
|
-
const
|
|
7762
|
-
const
|
|
7882
|
+
const cwdInput = input["cwd"];
|
|
7883
|
+
const safeCwd = resolveProjectRoot(cwdInput);
|
|
7884
|
+
if (!safeCwd) {
|
|
7885
|
+
return { ok: false, error: "cwd must stay within the current project directory" };
|
|
7886
|
+
}
|
|
7887
|
+
const pkg = getPackageJson(safeCwd);
|
|
7763
7888
|
const currentVersion = pkg?.version ?? "unknown";
|
|
7764
7889
|
let latestTag = null;
|
|
7765
7890
|
let commitsSinceTag = 0;
|
|
7766
7891
|
try {
|
|
7767
|
-
const tagsOutput = runGit2(["describe", "--tags", "--abbrev=0"],
|
|
7892
|
+
const tagsOutput = runGit2(["describe", "--tags", "--abbrev=0"], safeCwd);
|
|
7768
7893
|
latestTag = tagsOutput || null;
|
|
7769
7894
|
if (latestTag) {
|
|
7770
|
-
const countOutput = runGit2(["rev-list", "--count", `${latestTag}..HEAD`],
|
|
7895
|
+
const countOutput = runGit2(["rev-list", "--count", `${latestTag}..HEAD`], safeCwd);
|
|
7771
7896
|
commitsSinceTag = Number.parseInt(countOutput, 10) || 0;
|
|
7772
7897
|
}
|
|
7773
7898
|
} catch {
|
|
@@ -7802,11 +7927,15 @@ var plugin27 = {
|
|
|
7802
7927
|
const from = input["from"];
|
|
7803
7928
|
const to = input["to"] ?? "HEAD";
|
|
7804
7929
|
const cwd = input["cwd"];
|
|
7930
|
+
const safeCwd = resolveProjectRoot(cwd);
|
|
7931
|
+
if (!safeCwd) {
|
|
7932
|
+
return { ok: false, error: "cwd must stay within the current project directory" };
|
|
7933
|
+
}
|
|
7805
7934
|
const format = input["format"] ?? "markdown";
|
|
7806
7935
|
const range = from ? `${from}..${to}` : to;
|
|
7807
7936
|
let commits;
|
|
7808
7937
|
try {
|
|
7809
|
-
const output = runGit2(["log", range === to ? "-30" : range, "--format=%H %s"],
|
|
7938
|
+
const output = runGit2(["log", range === to ? "-30" : range, "--format=%H %s"], safeCwd);
|
|
7810
7939
|
commits = output.split("\n").filter(Boolean).map((line) => {
|
|
7811
7940
|
const spaceIdx = line.indexOf(" ");
|
|
7812
7941
|
const hash = line.slice(0, spaceIdx);
|
|
@@ -8530,27 +8659,21 @@ function expandTemplate(template, variables) {
|
|
|
8530
8659
|
return result;
|
|
8531
8660
|
}
|
|
8532
8661
|
function expandConditionals(template, variables) {
|
|
8533
|
-
return template.replace(
|
|
8534
|
-
|
|
8535
|
-
|
|
8536
|
-
|
|
8537
|
-
return val !== void 0 && val !== "" && val !== "false" && val !== "0" ? content : "";
|
|
8538
|
-
}
|
|
8539
|
-
);
|
|
8662
|
+
return template.replace(/\{\{#if\s+(\w+)\}\}([\s\S]*?)\{\{\/if\}\}/g, (_, key, content) => {
|
|
8663
|
+
const val = variables[key];
|
|
8664
|
+
return val !== void 0 && val !== "" && val !== "false" && val !== "0" ? content : "";
|
|
8665
|
+
});
|
|
8540
8666
|
}
|
|
8541
8667
|
function expandLoops(template, variables) {
|
|
8542
|
-
return template.replace(
|
|
8543
|
-
|
|
8544
|
-
(
|
|
8545
|
-
|
|
8546
|
-
|
|
8547
|
-
|
|
8548
|
-
|
|
8549
|
-
|
|
8550
|
-
|
|
8551
|
-
return expandTemplate(content, variables);
|
|
8552
|
-
}
|
|
8553
|
-
);
|
|
8668
|
+
return template.replace(/\{\{#each\s+(\w+)\}\}([\s\S]*?)\{\{\/each\}\}/g, (_, key, content) => {
|
|
8669
|
+
const val = variables[key];
|
|
8670
|
+
if (!val) return "";
|
|
8671
|
+
if (typeof val === "string" && val.includes(",")) {
|
|
8672
|
+
const items = val.split(",").map((s) => s.trim());
|
|
8673
|
+
return items.map((item) => expandTemplate(content, { ...variables, [key]: item })).join("\n");
|
|
8674
|
+
}
|
|
8675
|
+
return expandTemplate(content, variables);
|
|
8676
|
+
});
|
|
8554
8677
|
}
|
|
8555
8678
|
function renderTemplate(template, variables, escapeHtml = true) {
|
|
8556
8679
|
let result = template;
|
|
@@ -8569,6 +8692,12 @@ function renderTemplateRaw(template, variables) {
|
|
|
8569
8692
|
result = expandTemplate(result, variables);
|
|
8570
8693
|
return result;
|
|
8571
8694
|
}
|
|
8695
|
+
function validateRelativeTemplatePath(field, value) {
|
|
8696
|
+
if (isAbsolute(value) || value.split(/[\\/]+/).includes("..")) {
|
|
8697
|
+
return `${field} must be a relative path without ".." components`;
|
|
8698
|
+
}
|
|
8699
|
+
return null;
|
|
8700
|
+
}
|
|
8572
8701
|
var plugin30 = {
|
|
8573
8702
|
name: "template-engine",
|
|
8574
8703
|
version: "0.1.0",
|
|
@@ -8597,13 +8726,19 @@ var plugin30 = {
|
|
|
8597
8726
|
inputSchema: {
|
|
8598
8727
|
type: "object",
|
|
8599
8728
|
properties: {
|
|
8600
|
-
template: {
|
|
8729
|
+
template: {
|
|
8730
|
+
type: "string",
|
|
8731
|
+
description: "Template string with {{variable}} placeholders"
|
|
8732
|
+
},
|
|
8601
8733
|
variables: {
|
|
8602
8734
|
type: "object",
|
|
8603
8735
|
description: "Variables to substitute into the template",
|
|
8604
8736
|
additionalProperties: { type: "string" }
|
|
8605
8737
|
},
|
|
8606
|
-
output_path: {
|
|
8738
|
+
output_path: {
|
|
8739
|
+
type: "string",
|
|
8740
|
+
description: "Optional path to write the expanded result"
|
|
8741
|
+
},
|
|
8607
8742
|
raw: { type: "boolean", default: false, description: "Disable HTML auto-escaping" }
|
|
8608
8743
|
},
|
|
8609
8744
|
required: ["template", "variables"]
|
|
@@ -8629,9 +8764,8 @@ var plugin30 = {
|
|
|
8629
8764
|
return { ok: false, error: String(err) };
|
|
8630
8765
|
}
|
|
8631
8766
|
if (output_path) {
|
|
8632
|
-
|
|
8633
|
-
|
|
8634
|
-
}
|
|
8767
|
+
const pathError = validateRelativeTemplatePath("output_path", output_path);
|
|
8768
|
+
if (pathError) return { ok: false, error: pathError };
|
|
8635
8769
|
const { writeFileSync: writeFileSync6 } = await import('fs');
|
|
8636
8770
|
writeFileSync6(output_path, result, "utf-8");
|
|
8637
8771
|
return {
|
|
@@ -8661,7 +8795,10 @@ var plugin30 = {
|
|
|
8661
8795
|
description: "Variables to substitute",
|
|
8662
8796
|
additionalProperties: { type: "string" }
|
|
8663
8797
|
},
|
|
8664
|
-
output_path: {
|
|
8798
|
+
output_path: {
|
|
8799
|
+
type: "string",
|
|
8800
|
+
description: "Optional path to write the rendered result"
|
|
8801
|
+
},
|
|
8665
8802
|
raw: { type: "boolean", default: false }
|
|
8666
8803
|
},
|
|
8667
8804
|
required: ["template_path", "variables"]
|
|
@@ -8676,6 +8813,8 @@ var plugin30 = {
|
|
|
8676
8813
|
if (!template_path || typeof template_path !== "string") {
|
|
8677
8814
|
return { ok: false, error: "template_path is required and must be a string" };
|
|
8678
8815
|
}
|
|
8816
|
+
const templatePathError = validateRelativeTemplatePath("template_path", template_path);
|
|
8817
|
+
if (templatePathError) return { ok: false, error: templatePathError };
|
|
8679
8818
|
if (!variables || typeof variables !== "object") {
|
|
8680
8819
|
return { ok: false, error: "variables is required and must be an object" };
|
|
8681
8820
|
}
|
|
@@ -8693,9 +8832,8 @@ var plugin30 = {
|
|
|
8693
8832
|
return { ok: false, error: `Template rendering failed: ${err}` };
|
|
8694
8833
|
}
|
|
8695
8834
|
if (output_path) {
|
|
8696
|
-
|
|
8697
|
-
|
|
8698
|
-
}
|
|
8835
|
+
const pathError = validateRelativeTemplatePath("output_path", output_path);
|
|
8836
|
+
if (pathError) return { ok: false, error: pathError };
|
|
8699
8837
|
const { writeFileSync: writeFileSync6 } = await import('fs');
|
|
8700
8838
|
writeFileSync6(output_path, result, "utf-8");
|
|
8701
8839
|
return {
|
|
@@ -8720,8 +8858,14 @@ var plugin30 = {
|
|
|
8720
8858
|
type: "object",
|
|
8721
8859
|
properties: {
|
|
8722
8860
|
name: { type: "string", description: "Unique name for this template" },
|
|
8723
|
-
content: {
|
|
8724
|
-
|
|
8861
|
+
content: {
|
|
8862
|
+
type: "string",
|
|
8863
|
+
description: "Template content with {{variable}} placeholders"
|
|
8864
|
+
},
|
|
8865
|
+
description: {
|
|
8866
|
+
type: "string",
|
|
8867
|
+
description: "Optional description of what this template is for"
|
|
8868
|
+
}
|
|
8725
8869
|
},
|
|
8726
8870
|
required: ["name", "content"]
|
|
8727
8871
|
},
|
|
@@ -10123,7 +10267,7 @@ function estimateRequestTokens(request, charsPerToken) {
|
|
|
10123
10267
|
walk(request["messages"]);
|
|
10124
10268
|
return Math.ceil(chars / charsPerToken);
|
|
10125
10269
|
}
|
|
10126
|
-
var sleep = (ms) => new Promise((
|
|
10270
|
+
var sleep = (ms) => new Promise((resolve6) => setTimeout(resolve6, ms));
|
|
10127
10271
|
var plugin35 = {
|
|
10128
10272
|
name: "token-throttle",
|
|
10129
10273
|
version: "0.1.0",
|
|
@@ -10380,9 +10524,26 @@ function isWrappedAsLinkOrCode(line, name) {
|
|
|
10380
10524
|
function escapeRegExp(s) {
|
|
10381
10525
|
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
10382
10526
|
}
|
|
10527
|
+
function mapMarkdownFences(lines) {
|
|
10528
|
+
const fenced = new Array(lines.length).fill(false);
|
|
10529
|
+
let inFence = false;
|
|
10530
|
+
for (let i = 0; i < lines.length; i++) {
|
|
10531
|
+
const line = lines[i];
|
|
10532
|
+
if (/^\s*```/.test(line)) {
|
|
10533
|
+
fenced[i] = true;
|
|
10534
|
+
inFence = !inFence;
|
|
10535
|
+
continue;
|
|
10536
|
+
}
|
|
10537
|
+
fenced[i] = inFence;
|
|
10538
|
+
}
|
|
10539
|
+
return fenced;
|
|
10540
|
+
}
|
|
10383
10541
|
function findUnlinkedReferences(lines, names) {
|
|
10384
10542
|
const found = /* @__PURE__ */ new Map();
|
|
10385
|
-
|
|
10543
|
+
const fencedLines = mapMarkdownFences(lines);
|
|
10544
|
+
for (let i = 0; i < lines.length; i++) {
|
|
10545
|
+
if (fencedLines[i]) continue;
|
|
10546
|
+
const line = lines[i];
|
|
10386
10547
|
if (line.length === 0) continue;
|
|
10387
10548
|
for (const name of names) {
|
|
10388
10549
|
const re = new RegExp(`(^|[^\\w-])${escapeRegExp(name)}(?![\\w-])`, "i");
|
|
@@ -10395,8 +10556,10 @@ function findUnlinkedReferences(lines, names) {
|
|
|
10395
10556
|
}
|
|
10396
10557
|
function wrapUnlinkedReferences(content) {
|
|
10397
10558
|
const lines = content.split("\n");
|
|
10559
|
+
const fencedLines = mapMarkdownFences(lines);
|
|
10398
10560
|
let changed = false;
|
|
10399
10561
|
for (let i = 0; i < lines.length; i++) {
|
|
10562
|
+
if (fencedLines[i]) continue;
|
|
10400
10563
|
const line = lines[i];
|
|
10401
10564
|
if (line.length === 0) continue;
|
|
10402
10565
|
const newLine = wrapLineReferences(line);
|
|
@@ -10512,7 +10675,9 @@ var plugin36 = {
|
|
|
10512
10675
|
state32.unlinkedCount += 1;
|
|
10513
10676
|
const limited = unlinked.slice(0, cfg.maxReferences);
|
|
10514
10677
|
const overflow = unlinked.length - limited.length;
|
|
10515
|
-
const lines = limited.map(
|
|
10678
|
+
const lines = limited.map(
|
|
10679
|
+
(name) => `- \`${name}\` \u2192 \`[${name}](${PLUGIN_CATALOG.get(name) ?? `./src/${name}`})\``
|
|
10680
|
+
).join("\n");
|
|
10516
10681
|
const overflowNote = overflow > 0 ? `
|
|
10517
10682
|
- \u2026and ${overflow} more` : "";
|
|
10518
10683
|
return {
|