maesttro 0.1.2 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,9 +1,10 @@
1
1
  # maesttro
2
2
 
3
- Isolated Docker sandbox for local coding agents — `claude`, `opencode`, `codex`,
4
- and others — running under a dedicated bot GitHub identity, never your personal
5
- `~/.ssh` or `~/.config/gh`. Install once, then `maesttro` just works from any
6
- project.
3
+ Isolated Docker runner for local coding agents — `opencode`, `claude`,
4
+ and others. Inside the runner, `gh` and `git` act as the Maesttro GitHub App's
5
+ bot, using a token that is scoped to the current repo and lasts at most an hour.
6
+ Your personal `~/.ssh` and `~/.config/gh` are never used. Install once, then
7
+ `maesttro` just works from any project.
7
8
 
8
9
  Companion piece to Maesttro's orchestration side: this is the local, single-
9
10
  machine entry point — a curated, versioned way to run any supported client
@@ -39,9 +40,10 @@ resolves against the same registry:
39
40
  npm i -g @esatto/maesttro
40
41
  ```
41
42
 
42
- **From source** (the package lives in the `stagehand/` directory of the Maesttro monorepo):
43
+ **From source** (the package lives in the `stagehand/` directory of the Maesttro monorepo;
44
+ the CLI is TypeScript bundled to `dist/` with Bun):
43
45
  ```bash
44
- npm i -g ./stagehand
46
+ cd stagehand && bun run build && npm i -g .
45
47
  ```
46
48
 
47
49
  ---
@@ -49,60 +51,116 @@ npm i -g ./stagehand
49
51
  ## Usage
50
52
 
51
53
  ```bash
52
- maesttro setup /path/to/bot-token.txt # one-time: build image, create volume, auth bot
54
+ maesttro setup # one-time: build image, create volume, log in to GitHub
55
+ maesttro login / logout / status # manage the GitHub login
53
56
  maesttro list # see available clients
54
57
  maesttro doctor # health check anytime
55
58
 
56
- maesttro # launch the default client (claude)
57
- maesttro opencode # launch opencode instead
58
- maesttro codex # launch codex instead
59
- maesttro bash # plain shell inside the sandbox, for debugging
59
+ maesttro # from a repo root: new session clone + default client (opencode)
60
+ maesttro opencode # same, with opencode
61
+ maesttro bash # plain shell inside the runner, for debugging
62
+ maesttro sessions # list this repo's kept sessions
63
+ maesttro resume <id> # continue one
64
+ maesttro push [branch] # push a branch a session created (shows what goes out, asks first)
60
65
 
61
66
  maesttro update # bump the package + rebuild image with refreshed configs
62
67
  ```
63
68
 
64
69
  ---
65
70
 
71
+ ## Sessions: what an agent can and can't touch
72
+
73
+ Run `maesttro` from the root of a repository (git 2.48+ required). Each run is a
74
+ **session** with its own isolated clone, `.worktrees/maesttro-<id>`, detached at
75
+ `origin/<default branch>` and mounted at `/workspace`. That clone is the only
76
+ part of the repo the container sees:
77
+
78
+ - Your real `.git` is never mounted, and neither is your main checkout or any
79
+ uncommitted work there. The container works in a `git clone --shared` that
80
+ borrows the real object store read-only.
81
+ - The host never runs git inside the clone, so agent-authored git config, hooks
82
+ or attributes can't make your host git run anything.
83
+ - The agent commits on its own branch in the clone. It **cannot push**:
84
+ the runner's GitHub token is read-only for code.
85
+ - When a branch is ready, the agent asks you to run `maesttro push <branch>`
86
+ from the repo root. That command pushes only branches a session created,
87
+ never the default branch or branches that existed before. It shows you the
88
+ commits and diff first.
89
+ - To review a session's work, read it through the main repo
90
+ (`git -C <repo> log refs/maesttro/<id>/<branch>`) rather than `cd`-ing into
91
+ the clone, so your shell prompt or IDE doesn't run git on agent-authored config.
92
+ - Every launch backs up the repo's branches and tags into a single bundle file
93
+ under `~/.config/maesttro/backups/`, so any earlier state can be restored.
94
+
95
+ `maesttro push` is plain git, so it works the same for Azure DevOps, GitLab and
96
+ other hosts.
97
+
98
+ ## GitHub access
99
+
100
+ Inside a session, `gh` works for **that repo only** (issues, pull requests,
101
+ comments). It acts as the Maesttro GitHub App's bot:
102
+
103
+ - `maesttro login` runs once. It's a GitHub device flow, and your token is kept in the OS keychain.
104
+ - Each launch asks the Maesttro broker for a token that is scoped to the repo and lasts at most an hour. The CLI refreshes it quietly and revokes it when you exit.
105
+ - The token can read code and write issues and PRs, but can't push, merge or delete branches.
106
+ - Commits are authored by you (your repo's `user.name` / `user.email`) and committed by the bot.
107
+ - The broker is reached over the tailnet. Off the tailnet, the session still starts, just without `gh`.
108
+
109
+ ## Your instructions come along
110
+
111
+ At every container start, `maesttro configure` copies your host
112
+ `~/.claude/CLAUDE.md`, `~/.config/opencode/AGENTS.md` and
113
+ `~/.claude/{agents,commands,skills}` into the runner, read-only at the
114
+ source, and adds a short section explaining the runner rules. Credentials and
115
+ settings files are never copied.
116
+
117
+ Upgrading from 0.1.x: the first launch removes the token you pasted before;
118
+ revoke that token on GitHub as well.
119
+
66
120
  ## Curated clients
67
121
 
68
- Defined in `src/clients.js`, installed in `docker/Dockerfile`, configured via
122
+ Defined in `src/clients.ts`, installed in `docker/Dockerfile`, configured via
69
123
  `docker/configs/<client>/`:
70
124
 
71
125
  | Key | Client | Package |
72
126
  |---|---|---|
73
- | `claude` (default) | Claude Code | `@anthropic-ai/claude-code` |
74
- | `opencode` | opencode | `opencode-ai` |
75
- | `codex` | OpenAI Codex CLI | `@openai/codex` |
127
+ | `opencode` (default) | opencode | `opencode-ai` |
128
+ | `claude` | Claude Code | `@anthropic-ai/claude-code` |
76
129
 
77
- Adding a new client: add an entry to `src/clients.js`, an install line in
130
+ Adding a new client: add an entry to `src/clients.ts`, an install line in
78
131
  `docker/Dockerfile`, and a `docker/configs/<key>/` directory with any default
79
- config — then `maesttro update` rebuilds the image with it included.
132
+ config — then `maesttro update` rebuilds the runner image with it included.
80
133
 
81
- ---
134
+ ### Maesttro provider
82
135
 
83
- ## How `maesttro update` keeps configs current
136
+ Every launch builds opencode's config on your machine: your own
137
+ `~/.config/opencode/opencode.json(c)`, with secrets removed, and Maesttro's
138
+ config on top (`$MAESTTRO_URL`, default `http://100.64.0.2`). Only the result
139
+ is mounted into the runner, read-only, and copied into place at start. Your
140
+ raw config never enters the container, and your host's config is never written.
84
141
 
85
- `docker/configs/<client>/` files are baked into the image at build time —
86
- they become each client's out-of-the-box defaults inside the sandbox. When you
87
- publish a new version of `maesttro` with updated curated configs:
142
+ Injected: a `maesttro` provider (Orchestrate) at `/v1`, a `dgx-spark` provider
143
+ (lorem direct on the DGX), compaction, default model `maesttro/maesttro`, and
144
+ thinking blocks shown. The two providers replace yours of the same name; the
145
+ rest (`mcp`, `plugin`, other providers) is merged. Removed secrets are listed
146
+ at launch by path. `{env:X}` / `$X` references are kept.
88
147
 
89
- ```bash
90
- maesttro update
91
- ```
148
+ Claude Code gets no Maesttro config and uses its own defaults and login.
92
149
 
93
- does two things, in order:
94
- 1. `npm i -g maesttro@latest` — refreshes the installed package files
95
- (including the bundled `docker/` directory) in place
96
- 2. Rebuilds the sandbox image from that same, now-refreshed, `docker/`
97
- directory
150
+ ---
98
151
 
99
- Because the Dockerfile path is resolved relative to the running script's own
100
- install location, step 2 automatically picks up whatever step 1 just wrote —
101
- no extra plumbing needed to keep the two in sync.
152
+ ## How `maesttro update` keeps configs current
102
153
 
103
- Your bot identity (in the `maesttro-home` volume) and your project files are
104
- untouched by an update — only the image (client versions + curated configs)
105
- gets refreshed.
154
+ `docker/configs/<client>/` holds the curated client defaults. They aren't baked
155
+ into the runner image: at every container start, `maesttro configure` runs from your
156
+ installed package, which is mounted read-only, and merges them into each client's
157
+ config, keeping your own keys. So `maesttro update` has two steps:
158
+ 1. `npm i -g maesttro@latest` refreshes the package, including `docker/configs/`,
159
+ which the next session picks up.
160
+ 2. It rebuilds the runner image, for new client and tool versions.
161
+
162
+ Your session state (in the `maesttro-home` volume) and your project files are
163
+ untouched by an update.
106
164
 
107
165
  ---
108
166
 
@@ -110,8 +168,11 @@ gets refreshed.
110
168
 
111
169
  | Mount | Source | Purpose |
112
170
  |---|---|---|
113
- | `/home/agent` | `maesttro-home` named volume | Bot's persistent identity (`gh` auth, per-client session state). Survives across runs, lives only in Docker. |
114
- | `/workspace` | current host directory | The project being worked on — repo-level config comes along automatically. |
171
+ | `/home/agent` | `maesttro-home` named volume | Per-client session state. Survives across runs, lives only in Docker. Holds no GitHub credential. |
172
+ | `/home/agent/.config/gh-maesttro` (read-only) | per-session temp dir on host | The session's GitHub token (`hosts.yml`). The CLI refreshes it before its 1 h expiry and revokes it on exit. |
173
+ | `<repo>/.git/objects` (read-only, same absolute path) | your repo's object store | Base history, borrowed by the clone through its `alternates`. The real `.git` is never mounted. |
174
+ | `/workspace` | the session clone (`.worktrees/<id>`) | The working directory: a `git clone --shared` detached at the default branch. |
175
+ | `/opt/maesttro`, `/opt/maesttro-host` (read-only) | this package; your instruction files | Used by `maesttro configure` at container start. |
115
176
  | `/home/agent/.claude/skills` (read-only) | `~/agent-shared/skills` on host, if present | Reusable skills/house-style files shared across sessions. Read-only. |
116
177
 
117
178
  Nothing from your personal `~/.ssh` or `~/.config/gh` is ever mounted.
@@ -123,22 +184,39 @@ Nothing from your personal `~/.ssh` or `~/.config/gh` is ever mounted.
123
184
  ```
124
185
  maesttro/
125
186
  ├── package.json
126
- ├── bin/maesttro.js # entry point, dispatches subcommands
127
187
  ├── src/
128
- │ ├── clients.js # curated client registry
129
- │ ├── setup.js # `maesttro setup`
130
- │ ├── update.js # `maesttro update`
131
- │ ├── doctor.js # `maesttro doctor`
132
- │ ├── launch.js # `maesttro` / `maesttro <client>` / `maesttro list`
133
- │ └── paths.js # locates bundled Dockerfile wherever npm installs the package
188
+ │ ├── cli.ts # entry point, bundled to dist/maesttro.js
189
+ │ ├── main.ts # dispatches subcommands
190
+ │ ├── clients.ts # curated client registry
191
+ │ ├── setup.ts # `maesttro setup`
192
+ │ ├── update.ts # `maesttro update`
193
+ │ ├── doctor.ts # `maesttro doctor`
194
+ │ ├── launch.ts # `maesttro` / `maesttro <client>` / `maesttro list`
195
+ │ ├── paths.ts # locates bundled Dockerfile wherever npm installs the package
196
+ │ ├── account.ts, device-flow.ts, credential-store.ts, github-login.ts
197
+ │ │ # `maesttro login` / `logout` / `status`, keychain storage
198
+ │ └── github-session.ts, broker-client.ts, session-dir.ts, refresher.ts
199
+ │ # per-session GitHub token: mint, mount, refresh, revoke
200
+ ├── broker/ # server-side token broker (not published; see libretto)
201
+ ├── tests/
134
202
  └── docker/
135
203
  ├── Dockerfile
204
+ ├── gitconfig # → /etc/gitconfig: SSH→HTTPS, gh as credential helper
136
205
  └── configs/
137
- ├── claude/
138
- ├── opencode/
139
- └── codex/
206
+ └── opencode/
207
+ ```
208
+
209
+ ## Trying it locally
210
+
211
+ ```bash
212
+ bun run stagehand:sandbox
140
213
  ```
141
214
 
215
+ This starts a self-contained sandbox (Docker-in-Docker, the real broker, a fake
216
+ GitHub, a demo repo, a fake login) and drops you into a shell where `maesttro`
217
+ is this checkout's build. No credentials needed. See libretto
218
+ `Modules/stagehand.md` → "Local sandbox".
219
+
142
220
  ## Publishing
143
221
 
144
222
  Released by CI, not by hand: bumping `version` in `package.json` and merging
@@ -148,8 +226,6 @@ registry it was installed from.
148
226
 
149
227
  ## Open items
150
228
 
151
- - Fill in real curated defaults in `docker/configs/*` (currently placeholders)
152
229
  - Pin exact client CLI versions in the Dockerfile once stable
153
- - Decide whether each client gets its own bot token/volume for finer audit
154
- trail, or shares one (current setup: shared)
230
+ - Decide whether each client gets its own volume, or shares one (current setup: shared)
155
231
  - Homebrew tap as an alternative install path for non-Node users — optional