keelrun-cli 0.4.1

The `keel` binary: run | init | doctor | status | explain. The product's face — every command has a byte-deterministic `--json` twin and stable exit codes (dx-spec §1–2, §5–6).
Documentation
[package]
name = "keelrun-cli"
description = "The `keel` binary: run | init | doctor | status | explain. The product's face — every command has a byte-deterministic `--json` twin and stable exit codes (dx-spec §1–2, §5–6)."
version.workspace = true
edition.workspace = true
rust-version.workspace = true
repository.workspace = true
license.workspace = true
readme.workspace = true
# Publishable (`cargo install keelrun-cli` → the `keel` binary; the plain
# `keel-cli` name is taken on crates.io by an unrelated project). The contract
# files the binary embeds are vendored into contract/ (drift-checked by
# build.rs); tests/ is excluded from the package because the integration
# tests embed repo-level conformance fixtures.
exclude = ["tests/"]

[lib]
name = "keel_cli"
path = "src/lib.rs"

[[bin]]
name = "keel"
path = "src/main.rs"

[dependencies]
keel-core-api = { path = "../keel-core-api", version = "0.4.1" }
keel-journal = { path = "../keel-journal", version = "0.4.1" }
# The real Tier-2 flow manager: `keel exec` (WS6, CCR-4) drives a `cmd:` flow
# through `FlowManager` (identity/lease/attempts/completion) over the same
# journal instance, so the CLI now links the engine, not just reads its files.
keel-core = { package = "keelrun-core", path = "../keel-core", version = "0.4.1" }
clap = { version = "4", features = ["derive"] }
# Read `.keel/journal.db` for `keel status` directly. The schema is frozen
# (contracts/journal.sql); keel-journal owns writes, the CLI only reads.
rusqlite = { version = "0.40.1", features = ["bundled"] }
# Decode journal step payloads (MessagePack, schema-tagged) for
# `keel replay --step` — same codec the core writes with.
rmp-serde = "1.3"
serde = { workspace = true }
serde_json = { workspace = true }
serde_path_to_error = { workspace = true }
# Parse the user's keel.toml for `doctor` validation and `init --diff`.
toml = "0.8"
# The Python front end's exact flow-hash convention (`_flow.py::_code_hash`):
# re-derived to detect resumable flows whose script changed since entry (WS6).
sha2 = { workspace = true }
# JS/TS/JSX parsing for the static scan (dx-spec §2) — pure Rust, so
# `keel init` needs no Node toolchain. Pinned exactly: oxc's AST/visit API
# churns between minors; bump all six crates together.
oxc_allocator = "=0.139.0"
oxc_ast = "=0.139.0"
oxc_ast_visit = "=0.139.0"
oxc_parser = "=0.139.0"
oxc_span = "=0.139.0"
oxc_syntax = "=0.139.0"
# Surgical keel.toml edits for applyable policy diffs (dx-spec §5): operate on
# the TOML *document*, so user formatting/comments outside touched regions
# survive byte-for-byte. Already in-tree as toml's own backend.
toml_edit = "0.22"

# `keel exec`'s dead-holder detection (WS6): `libc::kill(pid, 0)` probes a
# recorded holder's PID on unix; other platforms treat it as alive (the lease
# TTL is the reuse backstop). Unix-only so Windows builds carry no libc.
[target.'cfg(unix)'.dependencies]
libc = "0.2"

[dev-dependencies]
tempfile = "3.23"
# Integration tests build fixture DBs the way the front ends do: the journal via
# the frozen schema (rusqlite), the discovery store via keel-journal's own API.
keel-journal = { path = "../keel-journal" }
rusqlite = { version = "0.40.1", features = ["bundled"] }
# The `keel tail` parity leg (tests/tail.rs): the real engine writes an event
# feed through its sink and the CLI must render it byte-exactly, so the tail
# renderer can never drift from the engine's event vocabulary unnoticed.
keel-core = { package = "keelrun-core", path = "../keel-core" }
tokio = { version = "1.47", features = ["macros", "rt", "test-util", "time"] }

# This crate cannot inherit `[workspace.lints]`: the workspace FORBIDS
# unsafe_code (an absolute level no inner attribute can relax), but `keel exec`'s
# dead-holder detection needs two libc calls — `kill(pid, 0)` to probe a recorded
# holder's PID and `gethostname` to fence the probe to this host (WS6, CCR-4).
# So we restate the workspace lint posture verbatim, changing only unsafe_code —
# every other standard stays in force and CI still runs clippy pedantic
# `-D warnings` over this crate. The two unsafe blocks (exec.rs) are narrow with
# `// SAFETY:` notes.
[lints.rust]
unsafe_code = "allow"
missing_debug_implementations = "warn"
unused_qualifications = "warn"
rust_2018_idioms = { level = "warn", priority = -1 }

[lints.clippy]
all = { level = "warn", priority = -1 }
pedantic = { level = "warn", priority = -1 }
doc_markdown = "allow"
missing_errors_doc = "allow"
missing_panics_doc = "allow"
must_use_candidate = "allow"
module_name_repetitions = "allow"