From 847c683291bb66433378a607737f8d02a6db93f4 Mon Sep 17 00:00:00 2001 From: barckcode Date: Mon, 12 Oct 2026 19:20:53 +0200 Subject: [PATCH] feat(run): --agent pi_gentle (Pi + Gentle Shell) (0.1.29) The platform runs a third agent, pi_gentle: Pi launched with Gentle Shell, when the workspace has it installed. --agent accepts pi, pi_gentle and hermes; the platform refuses an agent the workspace lacks (agent_not_installed, exit 4 as before). Co-Authored-By: Claude Opus 5.5 --- README.md | 4 ++-- cmd/run.go | 12 +++++++++--- cmd/run_test.go | 31 +++++++++++++++++++++++++++++++ internal/tui/tui.go | 2 +- 4 files changed, 43 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 2e3a048..336eb7b 100644 --- a/README.md +++ b/README.md @@ -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. @@ -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. | diff --git a/cmd/run.go b/cmd/run.go index 9d9f38c..c2cb49c 100644 --- a/cmd/run.go +++ b/cmd/run.go @@ -8,6 +8,7 @@ import ( "os" "os/signal" "regexp" + "slices" "strings" "time" "unicode/utf8" @@ -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/ (fails outside a git repository)") @@ -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") diff --git a/cmd/run_test.go b/cmd/run_test.go index 8f0b798..c81a42f 100644 --- a/cmd/run_test.go +++ b/cmd/run_test.go @@ -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") diff --git a/internal/tui/tui.go b/internal/tui/tui.go index 5bb6d49..4b40663 100644 --- a/internal/tui/tui.go +++ b/internal/tui/tui.go @@ -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