@a2f0/agent-tool 0.1.27 → 0.1.28
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/package.json
CHANGED
|
@@ -40,7 +40,7 @@ export const CATALOG: CatalogEntry[] = [
|
|
|
40
40
|
// `bun x` runs only executables that `permissions.executables` classifies; project.ts adds those.
|
|
41
41
|
// Package scripts are not classified, so `bun run` keeps the harness default.
|
|
42
42
|
|
|
43
|
-
/** Direct spellings of classified actions.
|
|
43
|
+
/** Direct spellings of classified actions. Codex prompts for them, so a raw command cannot bypass its agent-tool form. */
|
|
44
44
|
export const GUARDS: { command: string; class: PermissionClass }[] = [
|
|
45
45
|
...["bun add", "bun a", "npm install", "npm i", "npm add", "pnpm add", "yarn add", "bunx", "bun x", "npx"].map(command => ({ command, class: "intake" as const })),
|
|
46
46
|
...["gh pr merge", "npm publish", "bun publish"].map(command => ({ command, class: "release" as const })),
|
|
@@ -56,18 +56,25 @@ export function strictest(entry: CatalogEntry): PermissionClass {
|
|
|
56
56
|
|
|
57
57
|
export interface ClaudePermissions { allow: string[]; ask: string[] }
|
|
58
58
|
|
|
59
|
-
/**
|
|
59
|
+
/**
|
|
60
|
+
* Claude Code matches `*` across spaces and splits compound commands before matching.
|
|
61
|
+
* Only allow rules are generated: Claude Code prompts for an explicit ask rule in every
|
|
62
|
+
* mode, bypassPermissions included, so a committed ask rule would override each user's
|
|
63
|
+
* chosen mode. Classes that ask stay unlisted, so Manual mode prompts for them and auto
|
|
64
|
+
* mode leaves them to the classifier. `ask` stays empty so that reinstalling removes the
|
|
65
|
+
* ask rules earlier versions recorded.
|
|
66
|
+
*/
|
|
60
67
|
export function claudePermissions(project: CatalogEntry[] = []): ClaudePermissions {
|
|
61
|
-
const
|
|
68
|
+
const allow: string[] = [];
|
|
62
69
|
for (const invocation of INVOCATIONS) {
|
|
63
70
|
for (const entry of [...CATALOG, ...project]) {
|
|
71
|
+
// An allow rule also matches the `--apply` form, so a command whose `--apply` form asks gets none.
|
|
72
|
+
if (CLASSES[strictest(entry)].decision !== "allow") continue;
|
|
64
73
|
const base = `${invocation} ${entry.command}`;
|
|
65
|
-
|
|
66
|
-
if (entry.applyClass && CLASSES[entry.applyClass].decision !== CLASSES[entry.class].decision) rules[CLASSES[entry.applyClass].decision].push(`Bash(${base} *--apply*)`);
|
|
74
|
+
allow.push(entry.command.startsWith("--") ? `Bash(${base})` : `Bash(${base} *)`);
|
|
67
75
|
}
|
|
68
76
|
}
|
|
69
|
-
|
|
70
|
-
return rules;
|
|
77
|
+
return { allow, ask: [] };
|
|
71
78
|
}
|
|
72
79
|
|
|
73
80
|
const quote = (value: string) => JSON.stringify(value);
|
|
@@ -69,7 +69,9 @@ function mergedSettings(settings: Settings, previous: ClaudePermissions | undefi
|
|
|
69
69
|
for (const key of ["allow", "ask"] as const) {
|
|
70
70
|
const owned = new Set(previous?.[key] ?? []);
|
|
71
71
|
const kept = ((permissions[key] as string[] | undefined) ?? []).filter(item => !owned.has(item) && !current[key].includes(item));
|
|
72
|
-
|
|
72
|
+
const merged = [...kept, ...current[key]];
|
|
73
|
+
if (merged.length) permissions[key] = merged;
|
|
74
|
+
else delete permissions[key];
|
|
73
75
|
}
|
|
74
76
|
return { ...settings, permissions };
|
|
75
77
|
}
|