Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
87 changes: 87 additions & 0 deletions docs/SAFE_EVALUATION.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
# Safe evaluation profile

A first-run "kick the tires" profile for evaluating jcode safely **before** pointing it at your main machine, primary credentials, or sensitive repositories.

## Quick start

```bash
jcode --safe-eval run "say hello"
```

That's it. The flag layers a conservative sandbox on top of whatever else you pass.

Equivalent without the flag:

```bash
JCODE_SAFE_EVAL=1 jcode run "say hello"
```

## What `--safe-eval` actually does

`--safe-eval` (and the matching `JCODE_SAFE_EVAL=1` env var) is translated at startup into a coordinated set of environment overrides:

| Env var set | Effect |
|---|---|
| `JCODE_HOME=~/.jcode-safe-eval/` (only if `JCODE_HOME` was not already set) | Isolated config / sessions / memory / auth dir. Your real `~/.jcode/` is **not** touched, read, or written. |
| `JCODE_OFFLINE=1` | Disables all startup network operations (update check, telemetry, provider model-list refresh). Provider API calls during a session are unaffected. |
| `JCODE_NO_TELEMETRY=1` | Belt-and-suspenders: even if telemetry was somehow re-enabled, no events are sent. |
| `JCODE_AMBIENT_DISABLED=1` | Ambient mode does not start a background runner. |
| `JCODE_NO_SELFDEV=1` | Self-dev auto-detection is suppressed. |

A short banner is printed at startup so you can confirm the profile took effect:

```
Safe-eval profile: isolated JCODE_HOME, telemetry off, offline, ambient/selfdev gated.
JCODE_HOME = /home/<user>/.jcode-safe-eval
```

(Pass `--quiet` to suppress the banner once you're comfortable with what it does.)

## What is **not** disabled

- The provider you choose still talks to its API during the session itself. `--safe-eval` is about jcode's startup behavior + persistent state, not about cutting off the LLM call you actually came to make.
- Built-in tools (`read`, `write`, `edit`, `bash`, …) are unchanged. You still have a powerful agent — just aimed at a sandboxed home dir.
- MCP servers from `~/.jcode-safe-eval/mcp.json` (if you create one inside the isolated home) still run. The isolated home means you won't accidentally pick up MCP configs from your real `~/.jcode/`.

## Recommended workflow for first-run evaluation

1. **Use a disposable repo / worktree.** `cd ~/sandbox && git clone --depth 1 …`.
2. **Run jcode under `--safe-eval`** so nothing from your real `~/.jcode/` is read or written.
3. **Pass an explicit cheap provider.** For instance:
```bash
jcode --safe-eval --provider deepseek run "explain this repo"
```
4. **Verify the isolation.** After the run:
```bash
ls ~/.jcode-safe-eval # session, auth, memory all live here
diff -r ~/.jcode ~/.jcode-safe-eval 2>/dev/null | head
```
Your real `~/.jcode/` should be unchanged.
5. **When happy, drop the flag and use jcode normally.**

## Cleanup

```bash
rm -rf ~/.jcode-safe-eval
```

That removes every artifact `--safe-eval` produced — sessions, auth tokens stored in the isolated home, transcripts, memory, etc.

## Compose with other flags

`--safe-eval` is layered, so all other flags still work and override or extend the defaults:

```bash
# Want telemetry on for a one-off in safe-eval mode?
JCODE_NO_TELEMETRY=0 jcode --safe-eval run "..."

# Want a different isolated home?
JCODE_HOME=/tmp/jcode-test jcode --safe-eval run "..."
```

## See also

- `--offline` / `JCODE_OFFLINE` — startup network kill switch (issue #24)
- `--no-context-files` / `-c` — skip AGENTS.md / CLAUDE.md context (issue #9)
- `OAUTH.md` — per-provider login flows
- `docs/SAFETY_SYSTEM.md` — the in-session safety / approval system
8 changes: 8 additions & 0 deletions src/cli/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,14 @@ pub(crate) struct Args {
#[arg(long, global = true)]
pub(crate) offline: bool,

/// First-run safe evaluation profile: isolated `~/.jcode-safe-eval/` home,
/// telemetry off, offline mode, ambient/swarm/selfdev gated. Layered on top of
/// any other flags so users can verify jcode behavior before pointing it at a
/// real repo + credentials. Equivalent to setting `JCODE_SAFE_EVAL=1`. See
/// `docs/SAFE_EVALUATION.md`.
#[arg(long = "safe-eval", global = true)]
pub(crate) safe_eval: bool,

/// Replace the built-in system prompt with the given text for this session.
/// Higher priority than `.jcode/SYSTEM.md` and the `provider.system_prompt`
/// config value. Equivalent to setting `JCODE_SYSTEM_PROMPT`.
Expand Down
25 changes: 25 additions & 0 deletions src/cli/startup.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,31 @@ fn parse_and_prepare_args() -> Result<Args> {
}
}

// --safe-eval: layered first-run sandbox. Sets an isolated JCODE_HOME and
// turns off network/ambient/telemetry/selfdev so users can poke at jcode
// without touching their main credentials/sessions/memory. Issue #60.
if args.safe_eval || std::env::var("JCODE_SAFE_EVAL").is_ok() {
crate::env::set_var("JCODE_SAFE_EVAL", "1");
if std::env::var_os("JCODE_HOME").is_none()
&& let Some(home) = dirs::home_dir()
{
let isolated = home.join(".jcode-safe-eval");
crate::env::set_var("JCODE_HOME", &isolated);
}
crate::env::set_var("JCODE_OFFLINE", "1");
crate::env::set_var("JCODE_NO_TELEMETRY", "1");
crate::env::set_var("JCODE_AMBIENT_DISABLED", "1");
crate::env::set_var("JCODE_NO_SELFDEV", "1");
if !args.quiet {
output::stderr_info(
"Safe-eval profile: isolated JCODE_HOME, telemetry off, offline, ambient/selfdev gated.",
);
if let Some(home) = std::env::var_os("JCODE_HOME") {
output::stderr_info(format!(" JCODE_HOME = {}", home.to_string_lossy()));
}
}
}

// --system-prompt / --append-system-prompt: translate to env vars so the
// build_system_prompt helpers (which run on demand from many code paths)
// can pick them up without threading args through every layer. Issue #22.
Expand Down