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
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ inside each tool later.

## Agent runs

`nan run` starts an agent (Pi or Hermes) in one of your
`nan run` starts an agent (Pi, Pi + Gentle Shell or Hermes) in one of your
[workspaces](https://cloud.nan.builders) and streams what it does. The run
happens in the workspace, not on your machine: it keeps going if the command
stops, and its log stays on nan.builders.
Expand All @@ -103,7 +103,7 @@ id=$(nan run --detach "upgrade the dependencies")
| Flag | Default | |
|---|---|---|
| `--ws NAME` | your only workspace | Workspace to run in. Required when you have more than one. |
| `--agent pi\|hermes` | `pi` | Agent to run. |
| `--agent pi\|pi_gentle\|hermes` | `pi` | Agent to run: `pi_gentle` is Pi with Gentle Shell. The workspace must have it installed. |
| `--model M` | the agent's own | Model for the agent. |
| `--cwd PATH` | `/home/nan` | Folder in the workspace. |
| `--worktree` / `--no-worktree` | auto | Work on a separate branch, or in the folder itself. See below. |
Expand Down
12 changes: 9 additions & 3 deletions cmd/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"os"
"os/signal"
"regexp"
"slices"
"strings"
"time"
"unicode/utf8"
Expand Down Expand Up @@ -140,7 +141,7 @@ func init() {
rootCmd.AddCommand(runCmd)
f := runCmd.Flags()
f.StringVar(&runOpts.workspace, "ws", "", "Workspace to run in (default: your only workspace)")
f.StringVar(&runOpts.agent, "agent", "pi", "Agent to run: pi or hermes")
f.StringVar(&runOpts.agent, "agent", "pi", "Agent to run: pi, pi_gentle (Pi + Gentle Shell) or hermes, if the workspace has it installed")
f.StringVar(&runOpts.model, "model", "", "Model for the agent (default: the agent's own)")
f.StringVar(&runOpts.cwd, "cwd", "", "Folder in the workspace to run in (default: /home/nan)")
f.BoolVar(&runOpts.worktree, "worktree", false, "Always work on a separate branch, nan-run/<id> (fails outside a git repository)")
Expand Down Expand Up @@ -397,9 +398,14 @@ func isTerminal(f *os.File) bool {
return err == nil && info.Mode()&os.ModeCharDevice != 0
}

// runAgents are the agents a run can use (the platform's runs.agent set):
// pi_gentle is Pi with gentle-shell. The workspace must have the agent
// installed; the platform answers agent_not_installed otherwise.
var runAgents = []string{"pi", "pi_gentle", "hermes"}

func buildRunRequest(in promptInput, opts runOptions, args []string) (*api.CreateRunRequest, error) {
if opts.agent != "pi" && opts.agent != "hermes" {
return nil, usageErrorf("--agent must be pi or hermes, not %q", opts.agent)
if !slices.Contains(runAgents, opts.agent) {
return nil, usageErrorf("--agent must be pi, pi_gentle or hermes, not %q", opts.agent)
}
if opts.worktree && opts.noWorktree {
return nil, usageErrorf("--worktree and --no-worktree contradict each other; pick one")
Expand Down
31 changes: 31 additions & 0 deletions cmd/run_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -525,6 +525,37 @@ func TestRunFlagsShapeTheRequest(t *testing.T) {
}
}

// --agent takes every agent the platform runs: pi, pi_gentle (Pi with
// gentle-shell) and hermes, sent as given. Anything else is a usage error.
func TestRunAgentValues(t *testing.T) {
for _, agent := range []string{"pi", "pi_gentle", "hermes"} {
h := newHarness(t, &fakePlatform{})
opts := defaultOpts()
opts.agent = agent
if err := doRun(context.Background(), h.env, opts, []string{"x"}); err != nil {
t.Fatalf("%s: %v", agent, err)
}
if h.fake.created["agent"] != agent {
t.Errorf("%s: created with %v", agent, h.fake.created["agent"])
}
}
for _, bad := range []string{"gentle_shell", "pi-gentle", "Pi", ""} {
h := newHarness(t, &fakePlatform{})
opts := defaultOpts()
opts.agent = bad
err := doRun(context.Background(), h.env, opts, []string{"x"})
if exitCode(err) != exitUsage || h.fake.created != nil {
t.Errorf("%q: exit %d, sent %v", bad, exitCode(err), h.fake.created)
}
if err != nil && !strings.Contains(err.Error(), "pi, pi_gentle or hermes") {
t.Errorf("%q: the error does not list the agents: %v", bad, err)
}
}
if f := runCmd.Flags().Lookup("agent"); f == nil || !strings.Contains(f.Usage, "pi_gentle") {
t.Errorf("--agent help does not mention pi_gentle")
}
}

func TestRunUsageErrorsExit64BeforeSendingAnything(t *testing.T) {
long := strings.Repeat("a", maxPromptBytes+1)
file := filepath.Join(t.TempDir(), "prompt.md")
Expand Down
2 changes: 1 addition & 1 deletion internal/tui/tui.go
Original file line number Diff line number Diff line change
Expand Up @@ -3610,7 +3610,7 @@ func (m model) renderSetup(l layout) string {

// ── about renderer ───────────────────────────────────────────────────────────

const Version = "0.1.28"
const Version = "0.1.29"

func renderAbout(l layout) string {
var b strings.Builder
Expand Down
Loading