/** * Permission rule matching — replicates Claude Code's actual permission matching logic. * * Two distinct systems depending on tool type: * - Bash rules: custom regex builder (exact | prefix | wildcard) * - Read/Edit/Write/Glob path rules: node-ignore (gitignore spec) * * Sources: decompiled from Claude Code binary v2.1.52 (nA0() function). * * @vendor-claim reviewed=2026-04-08 verify=Re-decompile a current Claude Code binary (or get vendor confirmation) and diff nA0() against the matcher below * * Note the version discrepancy this pin creates: docs/skill-quality-and-compatibility.md * establishes plugin-loader semantics from Claude Code 2.1.126, while the matching * logic here was read out of 2.1.52. Nothing reconciles the two, and no test can: * these are semantics of somebody else's binary, so the suite below can only assert * that our replica is self-consistent, never that it still matches the real one. * The `reviewed=` date above is the 2.1.52 read, not a re-confirmation against 2.1.126. * * KNOWN STALE, AND KNOWINGLY LEFT SO — do not re-investigate this from scratch. The * structural gate (`validateVendorClaimFreshness` in packages/dev-tools/src/validate-repo-structure.ts, * 90-day window, severity `warning`) has this annotation past due and prints a * STALE_VENDOR_CLAIM line on every `bun run validate`. It is a warning, so it blocks * nothing. Refreshing it costs exactly what `verify=` says: obtain and DECOMPILE a * current Claude Code binary, then diff its permission-matching function against the * matcher below. That is the whole cost — there is no cheaper substitute, no public * spec, and no test that can stand in for it. Bump `reviewed=` only after actually * doing the decompile; bumping the date alone converts a true "unwatched" signal into * a false "recently confirmed" one. */ /** Classification of a Bash permission rule */ export type BashRuleType = 'exact' | 'prefix' | 'wildcard'; /** Parsed Bash rule */ export interface ParsedBashRule { type: BashRuleType; /** Normalised rule content (after whitespace normalisation) */ content: string; /** Compiled regex for matching (wildcard type only) */ regex?: RegExp | undefined; } /** * Parse a full permission rule string into tool name and optional content. * Examples: * "Bash(npm run *)" → { toolName: "Bash", content: "npm run *" } * "Edit" → { toolName: "Edit", content: undefined } * "Read(./.env)" → { toolName: "Read", content: "./.env" } */ export declare function parsePermissionRule(rule: string): { toolName: string; content: string | undefined; }; /** * Classify a Bash rule content string into exact | prefix | wildcard. */ export declare function classifyBashRule(content: string): BashRuleType; /** * Parse a Bash rule content string into a ParsedBashRule for matching. */ export declare function parseBashRuleContent(content: string): ParsedBashRule; /** * Check whether a Bash command string matches a parsed Bash rule. * * @param command - The actual command to test (e.g. "git push origin main") * @param parsedRule - The parsed rule to match against */ export declare function matchesParsedBashRule(command: string, parsedRule: ParsedBashRule): boolean; /** * Check whether a Bash command matches a full Bash permission rule string. * Rule format: "Bash(npm run *)" or "Bash(git commit)" or bare "Bash" * * A bare "Bash" (no parens) matches all Bash calls. * * @param command - The actual command * @param rule - Full rule string e.g. "Bash(npm run *)" */ export declare function matchesBashRule(command: string, rule: string): boolean; /** * Check whether a file path matches a Read/Edit/Write/Glob permission rule. * Uses node-ignore (gitignore spec) for matching. * * Path prefixes handled: * - "./" → relative to cwd * - "~/" → relative to homedir * - "//" → absolute (strip one /) * - "/" → relative to project root (cwd) * - no prefix → relative to cwd * * @param filePath - The absolute file path to check * @param ruleContent - The path pattern from the rule (e.g. ".env", "~/.ssh/id_rsa") * @param cwd - Current working directory (for relative paths) */ export declare function matchesPathRule(filePath: string, ruleContent: string, cwd?: string): boolean; /** * Check whether a tool call is blocked by a permission rule. * * Handles both Bash rules (regex-based) and path-tool rules (gitignore-based). * A bare tool name (e.g. "Edit") matches all uses of that tool. * * @param toolName - The tool being called (e.g. "Bash", "Edit") * @param toolInput - For Bash: the command string. For path tools: the file path. * @param rule - Full permission rule string * @param cwd - Current working directory (for path-tool matching) */ export declare function matchesPermissionRule(toolName: string, toolInput: string, rule: string, cwd?: string): boolean; /** * Check whether `narrowRule` is subsumed by `broadRule`. * A broad rule subsumes a narrow rule if the broad rule matches everything the narrow one does. * * Examples: * isSubsumedBy("Bash(git push *)", "Bash(git *)") → true * isSubsumedBy("Bash(git *)", "Bash(*)") → true * isSubsumedBy("Edit", "Edit") → true (same rule) * * @param narrowRule - The narrower (more specific) rule * @param broadRule - The potentially broader rule */ export declare function isSubsumedBy(narrowRule: string, broadRule: string): boolean; //# sourceMappingURL=permission-matcher.d.ts.map