package approval import "encoding/json" // Verdict is what a set of standing rules says about one tool call, before // anyone is asked and before anything the session remembers is consulted. type Verdict int const ( // NoVerdict means no rule spoke, and the call is decided the ordinary way: // the mode, the session's memory, and then a prompt. NoVerdict Verdict = iota // VerdictAllow lets the call through without a prompt. VerdictAllow // VerdictAsk puts the call to a human even when the mode would have let // it through, and even when the session remembers allowing it. VerdictAsk // VerdictDeny refuses the call outright, in every mode, -yolo included. VerdictDeny ) // Ruling is a verdict and the rule that produced it, for the message the model // reads and the prompt a human reads. "Denied" with no reason is a dead end; // "denied by Bash(rm:*) in ~/.claude/settings.json" is something to fix. type Ruling struct { Verdict Verdict // Rule is the rule as it was written, e.g. Bash(npm run test:*). Rule string // Source is where the rule was written, e.g. ~/.claude/settings.json. Source string } // RuleSet is a set of standing rules that act before the session's own // memory: rules written down in a settings file rather than given as an // answer to a prompt. Claude Code's permissions.allow, deny and ask are the // ones that exist so far. // // It is an interface so this package stays ignorant of where rules come from // and of the grammar they are written in. Whoever loads them decides which // files are honoured; the policy only acts on what they say. type RuleSet interface { // Check rules on one call. Deny outranks ask, which outranks allow, so a // single rule set can never be argued out of a refusal by a broader // permission elsewhere in it. Check(tool string, input json.RawMessage) Ruling // Lines describe the rules for /allowed, grouped by where they came from. Lines() []string } // check is Rules.Check, tolerating a policy with no rules. func (p *Policy) check(tool string, input json.RawMessage) Ruling { if p.Rules == nil { return Ruling{} } return p.Rules.Check(tool, input) } // RuleLines is what /allowed shows under the session's own memory: the // standing rules, and the files they came from. Nil when there are none. func (p *Policy) RuleLines() []string { if p == nil || p.Rules == nil { return nil } return p.Rules.Lines() } // because names the rule behind a ruling, for a message. func (r Ruling) because() string { if r.Source == "" { return r.Rule } return r.Rule + " in " + r.Source }