From 40216fb635ddce086266a7ba468ae8e9ab264ee5 Mon Sep 17 00:00:00 2001 From: "K. Hodges" Date: Sat, 6 Jun 2026 13:32:00 -0700 Subject: [PATCH] Quickstart --- README.md | 2 + docs/quickstart.md | 268 +++++++++++++++++++++++++++++++++++++++++++++ src/app.rs | 10 +- src/commands.rs | 19 ++-- src/transcripts.rs | 5 +- 5 files changed, 292 insertions(+), 12 deletions(-) create mode 100644 docs/quickstart.md diff --git a/README.md b/README.md index eff2266..22ae8dc 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,8 @@ A cognitive shell for engineers who still want the controls. Exoshell is a local-first, shell-adjacent assistant for practitioners who want AI help without giving up operational awareness or control. The shell remains primary; Exoshell suggests, explains, preserves context, and keeps the human in the loop. Exoshell is not designed for “vibe coding.” +Start here: [Quickstart and Tour](docs/quickstart.md). + For the full project philosophy and design direction, read [docs/DESIGN.md](docs/DESIGN.md). ## Philosophy diff --git a/docs/quickstart.md b/docs/quickstart.md new file mode 100644 index 0000000..a000ffe --- /dev/null +++ b/docs/quickstart.md @@ -0,0 +1,268 @@ +# Quickstart + +This guide gets Exoshell running and shows the main Phase 2 workflow. + +Exoshell suggests commands and explains system work. It does not execute commands. + +## Prerequisites + +Install a current Rust toolchain with `rustup`. + +```sh +curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs -o /tmp/rustup-init.sh +sh /tmp/rustup-init.sh -y --profile default --default-toolchain stable +. "$HOME/.cargo/env" +``` + +Check the toolchain: + +```sh +cargo --version +rustc --version +rustfmt --version +``` + +On Ubuntu or WSL, provider dependencies may also need: + +```sh +sudo apt update +sudo apt install pkg-config libssl-dev +``` + +## Configure a Provider + +For OpenAI-compatible hosted providers, set an API key: + +```sh +export OPENAI_API_KEY="..." +``` + +For a local OpenAI-compatible provider, create a config file: + +```toml +[provider] +base_url = "http://localhost:11434/v1" +model = "local-model" +request_timeout_seconds = 120 + +[shell] +family = "posix" + +[interaction] +stance = "operator" +``` + +Local provider URLs such as `localhost` and `127.0.0.1` do not require an API key. + +## Start Exoshell + +Run with defaults: + +```sh +cargo run +``` + +Run with a config file: + +```sh +cargo run -- --config path/to/config.toml +``` + +Select a shell family: + +```sh +cargo run -- --shell posix +cargo run -- --shell powershell +``` + +Select an operating stance: + +```sh +cargo run -- --stance audit +``` + +## First Session + +At the prompt: + +```text +exo> /help +``` + +Attach a note as explicit context: + +```text +exo> /add-note this repo is a Rust CLI called Exoshell +``` + +Inspect context: + +```text +exo> /context +exo> /context stats +``` + +Ask a question: + +```text +exo> What should I inspect before changing command parsing? +``` + +Exoshell may return suggested commands in fenced shell blocks. Suggested commands are reviewable text. You decide whether to copy and run them in your shell. + +## Context Tour + +Context is explicit and session-scoped. Exoshell only sends enabled context to the model. + +Add a file: + +```text +exo> /add-file Cargo.toml +``` + +Add a shallow directory summary: + +```text +exo> /add-dir src +``` + +Paste command output without Exoshell running the command: + +```text +exo> /add-output +paste command output; finish with a single '.' line +... test result: ok +... . +``` + +Inspect one entry: + +```text +exo> /context show ctx-001 +``` + +Control inclusion: + +```text +exo> /context disable ctx-001 +exo> /context enable ctx-001 +``` + +Control pruning preference: + +```text +exo> /context pin ctx-001 +exo> /context priority ctx-001 high +``` + +Remove an entry: + +```text +exo> /context remove ctx-001 +``` + +## Stance Tour + +Stances change the compact behavior fragment in the prompt. + +Show the current stance: + +```text +exo> /stance +``` + +Switch stance: + +```text +exo> /stance operator +exo> /stance audit +exo> /stance teach +exo> /stance quiet +``` + +Use `operator` for concise next steps, `audit` for risk review, `teach` for fuller explanations, and `quiet` for minimal prose. + +## Command Suggestion Tour + +When a model response includes shell fenced blocks, Exoshell assigns command IDs such as `cmd-001`. + +Print a suggested command: + +```text +exo> /copy cmd-001 +``` + +Clipboard support is not implemented yet, so `/copy` prints the command. It does not execute it. + +Explain a suggestion: + +```text +exo> /explain cmd-001 +``` + +Discard a suggestion: + +```text +exo> /discard cmd-001 +``` + +Risk warnings are heuristic. Treat a warning as a prompt for careful review. Lack of a warning does not prove a command is safe. + +## Session Panel + +Show the current operating state: + +```text +exo> /panel +``` + +The panel includes stance, shell family, provider/model, transcript state, context entries, and prompt estimates. + +## Multi-Line Prompts + +Use `/multi` for longer prompts: + +```text +exo> /multi +multi-line input; finish with a single '.' line +... Review this plan: +... 1. Add parser tests. +... 2. Refactor command rendering. +... . +``` + +## Piped Input + +Pipe text into Exoshell as explicit context: + +```sh +printf 'build failed in openssl-sys\n' | cargo run +``` + +Exoshell records piped content as user-provided context. It does not claim to know the upstream command unless you provide that separately. + +## Quality Checks + +Run: + +```sh +cargo fmt --check +cargo test +cargo clippy --all-targets --all-features +``` + +If `cargo test` fails on Ubuntu or WSL with an OpenSSL or `pkg-config` error, install: + +```sh +sudo apt install pkg-config libssl-dev +``` + +## Exit + +Quit the REPL: + +```text +exo> /exit +``` + +If transcripts are enabled, Exoshell writes a markdown transcript at shutdown. diff --git a/src/app.rs b/src/app.rs index 839ae10..7a31675 100644 --- a/src/app.rs +++ b/src/app.rs @@ -368,7 +368,8 @@ impl App { if let Some(warning) = suggestion.detected_risk.warning() { explanation.push_str(&format!("{warning}\n")); } else { - explanation.push_str("No obvious destructive pattern was detected. Review before running.\n"); + explanation + .push_str("No obvious destructive pattern was detected. Review before running.\n"); } self.transcript .record_command_action(id, "explain", "operator requested explanation"); @@ -577,10 +578,11 @@ impl CliOptions { let value = args.next().ok_or_else(|| { crate::config::ConfigError::Invalid("--stance requires a value".into()) })?; - options.stance = - Some(value.parse().map_err(|error: crate::prompts::StanceError| { + options.stance = Some(value.parse().map_err( + |error: crate::prompts::StanceError| { crate::config::ConfigError::Invalid(error.to_string()) - })?); + }, + )?); } "--no-transcript" => options.transcript_enabled = Some(false), "--transcript-dir" => { diff --git a/src/commands.rs b/src/commands.rs index eec6699..afc0000 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -63,10 +63,7 @@ impl CommandRisk { if self.reasons.is_empty() { None } else { - Some(format!( - "review required: {}", - self.reasons.join("; ") - )) + Some(format!("review required: {}", self.reasons.join("; "))) } } } @@ -132,7 +129,9 @@ pub fn detect_command_risk(command: &str, shell: CommandShell) -> CommandRisk { if lowered.contains("rm -rf") || lowered.contains("rm -fr") - || lowered.contains("remove-item") && lowered.contains("-recurse") && lowered.contains("-force") + || lowered.contains("remove-item") + && lowered.contains("-recurse") + && lowered.contains("-force") || lowered.contains("del /s") { reasons.push("recursive or forced deletion".into()); @@ -200,7 +199,11 @@ pub fn render_suggestions(suggestions: &[CommandSuggestion]) -> String { "- {} [{}]{}", suggestion.id, suggestion.shell, - if suggestion.discarded { " discarded" } else { "" } + if suggestion.discarded { + " discarded" + } else { + "" + } )); if let Some(risk) = suggestion.model_risk { rendered.push_str(&format!(" model_risk={risk}")); @@ -210,7 +213,9 @@ pub fn render_suggestions(suggestions: &[CommandSuggestion]) -> String { } rendered.push('\n'); } - rendered.push_str("Use /copy , /explain , or /discard . Exoshell does not execute commands."); + rendered.push_str( + "Use /copy , /explain , or /discard . Exoshell does not execute commands.", + ); rendered } diff --git a/src/transcripts.rs b/src/transcripts.rs index 74e2c94..e77e6ca 100644 --- a/src/transcripts.rs +++ b/src/transcripts.rs @@ -236,7 +236,10 @@ enum TranscriptEntry { note: String, }, BudgetWarning(String), - StanceChange { previous: String, current: String }, + StanceChange { + previous: String, + current: String, + }, CommandSuggestion { id: String, shell: String,