prizmkit 1.1.39 → 1.1.41
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/bundled/VERSION.json +3 -3
- package/bundled/dev-pipeline/SCHEMA_ANALYSIS.md +1 -1
- package/bundled/dev-pipeline/run-bugfix.sh +74 -0
- package/bundled/dev-pipeline/run-feature.sh +74 -0
- package/bundled/dev-pipeline/run-refactor.sh +74 -0
- package/bundled/dev-pipeline/scripts/generate-bootstrap-prompt.py +0 -6
- package/bundled/dev-pipeline/scripts/generate-bugfix-prompt.py +118 -1
- package/bundled/dev-pipeline/scripts/generate-refactor-prompt.py +123 -8
- package/bundled/dev-pipeline/templates/bootstrap-tier1.md +0 -23
- package/bundled/dev-pipeline/templates/bootstrap-tier2.md +0 -23
- package/bundled/dev-pipeline/templates/bootstrap-tier3.md +0 -23
- package/bundled/dev-pipeline/templates/bug-fix-list-schema.json +22 -3
- package/bundled/dev-pipeline/templates/bugfix-bootstrap-prompt.md +56 -0
- package/bundled/dev-pipeline/templates/refactor-bootstrap-prompt.md +64 -4
- package/bundled/dev-pipeline/templates/refactor-list-schema.json +22 -3
- package/bundled/dev-pipeline/tests/test-deploy-safety.sh +223 -0
- package/bundled/skills/_metadata.json +3 -3
- package/bundled/skills/app-planner/SKILL.md +0 -3
- package/bundled/skills/bugfix-pipeline-launcher/SKILL.md +34 -6
- package/bundled/skills/feature-pipeline-launcher/SKILL.md +42 -18
- package/bundled/skills/prizmkit-committer/SKILL.md +0 -1
- package/bundled/skills/prizmkit-deploy/SKILL.md +491 -209
- package/bundled/skills/prizmkit-deploy/references/cloud-platform-deploy.md +93 -0
- package/bundled/skills/prizmkit-deploy/references/deploy-config-schema.md +147 -0
- package/bundled/skills/prizmkit-deploy/references/deploy-history-schema.md +62 -0
- package/bundled/skills/prizmkit-deploy/references/docker-deploy.md +31 -0
- package/bundled/skills/prizmkit-deploy/references/nginx-blue-green.md +59 -0
- package/bundled/skills/prizmkit-init/SKILL.md +0 -2
- package/bundled/skills/prizmkit-plan/SKILL.md +0 -3
- package/bundled/skills/recovery-workflow/SKILL.md +96 -7
- package/bundled/skills/refactor-pipeline-launcher/SKILL.md +40 -9
- package/package.json +1 -1
- package/bundled/dev-pipeline/templates/sections/phase-deploy-verification.md +0 -31
- package/bundled/skills/prizmkit-deploy/assets/deploy-template.md +0 -187
package/bundled/VERSION.json
CHANGED
|
@@ -325,7 +325,7 @@ pending, in_progress, completed, failed, skipped
|
|
|
325
325
|
| `failure-log-check.md` | Failure log diagnostics |
|
|
326
326
|
| `context-budget-rules.md` | Context window guardrails |
|
|
327
327
|
| `phase-browser-verification.md` | Playwright UI verification |
|
|
328
|
-
| `phase-deploy-verification.md` | Local deployment check |
|
|
328
|
+
| `phase-deploy-verification.md` | [DEPRECATED] Local deployment check (template removed) |
|
|
329
329
|
| `ac-verification-checklist.md` | Acceptance criteria verification |
|
|
330
330
|
| `feature-context.md` | Feature brief context |
|
|
331
331
|
| `phase-specify-plan-full.md` | Full spec + plan workflow |
|
|
@@ -50,6 +50,7 @@ MODEL=${MODEL:-""}
|
|
|
50
50
|
DEV_BRANCH=${DEV_BRANCH:-""}
|
|
51
51
|
AUTO_PUSH=${AUTO_PUSH:-0}
|
|
52
52
|
STOP_ON_FAILURE=${STOP_ON_FAILURE:-0}
|
|
53
|
+
ENABLE_DEPLOY=${ENABLE_DEPLOY:-0}
|
|
53
54
|
|
|
54
55
|
# Source shared common helpers (CLI/platform detection + logs + deps)
|
|
55
56
|
source "$SCRIPT_DIR/lib/common.sh"
|
|
@@ -955,6 +956,79 @@ main() {
|
|
|
955
956
|
log_success " Total subagent calls: $total_subagent_calls"
|
|
956
957
|
log_success "════════════════════════════════════════════════════"
|
|
957
958
|
|
|
959
|
+
# ── Deploy session (only if ENABLE_DEPLOY=1 and all bugs fixed) ──
|
|
960
|
+
if [[ "$ENABLE_DEPLOY" == "1" ]]; then
|
|
961
|
+
local incomplete_count
|
|
962
|
+
incomplete_count=$({ python3 -c "
|
|
963
|
+
import json, sys
|
|
964
|
+
with open(sys.argv[1]) as f:
|
|
965
|
+
data = json.load(f)
|
|
966
|
+
bad = [b for b in data.get('bugs', [])
|
|
967
|
+
if b.get('status') not in ('completed', 'skipped', 'needs_info')]
|
|
968
|
+
for b in bad:
|
|
969
|
+
print(f\" {b['id']}: {b.get('status', 'unknown')} — {b.get('title', '')}\")
|
|
970
|
+
print(len(bad))
|
|
971
|
+
" "$bug_list" 2>/dev/null || echo "0"; } | tee /dev/stderr | tail -1)
|
|
972
|
+
|
|
973
|
+
if [[ "$incomplete_count" -gt 0 ]]; then
|
|
974
|
+
echo ""
|
|
975
|
+
log_warn "DEPLOY BLOCKED: $incomplete_count bug(s) not fixed successfully."
|
|
976
|
+
log_warn "Fix failed bugs and re-run, or manually run /prizmkit-deploy."
|
|
977
|
+
else
|
|
978
|
+
echo ""
|
|
979
|
+
log_info "All bugs fixed — starting deploy session..."
|
|
980
|
+
log_info "ENABLE_DEPLOY=1"
|
|
981
|
+
|
|
982
|
+
local deploy_session_id="deploy-$(date +%Y%m%d%H%M%S)"
|
|
983
|
+
local deploy_session_dir="$STATE_DIR/deploy/$deploy_session_id"
|
|
984
|
+
mkdir -p "$deploy_session_dir/logs"
|
|
985
|
+
|
|
986
|
+
local deploy_prompt="$deploy_session_dir/bootstrap-prompt.md"
|
|
987
|
+
local _deploy_branch _deploy_commit
|
|
988
|
+
_deploy_branch=$(git -C "$_proj_root" rev-parse --abbrev-ref HEAD 2>/dev/null || echo "unknown")
|
|
989
|
+
_deploy_commit=$(git -C "$_proj_root" rev-parse --short HEAD 2>/dev/null || echo "unknown")
|
|
990
|
+
cat > "$deploy_prompt" << DEPLOY_PROMPT_EOF
|
|
991
|
+
## Deploy
|
|
992
|
+
|
|
993
|
+
All bugs have been fixed successfully.
|
|
994
|
+
|
|
995
|
+
- Branch: $_deploy_branch
|
|
996
|
+
- Commit: $_deploy_commit
|
|
997
|
+
|
|
998
|
+
Run /prizmkit-deploy to deploy the project. Read .prizmkit/deploy/deploy.config.json
|
|
999
|
+
for deployment configuration. If no deploy config exists, guide the user through
|
|
1000
|
+
setting one up before deploying.
|
|
1001
|
+
DEPLOY_PROMPT_EOF
|
|
1002
|
+
|
|
1003
|
+
log_info "Deploy prompt: $deploy_prompt"
|
|
1004
|
+
log_info "Deploy log: $deploy_session_dir/logs/session.log"
|
|
1005
|
+
|
|
1006
|
+
case "$CLI_CMD" in
|
|
1007
|
+
*claude*)
|
|
1008
|
+
"$CLI_CMD" \
|
|
1009
|
+
-p "$(cat "$deploy_prompt")" \
|
|
1010
|
+
--dangerously-skip-permissions \
|
|
1011
|
+
> "$deploy_session_dir/logs/session.log" 2>&1
|
|
1012
|
+
;;
|
|
1013
|
+
*)
|
|
1014
|
+
"$CLI_CMD" \
|
|
1015
|
+
--print \
|
|
1016
|
+
-y \
|
|
1017
|
+
< "$deploy_prompt" \
|
|
1018
|
+
> "$deploy_session_dir/logs/session.log" 2>&1
|
|
1019
|
+
;;
|
|
1020
|
+
esac
|
|
1021
|
+
local deploy_exit=$?
|
|
1022
|
+
|
|
1023
|
+
if [[ $deploy_exit -eq 0 ]]; then
|
|
1024
|
+
log_success "Deploy session completed (exit 0)"
|
|
1025
|
+
else
|
|
1026
|
+
log_warn "Deploy session exited with code $deploy_exit"
|
|
1027
|
+
log_warn "Review log: $deploy_session_dir/logs/session.log"
|
|
1028
|
+
fi
|
|
1029
|
+
fi
|
|
1030
|
+
fi
|
|
1031
|
+
|
|
958
1032
|
break
|
|
959
1033
|
fi
|
|
960
1034
|
|
|
@@ -53,6 +53,7 @@ MODEL=${MODEL:-""}
|
|
|
53
53
|
DEV_BRANCH=${DEV_BRANCH:-""}
|
|
54
54
|
AUTO_PUSH=${AUTO_PUSH:-0}
|
|
55
55
|
STOP_ON_FAILURE=${STOP_ON_FAILURE:-0}
|
|
56
|
+
ENABLE_DEPLOY=${ENABLE_DEPLOY:-0}
|
|
56
57
|
|
|
57
58
|
# Source shared common helpers (CLI/platform detection + logs + deps)
|
|
58
59
|
source "$SCRIPT_DIR/lib/common.sh"
|
|
@@ -1186,6 +1187,79 @@ print(count)
|
|
|
1186
1187
|
log_warn "Run './run-feature.sh unskip' to reset and retry them."
|
|
1187
1188
|
fi
|
|
1188
1189
|
|
|
1190
|
+
# ── Deploy session (only if ENABLE_DEPLOY=1 and all features completed) ──
|
|
1191
|
+
if [[ "$ENABLE_DEPLOY" == "1" ]]; then
|
|
1192
|
+
local incomplete_count
|
|
1193
|
+
incomplete_count=$({ python3 -c "
|
|
1194
|
+
import json, sys
|
|
1195
|
+
with open(sys.argv[1]) as f:
|
|
1196
|
+
data = json.load(f)
|
|
1197
|
+
bad = [f for f in data.get('features', [])
|
|
1198
|
+
if f.get('status') not in ('completed', 'skipped')]
|
|
1199
|
+
for f in bad:
|
|
1200
|
+
print(f\" {f['id']}: {f.get('status', 'unknown')} — {f.get('title', '')}\")
|
|
1201
|
+
print(len(bad))
|
|
1202
|
+
" "$feature_list" 2>/dev/null || echo "0"; } | tee /dev/stderr | tail -1)
|
|
1203
|
+
|
|
1204
|
+
if [[ "$incomplete_count" -gt 0 ]]; then
|
|
1205
|
+
echo ""
|
|
1206
|
+
log_warn "DEPLOY BLOCKED: $incomplete_count task(s) not completed successfully."
|
|
1207
|
+
log_warn "Fix failed tasks and re-run, or manually run /prizmkit-deploy."
|
|
1208
|
+
else
|
|
1209
|
+
echo ""
|
|
1210
|
+
log_info "All tasks completed — starting deploy session..."
|
|
1211
|
+
log_info "ENABLE_DEPLOY=1"
|
|
1212
|
+
|
|
1213
|
+
local deploy_session_id="deploy-$(date +%Y%m%d%H%M%S)"
|
|
1214
|
+
local deploy_session_dir="$STATE_DIR/deploy/$deploy_session_id"
|
|
1215
|
+
mkdir -p "$deploy_session_dir/logs"
|
|
1216
|
+
|
|
1217
|
+
local deploy_prompt="$deploy_session_dir/bootstrap-prompt.md"
|
|
1218
|
+
local _deploy_branch _deploy_commit
|
|
1219
|
+
_deploy_branch=$(git -C "$_proj_root" rev-parse --abbrev-ref HEAD 2>/dev/null || echo "unknown")
|
|
1220
|
+
_deploy_commit=$(git -C "$_proj_root" rev-parse --short HEAD 2>/dev/null || echo "unknown")
|
|
1221
|
+
cat > "$deploy_prompt" << DEPLOY_PROMPT_EOF
|
|
1222
|
+
## Deploy
|
|
1223
|
+
|
|
1224
|
+
All features in the pipeline completed successfully.
|
|
1225
|
+
|
|
1226
|
+
- Branch: $_deploy_branch
|
|
1227
|
+
- Commit: $_deploy_commit
|
|
1228
|
+
|
|
1229
|
+
Run /prizmkit-deploy to deploy the project. Read .prizmkit/deploy/deploy.config.json
|
|
1230
|
+
for deployment configuration. If no deploy config exists, guide the user through
|
|
1231
|
+
setting one up before deploying.
|
|
1232
|
+
DEPLOY_PROMPT_EOF
|
|
1233
|
+
|
|
1234
|
+
log_info "Deploy prompt: $deploy_prompt"
|
|
1235
|
+
log_info "Deploy log: $deploy_session_dir/logs/session.log"
|
|
1236
|
+
|
|
1237
|
+
case "$CLI_CMD" in
|
|
1238
|
+
*claude*)
|
|
1239
|
+
"$CLI_CMD" \
|
|
1240
|
+
-p "$(cat "$deploy_prompt")" \
|
|
1241
|
+
--dangerously-skip-permissions \
|
|
1242
|
+
> "$deploy_session_dir/logs/session.log" 2>&1
|
|
1243
|
+
;;
|
|
1244
|
+
*)
|
|
1245
|
+
"$CLI_CMD" \
|
|
1246
|
+
--print \
|
|
1247
|
+
-y \
|
|
1248
|
+
< "$deploy_prompt" \
|
|
1249
|
+
> "$deploy_session_dir/logs/session.log" 2>&1
|
|
1250
|
+
;;
|
|
1251
|
+
esac
|
|
1252
|
+
local deploy_exit=$?
|
|
1253
|
+
|
|
1254
|
+
if [[ $deploy_exit -eq 0 ]]; then
|
|
1255
|
+
log_success "Deploy session completed (exit 0)"
|
|
1256
|
+
else
|
|
1257
|
+
log_warn "Deploy session exited with code $deploy_exit"
|
|
1258
|
+
log_warn "Review log: $deploy_session_dir/logs/session.log"
|
|
1259
|
+
fi
|
|
1260
|
+
fi
|
|
1261
|
+
fi
|
|
1262
|
+
|
|
1189
1263
|
break
|
|
1190
1264
|
fi
|
|
1191
1265
|
|
|
@@ -52,6 +52,7 @@ DEV_BRANCH=${DEV_BRANCH:-""}
|
|
|
52
52
|
AUTO_PUSH=${AUTO_PUSH:-0}
|
|
53
53
|
STOP_ON_FAILURE=${STOP_ON_FAILURE:-0}
|
|
54
54
|
STRICT_BEHAVIOR_CHECK=${STRICT_BEHAVIOR_CHECK:-1}
|
|
55
|
+
ENABLE_DEPLOY=${ENABLE_DEPLOY:-0}
|
|
55
56
|
|
|
56
57
|
# Source shared common helpers (CLI/platform detection + logs + deps)
|
|
57
58
|
source "$SCRIPT_DIR/lib/common.sh"
|
|
@@ -990,6 +991,79 @@ main() {
|
|
|
990
991
|
log_success " Total subagent calls: $total_subagent_calls"
|
|
991
992
|
log_success "════════════════════════════════════════════════════"
|
|
992
993
|
|
|
994
|
+
# ── Deploy session (only if ENABLE_DEPLOY=1 and all refactors completed) ──
|
|
995
|
+
if [[ "$ENABLE_DEPLOY" == "1" ]]; then
|
|
996
|
+
local incomplete_count
|
|
997
|
+
incomplete_count=$({ python3 -c "
|
|
998
|
+
import json, sys
|
|
999
|
+
with open(sys.argv[1]) as f:
|
|
1000
|
+
data = json.load(f)
|
|
1001
|
+
bad = [r for r in data.get('refactors', [])
|
|
1002
|
+
if r.get('status') not in ('completed', 'skipped')]
|
|
1003
|
+
for r in bad:
|
|
1004
|
+
print(f\" {r['id']}: {r.get('status', 'unknown')} — {r.get('title', '')}\")
|
|
1005
|
+
print(len(bad))
|
|
1006
|
+
" "$refactor_list" 2>/dev/null || echo "0"; } | tee /dev/stderr | tail -1)
|
|
1007
|
+
|
|
1008
|
+
if [[ "$incomplete_count" -gt 0 ]]; then
|
|
1009
|
+
echo ""
|
|
1010
|
+
log_warn "DEPLOY BLOCKED: $incomplete_count refactor(s) not completed successfully."
|
|
1011
|
+
log_warn "Fix failed refactors and re-run, or manually run /prizmkit-deploy."
|
|
1012
|
+
else
|
|
1013
|
+
echo ""
|
|
1014
|
+
log_info "All refactors completed — starting deploy session..."
|
|
1015
|
+
log_info "ENABLE_DEPLOY=1"
|
|
1016
|
+
|
|
1017
|
+
local deploy_session_id="deploy-$(date +%Y%m%d%H%M%S)"
|
|
1018
|
+
local deploy_session_dir="$STATE_DIR/deploy/$deploy_session_id"
|
|
1019
|
+
mkdir -p "$deploy_session_dir/logs"
|
|
1020
|
+
|
|
1021
|
+
local deploy_prompt="$deploy_session_dir/bootstrap-prompt.md"
|
|
1022
|
+
local _deploy_branch _deploy_commit
|
|
1023
|
+
_deploy_branch=$(git -C "$_proj_root" rev-parse --abbrev-ref HEAD 2>/dev/null || echo "unknown")
|
|
1024
|
+
_deploy_commit=$(git -C "$_proj_root" rev-parse --short HEAD 2>/dev/null || echo "unknown")
|
|
1025
|
+
cat > "$deploy_prompt" << DEPLOY_PROMPT_EOF
|
|
1026
|
+
## Deploy
|
|
1027
|
+
|
|
1028
|
+
All refactor tasks have been completed successfully.
|
|
1029
|
+
|
|
1030
|
+
- Branch: $_deploy_branch
|
|
1031
|
+
- Commit: $_deploy_commit
|
|
1032
|
+
|
|
1033
|
+
Run /prizmkit-deploy to deploy the project. Read .prizmkit/deploy/deploy.config.json
|
|
1034
|
+
for deployment configuration. If no deploy config exists, guide the user through
|
|
1035
|
+
setting one up before deploying.
|
|
1036
|
+
DEPLOY_PROMPT_EOF
|
|
1037
|
+
|
|
1038
|
+
log_info "Deploy prompt: $deploy_prompt"
|
|
1039
|
+
log_info "Deploy log: $deploy_session_dir/logs/session.log"
|
|
1040
|
+
|
|
1041
|
+
case "$CLI_CMD" in
|
|
1042
|
+
*claude*)
|
|
1043
|
+
"$CLI_CMD" \
|
|
1044
|
+
-p "$(cat "$deploy_prompt")" \
|
|
1045
|
+
--dangerously-skip-permissions \
|
|
1046
|
+
> "$deploy_session_dir/logs/session.log" 2>&1
|
|
1047
|
+
;;
|
|
1048
|
+
*)
|
|
1049
|
+
"$CLI_CMD" \
|
|
1050
|
+
--print \
|
|
1051
|
+
-y \
|
|
1052
|
+
< "$deploy_prompt" \
|
|
1053
|
+
> "$deploy_session_dir/logs/session.log" 2>&1
|
|
1054
|
+
;;
|
|
1055
|
+
esac
|
|
1056
|
+
local deploy_exit=$?
|
|
1057
|
+
|
|
1058
|
+
if [[ $deploy_exit -eq 0 ]]; then
|
|
1059
|
+
log_success "Deploy session completed (exit 0)"
|
|
1060
|
+
else
|
|
1061
|
+
log_warn "Deploy session exited with code $deploy_exit"
|
|
1062
|
+
log_warn "Review log: $deploy_session_dir/logs/session.log"
|
|
1063
|
+
fi
|
|
1064
|
+
fi
|
|
1065
|
+
fi
|
|
1066
|
+
|
|
993
1067
|
break
|
|
994
1068
|
fi
|
|
995
1069
|
|
|
@@ -625,7 +625,6 @@ SECTION_TO_SKILL = {
|
|
|
625
625
|
"phase-implement": ("prizmkit-implement", "Implement + Test", []),
|
|
626
626
|
"phase-review": ("prizmkit-code-review", "Code Review", []),
|
|
627
627
|
"phase-browser": ("browser-verification", "Browser Verification", []),
|
|
628
|
-
"phase-deploy": ("deploy-verification", "Deploy Verification", []),
|
|
629
628
|
"phase-commit": None, # special: split into retrospective + committer
|
|
630
629
|
}
|
|
631
630
|
|
|
@@ -1072,11 +1071,6 @@ def assemble_sections(pipeline_mode, sections_dir, init_done, is_resume,
|
|
|
1072
1071
|
load_section(sections_dir,
|
|
1073
1072
|
browser_section_file)))
|
|
1074
1073
|
|
|
1075
|
-
# --- Deploy Verification ---
|
|
1076
|
-
sections.append(("phase-deploy",
|
|
1077
|
-
load_section(sections_dir,
|
|
1078
|
-
"phase-deploy-verification.md")))
|
|
1079
|
-
|
|
1080
1074
|
# --- Commit (tier-dependent) ---
|
|
1081
1075
|
if pipeline_mode == "full":
|
|
1082
1076
|
sections.append(("phase-commit",
|
|
@@ -260,6 +260,39 @@ def build_replacements(args, bug, global_context, script_dir):
|
|
|
260
260
|
# Determine verification type
|
|
261
261
|
vtype = bug.get("verification_type", "automated")
|
|
262
262
|
|
|
263
|
+
# Browser interaction - extract from bug if present
|
|
264
|
+
browser_interaction = bug.get("browser_interaction")
|
|
265
|
+
browser_enabled = False
|
|
266
|
+
browser_verify_steps = ""
|
|
267
|
+
browser_tool = "auto"
|
|
268
|
+
|
|
269
|
+
# Environment override
|
|
270
|
+
browser_verify_env = os.environ.get("BROWSER_VERIFY", "").lower()
|
|
271
|
+
if browser_verify_env == "false":
|
|
272
|
+
browser_interaction = None
|
|
273
|
+
|
|
274
|
+
# Extract browser config (same logic as feature pipeline)
|
|
275
|
+
if browser_interaction and isinstance(browser_interaction, bool):
|
|
276
|
+
browser_enabled = True
|
|
277
|
+
browser_tool = "auto"
|
|
278
|
+
browser_verify_steps = " # (no specific verify goals — reproduce the bug and verify it's fixed)"
|
|
279
|
+
elif browser_interaction and isinstance(browser_interaction, dict):
|
|
280
|
+
browser_tool = browser_interaction.get("tool", "auto")
|
|
281
|
+
if browser_tool not in ("playwright-cli", "opencli", "auto"):
|
|
282
|
+
LOGGER.warning("Unknown browser_interaction.tool '%s', defaulting to 'auto'", browser_tool)
|
|
283
|
+
browser_tool = "auto"
|
|
284
|
+
|
|
285
|
+
steps = browser_interaction.get("verify_steps", [])
|
|
286
|
+
if steps:
|
|
287
|
+
browser_enabled = True
|
|
288
|
+
browser_verify_steps = "\n".join(
|
|
289
|
+
" # Step {}: {}".format(i + 1, step)
|
|
290
|
+
for i, step in enumerate(steps)
|
|
291
|
+
)
|
|
292
|
+
elif browser_interaction.get("url") or browser_interaction.get("enabled", True):
|
|
293
|
+
browser_enabled = True
|
|
294
|
+
browser_verify_steps = " # (reproduce bug and verify fix)"
|
|
295
|
+
|
|
263
296
|
replacements = {
|
|
264
297
|
"{{RUN_ID}}": args.run_id,
|
|
265
298
|
"{{SESSION_ID}}": args.session_id,
|
|
@@ -284,13 +317,17 @@ def build_replacements(args, bug, global_context, script_dir):
|
|
|
284
317
|
"{{PROJECT_ROOT}}": project_root,
|
|
285
318
|
"{{FIX_SCOPE}}": fix_scope,
|
|
286
319
|
"{{TIMESTAMP}}": "", # Placeholder, agent fills in the timestamp
|
|
320
|
+
"{{BROWSER_ENABLED}}": "true" if browser_enabled else "false",
|
|
321
|
+
"{{BROWSER_TOOL}}": browser_tool,
|
|
322
|
+
"{{BROWSER_VERIFY_STEPS}}": browser_verify_steps,
|
|
287
323
|
}
|
|
288
324
|
|
|
289
325
|
return replacements
|
|
290
326
|
|
|
291
327
|
|
|
292
328
|
def process_conditional_blocks(content, bug):
|
|
293
|
-
"""Handle conditional blocks based on verification_type."""
|
|
329
|
+
"""Handle conditional blocks based on verification_type and browser_interaction."""
|
|
330
|
+
# Handle verification type blocks
|
|
294
331
|
vtype = bug.get("verification_type", "automated")
|
|
295
332
|
is_manual_or_hybrid = vtype in ("manual", "hybrid")
|
|
296
333
|
|
|
@@ -306,6 +343,64 @@ def process_conditional_blocks(content, bug):
|
|
|
306
343
|
"", content, flags=re.DOTALL,
|
|
307
344
|
)
|
|
308
345
|
|
|
346
|
+
# Handle browser interaction blocks
|
|
347
|
+
browser_interaction = bug.get("browser_interaction")
|
|
348
|
+
browser_enabled = False
|
|
349
|
+
browser_tool = "auto"
|
|
350
|
+
|
|
351
|
+
# Check environment override
|
|
352
|
+
browser_verify_env = os.environ.get("BROWSER_VERIFY", "").lower()
|
|
353
|
+
if browser_verify_env == "false":
|
|
354
|
+
browser_interaction = None
|
|
355
|
+
|
|
356
|
+
# Determine if browser is enabled
|
|
357
|
+
if browser_interaction:
|
|
358
|
+
if isinstance(browser_interaction, bool):
|
|
359
|
+
browser_enabled = True
|
|
360
|
+
elif isinstance(browser_interaction, dict):
|
|
361
|
+
steps = browser_interaction.get("verify_steps", [])
|
|
362
|
+
if steps or browser_interaction.get("url") or browser_interaction.get("enabled", True):
|
|
363
|
+
browser_enabled = True
|
|
364
|
+
browser_tool = browser_interaction.get("tool", "auto")
|
|
365
|
+
|
|
366
|
+
# Process browser interaction blocks
|
|
367
|
+
browser_open = "{{IF_BROWSER_INTERACTION}}"
|
|
368
|
+
browser_close = "{{END_IF_BROWSER_INTERACTION}}"
|
|
369
|
+
|
|
370
|
+
if browser_enabled:
|
|
371
|
+
# Keep content, remove tags
|
|
372
|
+
content = content.replace(browser_open + "\n", "")
|
|
373
|
+
content = content.replace(browser_open, "")
|
|
374
|
+
content = content.replace(browser_close + "\n", "")
|
|
375
|
+
content = content.replace(browser_close, "")
|
|
376
|
+
else:
|
|
377
|
+
# Remove entire block
|
|
378
|
+
pattern = re.escape(browser_open) + r".*?" + re.escape(browser_close) + r"\n?"
|
|
379
|
+
content = re.sub(pattern, "", content, flags=re.DOTALL)
|
|
380
|
+
|
|
381
|
+
# Process browser tool selection blocks (nested inside browser interaction)
|
|
382
|
+
tool_variants = ["PLAYWRIGHT", "OPENCLI", "AUTO"]
|
|
383
|
+
active_variant = {
|
|
384
|
+
"playwright-cli": "PLAYWRIGHT",
|
|
385
|
+
"opencli": "OPENCLI",
|
|
386
|
+
"auto": "AUTO",
|
|
387
|
+
}.get(browser_tool, "AUTO")
|
|
388
|
+
|
|
389
|
+
for variant in tool_variants:
|
|
390
|
+
tool_open = "{{{{IF_BROWSER_TOOL_{}}}}}".format(variant)
|
|
391
|
+
tool_close = "{{{{END_IF_BROWSER_TOOL_{}}}}}".format(variant)
|
|
392
|
+
|
|
393
|
+
if variant == active_variant and browser_enabled:
|
|
394
|
+
# Keep content, remove tags
|
|
395
|
+
content = content.replace(tool_open + "\n", "")
|
|
396
|
+
content = content.replace(tool_open, "")
|
|
397
|
+
content = content.replace(tool_close + "\n", "")
|
|
398
|
+
content = content.replace(tool_close, "")
|
|
399
|
+
else:
|
|
400
|
+
# Remove entire block
|
|
401
|
+
pat = re.escape(tool_open) + r".*?" + re.escape(tool_close) + r"\n?"
|
|
402
|
+
content = re.sub(pat, "", content, flags=re.DOTALL)
|
|
403
|
+
|
|
309
404
|
return content
|
|
310
405
|
|
|
311
406
|
|
|
@@ -529,6 +624,26 @@ def main():
|
|
|
529
624
|
|
|
530
625
|
# Success
|
|
531
626
|
bug_model = bug.get("model", "")
|
|
627
|
+
# Extract browser interaction state for output
|
|
628
|
+
browser_interaction = bug.get("browser_interaction")
|
|
629
|
+
browser_enabled = False
|
|
630
|
+
browser_tool = "auto"
|
|
631
|
+
|
|
632
|
+
browser_verify_env = os.environ.get("BROWSER_VERIFY", "").lower()
|
|
633
|
+
if browser_verify_env == "false":
|
|
634
|
+
browser_interaction = None
|
|
635
|
+
|
|
636
|
+
if browser_interaction:
|
|
637
|
+
if isinstance(browser_interaction, bool):
|
|
638
|
+
browser_enabled = True
|
|
639
|
+
elif isinstance(browser_interaction, dict):
|
|
640
|
+
steps = browser_interaction.get("verify_steps", [])
|
|
641
|
+
if steps or browser_interaction.get("url") or browser_interaction.get("enabled", True):
|
|
642
|
+
browser_enabled = True
|
|
643
|
+
browser_tool = browser_interaction.get("tool", "auto")
|
|
644
|
+
if browser_tool not in ("playwright-cli", "opencli", "auto"):
|
|
645
|
+
browser_tool = "auto"
|
|
646
|
+
|
|
532
647
|
output = {
|
|
533
648
|
"success": True,
|
|
534
649
|
"output_path": os.path.abspath(args.output),
|
|
@@ -537,6 +652,8 @@ def main():
|
|
|
537
652
|
"pipeline_mode": pipeline_mode,
|
|
538
653
|
"agent_count": agent_count,
|
|
539
654
|
"critic_enabled": critic_enabled,
|
|
655
|
+
"browser_enabled": browser_enabled,
|
|
656
|
+
"browser_tool": browser_tool
|
|
540
657
|
}
|
|
541
658
|
print(json.dumps(output, indent=2, ensure_ascii=False))
|
|
542
659
|
sys.exit(0)
|
|
@@ -420,6 +420,39 @@ def build_replacements(args, refactor, refactors, global_context, script_dir):
|
|
|
420
420
|
else:
|
|
421
421
|
new_tests_str = "- (none specified)"
|
|
422
422
|
|
|
423
|
+
# Browser interaction - extract from refactor if present
|
|
424
|
+
browser_interaction = refactor.get("browser_interaction")
|
|
425
|
+
browser_enabled = False
|
|
426
|
+
browser_verify_steps = ""
|
|
427
|
+
browser_tool = "auto"
|
|
428
|
+
|
|
429
|
+
# Environment override
|
|
430
|
+
browser_verify_env = os.environ.get("BROWSER_VERIFY", "").lower()
|
|
431
|
+
if browser_verify_env == "false":
|
|
432
|
+
browser_interaction = None
|
|
433
|
+
|
|
434
|
+
# Extract browser config (same logic as feature and bugfix pipelines)
|
|
435
|
+
if browser_interaction and isinstance(browser_interaction, bool):
|
|
436
|
+
browser_enabled = True
|
|
437
|
+
browser_tool = "auto"
|
|
438
|
+
browser_verify_steps = " # (no specific verify goals — validate UI renders correctly and feature still works)"
|
|
439
|
+
elif browser_interaction and isinstance(browser_interaction, dict):
|
|
440
|
+
browser_tool = browser_interaction.get("tool", "auto")
|
|
441
|
+
if browser_tool not in ("playwright-cli", "opencli", "auto"):
|
|
442
|
+
LOGGER.warning("Unknown browser_interaction.tool '%s', defaulting to 'auto'", browser_tool)
|
|
443
|
+
browser_tool = "auto"
|
|
444
|
+
|
|
445
|
+
steps = browser_interaction.get("verify_steps", [])
|
|
446
|
+
if steps:
|
|
447
|
+
browser_enabled = True
|
|
448
|
+
browser_verify_steps = "\n".join(
|
|
449
|
+
" # Step {}: {}".format(i + 1, step)
|
|
450
|
+
for i, step in enumerate(steps)
|
|
451
|
+
)
|
|
452
|
+
elif browser_interaction.get("url") or browser_interaction.get("enabled", True):
|
|
453
|
+
browser_enabled = True
|
|
454
|
+
browser_verify_steps = " # (validate UI renders correctly and feature still works)"
|
|
455
|
+
|
|
423
456
|
replacements = {
|
|
424
457
|
"{{RUN_ID}}": args.run_id,
|
|
425
458
|
"{{SESSION_ID}}": args.session_id,
|
|
@@ -452,16 +485,17 @@ def build_replacements(args, refactor, refactors, global_context, script_dir):
|
|
|
452
485
|
),
|
|
453
486
|
"{{TIMESTAMP}}": "", # Placeholder — agent fills in timestamp
|
|
454
487
|
"{{PLATFORM_CONVENTIONS}}": read_platform_conventions(project_root),
|
|
488
|
+
"{{BROWSER_ENABLED}}": "true" if browser_enabled else "false",
|
|
489
|
+
"{{BROWSER_TOOL}}": browser_tool,
|
|
490
|
+
"{{BROWSER_VERIFY_STEPS}}": browser_verify_steps,
|
|
455
491
|
}
|
|
456
492
|
|
|
457
493
|
return replacements
|
|
458
494
|
|
|
459
495
|
|
|
460
|
-
def process_conditional_blocks(content, resume_phase):
|
|
461
|
-
"""Handle conditional blocks based on resume_phase.
|
|
462
|
-
|
|
463
|
-
- {{IF_FRESH_START}}...{{END_IF_FRESH_START}} — include only on fresh start (resume_phase == "null")
|
|
464
|
-
"""
|
|
496
|
+
def process_conditional_blocks(content, resume_phase, refactor):
|
|
497
|
+
"""Handle conditional blocks based on resume_phase and browser_interaction."""
|
|
498
|
+
# Handle fresh start blocks
|
|
465
499
|
is_resume = resume_phase != "null"
|
|
466
500
|
|
|
467
501
|
if is_resume:
|
|
@@ -475,13 +509,72 @@ def process_conditional_blocks(content, resume_phase):
|
|
|
475
509
|
content = content.replace("{{END_IF_FRESH_START}}\n", "")
|
|
476
510
|
content = content.replace("{{END_IF_FRESH_START}}", "")
|
|
477
511
|
|
|
512
|
+
# Handle browser interaction blocks
|
|
513
|
+
browser_interaction = refactor.get("browser_interaction")
|
|
514
|
+
browser_enabled = False
|
|
515
|
+
browser_tool = "auto"
|
|
516
|
+
|
|
517
|
+
# Check environment override
|
|
518
|
+
browser_verify_env = os.environ.get("BROWSER_VERIFY", "").lower()
|
|
519
|
+
if browser_verify_env == "false":
|
|
520
|
+
browser_interaction = None
|
|
521
|
+
|
|
522
|
+
# Determine if browser is enabled
|
|
523
|
+
if browser_interaction:
|
|
524
|
+
if isinstance(browser_interaction, bool):
|
|
525
|
+
browser_enabled = True
|
|
526
|
+
elif isinstance(browser_interaction, dict):
|
|
527
|
+
steps = browser_interaction.get("verify_steps", [])
|
|
528
|
+
if steps or browser_interaction.get("url") or browser_interaction.get("enabled", True):
|
|
529
|
+
browser_enabled = True
|
|
530
|
+
browser_tool = browser_interaction.get("tool", "auto")
|
|
531
|
+
|
|
532
|
+
# Process browser interaction blocks
|
|
533
|
+
browser_open = "{{IF_BROWSER_INTERACTION}}"
|
|
534
|
+
browser_close = "{{END_IF_BROWSER_INTERACTION}}"
|
|
535
|
+
|
|
536
|
+
if browser_enabled:
|
|
537
|
+
# Keep content, remove tags
|
|
538
|
+
content = content.replace(browser_open + "\n", "")
|
|
539
|
+
content = content.replace(browser_open, "")
|
|
540
|
+
content = content.replace(browser_close + "\n", "")
|
|
541
|
+
content = content.replace(browser_close, "")
|
|
542
|
+
else:
|
|
543
|
+
# Remove entire block
|
|
544
|
+
pattern = re.escape(browser_open) + r".*?" + re.escape(browser_close) + r"\n?"
|
|
545
|
+
content = re.sub(pattern, "", content, flags=re.DOTALL)
|
|
546
|
+
|
|
547
|
+
# Process browser tool selection blocks (nested inside browser interaction)
|
|
548
|
+
tool_variants = ["PLAYWRIGHT", "OPENCLI", "AUTO"]
|
|
549
|
+
active_variant = {
|
|
550
|
+
"playwright-cli": "PLAYWRIGHT",
|
|
551
|
+
"opencli": "OPENCLI",
|
|
552
|
+
"auto": "AUTO",
|
|
553
|
+
}.get(browser_tool, "AUTO")
|
|
554
|
+
|
|
555
|
+
for variant in tool_variants:
|
|
556
|
+
tool_open = "{{{{IF_BROWSER_TOOL_{}}}}}".format(variant)
|
|
557
|
+
tool_close = "{{{{END_IF_BROWSER_TOOL_{}}}}}".format(variant)
|
|
558
|
+
|
|
559
|
+
if variant == active_variant and browser_enabled:
|
|
560
|
+
# Keep content, remove tags
|
|
561
|
+
content = content.replace(tool_open + "\n", "")
|
|
562
|
+
content = content.replace(tool_open, "")
|
|
563
|
+
content = content.replace(tool_close + "\n", "")
|
|
564
|
+
content = content.replace(tool_close, "")
|
|
565
|
+
else:
|
|
566
|
+
# Remove entire block
|
|
567
|
+
pat = re.escape(tool_open) + r".*?" + re.escape(tool_close) + r"\n?"
|
|
568
|
+
content = re.sub(pat, "", content, flags=re.DOTALL)
|
|
569
|
+
|
|
478
570
|
return content
|
|
479
571
|
|
|
480
572
|
|
|
481
|
-
|
|
573
|
+
|
|
574
|
+
def render_template(template_content, replacements, resume_phase, refactor):
|
|
482
575
|
"""Render the template by processing conditionals and replacing placeholders."""
|
|
483
576
|
# Step 1: Process conditional blocks
|
|
484
|
-
content = process_conditional_blocks(template_content, resume_phase)
|
|
577
|
+
content = process_conditional_blocks(template_content, resume_phase, refactor)
|
|
485
578
|
|
|
486
579
|
# Step 2: Replace all {{PLACEHOLDER}} variables
|
|
487
580
|
for placeholder, value in replacements.items():
|
|
@@ -560,7 +653,7 @@ def main():
|
|
|
560
653
|
replacements = build_replacements(args, refactor, refactors, global_context, script_dir)
|
|
561
654
|
|
|
562
655
|
# Render the template
|
|
563
|
-
rendered = render_template(template_content, replacements, args.resume_phase)
|
|
656
|
+
rendered = render_template(template_content, replacements, args.resume_phase, refactor)
|
|
564
657
|
|
|
565
658
|
# Write the output
|
|
566
659
|
err = write_output(args.output, rendered)
|
|
@@ -610,13 +703,35 @@ def main():
|
|
|
610
703
|
|
|
611
704
|
# Success
|
|
612
705
|
refactor_model = refactor.get("model", "")
|
|
706
|
+
|
|
707
|
+
# Extract browser state for JSON output
|
|
708
|
+
browser_interaction = refactor.get("browser_interaction")
|
|
709
|
+
browser_enabled = False
|
|
710
|
+
browser_tool = "auto"
|
|
711
|
+
|
|
712
|
+
browser_verify_env = os.environ.get("BROWSER_VERIFY", "").lower()
|
|
713
|
+
if browser_verify_env == "false":
|
|
714
|
+
browser_interaction = None
|
|
715
|
+
|
|
716
|
+
if browser_interaction:
|
|
717
|
+
if isinstance(browser_interaction, bool):
|
|
718
|
+
browser_enabled = True
|
|
719
|
+
elif isinstance(browser_interaction, dict):
|
|
720
|
+
steps = browser_interaction.get("verify_steps", [])
|
|
721
|
+
if steps or browser_interaction.get("url") or browser_interaction.get("enabled", True):
|
|
722
|
+
browser_enabled = True
|
|
723
|
+
browser_tool = browser_interaction.get("tool", "auto")
|
|
724
|
+
|
|
613
725
|
output = {
|
|
614
726
|
"success": True,
|
|
615
727
|
"output_path": os.path.abspath(args.output),
|
|
728
|
+
"checkpoint_path": checkpoint_path,
|
|
616
729
|
"model": refactor_model,
|
|
617
730
|
"pipeline_mode": pipeline_mode,
|
|
618
731
|
"agent_count": agent_count,
|
|
619
732
|
"critic_enabled": critic_enabled,
|
|
733
|
+
"browser_enabled": browser_enabled,
|
|
734
|
+
"browser_tool": browser_tool,
|
|
620
735
|
}
|
|
621
736
|
print(json.dumps(output, indent=2, ensure_ascii=False))
|
|
622
737
|
sys.exit(0)
|