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 +101 -0
- package/LICENSE +21 -0
- package/NOTICE +44 -0
- package/README.md +274 -2
- package/bin/npm-installer.mjs +183 -0
- package/package.json +37 -4
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
|
-
#
|
|
1
|
+
# bottega-kit
|
|
2
2
|
|
|
3
|
-
|
|
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.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
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
|
+
}
|