# `Pixir.Permissions`
[🔗](https://github.com/Ranvier-Technologies/pixir/blob/main/lib/pixir/permissions.ex#L1)

Permission policy (ADR 0006). Pure decision function — the Executor consults it and,
for `:ask`, calls a front-end-supplied *asker*.

Modes:

  * `:auto` (default) — everything is allowed; no prompts. The blessed common path.
  * `:ask` — only genuinely risky operations prompt. `read` never asks; `bash`
    commands on a conservative safe-list auto-run; `write` and non-safe `bash` ask.
  * `:read_only` — mutating tools are denied; reads and safe commands run.

Workspace confinement is enforced elsewhere (the tools) and is the real floor in every
mode — this layer is a convenience gate on top. Shell-token confinement is a
conservative pre-exec tripwire, not a shell parser or sandbox: it checks visible path
tokens, while intentionally ignoring RHS values of leading POSIX environment
assignments before a simple command, so residual runtime expansion such as
`VAR=/outside cmd $VAR` remains out of scope.

# `decision`

```elixir
@type decision() :: :allow | :deny | {:ask, String.t()}
```

# `mode`

```elixir
@type mode() :: :auto | :ask | :read_only
```

# `classify_parent_directory_token`

```elixir
@spec classify_parent_directory_token(term()) :: {:ok, boolean()}
```

Classify whether a shell token contains a parent-directory path segment.

# `decide`

```elixir
@spec decide(mode(), String.t(), map()) :: decision()
```

Decide whether a tool call may run under `mode`.

# `modes`

```elixir
@spec modes() :: [mode()]
```

All valid permission modes.

# `mutating?`

```elixir
@spec mutating?(String.t(), map()) :: boolean()
```

Whether a tool call mutates state (and so is gated outside `:auto`).

# `mutating_find_predicate?`

```elixir
@spec mutating_find_predicate?(term()) :: boolean()
```

Whether a `find` token is a mutating predicate such as `-delete` or `-exec`.

# `outside_workspace_shell_token`

```elixir
@spec outside_workspace_shell_token(term(), String.t()) :: {:ok, String.t() | nil}
```

Return the first shell token that resolves outside `workspace`.

This is a conservative workspace-confinement tripwire for shell-shaped commands. It
catches explicit parent-directory references, absolute paths outside the workspace,
home/env-home references, and relative paths whose deepest existing prefix resolves
outside the workspace. Leading POSIX environment assignments before a simple command
do not contribute their RHS as a path candidate; literal path arguments, redirection
targets, and non-leading `NAME=VALUE` arguments are still checked. It is not a full
shell parser or sandbox; callers should still keep shell execution behind their normal
permission and command-boundary gates.

# `safe_command?`

```elixir
@spec safe_command?(String.t()) :: boolean()
```

Whether a shell command is read-only and safe to auto-run: command executable
on the safe-list, no shell metacharacters, no parent-directory path references,
no mutating `git` subcommand, and no mutating `find` predicate.

# `strip_shell_quotes`

```elixir
@spec strip_shell_quotes(String.t()) :: String.t()
```

Strip simple single/double quote wrapping from a shell token.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
