@a2f0/agent-tool 0.1.53 → 0.1.54

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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@a2f0/agent-tool",
3
- "version": "0.1.53",
3
+ "version": "0.1.54",
4
4
  "packageManager": "bun@1.4.4",
5
5
  "type": "module",
6
6
  "description": "Portable cross-agent review, PR workflows, and shared skills",
@@ -16,9 +16,9 @@ const USAGE = `Usage:
16
16
  const EXECUTABLE = /^[\w.+-][\w.+-]*$/;
17
17
 
18
18
  /**
19
- * An executable whose class asks relies on a generated ask rule. If the recorded rules predate
20
- * its classification, an older allowance could run it without approval, so refuse until they
21
- * are regenerated.
19
+ * An executable whose class asks must have no generated allow rule. If the recorded rules
20
+ * predate its classification, an older allowance could preauthorize it, so refuse until
21
+ * they are regenerated.
22
22
  */
23
23
  function assertCurrentRules(root: string, command: string): void {
24
24
  if (!existsSync(path.join(root, ".agent-tool-permissions.json"))) return;
@@ -42,12 +42,6 @@ export const CATALOG: CatalogEntry[] = [
42
42
  // `bun x` runs only executables that `permissions.executables` classifies; project.ts adds those.
43
43
  // Package scripts are not classified, so `bun run` keeps the harness default.
44
44
 
45
- /** Direct spellings of classified actions. Codex prompts for them, so a raw command cannot bypass its agent-tool form. */
46
- export const GUARDS: { command: string; class: PermissionClass }[] = [
47
- ...["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 })),
48
- ...["gh pr merge", "npm publish", "bun publish"].map(command => ({ command, class: "release" as const })),
49
- ];
50
-
51
45
  /** How agents invoke an installed agent-tool: the package script, the bin link, or PATH. */
52
46
  export const INVOCATIONS = ["bun run agent-tool", "node_modules/.bin/agent-tool", "agent-tool"];
53
47
 
@@ -83,30 +77,34 @@ export function claudePermissions(project: CatalogEntry[] = []): ClaudePermissio
83
77
  const quote = (value: string) => JSON.stringify(value);
84
78
  const element = (values: string[]) => values.length === 1 ? quote(values[0]!) : `[${values.map(quote).join(", ")}]`;
85
79
 
86
- /** Codex prefix rules: `allow` runs outside the sandbox without a prompt; `prompt` always asks. */
80
+ /**
81
+ * Only allow rules are generated, as for Claude. Codex's explicit prompt rules
82
+ * reject commands when approval_policy is never, even with danger-full-access.
83
+ * Asking classes stay unlisted so the user's approval and sandbox policies apply.
84
+ */
87
85
  export function codexRules(project: CatalogEntry[] = []): string {
88
86
  const lines = [
89
87
  "# Managed by agent-tool; regenerate with `agent-tool permissions install --apply`.",
90
- "# Codex applies the most restrictive matching rule. Commands with an --apply form",
91
- "# use their stricter decision because prefix rules cannot match trailing flags.",
88
+ "# Only routine commands receive allow rules. Other commands keep the user's",
89
+ "# approval and sandbox policies; explicit prompts would reject them in never mode.",
90
+ "# A command whose --apply form asks gets no rule, since prefixes also match --apply.",
92
91
  "# Equal decisions group under the applied effect, not the read-only preview.",
93
92
  ];
94
93
  const groups = new Map<string, { prefix: string[]; last: string[]; class: PermissionClass }>();
95
94
  const add = (tokens: string[], value: PermissionClass) => {
95
+ if (CLASSES[value].decision !== "allow") return;
96
96
  const prefix = tokens.slice(0, -1), key = `${value}\0${prefix.join("\0")}`;
97
97
  const group = groups.get(key) ?? { prefix, last: [], class: value };
98
98
  group.last.push(tokens.at(-1)!); groups.set(key, group);
99
99
  };
100
100
  for (const invocation of INVOCATIONS) for (const entry of [...CATALOG, ...project]) add([...invocation.split(" "), ...entry.command.split(" ")], strictest(entry));
101
- for (const guard of GUARDS) add(guard.command.split(" "), guard.class);
102
101
  for (const value of Object.keys(CLASSES) as PermissionClass[]) {
103
102
  const rules = [...groups.values()].filter(group => group.class === value);
104
103
  if (!rules.length) continue;
105
104
  lines.push("", `# ${value}: ${CLASSES[value].description}`);
106
105
  for (const rule of rules) {
107
106
  const pattern = [...rule.prefix.map(quote), element(rule.last)].join(", ");
108
- const reason = CLASSES[value].decision === "ask" ? `decision="prompt", justification=${quote(CLASSES[value].description)}` : `decision="allow"`;
109
- lines.push(`prefix_rule(pattern=[${pattern}], ${reason}, match=[${quote([...rule.prefix, rule.last[0]!].join(" "))}])`);
107
+ lines.push(`prefix_rule(pattern=[${pattern}], decision="allow", match=[${quote([...rule.prefix, rule.last[0]!].join(" "))}])`);
110
108
  }
111
109
  }
112
110
  return `${lines.join("\n")}\n`;