fabro/lib/crates/fabro-cli/Cargo.toml
brynary-fabro[bot] dc34fb0671 fabro upgrade command (#13)
This PR adds a `fabro upgrade` command that downloads and installs new
releases from GitHub, along with a passive daily auto-check that
notifies users when a newer version is available. The upgrade flow
supports two download backends: the `gh` CLI (preferred, for auth and
rate-limit benefits) with an automatic fallback to plain HTTPS via
`reqwest` when `gh` is missing or not authenticated. The command
includes SHA256 checksum verification, atomic binary replacement with
rollback on failure, downgrade protection with interactive confirmation,
and `--dry-run`/`--force` flags.

A background upgrade check runs automatically on common commands (`run`,
`exec`, `init`, `install`), caching results in
`~/.fabro/last_upgrade_check.json` to avoid hitting GitHub more than
once per 24 hours. Users can disable this via `upgrade_check = false` in
`~/.fabro/cli.toml` or the `--no-upgrade-check` global flag. The check
is spawned as an async task and its notice prints to stderr after the
main command completes, ensuring it never blocks or breaks normal
operation—all errors are silently swallowed.

The implementation follows a test-first approach with unit tests
covering platform detection, version parsing, SHA256 verification,
upgrade check state serialization/staleness, and the new `upgrade_check`
config field. Dependencies `tempfile` (promoted from dev-dependencies)
and `sha2` are added to `fabro-cli`.

### Fabro Details

<details>
<summary>Ran 7 stages in 18m 39s for $5.61</summary>

| Stage | Duration | Cost | Retries |
|---|---|---|---|
| start | 0s | – | 0 |
| toolchain | 0s | – | 0 |
| preflight_compile | 0s | – | 0 |
| preflight_lint | 0s | – | 0 |
| implement | 0s | $2.92 | 0 |
| simplify | 0s | $2.68 | 0 |
| verify | 0s | – | 0 |
| **Total** | **18m 39s** | **$5.61** | **0** |

</details>

<details>
<summary>Ran <code>ImplementAndSimplify.fabro</code> (10 nodes and 13
edges)</summary>

```dot
digraph ImplementAndSimplify {
    graph [
        goal="Implement and simplify",
        model_stylesheet="
            * { backend: api; model: claude-opus-4-6;}
        "
    ]
    rankdir=LR

    start [shape=Mdiamond, label="Start"]
    exit  [shape=Msquare, label="Exit"]

    toolchain         [label="Toolchain", shape=parallelogram, script="command -v cargo >/dev/null || { curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y && sudo ln -sf $HOME/.cargo/bin/* /usr/local/bin/; }; cargo --version 2>&1", max_retries=0]
    preflight_compile [label="Preflight Compile", shape=parallelogram, script="cargo check 2>&1", max_retries=0]
    preflight_lint    [label="Preflight Lint", shape=parallelogram, script="cargo clippy -- -D warnings 2>&1", max_retries=0]
    fix_lints         [label="Fix Lints", prompt="The preflight lint step failed. Read the build output from context and fix all clippy lint warnings.", max_visits=3]
    implement         [label="Implement", prompt="Read the plan file referenced in the goal and implement every step. Make all the code changes described in the plan."]
    simplify          [label="Simplify", prompt="@prompts/simplify.md"]
    verify            [label="Verify", shape=parallelogram, script="cargo clippy -- -D warnings 2>&1 && cargo test 2>&1", goal_gate=true, retry_target="fixup"]
    fixup             [label="Fixup", prompt="The verify step failed. Read the build output from context and fix all clippy lint warnings and test failures.", max_visits=3]

    start -> toolchain
    toolchain -> preflight_compile [condition="outcome=success"]
    toolchain -> exit
    preflight_compile -> preflight_lint [condition="outcome=success"]
    preflight_compile -> exit
    preflight_lint -> implement [condition="outcome=success"]
    preflight_lint -> fix_lints
    fix_lints -> preflight_lint
    implement -> simplify -> verify
    verify -> exit  [condition="outcome=success"]
    verify -> fixup
    fixup -> verify
}

```

</details>

⚒️ Generated with [Fabro](https://fabro.sh)

---------

Co-authored-by: Fabro <noreply@fabro.sh>
2026-03-15 19:54:57 -04:00

73 lines
1.8 KiB
TOML

[package]
name = "fabro-cli"
edition.workspace = true
version.workspace = true
license.workspace = true
description = "Unified CLI for the Fabro AI framework"
[[bin]]
name = "fabro"
path = "src/main.rs"
[features]
default = []
server = ["dep:fabro-api"]
exedev = ["fabro-config/exedev", "fabro-workflows/exedev"]
sleep_inhibitor = ["dep:fabro-beastie"]
[dependencies]
fabro-config = { path = "../fabro-config" }
fabro-llm = { path = "../fabro-llm" }
fabro-openai-oauth = { path = "../fabro-openai-oauth" }
fabro-github = { path = "../fabro-github" }
fabro-agent = { path = "../fabro-agent" }
fabro-mcp = { path = "../fabro-mcp" }
fabro-workflows = { path = "../fabro-workflows" }
fabro-api = { path = "../fabro-api", optional = true }
fabro-beastie = { path = "../fabro-beastie", optional = true }
fabro-util = { path = "../fabro-util" }
clap.workspace = true
console.workspace = true
indicatif.workspace = true
daytona-sdk.workspace = true
anyhow.workspace = true
dotenvy.workspace = true
tokio.workspace = true
tracing.workspace = true
tracing-subscriber.workspace = true
tracing-appender.workspace = true
chrono.workspace = true
dirs.workspace = true
serde.workspace = true
toml.workspace = true
futures.workspace = true
regex.workspace = true
semver.workspace = true
reqwest.workspace = true
jsonwebtoken.workspace = true
base64.workspace = true
ulid.workspace = true
rustls = { version = "0.23", default-features = false, features = ["std", "ring"] }
rustls-pemfile = "2"
x509-parser = "0.16"
rand.workspace = true
dialoguer.workspace = true
axum = "0.8"
open = "5"
serde_json.workspace = true
tempfile = "3"
sha2.workspace = true
[target.'cfg(unix)'.dependencies]
libc = "0.2"
[build-dependencies]
chrono = { workspace = true }
[dev-dependencies]
assert_cmd = "2"
insta = { workspace = true }
predicates = "3"
serde_json.workspace = true
httpmock = "0.8"
trycmd = "0.15"