prizmkit 1.0.0 → 1.0.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/bundled/VERSION.json +5 -0
- package/bundled/adapters/claude/agent-adapter.js +108 -0
- package/bundled/adapters/claude/command-adapter.js +104 -0
- package/bundled/adapters/claude/paths.js +35 -0
- package/bundled/adapters/claude/rules-adapter.js +77 -0
- package/bundled/adapters/claude/settings-adapter.js +73 -0
- package/bundled/adapters/claude/team-adapter.js +183 -0
- package/bundled/adapters/codebuddy/agent-adapter.js +43 -0
- package/bundled/adapters/codebuddy/paths.js +29 -0
- package/bundled/adapters/codebuddy/settings-adapter.js +47 -0
- package/bundled/adapters/codebuddy/skill-adapter.js +68 -0
- package/bundled/adapters/codebuddy/team-adapter.js +46 -0
- package/bundled/adapters/shared/frontmatter.js +77 -0
- package/bundled/agents/prizm-dev-team-coordinator.md +142 -0
- package/bundled/agents/prizm-dev-team-dev.md +99 -0
- package/bundled/agents/prizm-dev-team-pm.md +114 -0
- package/bundled/agents/prizm-dev-team-reviewer.md +119 -0
- package/bundled/dev-pipeline/README.md +482 -0
- package/bundled/dev-pipeline/assets/feature-list-example.json +147 -0
- package/bundled/dev-pipeline/assets/prizm-dev-team-integration.md +138 -0
- package/bundled/dev-pipeline/launch-bugfix-daemon.sh +425 -0
- package/bundled/dev-pipeline/launch-daemon.sh +549 -0
- package/bundled/dev-pipeline/reset-feature.sh +209 -0
- package/bundled/dev-pipeline/retry-bug.sh +344 -0
- package/bundled/dev-pipeline/retry-feature.sh +338 -0
- package/bundled/dev-pipeline/run-bugfix.sh +638 -0
- package/bundled/dev-pipeline/run.sh +845 -0
- package/bundled/dev-pipeline/scripts/check-session-status.py +158 -0
- package/bundled/dev-pipeline/scripts/detect-stuck.py +385 -0
- package/bundled/dev-pipeline/scripts/generate-bootstrap-prompt.py +598 -0
- package/bundled/dev-pipeline/scripts/generate-bugfix-prompt.py +402 -0
- package/bundled/dev-pipeline/scripts/init-bugfix-pipeline.py +294 -0
- package/bundled/dev-pipeline/scripts/init-dev-team.py +134 -0
- package/bundled/dev-pipeline/scripts/init-pipeline.py +335 -0
- package/bundled/dev-pipeline/scripts/update-bug-status.py +748 -0
- package/bundled/dev-pipeline/scripts/update-feature-status.py +1076 -0
- package/bundled/dev-pipeline/templates/bootstrap-prompt.md +262 -0
- package/bundled/dev-pipeline/templates/bug-fix-list-schema.json +159 -0
- package/bundled/dev-pipeline/templates/bugfix-bootstrap-prompt.md +291 -0
- package/bundled/dev-pipeline/templates/feature-list-schema.json +112 -0
- package/bundled/dev-pipeline/templates/session-status-schema.json +77 -0
- package/bundled/skills/_metadata.json +267 -0
- package/bundled/skills/app-planner/SKILL.md +580 -0
- package/bundled/skills/app-planner/assets/planning-guide.md +313 -0
- package/bundled/skills/app-planner/scripts/validate-and-generate.py +758 -0
- package/bundled/skills/bug-planner/SKILL.md +235 -0
- package/bundled/skills/bugfix-pipeline-launcher/SKILL.md +252 -0
- package/bundled/skills/dev-pipeline-launcher/SKILL.md +223 -0
- package/bundled/skills/prizm-kit/SKILL.md +151 -0
- package/bundled/skills/prizm-kit/assets/claude-md-template.md +38 -0
- package/bundled/skills/prizm-kit/assets/codebuddy-md-template.md +35 -0
- package/bundled/skills/prizm-kit/assets/hooks/prizm-commit-hook.json +15 -0
- package/bundled/skills/prizmkit-adr-manager/SKILL.md +68 -0
- package/bundled/skills/prizmkit-adr-manager/assets/adr-template.md +26 -0
- package/bundled/skills/prizmkit-analyze/SKILL.md +194 -0
- package/bundled/skills/prizmkit-api-doc-generator/SKILL.md +56 -0
- package/bundled/skills/prizmkit-bug-fix-workflow/SKILL.md +351 -0
- package/bundled/skills/prizmkit-bug-reproducer/SKILL.md +62 -0
- package/bundled/skills/prizmkit-ci-cd-generator/SKILL.md +54 -0
- package/bundled/skills/prizmkit-clarify/SKILL.md +52 -0
- package/bundled/skills/prizmkit-code-review/SKILL.md +70 -0
- package/bundled/skills/prizmkit-committer/SKILL.md +117 -0
- package/bundled/skills/prizmkit-db-migration/SKILL.md +65 -0
- package/bundled/skills/prizmkit-dependency-health/SKILL.md +123 -0
- package/bundled/skills/prizmkit-deployment-strategy/SKILL.md +58 -0
- package/bundled/skills/prizmkit-error-triage/SKILL.md +55 -0
- package/bundled/skills/prizmkit-implement/SKILL.md +47 -0
- package/bundled/skills/prizmkit-init/SKILL.md +156 -0
- package/bundled/skills/prizmkit-log-analyzer/SKILL.md +55 -0
- package/bundled/skills/prizmkit-monitoring-setup/SKILL.md +75 -0
- package/bundled/skills/prizmkit-onboarding-generator/SKILL.md +70 -0
- package/bundled/skills/prizmkit-perf-profiler/SKILL.md +55 -0
- package/bundled/skills/prizmkit-plan/SKILL.md +54 -0
- package/bundled/skills/prizmkit-plan/assets/plan-template.md +37 -0
- package/bundled/skills/prizmkit-prizm-docs/SKILL.md +140 -0
- package/bundled/skills/prizmkit-prizm-docs/assets/PRIZM-SPEC.md +943 -0
- package/bundled/skills/prizmkit-retrospective/SKILL.md +79 -0
- package/bundled/skills/prizmkit-security-audit/SKILL.md +130 -0
- package/bundled/skills/prizmkit-specify/SKILL.md +52 -0
- package/bundled/skills/prizmkit-specify/assets/spec-template.md +37 -0
- package/bundled/skills/prizmkit-summarize/SKILL.md +51 -0
- package/bundled/skills/prizmkit-summarize/assets/registry-template.md +18 -0
- package/bundled/skills/prizmkit-tasks/SKILL.md +50 -0
- package/bundled/skills/prizmkit-tasks/assets/tasks-template.md +21 -0
- package/bundled/skills/prizmkit-tech-debt-tracker/SKILL.md +139 -0
- package/bundled/team/prizm-dev-team.json +47 -0
- package/bundled/templates/claude-md-template.md +38 -0
- package/bundled/templates/codebuddy-md-template.md +35 -0
- package/package.json +2 -1
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
# ============================================================
|
|
5
|
+
# dev-pipeline/reset-feature.sh - Reset a failed/stuck feature
|
|
6
|
+
#
|
|
7
|
+
# Clears all state and artifacts for a feature so it can be
|
|
8
|
+
# re-executed from scratch by the pipeline.
|
|
9
|
+
#
|
|
10
|
+
# Usage:
|
|
11
|
+
# ./reset-feature.sh <feature-id> [options] [feature-list.json]
|
|
12
|
+
#
|
|
13
|
+
# Options:
|
|
14
|
+
# --clean Also delete session history and .prizmkit/specs/{slug}/ artifacts
|
|
15
|
+
# --run After reset, immediately retry the feature (calls retry-feature.sh)
|
|
16
|
+
#
|
|
17
|
+
# Examples:
|
|
18
|
+
# ./reset-feature.sh F-007 # Reset status only
|
|
19
|
+
# ./reset-feature.sh F-007 --clean # Reset + delete artifacts
|
|
20
|
+
# ./reset-feature.sh F-007 --clean --run # Reset + delete + retry
|
|
21
|
+
# ./reset-feature.sh F-007 --clean my-features.json # Custom feature list
|
|
22
|
+
# ============================================================
|
|
23
|
+
|
|
24
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
25
|
+
STATE_DIR="$SCRIPT_DIR/state"
|
|
26
|
+
SCRIPTS_DIR="$SCRIPT_DIR/scripts"
|
|
27
|
+
|
|
28
|
+
# Colors
|
|
29
|
+
RED='\033[0;31m'
|
|
30
|
+
GREEN='\033[0;32m'
|
|
31
|
+
YELLOW='\033[1;33m'
|
|
32
|
+
BLUE='\033[0;34m'
|
|
33
|
+
BOLD='\033[1m'
|
|
34
|
+
NC='\033[0m'
|
|
35
|
+
|
|
36
|
+
log_info() { echo -e "${BLUE}[INFO]${NC} $*"; }
|
|
37
|
+
log_warn() { echo -e "${YELLOW}[WARN]${NC} $*"; }
|
|
38
|
+
log_error() { echo -e "${RED}[ERROR]${NC} $*"; }
|
|
39
|
+
log_success() { echo -e "${GREEN}[OK]${NC} $*"; }
|
|
40
|
+
|
|
41
|
+
# ============================================================
|
|
42
|
+
# Parse args
|
|
43
|
+
# ============================================================
|
|
44
|
+
|
|
45
|
+
FEATURE_ID=""
|
|
46
|
+
FEATURE_LIST=""
|
|
47
|
+
DO_CLEAN=false
|
|
48
|
+
DO_RUN=false
|
|
49
|
+
|
|
50
|
+
for arg in "$@"; do
|
|
51
|
+
case "$arg" in
|
|
52
|
+
--clean) DO_CLEAN=true ;;
|
|
53
|
+
--run) DO_RUN=true ;;
|
|
54
|
+
-h|--help) ;;
|
|
55
|
+
F-*) FEATURE_ID="$arg" ;;
|
|
56
|
+
*) FEATURE_LIST="$arg" ;;
|
|
57
|
+
esac
|
|
58
|
+
done
|
|
59
|
+
|
|
60
|
+
if [[ -z "$FEATURE_ID" ]]; then
|
|
61
|
+
echo "Usage: $0 <feature-id> [--clean] [--run] [feature-list.json]"
|
|
62
|
+
echo ""
|
|
63
|
+
echo " feature-id Feature to reset (e.g. F-007)"
|
|
64
|
+
echo " --clean Delete session history and .prizmkit artifacts"
|
|
65
|
+
echo " --run Retry the feature immediately after reset"
|
|
66
|
+
echo " feature-list.json Path to feature list (default: feature-list.json)"
|
|
67
|
+
exit 1
|
|
68
|
+
fi
|
|
69
|
+
|
|
70
|
+
FEATURE_LIST="${FEATURE_LIST:-feature-list.json}"
|
|
71
|
+
|
|
72
|
+
# Resolve absolute path
|
|
73
|
+
if [[ ! "$FEATURE_LIST" = /* ]]; then
|
|
74
|
+
FEATURE_LIST="$(pwd)/$FEATURE_LIST"
|
|
75
|
+
fi
|
|
76
|
+
|
|
77
|
+
# ============================================================
|
|
78
|
+
# Validation
|
|
79
|
+
# ============================================================
|
|
80
|
+
|
|
81
|
+
if [[ ! -f "$FEATURE_LIST" ]]; then
|
|
82
|
+
log_error "Feature list not found: $FEATURE_LIST"
|
|
83
|
+
exit 1
|
|
84
|
+
fi
|
|
85
|
+
|
|
86
|
+
if [[ ! -f "$STATE_DIR/pipeline.json" ]]; then
|
|
87
|
+
log_error "No pipeline state found. Run './run.sh run' first to initialize."
|
|
88
|
+
exit 1
|
|
89
|
+
fi
|
|
90
|
+
|
|
91
|
+
# Get feature info from feature list
|
|
92
|
+
FEATURE_INFO=$(python3 -c "
|
|
93
|
+
import json, sys, re
|
|
94
|
+
with open('$FEATURE_LIST') as f:
|
|
95
|
+
data = json.load(f)
|
|
96
|
+
for feat in data.get('features', []):
|
|
97
|
+
if feat.get('id') == '$FEATURE_ID':
|
|
98
|
+
title = feat.get('title', '')
|
|
99
|
+
# Compute slug
|
|
100
|
+
numeric = '$FEATURE_ID'.replace('F-', '').replace('f-', '').zfill(3)
|
|
101
|
+
slug = title.lower()
|
|
102
|
+
slug = re.sub(r'[^a-z0-9\s-]', '', slug)
|
|
103
|
+
slug = re.sub(r'[\s]+', '-', slug.strip())
|
|
104
|
+
slug = re.sub(r'-+', '-', slug).strip('-')
|
|
105
|
+
slug = '{}-{}'.format(numeric, slug)
|
|
106
|
+
print(json.dumps({'title': title, 'slug': slug, 'status': feat.get('status', 'unknown')}))
|
|
107
|
+
sys.exit(0)
|
|
108
|
+
sys.exit(1)
|
|
109
|
+
" 2>/dev/null) || {
|
|
110
|
+
log_error "Feature $FEATURE_ID not found in $FEATURE_LIST"
|
|
111
|
+
exit 1
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
FEATURE_TITLE=$(echo "$FEATURE_INFO" | python3 -c "import sys,json; print(json.load(sys.stdin)['title'])")
|
|
115
|
+
FEATURE_SLUG=$(echo "$FEATURE_INFO" | python3 -c "import sys,json; print(json.load(sys.stdin)['slug'])")
|
|
116
|
+
FEATURE_STATUS=$(echo "$FEATURE_INFO" | python3 -c "import sys,json; print(json.load(sys.stdin)['status'])")
|
|
117
|
+
|
|
118
|
+
# ============================================================
|
|
119
|
+
# Show current state
|
|
120
|
+
# ============================================================
|
|
121
|
+
|
|
122
|
+
echo ""
|
|
123
|
+
echo -e "${BOLD}════════════════════════════════════════════════════${NC}"
|
|
124
|
+
echo -e "${BOLD} Reset: $FEATURE_ID — $FEATURE_TITLE${NC}"
|
|
125
|
+
echo -e "${BOLD}════════════════════════════════════════════════════${NC}"
|
|
126
|
+
|
|
127
|
+
# Read current status.json
|
|
128
|
+
STATUS_FILE="$STATE_DIR/features/$FEATURE_ID/status.json"
|
|
129
|
+
if [[ -f "$STATUS_FILE" ]]; then
|
|
130
|
+
CURRENT_STATUS=$(python3 -c "import json; d=json.load(open('$STATUS_FILE')); print(d.get('status','?'))")
|
|
131
|
+
CURRENT_RETRY=$(python3 -c "import json; d=json.load(open('$STATUS_FILE')); print(d.get('retry_count',0))")
|
|
132
|
+
SESSION_COUNT=$(python3 -c "import json; d=json.load(open('$STATUS_FILE')); print(len(d.get('sessions',[])))")
|
|
133
|
+
log_info "Current status: $CURRENT_STATUS (retry $CURRENT_RETRY, $SESSION_COUNT sessions)"
|
|
134
|
+
else
|
|
135
|
+
log_info "No status file found (never executed)"
|
|
136
|
+
fi
|
|
137
|
+
|
|
138
|
+
# Count artifacts
|
|
139
|
+
SPECS_DIR="$(cd "$SCRIPT_DIR/.." 2>/dev/null && pwd)/.prizmkit/specs/$FEATURE_SLUG"
|
|
140
|
+
SPECS_COUNT=0
|
|
141
|
+
if [[ -d "$SPECS_DIR" ]]; then
|
|
142
|
+
SPECS_COUNT=$(find "$SPECS_DIR" -type f 2>/dev/null | wc -l | tr -d ' ')
|
|
143
|
+
log_info "PrizmKit artifacts: $SPECS_COUNT files in .prizmkit/specs/$FEATURE_SLUG/"
|
|
144
|
+
fi
|
|
145
|
+
|
|
146
|
+
SESSIONS_DIR="$STATE_DIR/features/$FEATURE_ID/sessions"
|
|
147
|
+
SESSIONS_COUNT=0
|
|
148
|
+
if [[ -d "$SESSIONS_DIR" ]]; then
|
|
149
|
+
SESSIONS_COUNT=$(find "$SESSIONS_DIR" -mindepth 1 -maxdepth 1 -type d 2>/dev/null | wc -l | tr -d ' ')
|
|
150
|
+
log_info "Session history: $SESSIONS_COUNT session(s)"
|
|
151
|
+
fi
|
|
152
|
+
|
|
153
|
+
echo -e "${BOLD}════════════════════════════════════════════════════${NC}"
|
|
154
|
+
|
|
155
|
+
# ============================================================
|
|
156
|
+
# Execute reset
|
|
157
|
+
# ============================================================
|
|
158
|
+
|
|
159
|
+
PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
160
|
+
|
|
161
|
+
if [[ "$DO_CLEAN" == true ]]; then
|
|
162
|
+
log_info "Cleaning $FEATURE_ID (reset + delete artifacts)..."
|
|
163
|
+
RESULT=$(python3 "$SCRIPTS_DIR/update-feature-status.py" \
|
|
164
|
+
--feature-list "$FEATURE_LIST" \
|
|
165
|
+
--state-dir "$STATE_DIR" \
|
|
166
|
+
--feature-id "$FEATURE_ID" \
|
|
167
|
+
--feature-slug "$FEATURE_SLUG" \
|
|
168
|
+
--project-root "$PROJECT_ROOT" \
|
|
169
|
+
--action clean 2>&1)
|
|
170
|
+
else
|
|
171
|
+
log_info "Resetting $FEATURE_ID status..."
|
|
172
|
+
RESULT=$(python3 "$SCRIPTS_DIR/update-feature-status.py" \
|
|
173
|
+
--feature-list "$FEATURE_LIST" \
|
|
174
|
+
--state-dir "$STATE_DIR" \
|
|
175
|
+
--feature-id "$FEATURE_ID" \
|
|
176
|
+
--action reset 2>&1)
|
|
177
|
+
fi
|
|
178
|
+
|
|
179
|
+
# Check for errors
|
|
180
|
+
if echo "$RESULT" | python3 -c "import sys,json; d=json.load(sys.stdin); sys.exit(0 if 'error' not in d else 1)" 2>/dev/null; then
|
|
181
|
+
: # no error
|
|
182
|
+
else
|
|
183
|
+
ERROR_MSG=$(echo "$RESULT" | python3 -c "import sys,json; print(json.load(sys.stdin).get('error','unknown'))" 2>/dev/null || echo "$RESULT")
|
|
184
|
+
log_error "Reset failed: $ERROR_MSG"
|
|
185
|
+
exit 1
|
|
186
|
+
fi
|
|
187
|
+
|
|
188
|
+
# ============================================================
|
|
189
|
+
# Summary
|
|
190
|
+
# ============================================================
|
|
191
|
+
|
|
192
|
+
echo ""
|
|
193
|
+
if [[ "$DO_CLEAN" == true ]]; then
|
|
194
|
+
log_success "$FEATURE_ID cleaned: status → pending, $SESSIONS_COUNT session(s) deleted, $SPECS_COUNT artifact(s) deleted"
|
|
195
|
+
else
|
|
196
|
+
log_success "$FEATURE_ID reset: status → pending, retry count → 0"
|
|
197
|
+
fi
|
|
198
|
+
|
|
199
|
+
echo ""
|
|
200
|
+
echo -e "${BOLD}Next steps:${NC}"
|
|
201
|
+
if [[ "$DO_RUN" == true ]]; then
|
|
202
|
+
log_info "Auto-retrying $FEATURE_ID..."
|
|
203
|
+
echo ""
|
|
204
|
+
exec "$SCRIPT_DIR/retry-feature.sh" "$FEATURE_ID" "$FEATURE_LIST"
|
|
205
|
+
else
|
|
206
|
+
log_info " ./dev-pipeline/retry-feature.sh $FEATURE_ID # Retry once"
|
|
207
|
+
log_info " ./dev-pipeline/run.sh run feature-list.json # Resume pipeline"
|
|
208
|
+
fi
|
|
209
|
+
echo ""
|
|
@@ -0,0 +1,344 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
# ============================================================
|
|
5
|
+
# dev-pipeline/retry-bug.sh - Retry a single failed bug fix
|
|
6
|
+
#
|
|
7
|
+
# Runs exactly ONE AI CLI session for the specified bug, then exits.
|
|
8
|
+
# Use this to manually retry a failed bug without restarting
|
|
9
|
+
# the full bugfix pipeline.
|
|
10
|
+
#
|
|
11
|
+
# Usage:
|
|
12
|
+
# ./retry-bug.sh <bug-id> [bug-fix-list.json]
|
|
13
|
+
#
|
|
14
|
+
# Examples:
|
|
15
|
+
# ./retry-bug.sh B-001
|
|
16
|
+
# ./retry-bug.sh B-001 bug-fix-list.json
|
|
17
|
+
# SESSION_TIMEOUT=3600 ./retry-bug.sh B-001
|
|
18
|
+
# ============================================================
|
|
19
|
+
|
|
20
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
21
|
+
STATE_DIR="$SCRIPT_DIR/bugfix-state"
|
|
22
|
+
SCRIPTS_DIR="$SCRIPT_DIR/scripts"
|
|
23
|
+
|
|
24
|
+
SESSION_TIMEOUT=${SESSION_TIMEOUT:-0}
|
|
25
|
+
HEARTBEAT_INTERVAL=${HEARTBEAT_INTERVAL:-30}
|
|
26
|
+
|
|
27
|
+
# AI CLI detection
|
|
28
|
+
if [[ -n "${AI_CLI:-}" ]]; then
|
|
29
|
+
CLI_CMD="$AI_CLI"
|
|
30
|
+
elif [[ -n "${CODEBUDDY_CLI:-}" ]]; then
|
|
31
|
+
CLI_CMD="$CODEBUDDY_CLI"
|
|
32
|
+
elif command -v cbc &>/dev/null; then
|
|
33
|
+
CLI_CMD="cbc"
|
|
34
|
+
elif command -v claude &>/dev/null; then
|
|
35
|
+
CLI_CMD="claude"
|
|
36
|
+
else
|
|
37
|
+
echo "ERROR: No AI CLI found. Install CodeBuddy (cbc) or Claude Code (claude)." >&2
|
|
38
|
+
exit 1
|
|
39
|
+
fi
|
|
40
|
+
|
|
41
|
+
# Platform detection
|
|
42
|
+
if [[ -n "${PRIZMKIT_PLATFORM:-}" ]]; then
|
|
43
|
+
PLATFORM="$PRIZMKIT_PLATFORM"
|
|
44
|
+
elif [[ "$CLI_CMD" == *"claude"* ]]; then
|
|
45
|
+
PLATFORM="claude"
|
|
46
|
+
else
|
|
47
|
+
PLATFORM="codebuddy"
|
|
48
|
+
fi
|
|
49
|
+
export PRIZMKIT_PLATFORM="$PLATFORM"
|
|
50
|
+
|
|
51
|
+
# Colors
|
|
52
|
+
RED='\033[0;31m'
|
|
53
|
+
GREEN='\033[0;32m'
|
|
54
|
+
YELLOW='\033[1;33m'
|
|
55
|
+
BLUE='\033[0;34m'
|
|
56
|
+
BOLD='\033[1m'
|
|
57
|
+
NC='\033[0m'
|
|
58
|
+
|
|
59
|
+
log_info() { echo -e "${BLUE}[INFO]${NC} $(date '+%Y-%m-%d %H:%M:%S') $*"; }
|
|
60
|
+
log_warn() { echo -e "${YELLOW}[WARN]${NC} $(date '+%Y-%m-%d %H:%M:%S') $*"; }
|
|
61
|
+
log_error() { echo -e "${RED}[ERROR]${NC} $(date '+%Y-%m-%d %H:%M:%S') $*"; }
|
|
62
|
+
log_success() { echo -e "${GREEN}[SUCCESS]${NC} $(date '+%Y-%m-%d %H:%M:%S') $*"; }
|
|
63
|
+
|
|
64
|
+
# ============================================================
|
|
65
|
+
# Args
|
|
66
|
+
# ============================================================
|
|
67
|
+
|
|
68
|
+
if [[ $# -lt 1 ]]; then
|
|
69
|
+
echo "Usage: $0 <bug-id> [bug-fix-list.json]"
|
|
70
|
+
echo ""
|
|
71
|
+
echo " bug-id Bug to retry (e.g. B-001)"
|
|
72
|
+
echo " bug-fix-list.json Path to bug fix list (default: bug-fix-list.json)"
|
|
73
|
+
echo ""
|
|
74
|
+
echo "Environment Variables:"
|
|
75
|
+
echo " SESSION_TIMEOUT Timeout in seconds (default: 0 = no limit)"
|
|
76
|
+
echo " HEARTBEAT_INTERVAL Heartbeat interval in seconds (default: 30)"
|
|
77
|
+
exit 1
|
|
78
|
+
fi
|
|
79
|
+
|
|
80
|
+
BUG_ID="$1"
|
|
81
|
+
BUG_LIST="${2:-bug-fix-list.json}"
|
|
82
|
+
|
|
83
|
+
if [[ ! "$BUG_LIST" = /* ]]; then
|
|
84
|
+
BUG_LIST="$(pwd)/$BUG_LIST"
|
|
85
|
+
fi
|
|
86
|
+
|
|
87
|
+
# ============================================================
|
|
88
|
+
# Validation
|
|
89
|
+
# ============================================================
|
|
90
|
+
|
|
91
|
+
if [[ ! -f "$BUG_LIST" ]]; then
|
|
92
|
+
log_error "Bug fix list not found: $BUG_LIST"
|
|
93
|
+
exit 1
|
|
94
|
+
fi
|
|
95
|
+
|
|
96
|
+
if ! command -v jq &>/dev/null; then
|
|
97
|
+
log_error "jq is required. Install with: brew install jq"
|
|
98
|
+
exit 1
|
|
99
|
+
fi
|
|
100
|
+
|
|
101
|
+
# Initialize state if needed
|
|
102
|
+
if [[ ! -f "$STATE_DIR/pipeline.json" ]]; then
|
|
103
|
+
log_info "Initializing bugfix pipeline state..."
|
|
104
|
+
python3 "$SCRIPTS_DIR/init-bugfix-pipeline.py" \
|
|
105
|
+
--bug-list "$BUG_LIST" \
|
|
106
|
+
--state-dir "$STATE_DIR" >/dev/null 2>&1 || {
|
|
107
|
+
log_error "Failed to initialize bugfix pipeline state"
|
|
108
|
+
exit 1
|
|
109
|
+
}
|
|
110
|
+
fi
|
|
111
|
+
|
|
112
|
+
# Verify bug exists
|
|
113
|
+
BUG_TITLE=$(python3 -c "
|
|
114
|
+
import json, sys
|
|
115
|
+
with open('$BUG_LIST') as f:
|
|
116
|
+
data = json.load(f)
|
|
117
|
+
for bug in data.get('bugs', []):
|
|
118
|
+
if bug.get('id') == '$BUG_ID':
|
|
119
|
+
print(bug.get('title', ''))
|
|
120
|
+
sys.exit(0)
|
|
121
|
+
sys.exit(1)
|
|
122
|
+
" 2>/dev/null) || {
|
|
123
|
+
log_error "Bug $BUG_ID not found in $BUG_LIST"
|
|
124
|
+
exit 1
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
BUG_SEVERITY=$(python3 -c "
|
|
128
|
+
import json, sys
|
|
129
|
+
with open('$BUG_LIST') as f:
|
|
130
|
+
data = json.load(f)
|
|
131
|
+
for bug in data.get('bugs', []):
|
|
132
|
+
if bug.get('id') == '$BUG_ID':
|
|
133
|
+
print(bug.get('severity', 'medium'))
|
|
134
|
+
sys.exit(0)
|
|
135
|
+
sys.exit(1)
|
|
136
|
+
" 2>/dev/null) || BUG_SEVERITY="medium"
|
|
137
|
+
|
|
138
|
+
# ============================================================
|
|
139
|
+
# Clean bug artifacts + reset status for a full restart
|
|
140
|
+
# ============================================================
|
|
141
|
+
|
|
142
|
+
PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
143
|
+
|
|
144
|
+
log_info "Cleaning $BUG_ID artifacts for full restart..."
|
|
145
|
+
python3 "$SCRIPTS_DIR/update-bug-status.py" \
|
|
146
|
+
--bug-list "$BUG_LIST" \
|
|
147
|
+
--state-dir "$STATE_DIR" \
|
|
148
|
+
--bug-id "$BUG_ID" \
|
|
149
|
+
--project-root "$PROJECT_ROOT" \
|
|
150
|
+
--action clean >/dev/null 2>&1 || {
|
|
151
|
+
log_warn "Failed to clean bug artifacts (continuing with fresh session only)"
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
# ============================================================
|
|
155
|
+
# Generate bootstrap prompt
|
|
156
|
+
# ============================================================
|
|
157
|
+
|
|
158
|
+
RUN_ID=$(jq -r '.run_id' "$STATE_DIR/pipeline.json")
|
|
159
|
+
SESSION_ID="${BUG_ID}-$(date +%Y%m%d%H%M%S)"
|
|
160
|
+
SESSION_DIR="$STATE_DIR/bugs/$BUG_ID/sessions/$SESSION_ID"
|
|
161
|
+
mkdir -p "$SESSION_DIR/logs"
|
|
162
|
+
|
|
163
|
+
BOOTSTRAP_PROMPT="$SESSION_DIR/bootstrap-prompt.md"
|
|
164
|
+
|
|
165
|
+
log_info "Generating bugfix bootstrap prompt..."
|
|
166
|
+
python3 "$SCRIPTS_DIR/generate-bugfix-prompt.py" \
|
|
167
|
+
--bug-list "$BUG_LIST" \
|
|
168
|
+
--bug-id "$BUG_ID" \
|
|
169
|
+
--session-id "$SESSION_ID" \
|
|
170
|
+
--run-id "$RUN_ID" \
|
|
171
|
+
--retry-count 0 \
|
|
172
|
+
--resume-phase "null" \
|
|
173
|
+
--state-dir "$STATE_DIR" \
|
|
174
|
+
--output "$BOOTSTRAP_PROMPT" >/dev/null 2>&1
|
|
175
|
+
|
|
176
|
+
# ============================================================
|
|
177
|
+
# Run single AI CLI session
|
|
178
|
+
# ============================================================
|
|
179
|
+
|
|
180
|
+
echo ""
|
|
181
|
+
echo -e "${BOLD}════════════════════════════════════════════════════${NC}"
|
|
182
|
+
echo -e "${BOLD} Retry Bug Fix: $BUG_ID — $BUG_TITLE${NC}"
|
|
183
|
+
echo -e "${BOLD} Severity: $BUG_SEVERITY${NC}"
|
|
184
|
+
echo -e "${BOLD}════════════════════════════════════════════════════${NC}"
|
|
185
|
+
log_info "CLI: $CLI_CMD (platform: $PLATFORM)"
|
|
186
|
+
if [[ $SESSION_TIMEOUT -gt 0 ]]; then
|
|
187
|
+
log_info "Session timeout: ${SESSION_TIMEOUT}s"
|
|
188
|
+
else
|
|
189
|
+
log_info "Session timeout: none"
|
|
190
|
+
fi
|
|
191
|
+
log_info "Prompt: $BOOTSTRAP_PROMPT"
|
|
192
|
+
log_info "Log: $SESSION_DIR/logs/session.log"
|
|
193
|
+
echo -e "${BOLD}════════════════════════════════════════════════════${NC}"
|
|
194
|
+
echo ""
|
|
195
|
+
|
|
196
|
+
SESSION_LOG="$SESSION_DIR/logs/session.log"
|
|
197
|
+
|
|
198
|
+
# Spawn AI CLI session
|
|
199
|
+
case "$CLI_CMD" in
|
|
200
|
+
*claude*)
|
|
201
|
+
"$CLI_CMD" \
|
|
202
|
+
--print \
|
|
203
|
+
-p "$(cat "$BOOTSTRAP_PROMPT")" \
|
|
204
|
+
--yes \
|
|
205
|
+
> "$SESSION_LOG" 2>&1 &
|
|
206
|
+
;;
|
|
207
|
+
*)
|
|
208
|
+
"$CLI_CMD" \
|
|
209
|
+
--print \
|
|
210
|
+
-y \
|
|
211
|
+
< "$BOOTSTRAP_PROMPT" \
|
|
212
|
+
> "$SESSION_LOG" 2>&1 &
|
|
213
|
+
;;
|
|
214
|
+
esac
|
|
215
|
+
CLI_PID=$!
|
|
216
|
+
|
|
217
|
+
# Timeout watchdog
|
|
218
|
+
WATCHER_PID=""
|
|
219
|
+
if [[ $SESSION_TIMEOUT -gt 0 ]]; then
|
|
220
|
+
( sleep "$SESSION_TIMEOUT" && kill -TERM "$CLI_PID" 2>/dev/null ) &
|
|
221
|
+
WATCHER_PID=$!
|
|
222
|
+
fi
|
|
223
|
+
|
|
224
|
+
# Heartbeat
|
|
225
|
+
(
|
|
226
|
+
elapsed=0
|
|
227
|
+
prev_size=0
|
|
228
|
+
while kill -0 "$CLI_PID" 2>/dev/null; do
|
|
229
|
+
sleep "$HEARTBEAT_INTERVAL"
|
|
230
|
+
elapsed=$((elapsed + HEARTBEAT_INTERVAL))
|
|
231
|
+
kill -0 "$CLI_PID" 2>/dev/null || break
|
|
232
|
+
|
|
233
|
+
cur_size=0
|
|
234
|
+
if [[ -f "$SESSION_LOG" ]]; then
|
|
235
|
+
cur_size=$(wc -c < "$SESSION_LOG" 2>/dev/null || echo 0)
|
|
236
|
+
cur_size=$(echo "$cur_size" | tr -d ' ')
|
|
237
|
+
fi
|
|
238
|
+
|
|
239
|
+
growth=$((cur_size - prev_size))
|
|
240
|
+
prev_size=$cur_size
|
|
241
|
+
|
|
242
|
+
if [[ $cur_size -gt 1048576 ]]; then
|
|
243
|
+
size_display="$((cur_size / 1048576))MB"
|
|
244
|
+
elif [[ $cur_size -gt 1024 ]]; then
|
|
245
|
+
size_display="$((cur_size / 1024))KB"
|
|
246
|
+
else
|
|
247
|
+
size_display="${cur_size}B"
|
|
248
|
+
fi
|
|
249
|
+
|
|
250
|
+
mins=$((elapsed / 60))
|
|
251
|
+
secs=$((elapsed % 60))
|
|
252
|
+
|
|
253
|
+
last_activity=""
|
|
254
|
+
if [[ -f "$SESSION_LOG" ]]; then
|
|
255
|
+
last_activity=$(tail -20 "$SESSION_LOG" 2>/dev/null | grep -v '^$' | tail -1 | cut -c1-80 || echo "")
|
|
256
|
+
fi
|
|
257
|
+
|
|
258
|
+
if [[ $growth -gt 0 ]]; then
|
|
259
|
+
icon="${GREEN}▶${NC}"
|
|
260
|
+
else
|
|
261
|
+
icon="${YELLOW}⏸${NC}"
|
|
262
|
+
fi
|
|
263
|
+
|
|
264
|
+
echo -e " ${icon} ${BLUE}[HEARTBEAT]${NC} ${mins}m${secs}s | log: ${size_display} (+${growth}B) | ${last_activity}"
|
|
265
|
+
done
|
|
266
|
+
) &
|
|
267
|
+
HEARTBEAT_PID=$!
|
|
268
|
+
|
|
269
|
+
# Ctrl+C cleanup
|
|
270
|
+
cleanup() {
|
|
271
|
+
echo ""
|
|
272
|
+
log_warn "Interrupted. Killing session..."
|
|
273
|
+
kill "$CLI_PID" 2>/dev/null || true
|
|
274
|
+
[[ -n "$WATCHER_PID" ]] && kill "$WATCHER_PID" 2>/dev/null || true
|
|
275
|
+
kill "$HEARTBEAT_PID" 2>/dev/null || true
|
|
276
|
+
wait "$CLI_PID" 2>/dev/null || true
|
|
277
|
+
[[ -n "$WATCHER_PID" ]] && wait "$WATCHER_PID" 2>/dev/null || true
|
|
278
|
+
wait "$HEARTBEAT_PID" 2>/dev/null || true
|
|
279
|
+
log_info "Session log: $SESSION_LOG"
|
|
280
|
+
exit 130
|
|
281
|
+
}
|
|
282
|
+
trap cleanup SIGINT SIGTERM
|
|
283
|
+
|
|
284
|
+
# Wait
|
|
285
|
+
EXIT_CODE=0
|
|
286
|
+
if wait "$CLI_PID" 2>/dev/null; then
|
|
287
|
+
EXIT_CODE=0
|
|
288
|
+
else
|
|
289
|
+
EXIT_CODE=$?
|
|
290
|
+
fi
|
|
291
|
+
|
|
292
|
+
# Cleanup background processes
|
|
293
|
+
[[ -n "$WATCHER_PID" ]] && kill "$WATCHER_PID" 2>/dev/null || true
|
|
294
|
+
kill "$HEARTBEAT_PID" 2>/dev/null || true
|
|
295
|
+
[[ -n "$WATCHER_PID" ]] && wait "$WATCHER_PID" 2>/dev/null || true
|
|
296
|
+
wait "$HEARTBEAT_PID" 2>/dev/null || true
|
|
297
|
+
|
|
298
|
+
[[ $EXIT_CODE -eq 143 ]] && EXIT_CODE=124
|
|
299
|
+
|
|
300
|
+
# ============================================================
|
|
301
|
+
# Check result
|
|
302
|
+
# ============================================================
|
|
303
|
+
|
|
304
|
+
echo ""
|
|
305
|
+
if [[ -f "$SESSION_LOG" ]]; then
|
|
306
|
+
FINAL_LINES=$(wc -l < "$SESSION_LOG" 2>/dev/null | tr -d ' ')
|
|
307
|
+
FINAL_SIZE=$(wc -c < "$SESSION_LOG" 2>/dev/null | tr -d ' ')
|
|
308
|
+
log_info "Session log: $FINAL_LINES lines, $((FINAL_SIZE / 1024))KB"
|
|
309
|
+
fi
|
|
310
|
+
|
|
311
|
+
SESSION_STATUS_FILE="$SESSION_DIR/session-status.json"
|
|
312
|
+
|
|
313
|
+
if [[ $EXIT_CODE -eq 124 ]]; then
|
|
314
|
+
log_warn "Session timed out after ${SESSION_TIMEOUT}s"
|
|
315
|
+
SESSION_STATUS="timed_out"
|
|
316
|
+
elif [[ -f "$SESSION_STATUS_FILE" ]]; then
|
|
317
|
+
SESSION_STATUS=$(python3 "$SCRIPTS_DIR/check-session-status.py" \
|
|
318
|
+
--status-file "$SESSION_STATUS_FILE" 2>/dev/null) || SESSION_STATUS="crashed"
|
|
319
|
+
else
|
|
320
|
+
log_warn "Session ended without status file"
|
|
321
|
+
SESSION_STATUS="crashed"
|
|
322
|
+
fi
|
|
323
|
+
|
|
324
|
+
# Update bug status
|
|
325
|
+
python3 "$SCRIPTS_DIR/update-bug-status.py" \
|
|
326
|
+
--bug-list "$BUG_LIST" \
|
|
327
|
+
--state-dir "$STATE_DIR" \
|
|
328
|
+
--bug-id "$BUG_ID" \
|
|
329
|
+
--session-status "$SESSION_STATUS" \
|
|
330
|
+
--session-id "$SESSION_ID" \
|
|
331
|
+
--max-retries 999 \
|
|
332
|
+
--action update >/dev/null 2>&1 || true
|
|
333
|
+
|
|
334
|
+
echo ""
|
|
335
|
+
if [[ "$SESSION_STATUS" == "success" ]]; then
|
|
336
|
+
log_success "════════════════════════════════════════════════════"
|
|
337
|
+
log_success " $BUG_ID fixed successfully!"
|
|
338
|
+
log_success "════════════════════════════════════════════════════"
|
|
339
|
+
else
|
|
340
|
+
log_error "════════════════════════════════════════════════════"
|
|
341
|
+
log_error " $BUG_ID result: $SESSION_STATUS"
|
|
342
|
+
log_error " Review log: $SESSION_LOG"
|
|
343
|
+
log_error "════════════════════════════════════════════════════"
|
|
344
|
+
fi
|