@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.
- package/dist/index.mjs +152 -19
- 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.
|
|
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(
|
|
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(
|
|
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
|
-
|
|
2885
|
-
|
|
2886
|
-
|
|
2887
|
-
|
|
2888
|
-
|
|
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",
|
|
6249
|
+
return ["-a", "never", "exec", ...permission.args, prompt2];
|
|
6125
6250
|
}
|
|
6126
6251
|
if (agent === "antigravity") {
|
|
6127
|
-
return ["-p", prompt2,
|
|
6252
|
+
return ["-p", prompt2, ...permission.args];
|
|
6128
6253
|
}
|
|
6129
|
-
return ["-p",
|
|
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
|
-
|
|
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
|
|
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 = "";
|