Skip to main content

kaish_types/
command.rs

1//! Command classification — how the kernel will resolve a command name.
2//!
3//! `CommandKind` is the typed answer to "what will kaish actually run for this
4//! name?" It exists so embedders (kaijutsu's consent gate, kaibo) can walk a
5//! parsed script and bucket each command node without re-implementing kaish's
6//! resolution rules. Re-deriving those rules in the embedder forks kaish's
7//! command-resolution truth: the day the kernel refines how a name resolves, a
8//! hand-rolled copy silently disagrees with what kaish will actually execute —
9//! a security-relevant divergence for anything gating external commands.
10
11/// The category the kernel resolves a command name into.
12///
13/// Returned by `Kernel::classify_command`. The classification mirrors the
14/// interpreter's real resolution order (`execute_command_depth`), not the
15/// validator's warning heuristics — it answers "what will run", which is what a
16/// consent gate needs.
17///
18/// The safe direction of any imprecision is to *over*-report `External`: an
19/// embedder gating external commands should never see something classified as
20/// internal that in fact escapes to `PATH`.
21#[non_exhaustive]
22#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
23#[serde(rename_all = "snake_case")]
24pub enum CommandKind {
25    /// A built-in tool run in-process (e.g. `cat`, `grep`, `jq`).
26    Builtin,
27    /// A user-defined function (`fn name { … }`) — shadows a builtin of the same
28    /// name, matching the interpreter's user-tools-first resolution.
29    UserTool,
30    /// An interpreter special-form handled directly, never resolved through the
31    /// registry or `PATH`: `true`, `false`, `source`, `.`.
32    Special,
33    /// The name can't be resolved statically because it's a variable or
34    /// command-substitution expansion (`$cmd`, `$(pick)`). The embedder must
35    /// treat it conservatively.
36    Dynamic,
37    /// Not a builtin, user function, or special-form: kaish will look it up as a
38    /// `.kai` script or external binary on `PATH`. This is the bucket a consent
39    /// gate cares about.
40    External,
41}
42
43impl CommandKind {
44    /// True when the name escapes the kernel to a `PATH` lookup (`External`) or
45    /// can't be resolved statically (`Dynamic`) — the two cases an external-command
46    /// consent gate must scrutinize.
47    pub fn escapes_kernel(self) -> bool {
48        matches!(self, CommandKind::External | CommandKind::Dynamic)
49    }
50}