maesttro 0.1.2 → 0.2.0

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
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.
4
+ and others. Inside the sandbox, `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,23 +51,76 @@ 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 # from a repo root: new session clone + default client (claude)
60
+ maesttro opencode # same, with opencode
61
+ maesttro codex # same, with codex
59
62
  maesttro bash # plain shell inside the sandbox, for debugging
63
+ maesttro sessions # list this repo's kept sessions
64
+ maesttro resume <id> # continue one
65
+ maesttro push [branch] # push a branch a session created (shows what goes out, asks first)
60
66
 
61
67
  maesttro update # bump the package + rebuild image with refreshed configs
62
68
  ```
63
69
 
64
70
  ---
65
71
 
72
+ ## Sessions: what an agent can and can't touch
73
+
74
+ Run `maesttro` from the root of a repository (git 2.48+ required). Each run is a
75
+ **session** with its own isolated clone, `.worktrees/maesttro-<id>`, detached at
76
+ `origin/<default branch>` and mounted at `/workspace`. That clone is the only
77
+ part of the repo the container sees:
78
+
79
+ - Your real `.git` is never mounted, and neither is your main checkout or any
80
+ uncommitted work there. The container works in a `git clone --shared` that
81
+ borrows the real object store read-only.
82
+ - The host never runs git inside the clone, so agent-authored git config, hooks
83
+ or attributes can't make your host git run anything.
84
+ - The agent commits on its own branch in the clone. It **cannot push**:
85
+ the sandbox's GitHub token is read-only for code.
86
+ - When a branch is ready, the agent asks you to run `maesttro push <branch>`
87
+ from the repo root. That command pushes only branches a session created,
88
+ never the default branch or branches that existed before. It shows you the
89
+ commits and diff first.
90
+ - To review a session's work, read it through the main repo
91
+ (`git -C <repo> log refs/maesttro/<id>/<branch>`) rather than `cd`-ing into
92
+ the clone, so your shell prompt or IDE doesn't run git on agent-authored config.
93
+ - Every launch backs up the repo's branches and tags into a single bundle file
94
+ under `~/.config/maesttro/backups/`, so any earlier state can be restored.
95
+
96
+ `maesttro push` is plain git, so it works the same for Azure DevOps, GitLab and
97
+ other hosts.
98
+
99
+ ## GitHub access
100
+
101
+ Inside a session, `gh` works for **that repo only** (issues, pull requests,
102
+ comments). It acts as the Maesttro GitHub App's bot:
103
+
104
+ - `maesttro login` runs once. It's a GitHub device flow, and your token is kept in the OS keychain.
105
+ - 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.
106
+ - The token can read code and write issues and PRs, but can't push, merge or delete branches.
107
+ - Commits are authored by you (your repo's `user.name` / `user.email`) and committed by the bot.
108
+ - The broker is reached over the tailnet. Off the tailnet, the session still starts, just without `gh`.
109
+
110
+ ## Your instructions come along
111
+
112
+ At every container start, `maesttro configure` copies your host
113
+ `~/.claude/CLAUDE.md`, `~/.config/opencode/AGENTS.md`, `~/.codex/AGENTS.md` and
114
+ `~/.claude/{agents,commands,skills}` into the sandbox, read-only at the
115
+ source, and adds a short section explaining the sandbox rules. Credentials and
116
+ settings files are never copied.
117
+
118
+ Upgrading from 0.1.x: the first launch removes the token you pasted before;
119
+ revoke that token on GitHub as well.
120
+
66
121
  ## Curated clients
67
122
 
68
- Defined in `src/clients.js`, installed in `docker/Dockerfile`, configured via
123
+ Defined in `src/clients.ts`, installed in `docker/Dockerfile`, configured via
69
124
  `docker/configs/<client>/`:
70
125
 
71
126
  | Key | Client | Package |
@@ -74,7 +129,7 @@ Defined in `src/clients.js`, installed in `docker/Dockerfile`, configured via
74
129
  | `opencode` | opencode | `opencode-ai` |
75
130
  | `codex` | OpenAI Codex CLI | `@openai/codex` |
76
131
 
77
- Adding a new client: add an entry to `src/clients.js`, an install line in
132
+ Adding a new client: add an entry to `src/clients.ts`, an install line in
78
133
  `docker/Dockerfile`, and a `docker/configs/<key>/` directory with any default
79
134
  config — then `maesttro update` rebuilds the image with it included.
80
135
 
@@ -82,27 +137,16 @@ config — then `maesttro update` rebuilds the image with it included.
82
137
 
83
138
  ## How `maesttro update` keeps configs current
84
139
 
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:
88
-
89
- ```bash
90
- maesttro update
91
- ```
92
-
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
98
-
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.
140
+ `docker/configs/<client>/` holds the curated client defaults. They aren't baked
141
+ into the image: at every container start, `maesttro configure` runs from your
142
+ installed package, which is mounted read-only, and merges them into each client's
143
+ config, keeping your own keys. So `maesttro update` has two steps:
144
+ 1. `npm i -g maesttro@latest` refreshes the package, including `docker/configs/`,
145
+ which the next session picks up.
146
+ 2. It rebuilds the sandbox image, for new client and tool versions.
102
147
 
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.
148
+ Your session state (in the `maesttro-home` volume) and your project files are
149
+ untouched by an update.
106
150
 
107
151
  ---
108
152
 
@@ -110,8 +154,11 @@ gets refreshed.
110
154
 
111
155
  | Mount | Source | Purpose |
112
156
  |---|---|---|
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. |
157
+ | `/home/agent` | `maesttro-home` named volume | Per-client session state. Survives across runs, lives only in Docker. Holds no GitHub credential. |
158
+ | `/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. |
159
+ | `<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. |
160
+ | `/workspace` | the session clone (`.worktrees/<id>`) | The working directory: a `git clone --shared` detached at the default branch. |
161
+ | `/opt/maesttro`, `/opt/maesttro-host` (read-only) | this package; your instruction files | Used by `maesttro configure` at container start. |
115
162
  | `/home/agent/.claude/skills` (read-only) | `~/agent-shared/skills` on host, if present | Reusable skills/house-style files shared across sessions. Read-only. |
116
163
 
117
164
  Nothing from your personal `~/.ssh` or `~/.config/gh` is ever mounted.
@@ -123,22 +170,41 @@ Nothing from your personal `~/.ssh` or `~/.config/gh` is ever mounted.
123
170
  ```
124
171
  maesttro/
125
172
  ├── package.json
126
- ├── bin/maesttro.js # entry point, dispatches subcommands
127
173
  ├── 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
174
+ │ ├── cli.ts # entry point, bundled to dist/maesttro.js
175
+ │ ├── main.ts # dispatches subcommands
176
+ │ ├── clients.ts # curated client registry
177
+ │ ├── setup.ts # `maesttro setup`
178
+ │ ├── update.ts # `maesttro update`
179
+ │ ├── doctor.ts # `maesttro doctor`
180
+ │ ├── launch.ts # `maesttro` / `maesttro <client>` / `maesttro list`
181
+ │ ├── paths.ts # locates bundled Dockerfile wherever npm installs the package
182
+ │ ├── account.ts, device-flow.ts, credential-store.ts, github-login.ts
183
+ │ │ # `maesttro login` / `logout` / `status`, keychain storage
184
+ │ └── github-session.ts, broker-client.ts, session-dir.ts, refresher.ts
185
+ │ # per-session GitHub token: mint, mount, refresh, revoke
186
+ ├── broker/ # server-side token broker (not published; see libretto)
187
+ ├── tests/
134
188
  └── docker/
135
189
  ├── Dockerfile
190
+ ├── gitconfig # → /etc/gitconfig: SSH→HTTPS, gh as credential helper
136
191
  └── configs/
137
192
  ├── claude/
138
193
  ├── opencode/
139
194
  └── codex/
140
195
  ```
141
196
 
197
+ ## Trying it locally
198
+
199
+ ```bash
200
+ bun run stagehand:sandbox
201
+ ```
202
+
203
+ This starts a self-contained sandbox (Docker-in-Docker, the real broker, a fake
204
+ GitHub, a demo repo, a fake login) and drops you into a shell where `maesttro`
205
+ is this checkout's build. No credentials needed. See libretto
206
+ `Modules/stagehand.md` → "Local sandbox".
207
+
142
208
  ## Publishing
143
209
 
144
210
  Released by CI, not by hand: bumping `version` in `package.json` and merging
@@ -150,6 +216,5 @@ registry it was installed from.
150
216
 
151
217
  - Fill in real curated defaults in `docker/configs/*` (currently placeholders)
152
218
  - 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)
219
+ - Decide whether each client gets its own volume, or shares one (current setup: shared)
155
220
  - Homebrew tap as an alternative install path for non-Node users — optional