Enhance permission gate with auto-allow for safe operations
Permission gate now automatically allows read operations on tracked files
and safe jj commands (diff, log, show, status) without prompting.
Also updates default model to claude-sonnet-4-0.

Assisted-by: Claude Sonnet 4.0 via Claude Code
change vwozozmykqosoxqnzzptrvlmuqlxtxvs
commit 9134e0fcd07eeac1a25aa45688e0b182cf3bb388
author Alpha Chen <alpha@kejadlen.dev>
date
parent psxzlnoo
diff --git a/.pi/agent/extensions/permission-gate.ts b/.pi/agent/extensions/permission-gate.ts
index bceaa6d4..79e55cb4 100644
--- a/.pi/agent/extensions/permission-gate.ts
+++ b/.pi/agent/extensions/permission-gate.ts
@@ -2,14 +2,59 @@
  * Permission Gate Extension
  *
  * Requires explicit user confirmation before every tool call.
+ * Auto-allows read-only operations (read tool, non-destructive jj commands).
  * Shows tool name and a summary of the arguments, then asks to allow or block.
  */
 
 import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
+import { execSync } from "node:child_process";
+import * as path from "node:path";
+
+const ALLOWED_JJ_SUBCOMMANDS = ["diff", "log", "show", "status"];
+
+let trackedFiles: Set<string> | null = null;
+
+function getTrackedFiles(cwd: string): Set<string> {
+	if (trackedFiles) return trackedFiles;
+	try {
+		const output = execSync("jj file list", { cwd, encoding: "utf-8" });
+		trackedFiles = new Set(output.trim().split("\n").map((f) => path.resolve(cwd, f)));
+	} catch {
+		trackedFiles = new Set();
+	}
+	return trackedFiles;
+}
+
+function isAllowed(toolName: string, input: Record<string, unknown>, cwd: string): boolean {
+	if (toolName === "read") {
+		const filePath = path.resolve(cwd, String(input.path ?? ""));
+		return getTrackedFiles(cwd).has(filePath);
+	}
+
+	if (toolName === "bash") {
+		const cmd = String(input.command ?? "").trimStart();
+		if (cmd.startsWith("jj ")) {
+			const subcommand = cmd.slice(3).trimStart().split(/\s/)[0];
+			return ALLOWED_JJ_SUBCOMMANDS.includes(subcommand);
+		}
+	}
+
+	return false;
+}
 
 export default function (pi: ExtensionAPI) {
+	pi.on("tool_result", async (event) => {
+		if (event.toolName === "bash") {
+			const cmd = String(event.input.command ?? "").trimStart();
+			if (cmd.startsWith("jj ")) {
+				trackedFiles = null;
+			}
+		}
+	});
+
 	pi.on("tool_call", async (event, ctx) => {
 		if (!ctx.hasUI) return;
+		if (isAllowed(event.toolName, event.input, ctx.cwd)) return;
 
 		const summary = formatArgs(event.toolName, event.input);
 		const allowed = await ctx.ui.confirm(event.toolName, summary);
diff --git a/.pi/agent/settings.json b/.pi/agent/settings.json
index 36e43037..234ce17a 100644
--- a/.pi/agent/settings.json
+++ b/.pi/agent/settings.json
@@ -1,7 +1,7 @@
 {
   "lastChangelogVersion": "0.52.6",
   "defaultProvider": "anthropic",
-  "defaultModel": "claude-opus-4-6",
+  "defaultModel": "claude-sonnet-4-0",
   "skills": [
     "~/.claude/skills"
   ],