Allow reading from skills directories in permission gate
Permission gate now auto-allows reading files from configured skills
directories (e.g. ~/.claude/skills) in addition to tracked files.
This enables seamless access to skill documentation and examples.

Assisted-by: Claude Sonnet 4.0 via Claude Code
change yuwnzvoysyrykspvoztouukurolsvmtx
commit fd40daf0a0e01b4ecaee8524bf79c65294b03968
author Alpha Chen <alpha@kejadlen.dev>
date
parent vwozozmy
diff --git a/.pi/agent/extensions/permission-gate.ts b/.pi/agent/extensions/permission-gate.ts
index 79e55cb4..05734a7b 100644
--- a/.pi/agent/extensions/permission-gate.ts
+++ b/.pi/agent/extensions/permission-gate.ts
@@ -3,16 +3,20 @@
  *
  * Requires explicit user confirmation before every tool call.
  * Auto-allows read-only operations (read tool, non-destructive jj commands).
+ * Auto-allows reading from tracked files and skills directories.
  * 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";
+import * as fs from "node:fs";
+import * as os from "node:os";
 
 const ALLOWED_JJ_SUBCOMMANDS = ["diff", "log", "show", "status"];
 
 let trackedFiles: Set<string> | null = null;
+let skillsDirs: string[] | null = null;
 
 function getTrackedFiles(cwd: string): Set<string> {
 	if (trackedFiles) return trackedFiles;
@@ -25,10 +29,33 @@ function getTrackedFiles(cwd: string): Set<string> {
 	return trackedFiles;
 }
 
+function getSkillsDirectories(cwd: string): string[] {
+	if (skillsDirs) return skillsDirs;
+	try {
+		const settingsPath = path.join(cwd, ".pi", "agent", "settings.json");
+		const settings = JSON.parse(fs.readFileSync(settingsPath, "utf-8"));
+		skillsDirs = (settings.skills || []).map((skillPath: string) => {
+			// Handle ~ expansion
+			if (skillPath.startsWith("~/")) {
+				return path.resolve(os.homedir(), skillPath.slice(2));
+			}
+			return path.resolve(cwd, skillPath);
+		});
+	} catch {
+		skillsDirs = [];
+	}
+	return skillsDirs;
+}
+
+function isInSkillsDirectory(filePath: string, cwd: string): boolean {
+	const skillsDirs = getSkillsDirectories(cwd);
+	return skillsDirs.some(skillsDir => filePath.startsWith(skillsDir + path.sep));
+}
+
 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);
+		return getTrackedFiles(cwd).has(filePath) || isInSkillsDirectory(filePath, cwd);
 	}
 
 	if (toolName === "bash") {
@@ -50,6 +77,12 @@ export default function (pi: ExtensionAPI) {
 				trackedFiles = null;
 			}
 		}
+		if (event.toolName === "write" || event.toolName === "edit") {
+			const filePath = String(event.input.path ?? "");
+			if (filePath.includes("settings.json")) {
+				skillsDirs = null;
+			}
+		}
 	});
 
 	pi.on("tool_call", async (event, ctx) => {
diff --git a/.pi/agent/settings.json b/.pi/agent/settings.json
index 234ce17a..36e43037 100644
--- a/.pi/agent/settings.json
+++ b/.pi/agent/settings.json
@@ -1,7 +1,7 @@
 {
   "lastChangelogVersion": "0.52.6",
   "defaultProvider": "anthropic",
-  "defaultModel": "claude-sonnet-4-0",
+  "defaultModel": "claude-opus-4-6",
   "skills": [
     "~/.claude/skills"
   ],