Skip to content

Commit 600ff96

Browse files
committed
setup: Implement setup orchestration and flake-level verification
Wire `sce setup` to execute repository-root installation for resolved targets, replace placeholder setup messaging with deterministic completion output, and mark setup as implemented in the command surface. Add focused parser/run-path coverage and integrate a dedicated CLI setup command-surface check into `nix flake check`.
1 parent d95d10b commit 600ff96

14 files changed

Lines changed: 353 additions & 111 deletions

cli/README.md

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@
33
This crate provides the early command-surface scaffold for the Shared Context
44
Engineering CLI (`sce`).
55

6-
Current scope is intentionally narrow: deterministic command dispatch, explicit
7-
placeholder messaging, and service contracts that reserve future implementation
8-
seams.
6+
Current scope is intentionally narrow: deterministic command dispatch, an
7+
implemented repository `setup` flow, and explicit placeholders for commands
8+
that are still deferred.
99

1010
## Quick start
1111

@@ -20,7 +20,15 @@ cargo run --manifest-path cli/Cargo.toml -- sync
2020
## Current behavior
2121

2222
- `help` is implemented and prints the current command surface.
23-
- `setup` is a placeholder that returns a deferred setup-plan message.
23+
- `setup` is implemented:
24+
- default mode is interactive target selection (`OpenCode`, `Claude`,
25+
`Both`) via `inquire`
26+
- non-interactive mode is available with one mutually-exclusive flag:
27+
`--opencode`, `--claude`, or `--both`
28+
- setup assets are embedded at compile time from `config/.opencode/**` and
29+
`config/.claude/**`
30+
- installation writes to repository-root `.opencode/` and/or `.claude/`
31+
using backup-and-replace safety with rollback on swap failures
2432
- `mcp` is a placeholder for future file-cache tooling contracts
2533
(`cache-put`/`cache-get`).
2634
- `hooks` is a placeholder for future git hook event and generated-region
@@ -30,8 +38,8 @@ cargo run --manifest-path cli/Cargo.toml -- sync
3038

3139
## Safety and limitations
3240

33-
- Placeholder commands do not perform repository setup, MCP transport, hook
34-
installation, or cloud sync.
41+
- `mcp`, `hooks`, and `sync` remain placeholders and do not perform MCP
42+
transport, hook installation, or cloud sync.
3543
- `sync` only validates local adapter wiring and does not require remote auth.
3644
- This crate is scaffolding for incremental delivery and should not be treated
3745
as production-ready workflow automation.
@@ -53,3 +61,10 @@ cargo check --manifest-path cli/Cargo.toml
5361
cargo test --manifest-path cli/Cargo.toml
5462
cargo build --manifest-path cli/Cargo.toml
5563
```
64+
65+
Run repository flake checks (includes targeted setup command-surface checks from
66+
`cli/`):
67+
68+
```bash
69+
nix flake check
70+
```

cli/flake.lock

Lines changed: 61 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

cli/flake.nix

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
{
2+
description = "SCE CLI flake";
3+
4+
inputs = {
5+
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
6+
flake-utils.url = "github:numtide/flake-utils";
7+
};
8+
9+
outputs =
10+
{
11+
self,
12+
nixpkgs,
13+
flake-utils,
14+
}:
15+
flake-utils.lib.eachDefaultSystem (
16+
system:
17+
let
18+
pkgs = import nixpkgs {
19+
inherit system;
20+
};
21+
in
22+
{
23+
checks.cli-setup-command-surface = pkgs.rustPlatform.buildRustPackage {
24+
pname = "sce-cli-setup-command-surface-check";
25+
version = "0.1.0";
26+
src = builtins.path {
27+
path = ../.;
28+
name = "source";
29+
};
30+
sourceRoot = "source/cli";
31+
32+
cargoLock = {
33+
lockFile = ../cli/Cargo.lock;
34+
};
35+
36+
nativeBuildInputs = [ pkgs.rustfmt ];
37+
38+
buildPhase = ''
39+
runHook preBuild
40+
runHook postBuild
41+
'';
42+
43+
checkPhase = ''
44+
runHook preCheck
45+
46+
cargo fmt --check
47+
cargo test command_surface::tests::help_text_mentions_setup_target_flags
48+
cargo test parser_routes_setup
49+
cargo test run_setup_reports
50+
51+
runHook postCheck
52+
'';
53+
54+
installPhase = ''
55+
runHook preInstall
56+
mkdir -p "$out"
57+
runHook postInstall
58+
'';
59+
};
60+
}
61+
);
62+
}

cli/src/app.rs

Lines changed: 37 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use std::process::ExitCode;
22

33
use crate::{command_surface, dependency_contract, services};
4-
use anyhow::{bail, Result};
4+
use anyhow::{bail, Context, Result};
55

66
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
77
enum Command {
@@ -130,7 +130,12 @@ fn dispatch(command: Command) -> Result<()> {
130130

131131
match dispatch {
132132
services::setup::SetupDispatch::Proceed(mode) => {
133-
println!("{}", services::setup::run_placeholder_setup_for_mode(mode)?);
133+
let repository_root =
134+
std::env::current_dir().context("Failed to determine current directory")?;
135+
println!(
136+
"{}",
137+
services::setup::run_setup_for_mode(&repository_root, mode)?
138+
);
134139
}
135140
services::setup::SetupDispatch::Cancelled => {
136141
println!("{}", services::setup::setup_cancelled_text());
@@ -161,12 +166,8 @@ mod tests {
161166
}
162167

163168
#[test]
164-
fn placeholder_command_exits_success() {
165-
let code = run(vec![
166-
"sce".to_string(),
167-
"setup".to_string(),
168-
"--opencode".to_string(),
169-
]);
169+
fn hooks_command_exits_success() {
170+
let code = run(vec!["sce".to_string(), "hooks".to_string()]);
170171
assert_eq!(code, ExitCode::SUCCESS);
171172
}
172173

@@ -219,6 +220,34 @@ mod tests {
219220
);
220221
}
221222

223+
#[test]
224+
fn parser_routes_setup_claude_flag_to_non_interactive_mode() {
225+
let command = parse_command(vec![
226+
"sce".to_string(),
227+
"setup".to_string(),
228+
"--claude".to_string(),
229+
])
230+
.expect("command should parse");
231+
assert_eq!(
232+
command,
233+
Command::Setup(SetupMode::NonInteractive(SetupTarget::Claude,))
234+
);
235+
}
236+
237+
#[test]
238+
fn parser_routes_setup_both_flag_to_non_interactive_mode() {
239+
let command = parse_command(vec![
240+
"sce".to_string(),
241+
"setup".to_string(),
242+
"--both".to_string(),
243+
])
244+
.expect("command should parse");
245+
assert_eq!(
246+
command,
247+
Command::Setup(SetupMode::NonInteractive(SetupTarget::Both,))
248+
);
249+
}
250+
222251
#[test]
223252
fn parser_routes_setup_without_flags_to_interactive_mode() {
224253
let command = parse_command(vec!["sce".to_string(), "setup".to_string()])

cli/src/command_surface.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ pub const COMMANDS: &[CommandContract] = &[
2121
},
2222
CommandContract {
2323
name: services::setup::NAME,
24-
status: ImplementationStatus::Placeholder,
24+
status: ImplementationStatus::Implemented,
2525
purpose: "Prepare local repository/workspace prerequisites",
2626
},
2727
CommandContract {
@@ -67,7 +67,9 @@ pub fn help_text() -> String {
6767
out.push_str(
6868
"\nSetup defaults to interactive target selection when no setup target flag is passed.\n",
6969
);
70-
out.push_str("Only command-surface scaffolding is implemented in this task slice.\n");
70+
out.push_str(
71+
"`setup` is implemented; `mcp`, `hooks`, and `sync` remain placeholder-oriented.\n",
72+
);
7173
out
7274
}
7375

0 commit comments

Comments
 (0)