bottega-kit 0.0.0-stage → 0.1.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/CHANGELOG.md ADDED
@@ -0,0 +1,101 @@
1
+ # Changelog
2
+
3
+ All notable changes to bottega-kit. Dates are when the version was cut.
4
+
5
+ ## [Unreleased]
6
+
7
+ ## [0.1.1] — 2026-10-23
8
+
9
+ Documentation only; the commands behave as in 0.1.0.
10
+
11
+ **Documentation.** The README says why the roles are separate, what to do
12
+ after `run` (read the worktree, `show`, `list`, the four ways on from a stop,
13
+ `pr` on a stopped task, `rm`), what a task costs, and which starter `init`
14
+ writes for each set of CLIs. `docs/process.md` opens with one task followed
15
+ through every command, including the `task.md` it ends with and a task that
16
+ stops. The README and `docs/configuration.md` no longer say every harness
17
+ must report its model: Codex reports none, and its receipts say
18
+ `requested-only`.
19
+
20
+ **Verification.** `docs/compatibility.md` has a new verified snapshot: one
21
+ live task on 2026-10-23 with Claude Code 2.1.309 and the starter
22
+ configuration, done in round 1, 2.07 USD, then `bottega pr` on GitHub. The
23
+ 0.1.0 snapshot is kept below it, marked as not re-run.
24
+
25
+ ## [0.1.0] — 2026-10-23
26
+
27
+ The first release. A planner, an implementer, a reviewer and an optional judge
28
+ run on Claude Code and Codex in a git worktree, with a receipt for every
29
+ dispatch.
30
+
31
+ **Tasks and configuration.** `bottega init` writes a starter configuration for
32
+ the harnesses it finds on PATH, and `bottega doctor` checks python3, git, gh,
33
+ the configuration, each role's harness and the environment variables profiles
34
+ reference. A `${NAME}` a profile references is read from your environment or
35
+ from `~/.config/bottega-kit/secrets.env`, one `NAME=value` line each, parsed
36
+ and never executed, refused while anyone but you can read it. `bottega new`
37
+ creates a task: a directory outside the repository
38
+ with `task.md` holding your request verbatim, a worktree beside the repository
39
+ and a `bottega/<task-id>` branch. `show`, `list` and `rm` manage tasks.
40
+ `bottega signal` is the command agents run to report `ready`, `needs-work` or
41
+ `blocked`.
42
+
43
+ **Planning and approval.** `bottega plan` runs the planner once; its final
44
+ message becomes the plan, or questions you answer inside `task.md` before the
45
+ next `plan`. `bottega approve` checks the plan, gives each to-do item a stable
46
+ `[T<n>]` id, and freezes the plan's hash, the roles and the round cap. The
47
+ `claude` and `codex` drivers keep every flag in one table. A proxied Claude
48
+ profile runs without your Anthropic credentials. Prompts can be overridden
49
+ per user.
50
+
51
+ **The loop.** `bottega run` alternates the implementer and the reviewer up to
52
+ the cap, then asks the judge if one is bound. It ends done (exit 0) or stopped
53
+ (exit 3) with a reason, and never retries. The reviewer's findings and the
54
+ judge's verdict replace their sections of `task.md`. A planner, reviewer or
55
+ judge that changes tracked code stops the task, and so does a harness that
56
+ reports another model. `bottega resume` continues a stopped task with a fresh
57
+ cap.
58
+
59
+ **Verified live.** On 2026-10-17 and 2026-10-19 seven tasks ran on two
60
+ throwaway repositories with Claude Code 2.1.302 and 2.1.303 and Codex CLI
61
+ 0.163.0, each harness in every role, and `bottega pr` opened a pull request
62
+ on GitHub (`docs/compatibility.md`). The runs found two bugs. A Codex reviewer
63
+ could not find the task by id, because Codex does not pass the orchestrator's
64
+ environment to the agent's shell: `bottega signal` now also accepts the
65
+ absolute task directory, and every prompt names it. A plan with a fenced
66
+ block inside a longer fence lost five lines in the merge: a fence now closes
67
+ only with the same character and at least the same length, as in CommonMark.
68
+ The runs also changed the prompts: the implementer and the reviewer are told
69
+ to give a test suite a long timeout, and that a shell tool handing control
70
+ back while the command runs means wait, not failed. On 2026-10-20 the
71
+ README's proxied profile ran as reviewer: Claude Code 2.1.304 on Z.AI's
72
+ endpoint reported the requested `glm-5.3-flash`, with the key read from
73
+ `secrets.env`.
74
+
75
+ **Pull requests and packaging.** `bottega pr` commits what is left in the
76
+ worktree, pushes the branch to `origin` and opens the pull request with `gh`.
77
+ It works on a done or a stopped task, and a stopped task's pull request says
78
+ why it stopped. An existing pull request is reported, not duplicated.
79
+ `npx bottega-kit` installs the release that matches the npm package. The
80
+ README and `docs/` describe the process, the configuration and the harness
81
+ flags.
82
+
83
+ ### Known limits
84
+
85
+ - Verified live on one machine, on three days, with Claude Code 2.1.302 to
86
+ 2.1.304 and Codex CLI 0.163.0, each in every role, and `bottega pr` on a
87
+ private github.com repository (`docs/compatibility.md`). The Claude Bash
88
+ deny rules were seen to deny every forbidden command; a 150 s test suite
89
+ passed under a Claude reviewer and, after a prompt change, under a Codex
90
+ reviewer that had first given up on it; the Claude `Write` tool created
91
+ files under `acceptEdits`; a proxied profile on Z.AI ran once, as
92
+ reviewer, and Claude Code priced it at its own default rate. A GitHub
93
+ Enterprise host was not exercised; Codex reports no model, so its identity
94
+ stays `requested-only`.
95
+ - Two harnesses: `claude` and `codex`.
96
+ - A text-patch harness for chat-completion endpoints, and Kimi, Grok and
97
+ Gemini drivers, are planned for later (`docs/later.md`).
98
+
99
+ ## [0.0.0] — 2026-10-17
100
+
101
+ - Repository created. Nothing released.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Matteo Scurati
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/NOTICE ADDED
@@ -0,0 +1,44 @@
1
+ bottega-kit
2
+ Copyright (c) 2026 Matteo Scurati
3
+ Licensed under the MIT License (see LICENSE).
4
+
5
+ This project adapts material from vdaubry/bottega
6
+ (https://github.com/vdaubry/bottega), used under the MIT License.
7
+ bottega-kit takes bottega's process (spec-first planning, a human gate on the
8
+ plan, an implementer and a reviewer alternating over one task document). It
9
+ does not take bottega's platform (web server, database, user interface).
10
+
11
+ Files adapted from vdaubry/bottega (HEAD 7e5137f, 2026-10-05):
12
+
13
+ reference/server/constants/templates/plan-template.md -> templates/task.md
14
+ reference/server/constants/prompts/planification.md -> prompts/planner.md
15
+ reference/server/constants/prompts/implementation.md -> prompts/implementer.md
16
+ reference/server/constants/prompts/review.md -> prompts/reviewer.md
17
+
18
+ prompts/judge.md is original to bottega-kit.
19
+
20
+ The vdaubry/bottega licence follows.
21
+
22
+ --------------------------------------------------------------------------------
23
+
24
+ MIT License
25
+
26
+ Copyright (c) 2026 VDA
27
+
28
+ Permission is hereby granted, free of charge, to any person obtaining a copy
29
+ of this software and associated documentation files (the "Software"), to deal
30
+ in the Software without restriction, including without limitation the rights
31
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
32
+ copies of the Software, and to permit persons to whom the Software is
33
+ furnished to do so, subject to the following conditions:
34
+
35
+ The above copyright notice and this permission notice shall be included in all
36
+ copies or substantial portions of the Software.
37
+
38
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
39
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
40
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
41
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
42
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
43
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
44
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,275 @@
1
- # Temporary Holding Version
1
+ # bottega-kit
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ bottega-kit runs a planner, an implementer, a reviewer and an optional judge on the
4
+ coding agents you already have — Claude Code and Codex today — one role each, in a
5
+ git worktree, with a receipt for every step. You need git, gh, python3 and at least
6
+ one of those CLIs.
7
+
8
+ Why roles: the agent that reviews the code is not the one that wrote it, and
9
+ it can run on another model or another vendor. You read and approve the plan
10
+ once; after that nothing asks you anything until the task is done or stops,
11
+ and every step is on disk for you to read.
12
+
13
+ This is not vdaubry/bottega: the process is
14
+ [theirs](https://github.com/vdaubry/bottega); the local, file-based, multi-vendor
15
+ loop is this project.
16
+
17
+ A task is one plan, which you approve, and then rounds of implementer and
18
+ reviewer until the reviewer is satisfied or the round cap is reached:
19
+
20
+ ```
21
+ bottega new task directory, worktree and branch; task.md holds your request
22
+ bottega plan one planner dispatch; may end with questions; repeat until there is a plan
23
+ bottega approve ids for the to-do items; plan hash, roles and cap recorded
24
+ bottega run implementer and reviewer, round after round, up to the cap; then the judge
25
+ bottega pr commit if needed, push to origin, open the pull request
26
+ bottega resume after a stop, with a fresh cap
27
+ ```
28
+
29
+ A round is one implementer pass and one reviewer pass; the cap is 3 unless
30
+ you change it. Each role runs under a profile from your configuration: a
31
+ harness, which is the CLI that runs the role (`claude` or `codex`), a model
32
+ and an effort. One profile may serve several roles.
33
+
34
+ ## Current release: 0.1.1
35
+
36
+ Documentation: what to do after `run`, a task followed end to end, what a
37
+ task costs. The commands are those of 0.1.0: `init`, `doctor`, `new`,
38
+ `plan`, `approve`, `run`, `resume`, `pr` and the task commands, on the
39
+ `claude` and `codex` harnesses. 0.1.1 was verified live on 2026-10-23 with
40
+ Claude Code 2.1.309 and the starter configuration, through `bottega pr` on
41
+ GitHub; 0.1.0 was verified with Claude Code 2.1.302 to 2.1.304 and Codex CLI
42
+ 0.163.0, each in every role, and a reviewer on Z.AI's endpoint. See
43
+ [docs/compatibility.md](docs/compatibility.md).
44
+
45
+ ## What you need
46
+
47
+ - git
48
+ - gh, logged in, for `bottega pr`
49
+ - python3 3.9 or newer
50
+ - at least one of `claude` (Claude Code) and `codex` (Codex CLI), logged in
51
+ - node 18 or newer, only for `npx bottega-kit`
52
+
53
+ ## Install
54
+
55
+ ```sh
56
+ npx bottega-kit
57
+ ```
58
+
59
+ This clones the release that matches the npm package, runs its `./install.sh`,
60
+ then `./doctor.sh`, and deletes the clone. `doctor` fails on a new machine
61
+ until you run `bottega init`. Or from a clone:
62
+
63
+ ```sh
64
+ git clone https://github.com/reasance/bottega-kit
65
+ cd bottega-kit
66
+ ./install.sh
67
+ ```
68
+
69
+ Both copy the command to `~/.local/share/bottega-kit` and link
70
+ `~/.local/bin/bottega`. `./uninstall.sh`, from a clone, removes them and keeps
71
+ your tasks and configuration.
72
+
73
+ ## Example: Fable plans and judges, Opus implements and reviews
74
+
75
+ `~/.config/bottega-kit/config.json`, which `bottega init` writes when `claude`
76
+ is on PATH:
77
+
78
+ ```json
79
+ {
80
+ "schema_version": 1,
81
+ "profiles": {
82
+ "fable": {"harness": "claude", "model": "claude-fable-5-1", "effort": "max"},
83
+ "opus-builder": {"harness": "claude", "model": "claude-opus-5-5", "effort": "high"},
84
+ "opus-reviewer": {"harness": "claude", "model": "claude-opus-5-5", "effort": "high"}
85
+ },
86
+ "roles": {"planner": "fable", "implementer": "opus-builder",
87
+ "reviewer": "opus-reviewer", "judge": "fable"},
88
+ "rounds": 3
89
+ }
90
+ ```
91
+
92
+ With `codex` on PATH as well, `init` also writes a `sol-builder` profile
93
+ (`codex`, `gpt-5.6-sol`, effort `high`) and binds it to no role. With `codex`
94
+ alone, `sol-builder` is the planner, the implementer and the reviewer, and
95
+ there is no judge. Edit the file to bind the roles as you like; `bottega
96
+ doctor` checks it.
97
+
98
+ The whole sequence, from a repository with an `origin` on GitHub:
99
+
100
+ ```sh
101
+ bottega init
102
+ bottega doctor
103
+ cd your-repo
104
+ echo '{"test_cmd": "make test"}' > bottega.json # optional: the test command the agents run
105
+ bottega new --title "Add a dry-run flag" --request "Add --dry-run to the export command."
106
+ bottega plan 20261017-0930-add-a-dry-run-flag # a unique prefix or suffix is enough
107
+ # read task.md; answer any questions in it and run plan again
108
+ bottega approve dry-run-flag
109
+ bottega run dry-run-flag
110
+ # read the changes in the worktree; bottega show dry-run-flag prints where it is
111
+ bottega pr dry-run-flag
112
+ ```
113
+
114
+ `plan` and `run` print one line per dispatch on stdout, and `plan` adds the
115
+ planner's questions when it has any; everything else goes to stderr:
116
+
117
+ ```
118
+ 001 planner fable claude-fable-5-1 → planned (2m41s)
119
+ 002 implementer opus-builder claude-opus-5-5 → done (6m02s)
120
+ 003 reviewer opus-reviewer claude-opus-5-5 → needs-work (2m14s)
121
+ 004 implementer opus-builder claude-opus-5-5 → done (3m40s)
122
+ 005 reviewer opus-reviewer claude-opus-5-5 → ready (1m55s)
123
+ 006 judge fable claude-fable-5-1 → ready (48s)
124
+ ```
125
+
126
+ `pr` prints the pull request's URL, or `pr exists: <url>`.
127
+ [docs/process.md](docs/process.md#a-task-end-to-end) follows one task
128
+ through every command, with the `task.md` it ends with.
129
+
130
+ ## After `run`
131
+
132
+ `bottega show <task>` prints the worktree, the branch and the path of
133
+ `task.md`, then the phase, the round, the last signal, why the task stopped,
134
+ and one line per dispatch. `bottega list` prints every task and its phase.
135
+
136
+ **Done** (exit 0). Read the work before you open the pull request. The
137
+ changes are in the worktree, uncommitted: `git status` and `git diff` there,
138
+ and new files are untracked until `pr` commits them. `task.md` holds the
139
+ reviewer's `## Review Findings` and the judge's `## Verdict`. Then
140
+ `bottega pr`.
141
+
142
+ **Stopped** (exit 3). `run` prints the reason, the receipt of the dispatch
143
+ that stopped it and the resume command; `show` prints them again. Then one
144
+ of:
145
+
146
+ - `bottega resume <task>`: the same plan, a fresh cap. After `cap-reached` or
147
+ `judge-needs-work` a new round starts with the implementer; after any other
148
+ stop the role that stopped runs again.
149
+ - Edit the plan in `task.md`, `bottega approve` it again and `bottega run`:
150
+ the ids are kept and the loop starts over at round 1. After `blocked`, the
151
+ reviewer's findings say what is missing.
152
+ - `bottega pr` anyway: the pull request's body starts with the stop reason.
153
+ - `bottega rm <task>`: removes the worktree and the branch. The task
154
+ directory and its receipts stay.
155
+
156
+ ## What a task costs
157
+
158
+ You pay your providers; bottega-kit adds nothing. Each Claude Code dispatch's
159
+ cost, as Claude Code reports it, is `cost_usd` in its `run.json`. Codex
160
+ reports none: its usage goes to your ChatGPT plan. For scale, on 2026-10-23
161
+ the starter configuration above added an option to a 21-line Python CLI in
162
+ one round: four dispatches, 2.07 USD and under five minutes, three quarters
163
+ of it the Fable planner and judge. In the 0.1.0 runs, Sonnet at effort
164
+ `medium` cost 0.03 to 0.08 USD a dispatch on the same kind of task. A larger
165
+ task costs more: more files to read, more rounds.
166
+ [docs/compatibility.md](docs/compatibility.md) lists every dispatch of the
167
+ live runs.
168
+
169
+ ## Example: three vendors, no judge
170
+
171
+ The planner on Claude Code, the implementer on Codex, the reviewer on Claude
172
+ Code pointed at Z.AI's Anthropic-compatible endpoint:
173
+
174
+ ```json
175
+ {
176
+ "schema_version": 1,
177
+ "profiles": {
178
+ "fable": {"harness": "claude", "model": "claude-fable-5-1", "effort": "max"},
179
+ "sol-builder": {"harness": "codex", "model": "gpt-5.6-sol", "effort": "high"},
180
+ "glm-reviewer": {"harness": "claude", "model": "glm-5.3-flash", "effort": "max",
181
+ "env": {"ANTHROPIC_BASE_URL": "https://api.z.ai/api/anthropic",
182
+ "ANTHROPIC_AUTH_TOKEN": "${ZAI_API_KEY}",
183
+ "ANTHROPIC_DEFAULT_OPUS_MODEL": "glm-5.3-flash"},
184
+ "aliases": ["opus"]}
185
+ },
186
+ "roles": {"planner": "fable", "implementer": "sol-builder",
187
+ "reviewer": "glm-reviewer"},
188
+ "rounds": 3
189
+ }
190
+ ```
191
+
192
+ `${ZAI_API_KEY}` is read when the reviewer runs, from your environment or
193
+ from `~/.config/bottega-kit/secrets.env` (one `NAME=value` line each, mode
194
+ 600, parsed and never executed); `doctor` and `approve` refuse while it is set
195
+ in neither. Because `ANTHROPIC_BASE_URL` is set, the reviewer runs with a fresh
196
+ Claude configuration directory and without your Anthropic credentials.
197
+ `aliases` lists other names the endpoint may report for the model; any other
198
+ name stops the run as `identity-mismatch` (on 2026-10-20 Z.AI reported
199
+ `glm-5.3-flash`, the requested name, so the alias went unused). To change one
200
+ role for one task only, pass `--role implementer=sol-builder` to `bottega new`.
201
+
202
+ ## The roles
203
+
204
+ | role | what it does | what it may change | its product |
205
+ |---|---|---|---|
206
+ | planner | reads the repository and the request, writes the plan or asks questions | nothing | its final message becomes the plan in `task.md` |
207
+ | implementer | works through the unchecked to-do items, adds tests, ticks items | the worktree and the boxes in `task.md`; never commits or pushes | a summary, kept in the receipt |
208
+ | reviewer | checks every ticked item, runs the tests and the manual QA scenarios | nothing it keeps: a changed tracked file stops the task | `## Review Findings` and a signal: `ready`, `needs-work` or `blocked` |
209
+ | judge | optional; reads the plan and the diff, says whether to merge | nothing | `## Verdict` and a signal: `ready` or `needs-work` |
210
+
211
+ Agents report back by running `bottega signal`. bottega-kit decides what
212
+ happens next from that signal and the files on disk, never from the agent's
213
+ prose. [docs/process.md](docs/process.md) has the loop step by step.
214
+
215
+ ## Exit codes
216
+
217
+ | code | meaning |
218
+ |---|---|
219
+ | 0 | done |
220
+ | 2 | refused: wrong phase, plan changed since approval, invalid configuration, a binding that no longer works, task locked |
221
+ | 3 | stopped: `blocked`, `cap-reached`, `judge-needs-work`, `dispatch-failed`, `timeout`, `identity-mismatch`, `role-modified-worktree`, `interrupted` |
222
+ | 64 | bad invocation |
223
+
224
+ A stopped task continues with `bottega resume <task> [--rounds N]`.
225
+
226
+ ## Where things live
227
+
228
+ - Configuration: `~/.config/bottega-kit/config.json`, and prompt overrides in
229
+ `~/.config/bottega-kit/prompts/`.
230
+ - Tasks: `~/.local/share/bottega-kit/tasks/<project>/<task-id>/`, holding
231
+ `task.md`, `state.json` and one receipt directory per dispatch under `runs/`.
232
+ They live outside the repository, so they outlive the worktree.
233
+ - The worktree: `<repo>-worktrees/<task-id>/`, beside the repository, on the
234
+ branch `bottega/<task-id>`. `bottega rm` removes it and the branch.
235
+ - A repository may hold `bottega.json` with `test_cmd` and `base`.
236
+
237
+ [docs/configuration.md](docs/configuration.md) covers every setting.
238
+
239
+ ## What bottega-kit never does
240
+
241
+ - Retry a failed dispatch, fall back to another model, or substitute a profile.
242
+ - Bring a model of its own. Every dispatch runs a profile you bound. Claude
243
+ Code reports the model it ran, and it must be the one you asked for; Codex
244
+ reports none, so its receipts say `requested-only` and rest on the request.
245
+ - Put a secret in its configuration, its prompts, a task or a receipt.
246
+ Profiles name environment variables; the key file you keep at mode 600 may
247
+ hold their values; receipts record names, never values.
248
+ - Merge, or push anywhere but `origin`.
249
+ - Let the planner, the reviewer or the judge change your code: their runs are
250
+ checked against the worktree before and after.
251
+
252
+ ## Not vdaubry/bottega
253
+
254
+ [vdaubry/bottega](https://github.com/vdaubry/bottega) is a web platform with a
255
+ server, a database and a UI. bottega-kit took its process: the spec-first task
256
+ document, the planner, implementer and reviewer prompts, the plan template,
257
+ the "agents signal with a command" rule, the review cap with no unreviewed fix
258
+ pass, and one worktree per task. It did not take the platform. The judge
259
+ role, the profiles across vendors and the receipts are this project's. See
260
+ `NOTICE` for the files adapted.
261
+
262
+ ## Documentation
263
+
264
+ - [docs/process.md](docs/process.md): the loop, rounds, resume, signals,
265
+ receipts, rails.
266
+ - [docs/configuration.md](docs/configuration.md): profiles, roles,
267
+ `bottega.json`, prompt overrides, environment variables.
268
+ - [docs/compatibility.md](docs/compatibility.md): what each harness is run
269
+ with, and what has been verified.
270
+ - [docs/later.md](docs/later.md): what is not built yet.
271
+
272
+ ## Licence
273
+
274
+ MIT. The process and parts of the prompts and the template come from
275
+ [vdaubry/bottega](https://github.com/vdaubry/bottega), also MIT; see `NOTICE`.
@@ -0,0 +1,183 @@
1
+ #!/usr/bin/env node
2
+ // bottega-kit npm installer: a thin wrapper over the repository's ./install.sh.
3
+ //
4
+ // `npx bottega-kit` clones https://github.com/reasance/bottega-kit at the
5
+ // tag that matches this package's own version (bottega-kit--v<version>), runs
6
+ // ./install.sh from the clone, then ./doctor.sh, then deletes the clone.
7
+ // install.sh copies what it installs into BOTTEGA_DATA_HOME and records the
8
+ // repository and ref (never the temporary path) in installed-version.json, so
9
+ // nothing installed points at the deleted clone.
10
+ //
11
+ // Usage:
12
+ // npx bottega-kit [--ref <tag-or-branch>] [--repo <git-url>] [--skip-doctor]
13
+ //
14
+ // Environment: BOTTEGA_DATA_HOME and BOTTEGA_BIN_HOME, as for ./install.sh.
15
+ // The wrapper never writes a configuration and never handles credentials.
16
+
17
+ import { spawnSync } from "node:child_process";
18
+ import { mkdtempSync, readFileSync, rmSync, statSync } from "node:fs";
19
+ import { tmpdir } from "node:os";
20
+ import { join } from "node:path";
21
+ import process from "node:process";
22
+
23
+ const DEFAULT_REPO = "https://github.com/reasance/bottega-kit";
24
+ const TAG_PREFIX = "bottega-kit--v";
25
+ const USAGE =
26
+ "usage: npx bottega-kit [--ref <tag-or-branch>] [--repo <git-url>] [--skip-doctor]";
27
+ const REQUIRED = [
28
+ "install.sh",
29
+ "doctor.sh",
30
+ "bin/bottega",
31
+ "bin/lib/bottega/version.py",
32
+ ];
33
+
34
+ class WrapperError extends Error {
35
+ constructor(message, code = 1) {
36
+ super(message);
37
+ this.code = code;
38
+ }
39
+ }
40
+
41
+ function packageVersion() {
42
+ const text = readFileSync(new URL("../package.json", import.meta.url), "utf8");
43
+ return JSON.parse(text).version;
44
+ }
45
+
46
+ function parseArgs(argv) {
47
+ const options = { ref: null, repo: null, skipDoctor: false, help: false };
48
+ for (let i = 0; i < argv.length; i += 1) {
49
+ const arg = argv[i];
50
+ const [name, inline] = arg.startsWith("--") && arg.includes("=")
51
+ ? [arg.slice(0, arg.indexOf("=")), arg.slice(arg.indexOf("=") + 1)]
52
+ : [arg, null];
53
+ switch (name) {
54
+ case "--ref":
55
+ case "--repo": {
56
+ const value = inline ?? argv[i + 1];
57
+ if (inline === null) i += 1;
58
+ if (!value || value.startsWith("--")) {
59
+ throw new WrapperError(`${name} needs a value\n${USAGE}`, 64);
60
+ }
61
+ options[name.slice(2)] = value;
62
+ break;
63
+ }
64
+ case "--skip-doctor":
65
+ options.skipDoctor = true;
66
+ break;
67
+ case "-h":
68
+ case "--help":
69
+ options.help = true;
70
+ break;
71
+ default:
72
+ // install.sh takes no arguments, so nothing is forwarded to it.
73
+ throw new WrapperError(`unknown argument: ${arg}\n${USAGE}`, 64);
74
+ }
75
+ }
76
+ return options;
77
+ }
78
+
79
+ function run(command, args, options = {}) {
80
+ const result = spawnSync(command, args, { stdio: "inherit", ...options });
81
+ if (result.error) {
82
+ throw new WrapperError(`could not run ${command}: ${result.error.message}`);
83
+ }
84
+ return result;
85
+ }
86
+
87
+ function requireGit() {
88
+ const probe = spawnSync("git", ["--version"], { stdio: "ignore" });
89
+ if (probe.error || probe.status !== 0) {
90
+ throw new WrapperError("git is required", 69);
91
+ }
92
+ }
93
+
94
+ function clone(repo, ref, dir) {
95
+ const result = run(
96
+ "git",
97
+ ["clone", "--quiet", "--depth", "1", "--branch", ref, repo, dir],
98
+ { stdio: ["ignore", "ignore", "pipe"], encoding: "utf8" },
99
+ );
100
+ if (result.status !== 0) {
101
+ const detail = (result.stderr || "").trim();
102
+ throw new WrapperError(
103
+ `could not clone ${repo} at ${ref}${detail ? `:\n${detail}` : ""}`,
104
+ );
105
+ }
106
+ }
107
+
108
+ function checkCheckout(dir, expectedVersion, ref) {
109
+ for (const file of REQUIRED) {
110
+ let ok = false;
111
+ try {
112
+ ok = statSync(join(dir, file)).isFile();
113
+ } catch {
114
+ ok = false;
115
+ }
116
+ if (!ok) {
117
+ throw new WrapperError(`${ref} has no ${file}; it is not a bottega-kit release`);
118
+ }
119
+ }
120
+ const text = readFileSync(join(dir, "bin/lib/bottega/version.py"), "utf8");
121
+ const found = /^__version__ = "([^"]+)"$/m.exec(text)?.[1];
122
+ if (expectedVersion && found !== expectedVersion) {
123
+ throw new WrapperError(
124
+ `${ref} holds bottega-kit ${found ?? "(no version)"}, not ${expectedVersion}`,
125
+ );
126
+ }
127
+ }
128
+
129
+ function main() {
130
+ const options = parseArgs(process.argv.slice(2));
131
+ if (options.help) {
132
+ console.log(USAGE);
133
+ return 0;
134
+ }
135
+ requireGit();
136
+ const version = packageVersion();
137
+ const repo = options.repo ?? DEFAULT_REPO;
138
+ const ref = options.ref ?? `${TAG_PREFIX}${version}`;
139
+ const parent = mkdtempSync(join(tmpdir(), "bottega-kit-"));
140
+ const checkout = join(parent, "bottega-kit");
141
+ try {
142
+ console.log(`bottega-kit: installing ${ref} from ${repo}`);
143
+ clone(repo, ref, checkout);
144
+ // Only the default tag must match this package; --ref is the user's choice.
145
+ checkCheckout(checkout, options.ref ? null : version, ref);
146
+ const env = {
147
+ ...process.env,
148
+ BOTTEGA_INSTALL_SOURCE: repo,
149
+ BOTTEGA_INSTALL_REF: ref,
150
+ };
151
+ const install = run("./install.sh", [], { cwd: checkout, env });
152
+ if (install.status !== 0) {
153
+ throw new WrapperError(
154
+ `install.sh exited with ${install.status}`,
155
+ install.status ?? 1,
156
+ );
157
+ }
158
+ if (options.skipDoctor) {
159
+ return 0;
160
+ }
161
+ const doctor = run("./doctor.sh", [], { cwd: checkout, env });
162
+ if (doctor.status !== 0) {
163
+ console.error(
164
+ `bottega-kit: installed, but doctor reported problems (exit ${doctor.status}). ` +
165
+ "A new install has no configuration yet: run bottega init, then bottega doctor.",
166
+ );
167
+ return doctor.status ?? 1;
168
+ }
169
+ return 0;
170
+ } finally {
171
+ rmSync(parent, { recursive: true, force: true });
172
+ }
173
+ }
174
+
175
+ let code;
176
+ try {
177
+ code = main();
178
+ } catch (error) {
179
+ if (!(error instanceof WrapperError)) throw error;
180
+ console.error(`bottega-kit: ${error.message}`);
181
+ code = error.code;
182
+ }
183
+ process.exit(code);
package/package.json CHANGED
@@ -1,6 +1,39 @@
1
1
  {
2
2
  "name": "bottega-kit",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.1.1",
4
+ "description": "A planner, an implementer, a reviewer and an optional judge on the coding agents you already have, in a git worktree, with a receipt for every step.",
5
+ "keywords": [
6
+ "claude-code",
7
+ "codex",
8
+ "coding-agents",
9
+ "git-worktree",
10
+ "developer-tools"
11
+ ],
12
+ "author": {
13
+ "name": "Matteo Scurati",
14
+ "url": "https://github.com/matteoscurati"
15
+ },
16
+ "homepage": "https://github.com/reasance/bottega-kit#readme",
17
+ "repository": {
18
+ "type": "git",
19
+ "url": "git+https://github.com/reasance/bottega-kit.git"
20
+ },
21
+ "bugs": {
22
+ "url": "https://github.com/reasance/bottega-kit/issues"
23
+ },
24
+ "license": "MIT",
25
+ "type": "module",
26
+ "bin": {
27
+ "bottega-kit": "./bin/npm-installer.mjs"
28
+ },
29
+ "files": [
30
+ "bin/npm-installer.mjs",
31
+ "README.md",
32
+ "LICENSE",
33
+ "NOTICE",
34
+ "CHANGELOG.md"
35
+ ],
36
+ "engines": {
37
+ "node": ">=18"
38
+ }
39
+ }