@wrongstack/plugins 0.306.4 → 0.307.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/index.js CHANGED
@@ -9343,7 +9343,7 @@ var DEFAULT_ARTIFACT_PATTERNS = Object.freeze([
9343
9343
  "*.env.*"
9344
9344
  ]);
9345
9345
  function toForwardSlashes(p) {
9346
- return sep3 === "/" ? p : p.split(sep3).join("/");
9346
+ return p.split(sep3).join("/").replaceAll("\\", "/");
9347
9347
  }
9348
9348
  function globToSource(glob) {
9349
9349
  return glob.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, "[^/]*").replace(/\?/g, "[^/]");
@@ -13544,6 +13544,19 @@ var plugin38 = {
13544
13544
  }
13545
13545
  }
13546
13546
  state35.eventUnsubscribers = [];
13547
+ if (api.tools?.unregister) {
13548
+ try {
13549
+ api.tools.unregister("notify_send");
13550
+ api.tools.unregister("notify_hub_status");
13551
+ } catch {
13552
+ }
13553
+ }
13554
+ if (api.notifier?.unregisterChannel) {
13555
+ try {
13556
+ api.notifier.unregisterChannel("webhook");
13557
+ } catch {
13558
+ }
13559
+ }
13547
13560
  const channelCounters = state35.channel?.counters();
13548
13561
  state35.channel = null;
13549
13562
  state35.lastDelivery = null;
@@ -13565,40 +13578,7 @@ var plugin38 = {
13565
13578
  };
13566
13579
  var notify_hub_default = plugin38;
13567
13580
 
13568
- // src/path-guard/index.ts
13569
- function createState() {
13570
- return { invocations: 0, blocks: 0, warns: 0, lastBlock: null, hookUnregister: null };
13571
- }
13572
- var states = /* @__PURE__ */ new WeakMap();
13573
- var latestState = createState();
13574
- var DEFAULT_PROTECT = [
13575
- "pnpm-lock.yaml",
13576
- "package-lock.json",
13577
- "yarn.lock",
13578
- "bun.lockb",
13579
- "Cargo.lock",
13580
- "poetry.lock",
13581
- ".env",
13582
- ".env.*",
13583
- ".git/**",
13584
- "**/migrations/**"
13585
- ];
13586
- var DEFAULTS33 = {
13587
- enabled: true,
13588
- mode: "block",
13589
- protect: DEFAULT_PROTECT,
13590
- allow: []
13591
- };
13592
- function readConfig34(raw) {
13593
- if (!raw || typeof raw !== "object") return { ...DEFAULTS33, protect: [...DEFAULT_PROTECT] };
13594
- const r = raw;
13595
- return {
13596
- enabled: r["enabled"] !== false,
13597
- mode: r["mode"] === "warn" ? "warn" : "block",
13598
- protect: Array.isArray(r["protect"]) ? r["protect"].filter((p) => typeof p === "string" && p.length > 0) : [...DEFAULT_PROTECT],
13599
- allow: Array.isArray(r["allow"]) ? r["allow"].filter((p) => typeof p === "string" && p.length > 0) : []
13600
- };
13601
- }
13581
+ // src/path-guard/glob.ts
13602
13582
  function compilePathGlob(pattern) {
13603
13583
  const normalized = pattern.replace(/\\/g, "/");
13604
13584
  let source = "";
@@ -13764,337 +13744,101 @@ function targetFullyAllowed(target, allowTexts, allowRes) {
13764
13744
  return allowNormalized.endsWith("/**") && allowRe?.test(`${normalized}/.path-guard-probe`);
13765
13745
  });
13766
13746
  }
13767
- var WRITE_CAPABILITIES = /* @__PURE__ */ new Set(["fs.write", "fs.write.outside-project"]);
13768
- var DISK_MUTATING_CAPABILITIES = /* @__PURE__ */ new Set(["package.install"]);
13769
- var SHELL_CAPABILITIES = /* @__PURE__ */ new Set(["shell.arbitrary", "shell.restricted", "shell.exec"]);
13770
- var LEGACY_SHELL_TOOLS = /* @__PURE__ */ new Set(["bash", "shell", "exec"]);
13771
- var LEGACY_WRITE_TOOLS = /* @__PURE__ */ new Set([
13772
- "write",
13773
- "edit",
13774
- "patch",
13775
- "scaffold",
13776
- "format",
13777
- "replace",
13778
- "design",
13779
- "install"
13747
+
13748
+ // src/path-guard/shell-targets.ts
13749
+ var VALUE_TAKING_GIT_OPTIONS = /* @__PURE__ */ new Set([
13750
+ "-C",
13751
+ "-c",
13752
+ "--git-dir",
13753
+ "--work-tree",
13754
+ "--namespace",
13755
+ "--super-prefix",
13756
+ "--exec-path",
13757
+ "--config-env",
13758
+ "--attr-source"
13780
13759
  ]);
13781
- function writesToDisk(input) {
13782
- const capabilities = input.toolCapabilities;
13783
- const capabilitiesUndeclared = !capabilities || capabilities.length === 0;
13784
- const hasWriteCap = capabilities?.some((capability) => WRITE_CAPABILITIES.has(capability)) ?? false;
13785
- const hasDiskMutatingCap = capabilities?.some((capability) => DISK_MUTATING_CAPABILITIES.has(capability)) ?? false;
13786
- const hasCommandString = typeof input.toolInput?.["command"] === "string";
13787
- if (hasCommandString && executesShell(input) && !hasWriteCap && !hasDiskMutatingCap && !(capabilitiesUndeclared && LEGACY_WRITE_TOOLS.has(input.toolName ?? "")))
13788
- return false;
13789
- if (!capabilitiesUndeclared) {
13790
- if (hasWriteCap || hasDiskMutatingCap) return true;
13791
- if (capabilities.includes("mcp.proxy")) {
13792
- const segmentedName = (input.toolName ?? "").replace(/([a-z0-9])([A-Z])/g, "$1_$2").replace(/[^a-z0-9]+/gi, "_").toLowerCase();
13793
- const filesystemWriter = /(?:^|_)filesystem(?:_|$)/.test(segmentedName) && /(?:^|_)(?:add|append|copy|create|delete|edit|mkdir|move|remove|rename|write)(?:_|$)/.test(
13794
- segmentedName
13795
- );
13796
- const toolInput = input.toolInput ?? {};
13797
- const hasKnownPath = [...PATH_FIELDS, ...PATH_LIST_FIELDS].some(
13798
- (field) => toolInput[field] !== void 0
13799
- );
13800
- if (filesystemWriter && hasKnownPath) return true;
13760
+ var MAX_LAUNCHER_TOKENS = 256;
13761
+ var MAX_LAUNCHER_LENGTH = 64 * 1024;
13762
+ function boundedShellTokens(raw) {
13763
+ const tokens = [];
13764
+ let token = "";
13765
+ let tokenStart = -1;
13766
+ let quote = null;
13767
+ const limit = Math.min(raw.length, MAX_LAUNCHER_LENGTH);
13768
+ for (let index = 0; index < limit && tokens.length < MAX_LAUNCHER_TOKENS; index += 1) {
13769
+ const char = raw[index] ?? "";
13770
+ if (tokenStart < 0 && !/\s/.test(char)) tokenStart = index;
13771
+ if (char === "\\" && quote !== "'" && index + 1 < limit) {
13772
+ const escaped = raw[index + 1] ?? "";
13773
+ const shellEscaped = quote === '"' ? /[$\x60"\\\r\n]/.test(escaped) : /[\s'"\\;&|()`]/.test(escaped);
13774
+ token += shellEscaped ? escaped : `\\${escaped}`;
13775
+ index += 1;
13776
+ continue;
13801
13777
  }
13802
- return false;
13778
+ if (char === "'" || char === '"') {
13779
+ if (quote === char) quote = null;
13780
+ else if (quote === null) quote = char;
13781
+ else token += char;
13782
+ continue;
13783
+ }
13784
+ if (quote === null && /\s/.test(char)) {
13785
+ if (tokenStart >= 0) tokens.push({ value: token, start: tokenStart, end: index });
13786
+ token = "";
13787
+ tokenStart = -1;
13788
+ continue;
13789
+ }
13790
+ token += char;
13803
13791
  }
13804
- if (input.toolMutating !== void 0) return input.toolMutating;
13805
- return LEGACY_WRITE_TOOLS.has(input.toolName ?? "");
13806
- }
13807
- function executesShell(input) {
13808
- const toolName = input.toolName ?? "";
13809
- if (LEGACY_SHELL_TOOLS.has(toolName)) return true;
13810
- if (toolName === "git") return false;
13811
- const segmentedName = toolName.replace(/([a-z0-9])([A-Z])/g, "$1_$2");
13812
- const isCommandStringRunner = /(?:^|[_.-])(?:command|exec|execute|shell|terminal)(?:$|[_.-])/i.test(segmentedName);
13813
- const hasShellCapability = input.toolCapabilities?.some((capability) => SHELL_CAPABILITIES.has(capability)) ?? false;
13814
- const hasCommandString = typeof input.toolInput?.["command"] === "string";
13815
- return isCommandStringRunner && hasShellCapability || hasCommandString && input.toolMutating === true;
13816
- }
13817
- var PATH_FIELDS = [
13818
- "path",
13819
- "file_path",
13820
- "filePath",
13821
- "target",
13822
- "target_path",
13823
- "targetPath",
13824
- "destination",
13825
- "dest",
13826
- "output_path",
13827
- "outputPath",
13828
- "out",
13829
- "file"
13830
- ];
13831
- var PATH_LIST_FIELDS = ["files", "paths"];
13832
- var SOURCE_PATH_FIELDS = [
13833
- "source",
13834
- "from",
13835
- "src",
13836
- "input_path",
13837
- "inputPath",
13838
- "old_path",
13839
- "oldPath"
13840
- ];
13841
- var MOVE_TOOL_NAME = /(?:^|[_.-])(move|rename)(?:$|[_.-])/i;
13842
- var DIRECTORY_DELETE_TOOL_NAME = /(?:^|[_.-])(?:rmdir|(?:delete|remove)[_.-](?:dir|directory))(?:$|[_.-])/i;
13843
- function segmentedToolName(toolName) {
13844
- return toolName.replace(/([a-z0-9])([A-Z])/g, "$1_$2");
13845
- }
13846
- function isMoveToolName(toolName) {
13847
- return MOVE_TOOL_NAME.test(segmentedToolName(toolName));
13848
- }
13849
- function isDirectoryDeleteToolName(toolName) {
13850
- return DIRECTORY_DELETE_TOOL_NAME.test(segmentedToolName(toolName));
13851
- }
13852
- function cleanPatchPath(raw) {
13853
- const trimmed = raw.trim();
13854
- if (!trimmed || trimmed === "/dev/null") return null;
13855
- const quoted = /^"([^"]+)"(?:\s|$)/.exec(trimmed)?.[1];
13856
- const withoutTimestamp = quoted ?? trimmed.replace(/(?:\t|\s+)\d{4}-\d{2}-\d{2}(?:\s.*)?$/, "").trim();
13857
- if (!withoutTimestamp || withoutTimestamp === "/dev/null") return null;
13858
- return withoutTimestamp.replace(/^[ab]\//, "");
13859
- }
13860
- function pathsFromPatch(patch) {
13861
- const paths = [];
13862
- const lines = patch.split(/\r?\n/);
13863
- for (let index = 0; index + 2 < lines.length; index += 1) {
13864
- const oldHeader = lines[index];
13865
- const newHeader = lines[index + 1];
13866
- const hunkHeader = lines[index + 2];
13867
- if (!oldHeader?.startsWith("--- ") || !newHeader?.startsWith("+++ ")) continue;
13868
- if (!hunkHeader?.startsWith("@@ ")) continue;
13869
- const oldPath = cleanPatchPath(oldHeader.slice(4));
13870
- const newPath = cleanPatchPath(newHeader.slice(4));
13871
- if (oldPath) paths.push(oldPath);
13872
- if (newPath) paths.push(newPath);
13792
+ if (tokenStart >= 0 && tokens.length < MAX_LAUNCHER_TOKENS) {
13793
+ tokens.push({ value: token, start: tokenStart, end: limit });
13873
13794
  }
13874
- return paths;
13795
+ return tokens;
13875
13796
  }
13876
- function appendTarget(targets, value, kind, base) {
13877
- if (typeof value !== "string" || value.length === 0) return;
13878
- targets.push({
13879
- path: resolveTargetPath(value, base),
13880
- kind
13881
- });
13797
+ function shellTokens(raw) {
13798
+ return boundedShellTokens(raw).map((token) => token.value);
13882
13799
  }
13883
- function appendTargetList(targets, value, directoryCapable, base) {
13884
- const appendPath = (path) => {
13885
- targets.push({
13886
- path: resolveTargetPath(path, base),
13887
- kind: isUnresolvedPathScope(path) || isRootPathScope(path) || directoryCapable && isDirectoryAmbiguousPath(path) ? "scope" : "file"
13888
- });
13889
- };
13890
- if (typeof value === "string") {
13891
- const paths = value.split(",").map((item) => item.trim()).filter((path) => path.length > 0);
13892
- for (const path of paths) appendPath(path);
13893
- return;
13800
+ function gitInvocationArguments(command) {
13801
+ const argumentsList = [];
13802
+ const quotedIndexes = new Uint8Array(command.length);
13803
+ let activeQuote = null;
13804
+ for (let index = 0; index < command.length; index += 1) {
13805
+ const char = command[index];
13806
+ if (isQuoteBoundary(command, index, activeQuote)) {
13807
+ quotedIndexes[index] = 1;
13808
+ activeQuote = activeQuote === char ? null : char === "'" ? "'" : '"';
13809
+ } else if (activeQuote !== null) {
13810
+ quotedIndexes[index] = 1;
13811
+ }
13894
13812
  }
13895
- if (!Array.isArray(value)) return;
13896
- for (const item of value) {
13897
- if (typeof item !== "string") continue;
13898
- const path = item.trim();
13899
- if (path.length > 0) appendPath(path);
13813
+ const gitStart = /(?:^|[;&|\r\n]\s*|\(\s*|`\s*)git\b/gi;
13814
+ let match = gitStart.exec(command);
13815
+ while (match !== null) {
13816
+ const gitOffset = match[0].toLowerCase().lastIndexOf("git");
13817
+ if (gitOffset < 0 || quotedIndexes[match.index + gitOffset] === 1) {
13818
+ match = gitStart.exec(command);
13819
+ continue;
13820
+ }
13821
+ const start = gitStart.lastIndex;
13822
+ let quote = null;
13823
+ let end = start;
13824
+ for (; end < command.length; end += 1) {
13825
+ const char = command[end] ?? "";
13826
+ if (char === "\\" && quote !== "'") {
13827
+ end += 1;
13828
+ continue;
13829
+ }
13830
+ if (char === "'" || char === '"') {
13831
+ if (quote === char) quote = null;
13832
+ else if (quote === null) quote = char;
13833
+ continue;
13834
+ }
13835
+ if (quote === null && /[;&|)`\r\n]/.test(char)) break;
13836
+ }
13837
+ argumentsList.push(command.slice(start, end));
13838
+ gitStart.lastIndex = Math.max(end, start);
13839
+ match = gitStart.exec(command);
13900
13840
  }
13901
- }
13902
- function isReadOnlyInvocation(toolName, toolInput) {
13903
- if (toolName === "git") {
13904
- const command = toolInput["command"];
13905
- if (command === "status" || command === "log" || command === "diff" || command === "fetch" || command === "commit" || command === "branch" || command === "push") {
13906
- return true;
13907
- }
13908
- return command === "worktree" && toolInput["worktreeAction"] === "list";
13909
- }
13910
- if (toolName === "design") {
13911
- const action = toolInput["action"] ?? "list";
13912
- return action === "list" || action === "foundations" || action === "verify";
13913
- }
13914
- if (toolName === "format") return toolInput["check"] === true;
13915
- if (toolName === "patch" || toolName === "scaffold") return toolInput["dry_run"] === true;
13916
- if (toolName === "replace") return toolInput["dry_run"] !== false;
13917
- return false;
13918
- }
13919
- function pathsFromToolInput(toolInput, toolName, invocationCwd) {
13920
- const targets = [];
13921
- const effectiveCwd = effectiveToolCwd(toolInput["cwd"], invocationCwd);
13922
- const directoryDelete = isDirectoryDeleteToolName(toolName);
13923
- for (const field of PATH_FIELDS) {
13924
- const value = toolInput[field];
13925
- appendTarget(
13926
- targets,
13927
- value,
13928
- directoryDelete ? "deletion-scope" : typeof value === "string" && isUnresolvedPathScope(value) ? "scope" : "file",
13929
- effectiveCwd
13930
- );
13931
- }
13932
- const directoryCapableLists = toolName === "format" || toolName === "replace";
13933
- for (const field of PATH_LIST_FIELDS) {
13934
- appendTargetList(
13935
- targets,
13936
- toolInput[field],
13937
- directoryCapableLists && field === "files",
13938
- effectiveCwd
13939
- );
13940
- }
13941
- if (toolName === "install") {
13942
- appendTarget(targets, effectiveCwd ?? invocationCwd ?? ".", "scope");
13943
- }
13944
- if (toolName === "git") {
13945
- const command = toolInput["command"];
13946
- if (command === "worktree" && (toolInput["worktreeAction"] === "add" || toolInput["worktreeAction"] === "remove")) {
13947
- appendTarget(targets, toolInput["worktreePath"] ?? toolInput["worktree_path"], "file");
13948
- }
13949
- }
13950
- if (toolName === "scaffold") {
13951
- const cwdValue = toolInput["cwd"];
13952
- const cwd = typeof cwdValue === "string" && cwdValue.length > 0 ? cwdValue : invocationCwd ?? ".";
13953
- const base = normalizePath2(cwd).replace(/\/$/, "");
13954
- const name = typeof toolInput["name"] === "string" ? toolInput["name"] : "";
13955
- const template = toolInput["template"];
13956
- const filesByTemplate = {
13957
- "npm-package": ["package.json", "tsconfig.json", "src/index.ts", "src/index.test.ts"],
13958
- "cli-tool": ["package.json", "src/index.ts"],
13959
- "react-component": name ? [`${name}.tsx`, `${name}.test.tsx`] : []
13960
- };
13961
- for (const file of typeof template === "string" ? filesByTemplate[template] ?? [] : []) {
13962
- targets.push({ path: `${base}/${file}`, kind: "file" });
13963
- }
13964
- targets.push({ path: base, kind: "scope" });
13965
- }
13966
- if (isMoveToolName(toolName)) {
13967
- for (const field of SOURCE_PATH_FIELDS) {
13968
- appendTarget(targets, toolInput[field], "deletion-scope", effectiveCwd);
13969
- }
13970
- }
13971
- const patch = toolInput["patch"];
13972
- if (typeof patch === "string") {
13973
- const patchTargets = pathsFromPatch(patch);
13974
- const directory = toolInput["directory"];
13975
- const baseValue = typeof directory === "string" && directory.length > 0 ? directory : invocationCwd;
13976
- if (baseValue) {
13977
- const base = normalizePath2(baseValue).replace(/\/$/, "");
13978
- targets.push(
13979
- ...patchTargets.map((target) => ({ path: `${base}/${target}`, kind: "file" }))
13980
- );
13981
- } else {
13982
- targets.push(...patchTargets.map((path) => ({ path, kind: "file" })));
13983
- }
13984
- }
13985
- if (targets.length === 0) {
13986
- const cwd = effectiveCwd ?? ".";
13987
- targets.push({ path: normalizePath2(cwd).replace(/\/$/, "") || ".", kind: "scope" });
13988
- }
13989
- const seen = /* @__PURE__ */ new Set();
13990
- return targets.map((target) => ({
13991
- ...target,
13992
- path: relativeToInvocationCwd(target.path, invocationCwd)
13993
- })).filter((target) => {
13994
- const key = `${target.kind}:${target.path}`;
13995
- if (seen.has(key)) return false;
13996
- seen.add(key);
13997
- return true;
13998
- });
13999
- }
14000
- function operationLabel(toolName) {
14001
- if (toolName === "write") return "write";
14002
- if (toolName === "edit") return "edit";
14003
- return `write via "${toolName}"`;
14004
- }
14005
- var VALUE_TAKING_GIT_OPTIONS = /* @__PURE__ */ new Set([
14006
- "-C",
14007
- "-c",
14008
- "--git-dir",
14009
- "--work-tree",
14010
- "--namespace",
14011
- "--super-prefix",
14012
- "--exec-path",
14013
- "--config-env",
14014
- "--attr-source"
14015
- ]);
14016
- var MAX_LAUNCHER_TOKENS = 256;
14017
- var MAX_LAUNCHER_LENGTH = 64 * 1024;
14018
- function boundedShellTokens(raw) {
14019
- const tokens = [];
14020
- let token = "";
14021
- let tokenStart = -1;
14022
- let quote = null;
14023
- const limit = Math.min(raw.length, MAX_LAUNCHER_LENGTH);
14024
- for (let index = 0; index < limit && tokens.length < MAX_LAUNCHER_TOKENS; index += 1) {
14025
- const char = raw[index] ?? "";
14026
- if (tokenStart < 0 && !/\s/.test(char)) tokenStart = index;
14027
- if (char === "\\" && quote !== "'" && index + 1 < limit) {
14028
- const escaped = raw[index + 1] ?? "";
14029
- const shellEscaped = quote === '"' ? /[$\x60"\\\r\n]/.test(escaped) : /[\s'"\\;&|()`]/.test(escaped);
14030
- token += shellEscaped ? escaped : `\\${escaped}`;
14031
- index += 1;
14032
- continue;
14033
- }
14034
- if (char === "'" || char === '"') {
14035
- if (quote === char) quote = null;
14036
- else if (quote === null) quote = char;
14037
- else token += char;
14038
- continue;
14039
- }
14040
- if (quote === null && /\s/.test(char)) {
14041
- if (tokenStart >= 0) tokens.push({ value: token, start: tokenStart, end: index });
14042
- token = "";
14043
- tokenStart = -1;
14044
- continue;
14045
- }
14046
- token += char;
14047
- }
14048
- if (tokenStart >= 0 && tokens.length < MAX_LAUNCHER_TOKENS) {
14049
- tokens.push({ value: token, start: tokenStart, end: limit });
14050
- }
14051
- return tokens;
14052
- }
14053
- function shellTokens(raw) {
14054
- return boundedShellTokens(raw).map((token) => token.value);
14055
- }
14056
- function gitInvocationArguments(command) {
14057
- const argumentsList = [];
14058
- const quotedIndexes = new Uint8Array(command.length);
14059
- let activeQuote = null;
14060
- for (let index = 0; index < command.length; index += 1) {
14061
- const char = command[index];
14062
- if (isQuoteBoundary(command, index, activeQuote)) {
14063
- quotedIndexes[index] = 1;
14064
- activeQuote = activeQuote === char ? null : char === "'" ? "'" : '"';
14065
- } else if (activeQuote !== null) {
14066
- quotedIndexes[index] = 1;
14067
- }
14068
- }
14069
- const gitStart = /(?:^|[;&|\r\n]\s*|\(\s*|`\s*)git\b/gi;
14070
- let match = gitStart.exec(command);
14071
- while (match !== null) {
14072
- const gitOffset = match[0].toLowerCase().lastIndexOf("git");
14073
- if (gitOffset < 0 || quotedIndexes[match.index + gitOffset] === 1) {
14074
- match = gitStart.exec(command);
14075
- continue;
14076
- }
14077
- const start = gitStart.lastIndex;
14078
- let quote = null;
14079
- let end = start;
14080
- for (; end < command.length; end += 1) {
14081
- const char = command[end] ?? "";
14082
- if (char === "\\" && quote !== "'") {
14083
- end += 1;
14084
- continue;
14085
- }
14086
- if (char === "'" || char === '"') {
14087
- if (quote === char) quote = null;
14088
- else if (quote === null) quote = char;
14089
- continue;
14090
- }
14091
- if (quote === null && /[;&|)`\r\n]/.test(char)) break;
14092
- }
14093
- argumentsList.push(command.slice(start, end));
14094
- gitStart.lastIndex = Math.max(end, start);
14095
- match = gitStart.exec(command);
14096
- }
14097
- return argumentsList;
13841
+ return argumentsList;
14098
13842
  }
14099
13843
  function gitSubcommandIndex(tokens) {
14100
13844
  for (let index = 0; index < tokens.length; index += 1) {
@@ -14165,21 +13909,6 @@ function unwrapEnvSplitStringAtBoundary(command) {
14165
13909
  }
14166
13910
  return command;
14167
13911
  }
14168
- function stripTransparentLaunchers(command) {
14169
- let stripped = command;
14170
- let previous;
14171
- do {
14172
- previous = stripped;
14173
- let beforeUnwrap;
14174
- do {
14175
- beforeUnwrap = stripped;
14176
- stripped = unwrapEnvSplitStringAtBoundary(stripped);
14177
- } while (stripped !== beforeUnwrap);
14178
- stripped = stripLauncherAtBoundary(stripped, "env", ENV_VALUE_TAKING);
14179
- stripped = stripLauncherAtBoundary(stripped, "sudo", SUDO_VALUE_TAKING);
14180
- } while (stripped !== previous);
14181
- return stripped;
14182
- }
14183
13912
  var SUDO_VALUE_TAKING = /* @__PURE__ */ new Set([
14184
13913
  "-u",
14185
13914
  "-g",
@@ -14221,6 +13950,21 @@ var ENV_FLAG_OPTIONS = /* @__PURE__ */ new Set([
14221
13950
  "--help",
14222
13951
  "--version"
14223
13952
  ]);
13953
+ function stripTransparentLaunchers(command) {
13954
+ let stripped = command;
13955
+ let previous;
13956
+ do {
13957
+ previous = stripped;
13958
+ let beforeUnwrap;
13959
+ do {
13960
+ beforeUnwrap = stripped;
13961
+ stripped = unwrapEnvSplitStringAtBoundary(stripped);
13962
+ } while (stripped !== beforeUnwrap);
13963
+ stripped = stripLauncherAtBoundary(stripped, "env", ENV_VALUE_TAKING);
13964
+ stripped = stripLauncherAtBoundary(stripped, "sudo", SUDO_VALUE_TAKING);
13965
+ } while (stripped !== previous);
13966
+ return stripped;
13967
+ }
14224
13968
  function firstUnquotedShellSeparator(raw, start, end) {
14225
13969
  let quote = null;
14226
13970
  for (let index = start; index < end; index += 1) {
@@ -14357,9 +14101,7 @@ function executableCommandSubstitutions(command) {
14357
14101
  }
14358
14102
  const commandSubstitution = char === "$" && command[index + 1] === "(";
14359
14103
  const processSubstitution = (char === ">" || char === "<") && command[index + 1] === "(";
14360
- if (!commandSubstitution && !processSubstitution || // Arithmetic expansion evaluates an expression; it does not execute its
14361
- // contents as a shell command substitution.
14362
- commandSubstitution && command[index + 2] === "(")
14104
+ if (!commandSubstitution && !processSubstitution || commandSubstitution && command[index + 2] === "(")
14363
14105
  continue;
14364
14106
  let depth = 1;
14365
14107
  let innerQuote = null;
@@ -14807,6 +14549,281 @@ function destructiveTargetsAtDepth(command, depth) {
14807
14549
  )
14808
14550
  ];
14809
14551
  }
14552
+
14553
+ // src/path-guard/tool-targets.ts
14554
+ var WRITE_CAPABILITIES = /* @__PURE__ */ new Set(["fs.write", "fs.write.outside-project"]);
14555
+ var DISK_MUTATING_CAPABILITIES = /* @__PURE__ */ new Set(["package.install"]);
14556
+ var SHELL_CAPABILITIES = /* @__PURE__ */ new Set(["shell.arbitrary", "shell.restricted", "shell.exec"]);
14557
+ var LEGACY_SHELL_TOOLS = /* @__PURE__ */ new Set(["bash", "shell", "exec"]);
14558
+ var LEGACY_WRITE_TOOLS = /* @__PURE__ */ new Set([
14559
+ "write",
14560
+ "edit",
14561
+ "patch",
14562
+ "scaffold",
14563
+ "format",
14564
+ "replace",
14565
+ "design",
14566
+ "install"
14567
+ ]);
14568
+ var PATH_FIELDS = [
14569
+ "path",
14570
+ "file_path",
14571
+ "filePath",
14572
+ "target",
14573
+ "target_path",
14574
+ "targetPath",
14575
+ "destination",
14576
+ "dest",
14577
+ "output_path",
14578
+ "outputPath",
14579
+ "out",
14580
+ "file"
14581
+ ];
14582
+ var PATH_LIST_FIELDS = ["files", "paths"];
14583
+ var SOURCE_PATH_FIELDS = [
14584
+ "source",
14585
+ "from",
14586
+ "src",
14587
+ "input_path",
14588
+ "inputPath",
14589
+ "old_path",
14590
+ "oldPath"
14591
+ ];
14592
+ var MOVE_TOOL_NAME = /(?:^|[_.-])(move|rename)(?:$|[_.-])/i;
14593
+ var DIRECTORY_DELETE_TOOL_NAME = /(?:^|[_.-])(?:rmdir|(?:delete|remove)[_.-](?:dir|directory))(?:$|[_.-])/i;
14594
+ function segmentedToolName(toolName) {
14595
+ return toolName.replace(/([a-z0-9])([A-Z])/g, "$1_$2");
14596
+ }
14597
+ function isMoveToolName(toolName) {
14598
+ return MOVE_TOOL_NAME.test(segmentedToolName(toolName));
14599
+ }
14600
+ function isDirectoryDeleteToolName(toolName) {
14601
+ return DIRECTORY_DELETE_TOOL_NAME.test(segmentedToolName(toolName));
14602
+ }
14603
+ function cleanPatchPath(raw) {
14604
+ const trimmed = raw.trim();
14605
+ if (!trimmed || trimmed === "/dev/null") return null;
14606
+ const quoted = /^"([^"]+)"(?:\s|$)/.exec(trimmed)?.[1];
14607
+ const withoutTimestamp = quoted ?? trimmed.replace(/(?:\t|\s+)\d{4}-\d{2}-\d{2}(?:\s.*)?$/, "").trim();
14608
+ if (!withoutTimestamp || withoutTimestamp === "/dev/null") return null;
14609
+ return withoutTimestamp.replace(/^[ab]\//, "");
14610
+ }
14611
+ function pathsFromPatch(patch) {
14612
+ const paths = [];
14613
+ const lines = patch.split(/\r?\n/);
14614
+ for (let index = 0; index + 2 < lines.length; index += 1) {
14615
+ const oldHeader = lines[index];
14616
+ const newHeader = lines[index + 1];
14617
+ const hunkHeader = lines[index + 2];
14618
+ if (!oldHeader?.startsWith("--- ") || !newHeader?.startsWith("+++ ")) continue;
14619
+ if (!hunkHeader?.startsWith("@@ ")) continue;
14620
+ const oldPath = cleanPatchPath(oldHeader.slice(4));
14621
+ const newPath = cleanPatchPath(newHeader.slice(4));
14622
+ if (oldPath) paths.push(oldPath);
14623
+ if (newPath) paths.push(newPath);
14624
+ }
14625
+ return paths;
14626
+ }
14627
+ function appendTarget(targets, value, kind, base) {
14628
+ if (typeof value !== "string" || value.length === 0) return;
14629
+ targets.push({
14630
+ path: resolveTargetPath(value, base),
14631
+ kind
14632
+ });
14633
+ }
14634
+ function appendTargetList(targets, value, directoryCapable, base) {
14635
+ const appendPath = (path) => {
14636
+ targets.push({
14637
+ path: resolveTargetPath(path, base),
14638
+ kind: isUnresolvedPathScope(path) || isRootPathScope(path) || directoryCapable && isDirectoryAmbiguousPath(path) ? "scope" : "file"
14639
+ });
14640
+ };
14641
+ if (typeof value === "string") {
14642
+ const paths = value.split(",").map((item) => item.trim()).filter((path) => path.length > 0);
14643
+ for (const path of paths) appendPath(path);
14644
+ return;
14645
+ }
14646
+ if (!Array.isArray(value)) return;
14647
+ for (const item of value) {
14648
+ if (typeof item !== "string") continue;
14649
+ const path = item.trim();
14650
+ if (path.length > 0) appendPath(path);
14651
+ }
14652
+ }
14653
+ function isReadOnlyInvocation(toolName, toolInput) {
14654
+ if (toolName === "git") {
14655
+ const command = toolInput["command"];
14656
+ if (command === "status" || command === "log" || command === "diff" || command === "fetch" || command === "commit" || command === "branch" || command === "push") {
14657
+ return true;
14658
+ }
14659
+ return command === "worktree" && toolInput["worktreeAction"] === "list";
14660
+ }
14661
+ if (toolName === "design") {
14662
+ const action = toolInput["action"] ?? "list";
14663
+ return action === "list" || action === "foundations" || action === "verify";
14664
+ }
14665
+ if (toolName === "format") return toolInput["check"] === true;
14666
+ if (toolName === "patch" || toolName === "scaffold") return toolInput["dry_run"] === true;
14667
+ if (toolName === "replace") return toolInput["dry_run"] !== false;
14668
+ return false;
14669
+ }
14670
+ function writesToDisk(input) {
14671
+ const capabilities = input.toolCapabilities;
14672
+ const capabilitiesUndeclared = !capabilities || capabilities.length === 0;
14673
+ const hasWriteCap = capabilities?.some((capability) => WRITE_CAPABILITIES.has(capability)) ?? false;
14674
+ const hasDiskMutatingCap = capabilities?.some((capability) => DISK_MUTATING_CAPABILITIES.has(capability)) ?? false;
14675
+ const hasCommandString = typeof input.toolInput?.["command"] === "string";
14676
+ if (hasCommandString && executesShell(input) && !hasWriteCap && !hasDiskMutatingCap && !(capabilitiesUndeclared && LEGACY_WRITE_TOOLS.has(input.toolName ?? "")))
14677
+ return false;
14678
+ if (!capabilitiesUndeclared) {
14679
+ if (hasWriteCap || hasDiskMutatingCap) return true;
14680
+ if (capabilities.includes("mcp.proxy")) {
14681
+ const segmentedName = (input.toolName ?? "").replace(/([a-z0-9])([A-Z])/g, "$1_$2").replace(/[^a-z0-9]+/gi, "_").toLowerCase();
14682
+ const filesystemWriter = /(?:^|_)filesystem(?:_|$)/.test(segmentedName) && /(?:^|_)(?:add|append|copy|create|delete|edit|mkdir|move|remove|rename|write)(?:_|$)/.test(
14683
+ segmentedName
14684
+ );
14685
+ const toolInput = input.toolInput ?? {};
14686
+ const hasKnownPath = [...PATH_FIELDS, ...PATH_LIST_FIELDS].some(
14687
+ (field) => toolInput[field] !== void 0
14688
+ );
14689
+ if (filesystemWriter && hasKnownPath) return true;
14690
+ }
14691
+ return false;
14692
+ }
14693
+ if (input.toolMutating !== void 0) return input.toolMutating;
14694
+ return LEGACY_WRITE_TOOLS.has(input.toolName ?? "");
14695
+ }
14696
+ function executesShell(input) {
14697
+ const toolName = input.toolName ?? "";
14698
+ if (LEGACY_SHELL_TOOLS.has(toolName)) return true;
14699
+ if (toolName === "git") return false;
14700
+ const segmentedName = toolName.replace(/([a-z0-9])([A-Z])/g, "$1_$2");
14701
+ const isCommandStringRunner = /(?:^|[_.-])(?:command|exec|execute|shell|terminal)(?:$|[_.-])/i.test(segmentedName);
14702
+ const hasShellCapability = input.toolCapabilities?.some((capability) => SHELL_CAPABILITIES.has(capability)) ?? false;
14703
+ const hasCommandString = typeof input.toolInput?.["command"] === "string";
14704
+ return isCommandStringRunner && hasShellCapability || hasCommandString && input.toolMutating === true;
14705
+ }
14706
+ function pathsFromToolInput(toolInput, toolName, invocationCwd) {
14707
+ const targets = [];
14708
+ const effectiveCwd = effectiveToolCwd(toolInput["cwd"], invocationCwd);
14709
+ const directoryDelete = isDirectoryDeleteToolName(toolName);
14710
+ for (const field of PATH_FIELDS) {
14711
+ const value = toolInput[field];
14712
+ appendTarget(
14713
+ targets,
14714
+ value,
14715
+ directoryDelete ? "deletion-scope" : typeof value === "string" && isUnresolvedPathScope(value) ? "scope" : "file",
14716
+ effectiveCwd
14717
+ );
14718
+ }
14719
+ const directoryCapableLists = toolName === "format" || toolName === "replace";
14720
+ for (const field of PATH_LIST_FIELDS) {
14721
+ appendTargetList(
14722
+ targets,
14723
+ toolInput[field],
14724
+ directoryCapableLists && field === "files",
14725
+ effectiveCwd
14726
+ );
14727
+ }
14728
+ if (toolName === "install") {
14729
+ appendTarget(targets, effectiveCwd ?? invocationCwd ?? ".", "scope");
14730
+ }
14731
+ if (toolName === "git") {
14732
+ const command = toolInput["command"];
14733
+ if (command === "worktree" && (toolInput["worktreeAction"] === "add" || toolInput["worktreeAction"] === "remove")) {
14734
+ appendTarget(targets, toolInput["worktreePath"] ?? toolInput["worktree_path"], "file");
14735
+ }
14736
+ }
14737
+ if (toolName === "scaffold") {
14738
+ const cwdValue = toolInput["cwd"];
14739
+ const cwd = typeof cwdValue === "string" && cwdValue.length > 0 ? cwdValue : invocationCwd ?? ".";
14740
+ const base = normalizePath2(cwd).replace(/\/$/, "");
14741
+ const name = typeof toolInput["name"] === "string" ? toolInput["name"] : "";
14742
+ const template = toolInput["template"];
14743
+ const filesByTemplate = {
14744
+ "npm-package": ["package.json", "tsconfig.json", "src/index.ts", "src/index.test.ts"],
14745
+ "cli-tool": ["package.json", "src/index.ts"],
14746
+ "react-component": name ? [`${name}.tsx`, `${name}.test.tsx`] : []
14747
+ };
14748
+ for (const file of typeof template === "string" ? filesByTemplate[template] ?? [] : []) {
14749
+ targets.push({ path: `${base}/${file}`, kind: "file" });
14750
+ }
14751
+ targets.push({ path: base, kind: "scope" });
14752
+ }
14753
+ if (isMoveToolName(toolName)) {
14754
+ for (const field of SOURCE_PATH_FIELDS) {
14755
+ appendTarget(targets, toolInput[field], "deletion-scope", effectiveCwd);
14756
+ }
14757
+ }
14758
+ const patch = toolInput["patch"];
14759
+ if (typeof patch === "string") {
14760
+ const patchTargets = pathsFromPatch(patch);
14761
+ const directory = toolInput["directory"];
14762
+ const baseValue = typeof directory === "string" && directory.length > 0 ? directory : invocationCwd;
14763
+ if (baseValue) {
14764
+ const base = normalizePath2(baseValue).replace(/\/$/, "");
14765
+ targets.push(
14766
+ ...patchTargets.map((target) => ({ path: `${base}/${target}`, kind: "file" }))
14767
+ );
14768
+ } else {
14769
+ targets.push(...patchTargets.map((path) => ({ path, kind: "file" })));
14770
+ }
14771
+ }
14772
+ if (targets.length === 0) {
14773
+ const cwd = effectiveCwd ?? ".";
14774
+ targets.push({ path: normalizePath2(cwd).replace(/\/$/, "") || ".", kind: "scope" });
14775
+ }
14776
+ const seen = /* @__PURE__ */ new Set();
14777
+ return targets.map((target) => ({
14778
+ ...target,
14779
+ path: relativeToInvocationCwd(target.path, invocationCwd)
14780
+ })).filter((target) => {
14781
+ const key = `${target.kind}:${target.path}`;
14782
+ if (seen.has(key)) return false;
14783
+ seen.add(key);
14784
+ return true;
14785
+ });
14786
+ }
14787
+ function operationLabel(toolName) {
14788
+ if (toolName === "write") return "write";
14789
+ if (toolName === "edit") return "edit";
14790
+ return `write via "${toolName}"`;
14791
+ }
14792
+
14793
+ // src/path-guard/index.ts
14794
+ function createState() {
14795
+ return { invocations: 0, blocks: 0, warns: 0, lastBlock: null, hookUnregister: null };
14796
+ }
14797
+ var states = /* @__PURE__ */ new WeakMap();
14798
+ var latestState = createState();
14799
+ var DEFAULT_PROTECT = [
14800
+ "pnpm-lock.yaml",
14801
+ "package-lock.json",
14802
+ "yarn.lock",
14803
+ "bun.lockb",
14804
+ "Cargo.lock",
14805
+ "poetry.lock",
14806
+ ".env",
14807
+ ".env.*",
14808
+ ".git/**",
14809
+ "**/migrations/**"
14810
+ ];
14811
+ var DEFAULTS33 = {
14812
+ enabled: true,
14813
+ mode: "block",
14814
+ protect: DEFAULT_PROTECT,
14815
+ allow: []
14816
+ };
14817
+ function readConfig34(raw) {
14818
+ if (!raw || typeof raw !== "object") return { ...DEFAULTS33, protect: [...DEFAULT_PROTECT] };
14819
+ const r = raw;
14820
+ return {
14821
+ enabled: r["enabled"] !== false,
14822
+ mode: r["mode"] === "warn" ? "warn" : "block",
14823
+ protect: Array.isArray(r["protect"]) ? r["protect"].filter((p) => typeof p === "string" && p.length > 0) : [...DEFAULT_PROTECT],
14824
+ allow: Array.isArray(r["allow"]) ? r["allow"].filter((p) => typeof p === "string" && p.length > 0) : []
14825
+ };
14826
+ }
14810
14827
  var plugin39 = {
14811
14828
  name: "path-guard",
14812
14829
  version: "0.1.0",