1 # GitHub Copilot Instructions — smbcloud-cli
2
3 ## Project Overview
4
5 `smbcloud-cli` is a Rust CLI tool that ships as the `smb` binary. It lets users authenticate, manage projects, and trigger deployments on the smbCloud platform. The workspace targets Rust 1.93 (edition 2021) and is built as a Cargo workspace with multiple focused crates.
6
7 ## Workspace Structure
8
9 | Crate | Role |
10 |---|---|
11 | `crates/cli` | Main binary (`smb`) — all commands, entrypoint |
12 | `crates/smbcloud-model` | Shared `serde` data models (source of truth for API shapes) |
13 | `crates/smbcloud-network` | Network config, environment resolution, connectivity check |
14 | `crates/smbcloud-networking` | Core HTTP client (`SmbClient`) |
15 | `crates/smbcloud-auth` | Auth SDK API calls — login, logout, me |
16 | `crates/smbcloud-networking-project` | Project API calls |
17 | `crates/smbcloud-utils` | Shared utility helpers |
18 | `crates/gresiq` | Internal tooling |
19
20 ### CLI Source Layout (`crates/cli/src/`)
21
22 ```
23 main.rs — tokio entrypoint, logging setup, command dispatch
24 lib.rs — crate root, SmbClient accessor
25 cli/ — Cli struct, Commands enum, CommandResult
26 account/ — login, logout, me handlers
27 deploy/ — deploy command handler
28 project/ — project init, crud, process handlers
29 token/ — token storage/retrieval
30 ui/ — terminal output helpers (spinners, styles)
31 ```
32
33 ## Dependency Graph
34
35 ```
36 crates/cli
37 └── smbcloud-auth
38 └── smbcloud-networking-project
39 └── smbcloud-networking (SmbClient)
40 └── smbcloud-model (API types)
41 └── smbcloud-network (Environment, connectivity)
42 └── smbcloud-utils
43 ```
44
45 All workspace-level dependencies are declared **once** in the root `Cargo.toml` under `[workspace.dependencies]`. Crate-level `Cargo.toml` files must inherit from workspace with `{ workspace = true }`.
46
47 ## Coding Guidelines
48
49 ### Correctness & Clarity
50
51 - Prioritize correctness and clarity. Performance is a secondary concern unless explicitly stated.
52 - Comments explain **why**, never **what**. Do not write comments that summarize or re-describe the code.
53 - Implement new functionality in existing files when it fits logically. Avoid creating many small, single-purpose files.
54
55 ### Error Handling
56
57 - Use `?` to propagate errors. Never call `.unwrap()` or `.expect()` outside of test code.
58 - Never silently discard errors with `let _ =` on fallible operations:
59 - Propagate with `?` when the caller should handle it
60 - Use explicit `match` or `if let Err(e) = ...` when custom recovery logic is needed
61 - Log and ignore only when truly intentional and visibility is preserved
62 - All command handlers return `anyhow::Result<CommandResult>`. Surface user-facing errors with `anyhow!("descriptive message")`.
63 - Ensure async errors propagate to the top level so the user sees meaningful terminal output.
64
65 ### Naming & Style
66
67 - Use full words for all variable and parameter names — no single-letter names, no abbreviations (e.g. `environment` not `env`, `client` not `c`).
68 - Use variable shadowing to scope `.clone()` calls inside async closures, minimizing borrow lifetimes:
69 ```rust
70 cx.spawn({
71 let handle = handle.clone();
72 async move {
73 handle.do_work().await;
74 }
75 });
76 ```
77
78 ### Module Structure
79
80 - **Never create `mod.rs` files.** Use `src/some_module.rs` instead of `src/some_module/mod.rs`.
81 - When creating new crates, set `[lib] path = "src/<crate_name>.rs"` in `Cargo.toml` instead of relying on the default `lib.rs`.
82
83 ### Panics & Bounds
84
85 - Avoid any operation that can panic at runtime: no `.unwrap()`, no unchecked indexing, no `.expect()` in production code.
86 - When iterating or slicing, always validate bounds or use safe alternatives like `.get()`.
87
88 ## Architecture Patterns
89
90 ### Adding a New Command
91
92 1. Add a variant to the `Commands` enum in `crates/cli/src/cli/`
93 2. Create a handler function in `crates/cli/src/<domain>/`
94 3. Wire it in the `match cli.command { ... }` block in `main.rs`
95 4. Mark commands that require internet access in the `needs_internet` match block
96
97 ### Adding a New API Call
98
99 1. Add or update the request/response types in `crates/smbcloud-model/src/`
100 2. Implement the call in the relevant `crates/smbcloud-networking-*/src/` crate using `SmbClient`
101 3. Call the networking function from the CLI command handler
102
103 ### Environment & Auth
104
105 - The `Environment` type (from `smbcloud-network`) is threaded through all command handlers — always accept it as the first parameter.
106 - Auth tokens are managed through the `token` module — never hardcode credentials.
107 - The `CLI_CLIENT_SECRET` comes from the `.env` file via `dotenv_codegen` at compile time.
108
109 ### Terminal Output
110
111 - Use the `ui` module helpers for consistent spinner, progress, and styled output.
112 - Use `console::style(...)` for coloured terminal text.
113 - Errors are printed in red with a `✘` prefix (handled in `main.rs`).
114
115 ## Build & Test Commands
116
117 ```bash
118 # Verify compilation across the whole workspace
119 cargo check --workspace
120
121 # Build release binary
122 cargo build --release
123
124 # Run all tests
125 cargo test --all-features
126
127 # Lint — treat warnings as errors
128 cargo clippy --workspace --tests -- -D warnings
129
130 # Format
131 cargo fmt --all
132
133 # Check formatting without modifying files
134 cargo fmt --all -- --check
135
136 # Publish all workspace crates (requires cargo-workspaces)
137 cargo workspaces publish --publish-as-is
138 ```
139
140 ## Common Pitfalls
141
142 1. **`.env` must exist** with `CLI_CLIENT_SECRET` before building. CI generates it from secrets; locally you must create it manually.
143 2. **No `mod.rs`** — always use named files for submodules.
144 3. **All new dependencies go in the root `Cargo.toml`** under `[workspace.dependencies]`, then inherit them in each crate with `{ workspace = true }`.
145 4. **`#[tokio::main]` lives only in `main.rs`** — all other async entry points are plain `async fn`.
146 5. **The binary is named `smb`**, not `smbcloud-cli` — it comes from `[[bin]] name = "smb"` in `crates/cli/Cargo.toml`.
147 6. **`SmbClient` is a zero-cost static reference** — obtain it with the `client()` helper in `lib.rs`, don't construct it ad-hoc.
148 7. **Do not add crate-local dependencies** without first checking the workspace root — duplicate version declarations break the workspace resolver.
149
150 ## Pull Request Conventions
151
152 - Use a clear, imperative PR title: `Fix crash when project name is empty`, not `fixed stuff`.
153 - No conventional-commit prefixes (`feat:`, `fix:`, etc.) in the PR title.
154 - Include a `Release Notes:` section at the end of the PR body:
155 ```
156 Release Notes:
157
158 - Fixed ...
159 ```
160 Use `- N/A` for non-user-facing changes.