On this page
Core logic for managing profile permission rules.
#claudewheel.permission
#claudewheel.permission
Core logic for managing profile permission rules.
#validate_rule
def validate_rule(rule: str) -> NoneRaise ValueError if rule is not a valid permission rule string.
Rules are either bare tool names (Bash) or tool-with-pattern (Bash(git diff:*)). Empty, whitespace-only, and malformed strings are rejected.
#load_settings
def load_settings(settings_path: Path) -> dict[str, Any]Read and parse a profile's settings.json.
Raises FileNotFoundError if the file does not exist and json.JSONDecodeError if the content is not valid JSON.
#save_settings
def save_settings(settings_path: Path, data: dict[str, Any]) -> NoneAtomic-write data as JSON to settings_path.
Writes to a uniquely named staging file in the target's own directory first, then commits it with os.replace to avoid partial writes, preserving the file's mode.
#add_rule
def add_rule(data: dict[str, Any], category: str, rule: str) -> strAppend rule to data["permissions"][category].
Returns "added" on success or "already present" if the rule already exists in the list. The list is never sorted -- append only.
#remove_rule
def remove_rule(data: dict[str, Any], category: str, rule: str) -> strRemove rule from data["permissions"][category].
Returns "removed" on success or "not found" if the rule is not in the list.
#resolve_profiles
def resolve_profiles(ws: 'Workspace', profile: str | None, all_profiles: bool) -> list[tuple[str, Path]]Map the mutex flag values to a list of (name, settings_path) pairs.
"No target chosen" is refused rather than read as "every profile". The mutex group does not guarantee a choice arrives here: strictcli elects a string member on PRESENCE with any value, so --profile '' satisfies the group, and the CLI handlers normalize that empty string to None before calling. Both sides then arrive false with the parser satisfied. (The other spelling is gone: since strictcli 0.40.0 a present-but-false negatable boolean no longer elects, so --no-all-profiles is refused at parse time.)
Prints to stderr and exits on error. Enumeration uses the workspace's ProfileStore, so a corrupt token entry raises TokenStoreError -- the uniform hard-error contract; permission commands are settings.json operations, but an unreadable token entry is a workspace-integrity problem the operator must fix.