heddle-cli 0.4.0

An AI-native version control system
Documentation
[package]
name = "heddle-cli"
version.workspace = true
edition.workspace = true
authors.workspace = true
description.workspace = true
license.workspace = true
repository.workspace = true
keywords.workspace = true
categories.workspace = true
readme = "../../README.md"
# Exclude bulky integration-test fixtures (~24 MB of git-shaped tarballs)
# from the published tarball — they push the package over crates.io's
# 10 MB limit and only matter for tests run inside the workspace.
exclude = [
    "tests/realworld_git/fixtures/*",
    "tests/snapshots/**/*",
]

[dependencies]
# `anstyle` 1.x is already in our transitive deps via clap. Pinning it
# directly here makes `crates/cli/src/cli/style.rs` a first-class
# consumer; the version range matches what `cargo tree` resolves.
anstyle = "1"
anyhow.workspace = true
base64.workspace = true
blake3.workspace = true
chrono.workspace = true
clap.workspace = true
clap_complete.workspace = true
futures.workspace = true
hex.workspace = true
ignore.workspace = true
libc.workspace = true
objects = { path = "../objects", package = "heddle-objects", version = "0.4", features = ["memory-backend"] }
crypto = { path = "../crypto", package = "heddle-crypto", version = "0.4" }
daemon = { path = "../daemon", package = "heddle-daemon", version = "0.4" }
# `heddle-merge` is the native hunk-level text merge engine extracted from
# `heddle-semantic` so non-semantic builds (`--no-default-features`) retain
# text auto-merge. Always-on: the merge driver and rebase replay both call
# into it unconditionally.
merge = { path = "../merge", package = "heddle-merge", version = "0.4" }
grpc = { path = "../grpc", package = "heddle-grpc", version = "0.7" }
cli-shared = { path = "../cli-shared", package = "heddle-cli-shared", version = "0.4" }
heddle-client = { path = "../client", version = "0.4", optional = true }
weft-client-shim = { path = "../weft-client-shim", package = "weft-client-shim", version = "0.4" }
async-trait.workspace = true
# `default-features = false` here matches the pattern used for
# repo below: this crate's `zstd` feature controls the cascade
# end-to-end. Without these breaks, every dep's own default-on `zstd`
# would re-enable compression even when the user explicitly built
# with `--no-default-features`.
ingest = { path = "../ingest", package = "heddle-ingest", version = "0.4", default-features = false }
oplog = { path = "../oplog", package = "heddle-oplog", version = "0.4" }
wire = { path = "../wire", package = "heddle-wire", version = "0.4", default-features = false }
refs = { path = "../refs", package = "heddle-refs", version = "0.4" }
repo = { path = "../repo", package = "heddle-repo", version = "0.4", default-features = false }
semantic = { path = "../semantic", package = "heddle-semantic", version = "0.4", optional = true }
notify.workspace = true
opentelemetry = { workspace = true, optional = true }
opentelemetry-otlp = { workspace = true, optional = true }
opentelemetry_sdk = { workspace = true, optional = true }
prost-types.workspace = true
rmp-serde.workspace = true
schemars.workspace = true
serde.workspace = true
serde_json.workspace = true
sha2.workspace = true
sley.workspace = true
thiserror.workspace = true
tokio.workspace = true
tokio-tungstenite = { workspace = true, optional = true }
toml.workspace = true
tonic.workspace = true
# `uuid::Uuid::new_v4().simple()` suffixes rebase transaction ids
# (`rebase_ops::mint_rebase_transaction_id`) so a nanosecond-resolution
# clock collision can't silently drop the later rebase's batch via
# `flush_rebase_batch`'s dedup (heddle#198 r3 / Codex PR #218 P2).
uuid.workspace = true
# Health.Check probe for the local daemon UDS handshake. Only
# pulled in for the client side; the local daemon binary itself does
# not (yet) install a health reporter — see `client::local_daemon`.
tonic-health.workspace = true
# `tower::service_fn` adapts `tokio::net::UnixStream::connect` into a
# `tower::Service<Uri>` that tonic's `connect_with_connector` accepts.
# 0.5 is what tonic itself resolves transitively; pinning to the same
# major version keeps the trait impls compatible.
tower = { version = "0.5", default-features = false, features = ["util"] }
# `hyper-util`'s `TokioIo` adapter wraps a `UnixStream` into something
# that satisfies hyper's `Read`/`Write` traits — required by tonic
# 0.14's connector contract. Already a workspace dep used by the
# server crate; promoting it here for first-class consumption.
hyper-util.workspace = true
rustls.workspace = true
tracing.workspace = true
tracing-opentelemetry = { workspace = true, optional = true }
tracing-subscriber.workspace = true
walkdir.workspace = true

# Mount is platform-agnostic in source, but each adapter only
# compiles for one OS — FUSE on Linux (`fuse`), FSKit on macOS
# (`fskit`), ProjFS on Windows (`projfs`). We take the `mount`
# dep on each target separately so the CLI's `mount` feature can
# propagate the right per-OS backend without any one platform
# being forced to drag in another platform's kernel bindings.
[target.'cfg(target_os = "linux")'.dependencies]
mount = { path = "../mount", package = "heddle-mount", version = "0.4", optional = true }

[target.'cfg(target_os = "macos")'.dependencies]
mount = { path = "../mount", package = "heddle-mount", version = "0.4", optional = true }

[target.'cfg(target_os = "windows")'.dependencies]
mount = { path = "../mount", package = "heddle-mount", version = "0.4", optional = true }

[features]
# Keep the default `heddle` build focused on the local invisible-VCS
# workflow. Heavier import/hosted surfaces are opt-in for release
# builds that need them. `semantic` is included in defaults because
# `heddle diff --semantic` and the
# agent-loop self-review (`/heddle-review`) all reach for it; gating
# the default install behind an extra `--features semantic` flag
# turns these documented commands into an opaque error for first-run
# users. The binary cost is the four `tree-sitter-*` parsers
# (Rust/Python/JS/TS) — a small fraction of the existing footprint
# from tonic + server.  `semantic-extended` (Go/C/C++/Java) stays
# opt-in.
# `mount` is in the default set so a stock `cargo install heddle-cli`
# (or `cargo install --path crates/cli`) ships `heddle-fuse-worker`
# next to `heddle`. Without it, the sibling-binary lookup in
# `mount::worker::default_worker_binary` doesn't resolve and the
# mount lifecycle silently falls back to NFS on every Linux user
# who installed via the documented flow (heddle#190 r5 / Codex PR
# #225 P2). The platform-specific backends (`fuser` on Linux,
# `windows` on Windows, `cbindgen`/Swift on macOS) are target-gated
# inside `heddle-mount`, so default-on is platform-blind: each host
# pulls only its own native adapter plus the platform-agnostic NFS
# fallback (`nfsserve` + `tokio` async-trait). The OSS feature
# subsets that explicitly want a slim build (`--no-default-features
# --features git-overlay,...`) opt out the same way they do today.
default = ["git-overlay", "native", "local", "mount", "semantic", "zstd"]
local = []
# Repository mode features. At least one must be enabled. Released as
# three OSS CLI flavors: `git-overlay` only (works on top of existing
# Git repos; no native content-addressed init), `native` only (pure
# content-addressed VCS; no Git bridge), or both. The closed `heddle-hosted`
# build adds `client` on top of either / both.
git-overlay = ["repo/git-overlay", "ingest"]
native = ["repo/native"]
client = ["dep:heddle-client", "dep:tokio-tungstenite"]
# Deep git import + AI-session reasoning extraction now backs every Git
# overlay import path. Kept as a feature name for existing feature sets,
# but the dependency is always present so no legacy bridge importer is
# compiled as a fallback.
ingest = []
s3 = ["repo/s3"]
semantic = ["dep:semantic", "repo/tree-sitter-symbols"]
semantic-extended = ["semantic", "semantic?/extended-languages"]
observability = [
    "dep:opentelemetry",
    "dep:opentelemetry-otlp",
    "dep:opentelemetry_sdk",
    "dep:tracing-opentelemetry",
]
# Forward zstd compression to every layer that touches the pack
# format. The `?` syntax on optional deps activates the feature only
# when the dep itself is enabled (`ingest` is gated behind
# the `ingest` feature).
zstd = [
    "objects/zstd",
    "wire/zstd",
    "repo/zstd",
    "ingest/zstd",
]
# Wire the content-addressed mount through the CLI. Off by
# default — each native backend pulls in a kernel-side dep that
# only exists on its target OS (`fuser` on Linux, `swiftc`-compiled
# FSKit shim on macOS, `windows` crate's ProjFS bindings on
# Windows). Activate with `cargo build --features mount` on any
# of the three; the per-target cfg gates in `crates/mount/src/lib.rs`
# select the correct native backend. Cargo features are
# platform-blind so all backend features propagate from this one —
# the inactive ones compile to zero code via the cfg gates.
#
# The `nfs` feature is the universal fallback: a platform-agnostic
# adapter that stands up an in-process NFSv3 server and asks the
# host's built-in NFS client to mount it. The CLI's mount
# lifecycle prefers the host's native adapter and falls back to
# NFS at runtime when the native one is unavailable (missing
# kernel module, missing System Extension, missing optional
# feature, etc).
mount = [
    "dep:mount",
    "mount?/fuse",
    "mount?/fskit",
    "mount?/projfs",
    "mount?/nfs",
]

[dev-dependencies]
criterion = "0.8"
hyper-util.workspace = true
review = { path = "../review", package = "heddle-review" }
ntest.workspace = true
proptest.workspace = true
tokio-tungstenite.workspace = true
serial_test.workspace = true
tempfile.workspace = true
uuid.workspace = true

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

# Linux-only crash-isolation worker. Co-located in the CLI package
# so `cargo install --path crates/cli` (and `cargo install heddle`
# from crates.io) installs both `heddle` and `heddle-fuse-worker`
# into the same bin dir — the sibling-binary lookup in
# `mount::worker::default_worker_binary` only resolves when the two
# ship together. The file itself is `cfg(target_os = "linux",
# feature = "mount")`, so non-Linux and mount-less builds compile to
# nothing here.
[[bin]]
name = "heddle-fuse-worker"
path = "src/bin/heddle-fuse-worker.rs"
required-features = ["mount"]

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

# Integration tests for cli live under `tests/` (auto-discovered) so
# that `cargo publish` can package the crate cleanly.

[[test]]
name = "semantic_diff_integration"
path = "tests/semantic_diff_integration.rs"
required-features = ["semantic"]

[[bench]]
name = "local_ops"
harness = false
required-features = ["semantic"]

[[bench]]
name = "clonefile_threads"
harness = false