biotonomy 0.2.6 → 0.2.7

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/commands/loop.sh CHANGED
@@ -63,11 +63,25 @@ bt_cmd_loop() {
63
63
 
64
64
  local feat_dir
65
65
  feat_dir="$(bt_feature_dir "$feature")"
66
+
67
+ # Auto-run plan-review if PLAN_REVIEW.md is missing or unapproved
66
68
  local plan_review="$feat_dir/PLAN_REVIEW.md"
67
69
  if [[ ! -f "$plan_review" ]] || ! grep -qiE "Verdict:.*(APPROVE_PLAN|APPROVED_PLAN)" "$plan_review"; then
68
- bt_err "missing or unapproved $plan_review"
69
- bt_err "run: bt plan-review $feature"
70
- bt_die "loop hard-fails without approved PLAN_REVIEW verdict before implement/review"
70
+ bt_info "PLAN_REVIEW missing or unapproved; auto-running plan-review..."
71
+ # shellcheck source=/dev/null
72
+ source "$BT_ROOT/commands/plan-review.sh"
73
+ if ! bt_cmd_plan_review "$feature"; then
74
+ bt_err "auto plan-review failed for $feature"
75
+ bt_err "manually run: bt plan-review $feature"
76
+ bt_die "loop hard-fails without approved PLAN_REVIEW verdict before implement/review"
77
+ fi
78
+ # Re-check after auto-run
79
+ if [[ ! -f "$plan_review" ]] || ! grep -qiE "Verdict:.*(APPROVE_PLAN|APPROVED_PLAN)" "$plan_review"; then
80
+ bt_err "plan-review ran but did not produce an approved verdict"
81
+ bt_err "check $plan_review and re-run: bt plan-review $feature"
82
+ bt_die "loop hard-fails without approved PLAN_REVIEW verdict before implement/review"
83
+ fi
84
+ bt_info "plan-review auto-approved; continuing loop"
71
85
  fi
72
86
 
73
87
  bt_info "starting loop for: $feature (max iterations: $max_iter)"
package/lib/env.sh CHANGED
@@ -6,6 +6,14 @@ bt__export_kv() {
6
6
  local val="$2"
7
7
 
8
8
  [[ "$key" =~ ^[A-Za-z_][A-Za-z0-9_]*$ ]] || return 0
9
+
10
+ # Respect process env: only set if not already present in environment.
11
+ # This ensures BT_GATE_TEST='echo hi' bt gates works even when .bt.env
12
+ # contains an empty BT_GATE_TEST= line. (#38)
13
+ if [[ -n "${!key+x}" ]]; then
14
+ return 0
15
+ fi
16
+
9
17
  # Export without eval; keep value literal even if it contains spaces/symbols.
10
18
  export "$key=$val"
11
19
  }
package/lib/gates.sh CHANGED
@@ -76,6 +76,33 @@ bt_get_gate_config() {
76
76
 
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
+ bt__gate_script_exists() {
80
+ # Check if an npm/pnpm/yarn script actually exists in package.json.
81
+ # Returns 0 if the script exists or if we can't determine (non-npm project).
82
+ local cmd="$1"
83
+ local script_name=""
84
+
85
+ # Extract script name from common patterns
86
+ case "$cmd" in
87
+ "npm run "*) script_name="${cmd#npm run }" ;;
88
+ "npm test") script_name="test" ;;
89
+ "pnpm "*) script_name="${cmd#pnpm }" ;;
90
+ "yarn "*) script_name="${cmd#yarn }" ;;
91
+ *) return 0 ;; # Not an npm-style command, assume exists
92
+ esac
93
+ script_name="${script_name%% *}" # Take first word only
94
+
95
+ local pkg="$BT_PROJECT_ROOT/package.json"
96
+ [[ -f "$pkg" ]] || return 0 # No package.json, can't check
97
+
98
+ # Check if script exists in package.json
99
+ if command -v node >/dev/null 2>&1; then
100
+ node -e "const p=require('$pkg'); process.exit(p.scripts && p.scripts['$script_name'] ? 0 : 1)" 2>/dev/null
101
+ return $?
102
+ fi
103
+ return 0 # Can't verify, assume exists
104
+ }
105
+
79
106
  bt_run_gates() {
80
107
  local require_any=0
81
108
  if [[ "${1:-}" == "--require-any" ]]; then
@@ -93,14 +120,35 @@ bt_run_gates() {
93
120
 
94
121
  while IFS= read -r line; do
95
122
  [[ -n "$line" ]] || continue
96
- any=1
97
123
  k="${line%%=*}"
98
124
  v="${line#*=}"
99
125
 
126
+ # Skip gates explicitly set to "skip" or "" (disabled)
127
+ if [[ "$v" == "skip" || -z "$v" ]]; then
128
+ bt_warn "gate disabled: $k"
129
+ local entry k_json v_json
130
+ k_json="$(bt__json_escape "$k")"
131
+ v_json="$(bt__json_escape "$v")"
132
+ printf -v entry '"%s": {"cmd": "%s", "status": -1, "skipped": true}' "$k_json" "$v_json"
133
+ if [[ -z "$results_json" ]]; then results_json="$entry"; else results_json="$results_json, $entry"; fi
134
+ continue
135
+ fi
136
+
137
+ # Skip gates whose scripts don't exist
138
+ if ! bt__gate_script_exists "$v"; then
139
+ bt_warn "gate skipped (script missing): $k ($v)"
140
+ local entry k_json v_json
141
+ k_json="$(bt__json_escape "$k")"
142
+ v_json="$(bt__json_escape "$v")"
143
+ printf -v entry '"%s": {"cmd": "%s", "status": -1, "skipped": true}' "$k_json" "$v_json"
144
+ if [[ -z "$results_json" ]]; then results_json="$entry"; else results_json="$results_json, $entry"; fi
145
+ continue
146
+ fi
147
+
148
+ any=1
149
+
100
150
  bt_info "gate: $k ($v)"
101
151
  local status=0
102
- # Use bash -lc for interactivity if needed, but we typically want it non-interactive.
103
- # The original used bash -lc "$v". We'll stick to that but capture status.
104
152
  if ! (cd "$BT_PROJECT_ROOT" && bash -lc "$v"); then
105
153
  bt_err "gate failed: $k"
106
154
  status=1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "biotonomy",
3
- "version": "0.2.6",
3
+ "version": "0.2.7",
4
4
  "description": "Codex-native autonomous development loop CLI",
5
5
  "license": "MIT",
6
6
  "repository": {