biotonomy 0.2.1 → 0.2.3

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,211 +1,98 @@
1
1
  # Biotonomy
2
2
 
3
- Biotonomy is a command-line workflow for shipping code changes with Codex.
3
+ Biotonomy (`bt`) is a CLI for running a Codex-driven development workflow in a repo:
4
4
 
5
- - What it is: A CLI that runs a repeatable flow: `spec -> research -> implement -> review -> fix -> pr`.
6
- - Who it is for: Developers who want a structured way to ship small changes fast.
7
- - What problem it solves: Keeps work organized in files, runs quality checks, and reduces "what do I do next?" during AI-assisted coding.
5
+ `spec -> research -> plan-review -> implement -> review -> fix -> pr`
8
6
 
9
- ## 60-Second Quickstart
7
+ It supports both:
8
+ - manual stage-by-stage execution
9
+ - a one-command iterative loop with `bt loop`
10
10
 
11
- Install either way:
11
+ ## Quickstart
12
12
 
13
- ```bash
14
- npm i -g biotonomy
15
- # then use: bt ...
16
- ```
17
-
18
- ```bash
19
- npx biotonomy ...
20
- ```
21
-
22
- Minimal demo in a fresh repo:
23
-
24
- ```bash
25
- mkdir biotonomy-demo && cd biotonomy-demo
26
- git init
27
- npm init -y
28
-
29
- npx biotonomy bootstrap
30
- npx biotonomy spec hello-world
31
- npx biotonomy review hello-world
32
- npx biotonomy status
33
- ```
34
-
35
- Expected files after the demo:
36
-
37
- - `.bt.env`
38
- - `.bt/`
39
- - `specs/hello-world/SPEC.md`
40
- - `specs/hello-world/REVIEW.md`
41
- - `specs/hello-world/progress.txt`
42
- - `specs/hello-world/history/001-spec.md`
43
- - `specs/hello-world/history/002-review.md`
44
- - `specs/hello-world/.artifacts/codex-review.log`
45
-
46
- Notes:
47
-
48
- - `review` still creates `REVIEW.md` even if Codex is not installed.
49
- - `research` requires Codex.
50
-
51
- ## Ship A Small Change
52
-
53
- Use this for a real change from idea to PR.
54
-
55
- 1. `spec` (define the change)
56
-
57
- ```bash
58
- bt spec my-change
59
- # or from GitHub issue:
60
- # bt spec 123
61
- ```
62
-
63
- - Automated today: creates `specs/<feature>/SPEC.md`, history, and progress logs.
64
- - Manual today: fill in/clean up stories and acceptance criteria in `SPEC.md`.
65
-
66
- 2. `research` (gather context)
13
+ Prereqs: Node.js >= 18, `git`, Codex CLI available as `codex` (or set `BT_CODEX_BIN`).
67
14
 
68
15
  ```bash
69
- bt research my-change
70
- ```
71
-
72
- - Automated today: runs Codex in read-only mode and writes `RESEARCH.md`.
73
- - Manual today: confirm research quality and adjust plan if needed.
74
-
75
- 3. `implement` (make the change)
76
-
77
- ```bash
78
- bt implement my-change
79
- ```
80
-
81
- - Automated today: runs Codex in full-auto and then runs quality gates.
82
- - Manual today: if Codex is unavailable, this stage is a stub and you implement changes yourself.
83
-
84
- 4. `review` (check what changed)
85
-
86
- ```bash
87
- bt review my-change
88
- ```
89
-
90
- - Automated today: writes `REVIEW.md` (with a fallback stub if Codex fails).
91
- - Manual today: decide whether findings are acceptable for your team.
92
-
93
- 5. `fix` (address findings)
94
-
95
- ```bash
96
- bt fix my-change
97
- ```
98
-
99
- - Automated today: runs Codex fix pass and re-runs quality gates.
100
- - Manual today: rerun until you are satisfied; no built-in auto-loop to "done" yet.
101
-
102
- 6. `pr` (open pull request)
103
-
104
- ```bash
105
- bt pr my-change --dry-run
106
- bt pr my-change --run
107
- ```
108
-
109
- - Automated today: runs tests/lint, creates branch, optionally commits, pushes, opens PR via `gh`.
110
- - Manual today: choose reviewers/labels, final PR polish, and merge strategy.
111
-
112
- ## Current Limitations
113
-
114
- - No one-command autonomous loop yet (you run each stage yourself).
115
- - `research` needs Codex installed and available.
116
- - `implement`/`fix` can run as stubs without Codex (gates still run, code may not change).
117
- - PR flow depends on `gh` and repository permissions.
118
-
119
- ## Troubleshooting
120
-
121
- `gh` auth fails (`bt spec 123` or `bt pr ...`):
122
-
123
- ```bash
124
- gh auth status
125
- gh auth login
126
- ```
127
-
128
- Codex missing (`codex required` or `codex not found`):
129
-
130
- - Install Codex and make sure `codex` is on your `PATH`.
131
- - Or set a custom binary in `.bt.env`: `BT_CODEX_BIN=/path/to/codex`.
132
-
133
- Quality gate failures on `implement`/`fix`:
134
-
135
- ```bash
136
- bt gates my-change
137
- ```
138
-
139
- - Fix failing lint/typecheck/test commands.
140
- - Override gate commands in `.bt.env` if auto-detection is wrong:
141
- - `BT_GATE_LINT=...`
142
- - `BT_GATE_TYPECHECK=...`
143
- - `BT_GATE_TEST=...`
144
-
145
- ## Release Publish Steps
146
-
147
- Use this when preparing an npm release. This workflow validates readiness but does not publish automatically.
148
-
149
- 1. Confirm clean main branch and pull latest changes.
150
-
151
- ```bash
152
- git checkout main
153
- git pull --ff-only
154
- ```
16
+ # Install
17
+ npm i -g biotonomy
155
18
 
156
- 2. Run release preflight checks (tests, lint, pack verification, and `npm pack --dry-run` summary).
19
+ # In your project repo
20
+ bt bootstrap
157
21
 
158
- ```bash
159
- npm run release:ready
160
- ```
22
+ # Create a feature scaffold
23
+ FEATURE=hello-world
24
+ bt spec "$FEATURE"
161
25
 
162
- 3. Ensure npm authentication is ready for publish.
26
+ # Loop requires an approved plan review verdict first
27
+ cat > "specs/$FEATURE/PLAN_REVIEW.md" <<'MD'
28
+ Verdict: APPROVED_PLAN
29
+ MD
163
30
 
164
- ```bash
165
- npm whoami
166
- # if needed:
167
- npm login
31
+ # Run autonomous implement/review/fix iterations (with gates)
32
+ bt loop "$FEATURE" --max-iterations 3
168
33
  ```
169
34
 
170
- 4. Bump version and create a tag.
35
+ ## `bt loop`
171
36
 
172
- ```bash
173
- npm version patch
174
- # or: npm version minor / npm version major
175
- ```
37
+ `bt loop <feature> [--max-iterations N]` runs:
38
+ 1. preflight quality gates
39
+ 2. `implement`
40
+ 3. `review`
41
+ 4. `fix` only when review verdict is `NEEDS_CHANGES`
42
+ 5. repeat until verdict is `APPROVE`/`APPROVED` and gates pass, or max iterations is reached
176
43
 
177
- 5. Push commit and tag.
44
+ Loop hard-requires an approved `specs/<feature>/PLAN_REVIEW.md` verdict (`APPROVE_PLAN` or `APPROVED_PLAN`).
178
45
 
179
- ```bash
180
- git push --follow-tags
181
- ```
46
+ ## Artifacts And State
182
47
 
183
- 6. Publish manually.
48
+ Biotonomy writes feature state under `specs/<feature>/`:
49
+ - `SPEC.md`
50
+ - `RESEARCH.md`
51
+ - `PLAN_REVIEW.md`
52
+ - `REVIEW.md`
53
+ - `history/` stage snapshots (`###-<stage>.md`) and loop iteration snapshots (`*-loop-iter-###.md`)
54
+ - `loop-progress.json` loop summary and per-iteration status
55
+ - `progress.txt` append-only stage log
56
+ - `.artifacts/` Codex logs and command artifacts (for example `codex-implement.log`, `codex-review.log`, `codex-fix.log`)
57
+ - `gates.json` feature gate results when running `bt gates <feature>`
184
58
 
185
- ```bash
186
- npm publish --access public
187
- ```
188
-
189
- - If your npm account uses 2FA for publish, npm will require a one-time code during `npm publish`.
59
+ Global gate state is written to `.bt/state/gates.json` when running `bt gates` without a feature.
190
60
 
191
- ## Commands
61
+ ## Manual Commands
192
62
 
193
63
  ```bash
194
64
  bt bootstrap
195
65
  bt spec <feature|issue#>
196
66
  bt research <feature>
67
+ bt plan-review <feature>
197
68
  bt implement <feature>
198
69
  bt review <feature>
199
70
  bt fix <feature>
71
+ bt loop <feature> [--max-iterations N]
200
72
  bt gates [feature]
201
73
  bt status
202
- bt pr <feature> [--dry-run|--run]
203
- bt reset
74
+ bt pr <feature> [--run]
75
+ ```
76
+
77
+ ## Configuration
78
+
79
+ Project config lives in `.bt.env` (created by `bt bootstrap`). Common overrides:
80
+
81
+ ```bash
82
+ BT_SPECS_DIR=specs
83
+ BT_STATE_DIR=.bt
84
+ BT_GATE_LINT="npm run lint"
85
+ BT_GATE_TYPECHECK="tsc --noEmit"
86
+ BT_GATE_TEST="npm test"
87
+ BT_CODEX_BIN="/path/to/codex"
204
88
  ```
205
89
 
206
- ## Development
90
+ ## Release
91
+
92
+ Run the release readiness checks:
207
93
 
208
94
  ```bash
209
- npm test
210
- npm run lint
95
+ npm run release:ready
211
96
  ```
97
+
98
+ That script runs tests, lint, pack verification, and `npm pack --dry-run`.
package/bt CHANGED
@@ -1,5 +1,16 @@
1
1
  #!/usr/bin/env bash
2
2
  set -euo pipefail
3
3
 
4
- SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
4
+ bt_script_dir() {
5
+ local src="${BASH_SOURCE[0]}"
6
+ while [ -h "$src" ]; do
7
+ local dir
8
+ dir="$(cd -P "$(dirname "$src")" && pwd)"
9
+ src="$(readlink "$src")"
10
+ [[ "$src" != /* ]] && src="$dir/$src"
11
+ done
12
+ cd -P "$(dirname "$src")" && pwd
13
+ }
14
+
15
+ SCRIPT_DIR="$(bt_script_dir)"
5
16
  exec "$SCRIPT_DIR/bt.sh" "$@"
package/commands/loop.sh CHANGED
@@ -59,15 +59,6 @@ bt_cmd_loop() {
59
59
  bt_env_load || true
60
60
  bt_ensure_dirs
61
61
 
62
- bt_info "starting loop for: $feature (max iterations: $max_iter)"
63
-
64
- bt_info "running preflight gates..."
65
- if ! bt_run_gates; then
66
- bt_err "preflight gates failed; aborting before implement/review"
67
- return 1
68
- fi
69
- bt_info "preflight gates: PASS"
70
-
71
62
  local feat_dir
72
63
  feat_dir="$(bt_feature_dir "$feature")"
73
64
  local plan_review="$feat_dir/PLAN_REVIEW.md"
@@ -77,6 +68,27 @@ bt_cmd_loop() {
77
68
  bt_die "loop hard-fails without approved PLAN_REVIEW verdict before implement/review"
78
69
  fi
79
70
 
71
+ bt_info "starting loop for: $feature (max iterations: $max_iter)"
72
+
73
+ local -a gate_args=()
74
+ if [[ "${BT_LOOP_REQUIRE_GATES:-0}" == "1" ]]; then
75
+ gate_args=(--require-any)
76
+ fi
77
+
78
+ bt_info "running preflight gates..."
79
+ if (( ${#gate_args[@]} > 0 )); then
80
+ if ! bt_run_gates "${gate_args[@]}"; then
81
+ bt_err "preflight gates failed (or none configured); aborting before implement/review"
82
+ return 1
83
+ fi
84
+ else
85
+ if ! bt_run_gates; then
86
+ bt_err "preflight gates failed (or none configured); aborting before implement/review"
87
+ return 1
88
+ fi
89
+ fi
90
+ bt_info "preflight gates: PASS"
91
+
80
92
  # Source required commands so we can call them directly
81
93
  # shellcheck source=/dev/null
82
94
  source "$BT_ROOT/commands/implement.sh"
@@ -165,11 +177,20 @@ PY
165
177
  # bt_cmd_implement/fix return non-zero if gates fail, but we capture the status.
166
178
  # We call bt_run_gates here just to be sure of the final state post-review.
167
179
  local gates_ok=1
168
- if ! bt_run_gates; then
169
- gates_ok=0
170
- bt_info "gates: FAIL"
180
+ if (( ${#gate_args[@]} > 0 )); then
181
+ if ! bt_run_gates "${gate_args[@]}"; then
182
+ gates_ok=0
183
+ bt_info "gates: FAIL"
184
+ else
185
+ bt_info "gates: PASS"
186
+ fi
171
187
  else
172
- bt_info "gates: PASS"
188
+ if ! bt_run_gates; then
189
+ gates_ok=0
190
+ bt_info "gates: FAIL"
191
+ else
192
+ bt_info "gates: PASS"
193
+ fi
173
194
  fi
174
195
 
175
196
  local fix_status="SKIP"
@@ -31,7 +31,12 @@ EOF
31
31
 
32
32
  if bt_codex_available; then
33
33
  bt_info "running codex (full-auto) using prompts/plan-review.md"
34
- if BT_FEATURE="$feature" BT_CODEX_LOG_FILE="/tmp/codex.log" bt_codex_exec_full_auto "$BT_ROOT/prompts/plan-review.md"; then
34
+ local artifacts_dir codex_logf
35
+ artifacts_dir="$dir/.artifacts"
36
+ mkdir -p "$artifacts_dir"
37
+ codex_logf="$artifacts_dir/codex-plan-review.log"
38
+ : >"$codex_logf"
39
+ if BT_FEATURE="$feature" BT_CODEX_LOG_FILE="$codex_logf" bt_codex_exec_full_auto "$BT_ROOT/prompts/plan-review.md"; then
35
40
  :
36
41
  else
37
42
  bt_die "codex failed (plan-review)"
package/commands/pr.sh CHANGED
@@ -189,20 +189,19 @@ bt_cmd_pr() {
189
189
  fi
190
190
 
191
191
  # 2. Fail-loud preflight for unstaged expected files (before tests/commit flow).
192
- if [[ "$commit" == "1" ]]; then
193
- local unstaged=""
194
- local check_paths=(tests lib commands scripts specs prompts) # Added specs and prompts
195
- if git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
196
- unstaged="$(git ls-files --others --modified --exclude-standard -- "${check_paths[@]}" 2>/dev/null || true)"
197
- else
198
- # Outside a git repo, treat present implementation files as unstaged by definition.
199
- unstaged="$(find "${check_paths[@]}" -type f 2>/dev/null | LC_ALL=C sort || true)"
200
- fi
201
- if [[ -n "$unstaged" ]]; then
202
- bt_err "Found unstaged files that might be required for this feature:"
203
- printf '%s\n' "$unstaged" >&2
204
- bt_die "Abort: ship requires all feature files to be staged. Use git add and try again."
205
- fi
192
+ # P0 #18: fail-loud even with --no-commit
193
+ local unstaged=""
194
+ if git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
195
+ # Protect all repo files, not only a fixed allowlist of directories.
196
+ unstaged="$(git ls-files --others --modified --exclude-standard 2>/dev/null || true)"
197
+ else
198
+ # Outside a git repo, treat present files (except .git internals) as unstaged by definition.
199
+ unstaged="$(find . -path './.git' -prune -o -type f -print 2>/dev/null | sed 's#^\./##' | LC_ALL=C sort || true)"
200
+ fi
201
+ if [[ -n "$unstaged" ]]; then
202
+ bt_err "Found unstaged files that might be required for this feature:"
203
+ printf '%s\n' "$unstaged" >&2
204
+ bt_die "Abort: ship requires all feature files to be staged. Use git add and try again."
206
205
  fi
207
206
 
208
207
  if [[ "$run_mode" == "dry-run" ]]; then
@@ -255,14 +254,7 @@ bt_cmd_pr() {
255
254
  # 4. Commit changes if requested
256
255
  if [[ "$commit" == "1" ]]; then
257
256
  bt_info "committing changes..."
258
- local unstaged
259
- local check_paths=(tests lib commands scripts specs prompts)
260
- unstaged="$(git status --porcelain -- "${check_paths[@]}" 2>/dev/null || true)"
261
- if [[ -n "$unstaged" ]]; then
262
- bt_err "Found unstaged files that might be required for this feature:"
263
- printf '%s\n' "$unstaged" >&2
264
- bt_die "Abort: ship requires all feature files to be staged. Use git add and try again."
265
- fi
257
+ # (Unstaged check removed here as it is now redundant with global T0 check)
266
258
 
267
259
  if ! git diff --cached --quiet; then
268
260
  git commit -m "feat($feature): ship implementation"
package/commands/spec.sh CHANGED
@@ -38,6 +38,84 @@ NODE
38
38
  )"
39
39
  }
40
40
 
41
+ bt__stories_from_issue_json() {
42
+ bt__require_cmd node
43
+ node -e "$(
44
+ cat <<'NODE'
45
+ const fs = require("fs");
46
+ const j = JSON.parse(fs.readFileSync(0, "utf8") || "{}");
47
+
48
+ function clean(s) {
49
+ return String(s || "").replace(/\s+/g, " ").trim();
50
+ }
51
+
52
+ const title = clean(j.title);
53
+ const body = String(j.body || "").replace(/\r/g, "");
54
+ const lines = body.split("\n");
55
+
56
+ let inAcceptance = false;
57
+ const acceptanceBullets = [];
58
+ const allBullets = [];
59
+ for (const rawLine of lines) {
60
+ const line = rawLine || "";
61
+ if (/^\s*#{1,6}\s*acceptance\b/i.test(line) || /^\s*acceptance criteria\s*:?\s*$/i.test(line)) {
62
+ inAcceptance = true;
63
+ continue;
64
+ }
65
+ if (/^\s*#{1,6}\s+/.test(line) && inAcceptance) {
66
+ inAcceptance = false;
67
+ }
68
+
69
+ const m = line.match(/^\s*[-*]\s+(?:\[[ xX]\]\s*)?(.+?)\s*$/);
70
+ if (!m) {
71
+ continue;
72
+ }
73
+ const bullet = clean(m[1]);
74
+ if (!bullet) {
75
+ continue;
76
+ }
77
+ allBullets.push(bullet);
78
+ if (inAcceptance) {
79
+ acceptanceBullets.push(bullet);
80
+ }
81
+ }
82
+
83
+ const selectedBullets = acceptanceBullets.length > 0 ? acceptanceBullets : allBullets;
84
+ const storyTitles = [];
85
+ if (title) {
86
+ storyTitles.push(title);
87
+ }
88
+ for (const bullet of selectedBullets) {
89
+ if (storyTitles.length >= 5) {
90
+ break;
91
+ }
92
+ storyTitles.push(bullet);
93
+ }
94
+
95
+ if (storyTitles.length === 0) {
96
+ const fallback = clean(body).slice(0, 120);
97
+ if (fallback) {
98
+ storyTitles.push(fallback);
99
+ } else {
100
+ storyTitles.push("Capture issue requirements");
101
+ }
102
+ }
103
+
104
+ let out = "";
105
+ for (let i = 0; i < storyTitles.length; i += 1) {
106
+ const id = i + 1;
107
+ const titleText = storyTitles[i];
108
+ out += "## [ID:S" + id + "] " + titleText + "\n";
109
+ out += "- **status:** draft\n";
110
+ out += "- **priority:** " + (id <= 3 ? 1 : 2) + "\n";
111
+ out += "- **acceptance:** " + titleText + "\n";
112
+ out += "- **tests:**\n\n";
113
+ }
114
+ process.stdout.write(out);
115
+ NODE
116
+ )"
117
+ }
118
+
41
119
  bt_cmd_spec() {
42
120
  local force=0
43
121
  local arg=""
@@ -132,8 +210,9 @@ EOF
132
210
  [[ -n "$title" ]] || title="(untitled)"
133
211
  [[ -n "$url" ]] || url="https://github.com/$repo/issues/$issue"
134
212
 
135
- local summary
213
+ local summary stories
136
214
  summary="$(bt__summarize_body "$body")"
215
+ stories="$(printf '%s' "$json" | bt__stories_from_issue_json)"
137
216
 
138
217
  cat >"$spec" <<EOF
139
218
  ---
@@ -154,35 +233,7 @@ $summary
154
233
 
155
234
  # Stories
156
235
 
157
- ## [ID:S1] Confirm repo resolution and env fallback
158
- - **status:** draft
159
- - **priority:** 1
160
- - **acceptance:** bt can determine repo slug from git remote origin; otherwise requires BT_REPO
161
- - **tests:**
162
-
163
- ## [ID:S2] Fetch issue details via gh
164
- - **status:** draft
165
- - **priority:** 1
166
- - **acceptance:** bt spec <issue#> uses gh to retrieve title/body/url and handles errors clearly
167
- - **tests:**
168
-
169
- ## [ID:S3] Generate a SPEC.md with frontmatter + problem summary
170
- - **status:** draft
171
- - **priority:** 1
172
- - **acceptance:** SPEC includes required frontmatter, a Problem section, and a Stories section (3-7 stories)
173
- - **tests:**
174
-
175
- ## [ID:S4] Record exact gh commands used in SPEC footer
176
- - **status:** draft
177
- - **priority:** 2
178
- - **acceptance:** SPEC footer includes the exact gh command(s) executed
179
- - **tests:**
180
-
181
- ## [ID:S5] Add tests stubbing gh via PATH
182
- - **status:** draft
183
- - **priority:** 1
184
- - **acceptance:** tests run offline and validate SPEC content generation
185
- - **tests:**
236
+ ${stories}
186
237
 
187
238
  ---
188
239
 
package/lib/gates.sh CHANGED
@@ -77,6 +77,12 @@ bt_get_gate_config() {
77
77
  # Runs gates and returns a JSON string fragment with results.
78
78
  # Writes logs to stderr. Returns 0 if all gates passed, 1 otherwise.
79
79
  bt_run_gates() {
80
+ local require_any=0
81
+ if [[ "${1:-}" == "--require-any" ]]; then
82
+ require_any=1
83
+ shift
84
+ fi
85
+
80
86
  local config
81
87
  config="$(bt_get_gate_config)"
82
88
 
@@ -115,6 +121,9 @@ bt_run_gates() {
115
121
  if [[ "$any" == "0" ]]; then
116
122
  bt_warn "no gates ran"
117
123
  printf '{"ts": "%s", "results": {}}\n' "$(date -u +'%Y-%m-%dT%H:%M:%SZ')"
124
+ if [[ "$require_any" == "1" ]]; then
125
+ return 1
126
+ fi
118
127
  return 0
119
128
  fi
120
129
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "biotonomy",
3
- "version": "0.2.1",
3
+ "version": "0.2.3",
4
4
  "description": "Codex-native autonomous development loop CLI",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -12,7 +12,8 @@
12
12
  },
13
13
  "homepage": "https://github.com/archive-dot-com/biotonomy#readme",
14
14
  "bin": {
15
- "bt": "bt"
15
+ "bt": "bt",
16
+ "biotonomy": "bt"
16
17
  },
17
18
  "files": [
18
19
  "bt",