@dunnewold-labs/mr-manager 0.4.53 → 0.4.55

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.
Files changed (2) hide show
  1. package/dist/index.mjs +152 -19
  2. package/package.json +1 -1
package/dist/index.mjs CHANGED
@@ -185,7 +185,7 @@ import { fileURLToPath } from "url";
185
185
  // cli/package.json
186
186
  var package_default = {
187
187
  name: "@dunnewold-labs/mr-manager",
188
- version: "0.4.53",
188
+ version: "0.4.55",
189
189
  description: "Mr. Manager - Task and project management CLI",
190
190
  bin: {
191
191
  mr: "./dist/index.mjs"
@@ -1151,6 +1151,118 @@ function getAvailableAgentFallbackChain(agent, availability) {
1151
1151
  return getAgentFallbackChain(agent).filter((candidate) => availability[candidate] !== false);
1152
1152
  }
1153
1153
 
1154
+ // lib/permissions.ts
1155
+ var DEFAULT_HEADLESS_MODE = "bypass";
1156
+ var ENV_VAR = "MR_HEADLESS_PERMISSION_MODE";
1157
+ var DESTRUCTIVE_BASH_DENY = [
1158
+ // Privilege escalation.
1159
+ "Bash(sudo:*)",
1160
+ "Bash(su:*)",
1161
+ // Recursive deletes of broad / out-of-repo roots.
1162
+ "Bash(rm -rf /:*)",
1163
+ "Bash(rm -rf /*:*)",
1164
+ "Bash(rm -fr /:*)",
1165
+ "Bash(rm -rf ~:*)",
1166
+ "Bash(rm -rf ~/:*)",
1167
+ "Bash(rm -rf $HOME:*)",
1168
+ "Bash(rm -rf ..:*)",
1169
+ // Filesystem / device destruction.
1170
+ "Bash(mkfs:*)",
1171
+ "Bash(dd:*)",
1172
+ // Mass permission/ownership changes from root.
1173
+ "Bash(chmod -R 777 /:*)",
1174
+ "Bash(chown -R:*)",
1175
+ // Force-push (history rewrite / remote clobber).
1176
+ "Bash(git push --force:*)",
1177
+ "Bash(git push -f:*)",
1178
+ "Bash(git push origin --force:*)",
1179
+ "Bash(git push origin -f:*)",
1180
+ // Remote pipe-to-shell (curl|sh / wget|bash).
1181
+ "Bash(curl:* | sh)",
1182
+ "Bash(curl:* | bash)",
1183
+ "Bash(wget:* | sh)",
1184
+ "Bash(wget:* | bash)"
1185
+ ];
1186
+ var OUT_OF_REPO_WRITE_DENY = [
1187
+ "Write(/etc/**)",
1188
+ "Edit(/etc/**)",
1189
+ "Write(//usr/**)",
1190
+ "Edit(//usr/**)",
1191
+ "Write(~/.ssh/**)",
1192
+ "Edit(~/.ssh/**)",
1193
+ "Write(~/.aws/**)",
1194
+ "Edit(~/.aws/**)"
1195
+ ];
1196
+ var READ_ONLY_EXTRA_DENY = ["Edit", "Write", "MultiEdit", "NotebookEdit"];
1197
+ function resolveHeadlessMode(config) {
1198
+ const fromEnv = process.env[ENV_VAR]?.trim().toLowerCase();
1199
+ if (fromEnv === "auto" || fromEnv === "bypass") return fromEnv;
1200
+ const fromConfig = config?.headlessPermissionMode?.trim().toLowerCase();
1201
+ if (fromConfig === "auto" || fromConfig === "bypass") return fromConfig;
1202
+ if (config?.claudePermissionMode != null && config.headlessPermissionMode == null) {
1203
+ return "bypass";
1204
+ }
1205
+ return DEFAULT_HEADLESS_MODE;
1206
+ }
1207
+ function claudeDenyList(runKind) {
1208
+ const base = [...DESTRUCTIVE_BASH_DENY, ...OUT_OF_REPO_WRITE_DENY];
1209
+ if (runKind === "review" || runKind === "scan") {
1210
+ return [...base, ...READ_ONLY_EXTRA_DENY];
1211
+ }
1212
+ return base;
1213
+ }
1214
+ function resolvePermissionArgs(agent, runKind, mode) {
1215
+ if (runKind === "plan") {
1216
+ if (agent === "claude") {
1217
+ return { args: ["--permission-mode", "plan"], effectiveMode: "plan", mappedToBypassForLackOfSandbox: false };
1218
+ }
1219
+ return resolvePermissionArgs(agent, "execute", mode);
1220
+ }
1221
+ if (mode === "bypass") {
1222
+ return { args: bypassArgs(agent, runKind), effectiveMode: "bypass", mappedToBypassForLackOfSandbox: false };
1223
+ }
1224
+ switch (agent) {
1225
+ case "claude": {
1226
+ const deny = claudeDenyList(runKind);
1227
+ return {
1228
+ args: ["--permission-mode", "acceptEdits", "--disallowedTools", deny.join(",")],
1229
+ effectiveMode: "auto",
1230
+ mappedToBypassForLackOfSandbox: false
1231
+ };
1232
+ }
1233
+ case "codex": {
1234
+ return { args: ["-s", "workspace-write"], effectiveMode: "auto", mappedToBypassForLackOfSandbox: false };
1235
+ }
1236
+ case "antigravity":
1237
+ return { args: ["--dangerously-skip-permissions"], effectiveMode: "bypass", mappedToBypassForLackOfSandbox: true };
1238
+ default:
1239
+ return { args: bypassArgs(agent, runKind), effectiveMode: "bypass", mappedToBypassForLackOfSandbox: true };
1240
+ }
1241
+ }
1242
+ function bypassArgs(agent, _runKind) {
1243
+ switch (agent) {
1244
+ case "codex":
1245
+ return ["-s", "danger-full-access"];
1246
+ case "antigravity":
1247
+ case "claude":
1248
+ default:
1249
+ return ["--dangerously-skip-permissions"];
1250
+ }
1251
+ }
1252
+ function describePermissionMode(res) {
1253
+ if (res.mappedToBypassForLackOfSandbox) {
1254
+ return "bypass (no scoped sandbox for this agent \u2014 full access)";
1255
+ }
1256
+ switch (res.effectiveMode) {
1257
+ case "auto":
1258
+ return "auto (scoped: acceptEdits + destructive-command deny-list)";
1259
+ case "plan":
1260
+ return "plan (read-only)";
1261
+ case "bypass":
1262
+ return "bypass (--dangerously-skip-permissions \u2014 full access)";
1263
+ }
1264
+ }
1265
+
1154
1266
  // lib/task-workflow.ts
1155
1267
  function normalizeWhitespace(value) {
1156
1268
  return value.replace(/\s+/g, " ").trim();
@@ -2851,41 +2963,46 @@ function buildRefinementPrompt(proto, parentFiles, repoDir, options = {}) {
2851
2963
  ].join("\n");
2852
2964
  }
2853
2965
  function buildAgentArgs(agent, prompt2, mode, sessionId, name, resumeSession = false, systemPrompt, maxTurns, claudeModel) {
2966
+ const headlessMode = resolveHeadlessMode(loadConfig());
2967
+ const runKind = mode === "plan" ? "plan" : "execute";
2854
2968
  if (agent === "codex") {
2969
+ const permission2 = resolvePermissionArgs("codex", runKind, headlessMode);
2855
2970
  const args = [];
2856
2971
  if (mode === "execute") {
2857
2972
  args.push("-a", "never");
2858
2973
  }
2859
2974
  args.push("exec");
2860
2975
  if (mode === "execute") {
2861
- args.push("-s", "danger-full-access");
2976
+ args.push(...permission2.args);
2862
2977
  }
2863
2978
  const fullPrompt = systemPrompt ? `${prompt2}
2864
2979
 
2865
2980
  ${systemPrompt}` : prompt2;
2866
2981
  args.push(fullPrompt);
2867
- return { bin: "codex", args };
2982
+ return { bin: "codex", args, permission: permission2 };
2868
2983
  }
2869
2984
  if (agent === "antigravity") {
2985
+ const permission2 = resolvePermissionArgs("antigravity", runKind, headlessMode);
2870
2986
  const fullPrompt = systemPrompt ? `${prompt2}
2871
2987
 
2872
2988
  ${systemPrompt}` : prompt2;
2873
2989
  const args = ["-p", fullPrompt];
2874
2990
  if (mode === "execute") {
2875
- args.push("--dangerously-skip-permissions");
2991
+ args.push(...permission2.args);
2876
2992
  }
2877
- return { bin: AGENT_BINARIES.antigravity, args };
2993
+ return { bin: AGENT_BINARIES.antigravity, args, permission: permission2 };
2878
2994
  }
2995
+ const permission = resolvePermissionArgs("claude", runKind, headlessMode);
2879
2996
  const sessionArgs = sessionId ? resumeSession ? ["--resume", sessionId] : ["--session-id", sessionId] : [];
2880
2997
  const nameArgs = name ? ["--name", name] : [];
2881
2998
  const systemArgs = systemPrompt ? ["--append-system-prompt", systemPrompt] : [];
2882
2999
  const turnsArgs = maxTurns ? ["--max-turns", String(maxTurns)] : [];
2883
3000
  const modelArgs = claudeModel ? ["--model", claudeModel] : [];
2884
- if (mode === "plan") {
2885
- return { bin: "claude", args: [...sessionArgs, ...nameArgs, ...systemArgs, ...turnsArgs, ...modelArgs, "--permission-mode", "plan", "-p", prompt2] };
2886
- }
2887
- const permissionArgs = ["--dangerously-skip-permissions"];
2888
- return { bin: "claude", args: [...sessionArgs, ...nameArgs, ...systemArgs, ...turnsArgs, ...modelArgs, ...permissionArgs, "-p", prompt2] };
3001
+ return {
3002
+ bin: "claude",
3003
+ args: [...sessionArgs, ...nameArgs, ...systemArgs, ...turnsArgs, ...modelArgs, ...permission.args, "-p", prompt2],
3004
+ permission
3005
+ };
2889
3006
  }
2890
3007
  var AGENT_BINARIES = {
2891
3008
  claude: "claude",
@@ -2968,7 +3085,13 @@ function askYesNo(question) {
2968
3085
  function spawnAgent(agent, repoDir, prompt2, prefix, onActivity, sessionId, name, resumeSession = false, onSpawnError, systemPrompt, maxTurns, claudeModel, onOutputBytes) {
2969
3086
  const jobLabel = name ?? "unknown";
2970
3087
  console.log(`${timestamp()} ${prefix} ${paint("dim", tokenLogLine("agent", jobLabel, prompt2, systemPrompt))}`);
2971
- const { bin, args } = buildAgentArgs(agent, prompt2, "execute", sessionId, name, resumeSession, systemPrompt, maxTurns, claudeModel);
3088
+ const { bin, args, permission } = buildAgentArgs(agent, prompt2, "execute", sessionId, name, resumeSession, systemPrompt, maxTurns, claudeModel);
3089
+ const permLine = `${timestamp()} ${prefix} ${paint("dim", `permission: ${describePermissionMode(permission)}`)}`;
3090
+ if (permission.mappedToBypassForLackOfSandbox) {
3091
+ logWarn(prefix, `permission: ${describePermissionMode(permission)}`);
3092
+ } else {
3093
+ console.log(permLine);
3094
+ }
2972
3095
  const child = spawn4(bin, args, { cwd: repoDir, stdio: ["ignore", "pipe", "pipe"] });
2973
3096
  child.on("error", (err) => {
2974
3097
  logError(prefix, `Failed to spawn ${agent}: ${err.message}`);
@@ -6119,18 +6242,26 @@ async function resolveReviewAgentChain(preferred2 = "claude") {
6119
6242
  }
6120
6243
  return getAvailableAgentFallbackChain(preferred2, availability);
6121
6244
  }
6122
- function buildArgs(agent, prompt2) {
6245
+ function buildArgs(agent, prompt2, runKind) {
6246
+ const mode = resolveHeadlessMode(loadConfig());
6247
+ const permission = resolvePermissionArgs(agent, runKind, mode);
6123
6248
  if (agent === "codex") {
6124
- return ["-a", "never", "exec", "-s", "danger-full-access", prompt2];
6249
+ return ["-a", "never", "exec", ...permission.args, prompt2];
6125
6250
  }
6126
6251
  if (agent === "antigravity") {
6127
- return ["-p", prompt2, "--dangerously-skip-permissions"];
6252
+ return ["-p", prompt2, ...permission.args];
6128
6253
  }
6129
- return ["-p", "--dangerously-skip-permissions", prompt2];
6254
+ return ["-p", ...permission.args, prompt2];
6255
+ }
6256
+ function logPermission(agent, runKind) {
6257
+ const permission = resolvePermissionArgs(agent, runKind, resolveHeadlessMode(loadConfig()));
6258
+ const label = permission.mappedToBypassForLackOfSandbox ? "warning" : "info";
6259
+ console.error(`[review:${label}] ${agent} ${runKind} permission: ${describePermissionMode(permission)}`);
6130
6260
  }
6131
6261
  function runOnce(agent, prompt2, opts) {
6132
6262
  return new Promise((resolve9) => {
6133
- const child = spawn7(AGENT_BINARIES2[agent], buildArgs(agent, prompt2), {
6263
+ logPermission(agent, opts.runKind);
6264
+ const child = spawn7(AGENT_BINARIES2[agent], buildArgs(agent, prompt2, opts.runKind), {
6134
6265
  cwd: opts.cwd,
6135
6266
  stdio: opts.capture ? ["ignore", "pipe", "pipe"] : ["ignore", "inherit", "inherit"]
6136
6267
  });
@@ -6156,7 +6287,7 @@ async function runAgentCaptured(prompt2, opts) {
6156
6287
  }
6157
6288
  let lastError = "";
6158
6289
  for (const agent of opts.chain) {
6159
- const result = await runOnce(agent, prompt2, { cwd: opts.cwd, capture: true });
6290
+ const result = await runOnce(agent, prompt2, { cwd: opts.cwd, capture: true, runKind: "review" });
6160
6291
  if (result.ok && result.output) {
6161
6292
  return { output: result.output, agent };
6162
6293
  }
@@ -6170,7 +6301,7 @@ async function runAgentInteractive(prompt2, opts) {
6170
6301
  }
6171
6302
  let lastError = "";
6172
6303
  for (const agent of opts.chain) {
6173
- const result = await runOnce(agent, prompt2, { cwd: opts.cwd, capture: false });
6304
+ const result = await runOnce(agent, prompt2, { cwd: opts.cwd, capture: false, runKind: "execute" });
6174
6305
  if (result.ok) return { agent };
6175
6306
  lastError = result.spawnError ? `${agent} could not be spawned` : `${agent} exited non-zero`;
6176
6307
  }
@@ -7869,7 +8000,9 @@ async function fetchScanContext(opts) {
7869
8000
  }
7870
8001
  function runClaude(prompt2) {
7871
8002
  return new Promise((resolve9, reject) => {
7872
- const child = spawn8("claude", ["-p", "--dangerously-skip-permissions", prompt2], {
8003
+ const permission = resolvePermissionArgs("claude", "scan", resolveHeadlessMode(loadConfig()));
8004
+ console.error(`[scanner] permission: ${describePermissionMode(permission)}`);
8005
+ const child = spawn8("claude", ["-p", ...permission.args, prompt2], {
7873
8006
  stdio: ["ignore", "pipe", "pipe"]
7874
8007
  });
7875
8008
  let output = "";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dunnewold-labs/mr-manager",
3
- "version": "0.4.53",
3
+ "version": "0.4.55",
4
4
  "description": "Mr. Manager - Task and project management CLI",
5
5
  "bin": {
6
6
  "mr": "./dist/index.mjs"