prizmkit 1.0.151 → 1.0.152
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
CHANGED
|
@@ -8,15 +8,26 @@ set -euo pipefail
|
|
|
8
8
|
# re-executed from scratch by the pipeline.
|
|
9
9
|
#
|
|
10
10
|
# Usage:
|
|
11
|
-
# ./reset-feature.sh <feature-id> [options] [feature-list.json]
|
|
11
|
+
# ./reset-feature.sh <feature-id|range> [options] [feature-list.json]
|
|
12
|
+
#
|
|
13
|
+
# Feature selection:
|
|
14
|
+
# F-007 Single feature
|
|
15
|
+
# F-008:F-013 Range of features (inclusive)
|
|
16
|
+
# --auto-skipped All features with auto_skipped status
|
|
17
|
+
# --failed All features with failed status
|
|
18
|
+
# --stalled All non-completed features (failed + auto_skipped)
|
|
12
19
|
#
|
|
13
20
|
# Options:
|
|
14
21
|
# --clean Also delete session history and .prizmkit/specs/{slug}/ artifacts
|
|
15
|
-
# --run After reset, immediately retry
|
|
22
|
+
# --run After reset, immediately retry via pipeline (only with single feature)
|
|
16
23
|
#
|
|
17
24
|
# Examples:
|
|
18
25
|
# ./reset-feature.sh F-007 # Reset status only
|
|
19
26
|
# ./reset-feature.sh F-007 --clean # Reset + delete artifacts
|
|
27
|
+
# ./reset-feature.sh F-008:F-013 --clean # Reset range
|
|
28
|
+
# ./reset-feature.sh --auto-skipped # Reset all auto_skipped
|
|
29
|
+
# ./reset-feature.sh --failed --clean # Reset all failed + clean
|
|
30
|
+
# ./reset-feature.sh --stalled --clean # Reset all non-completed
|
|
20
31
|
# ./reset-feature.sh F-007 --clean --run # Reset + delete + retry
|
|
21
32
|
# ./reset-feature.sh F-007 --clean my-features.json # Custom feature list
|
|
22
33
|
# ============================================================
|
|
@@ -43,26 +54,48 @@ log_success() { echo -e "${GREEN}[OK]${NC} $*"; }
|
|
|
43
54
|
# ============================================================
|
|
44
55
|
|
|
45
56
|
FEATURE_ID=""
|
|
57
|
+
FEATURE_RANGE=""
|
|
46
58
|
FEATURE_LIST=""
|
|
47
59
|
DO_CLEAN=false
|
|
48
60
|
DO_RUN=false
|
|
61
|
+
FILTER_MODE=""
|
|
49
62
|
|
|
50
63
|
for arg in "$@"; do
|
|
51
64
|
case "$arg" in
|
|
52
|
-
--clean)
|
|
53
|
-
--run)
|
|
54
|
-
-
|
|
55
|
-
|
|
56
|
-
|
|
65
|
+
--clean) DO_CLEAN=true ;;
|
|
66
|
+
--run) DO_RUN=true ;;
|
|
67
|
+
--auto-skipped) FILTER_MODE="auto_skipped" ;;
|
|
68
|
+
--failed) FILTER_MODE="failed" ;;
|
|
69
|
+
--stalled) FILTER_MODE="stalled" ;;
|
|
70
|
+
-h|--help)
|
|
71
|
+
echo "Usage: $0 <feature-id|range> [--clean] [--run] [--auto-skipped|--failed|--stalled] [feature-list.json]"
|
|
72
|
+
echo ""
|
|
73
|
+
echo " feature-id Single feature (e.g. F-007)"
|
|
74
|
+
echo " F-008:F-013 Range of features (inclusive)"
|
|
75
|
+
echo " --auto-skipped Reset all auto_skipped features"
|
|
76
|
+
echo " --failed Reset all failed features"
|
|
77
|
+
echo " --stalled Reset all non-completed (failed + auto_skipped)"
|
|
78
|
+
echo " --clean Delete session history and .prizmkit artifacts"
|
|
79
|
+
echo " --run Retry immediately after reset (single feature only)"
|
|
80
|
+
echo " feature-list.json Path to feature list (default: feature-list.json)"
|
|
81
|
+
exit 0
|
|
82
|
+
;;
|
|
83
|
+
F-*:F-*|f-*:f-*) FEATURE_RANGE="$arg" ;;
|
|
84
|
+
F-*|f-*) FEATURE_ID="$arg" ;;
|
|
85
|
+
*) FEATURE_LIST="$arg" ;;
|
|
57
86
|
esac
|
|
58
87
|
done
|
|
59
88
|
|
|
60
|
-
if [[ -z "$FEATURE_ID" ]]; then
|
|
61
|
-
echo "Usage: $0 <feature-id> [--clean] [--run] [feature-list.json]"
|
|
89
|
+
if [[ -z "$FEATURE_ID" && -z "$FEATURE_RANGE" && -z "$FILTER_MODE" ]]; then
|
|
90
|
+
echo "Usage: $0 <feature-id|range> [--clean] [--run] [--auto-skipped|--failed|--stalled] [feature-list.json]"
|
|
62
91
|
echo ""
|
|
63
|
-
echo " feature-id
|
|
92
|
+
echo " feature-id Single feature (e.g. F-007)"
|
|
93
|
+
echo " F-008:F-013 Range of features (inclusive)"
|
|
94
|
+
echo " --auto-skipped Reset all auto_skipped features"
|
|
95
|
+
echo " --failed Reset all failed features"
|
|
96
|
+
echo " --stalled Reset all non-completed (failed + auto_skipped)"
|
|
64
97
|
echo " --clean Delete session history and .prizmkit artifacts"
|
|
65
|
-
echo " --run Retry
|
|
98
|
+
echo " --run Retry immediately after reset (single feature only)"
|
|
66
99
|
echo " feature-list.json Path to feature list (default: feature-list.json)"
|
|
67
100
|
exit 1
|
|
68
101
|
fi
|
|
@@ -88,16 +121,90 @@ if [[ ! -f "$STATE_DIR/pipeline.json" ]]; then
|
|
|
88
121
|
exit 1
|
|
89
122
|
fi
|
|
90
123
|
|
|
91
|
-
#
|
|
92
|
-
|
|
124
|
+
# ============================================================
|
|
125
|
+
# Resolve feature IDs to process
|
|
126
|
+
# ============================================================
|
|
127
|
+
|
|
128
|
+
FEATURE_IDS=()
|
|
129
|
+
|
|
130
|
+
if [[ -n "$FILTER_MODE" ]]; then
|
|
131
|
+
# Filter by status from state/features/*/status.json
|
|
132
|
+
while IFS= read -r fid; do
|
|
133
|
+
[[ -n "$fid" ]] && FEATURE_IDS+=("$fid")
|
|
134
|
+
done < <(python3 -c "
|
|
135
|
+
import json, os, sys
|
|
136
|
+
state_dir = '$STATE_DIR'
|
|
137
|
+
filter_mode = '$FILTER_MODE'
|
|
138
|
+
features_dir = os.path.join(state_dir, 'features')
|
|
139
|
+
if not os.path.isdir(features_dir):
|
|
140
|
+
sys.exit(0)
|
|
141
|
+
for fid in sorted(os.listdir(features_dir)):
|
|
142
|
+
status_file = os.path.join(features_dir, fid, 'status.json')
|
|
143
|
+
if not os.path.isfile(status_file):
|
|
144
|
+
continue
|
|
145
|
+
with open(status_file) as f:
|
|
146
|
+
status = json.load(f).get('status', '')
|
|
147
|
+
if filter_mode == 'auto_skipped' and status == 'auto_skipped':
|
|
148
|
+
print(fid)
|
|
149
|
+
elif filter_mode == 'failed' and status == 'failed':
|
|
150
|
+
print(fid)
|
|
151
|
+
elif filter_mode == 'stalled' and status in ('failed', 'auto_skipped'):
|
|
152
|
+
print(fid)
|
|
153
|
+
" 2>/dev/null)
|
|
154
|
+
|
|
155
|
+
if [[ ${#FEATURE_IDS[@]} -eq 0 ]]; then
|
|
156
|
+
log_info "No features found with status: $FILTER_MODE"
|
|
157
|
+
exit 0
|
|
158
|
+
fi
|
|
159
|
+
log_info "Found ${#FEATURE_IDS[@]} feature(s) matching --$FILTER_MODE: ${FEATURE_IDS[*]}"
|
|
160
|
+
|
|
161
|
+
elif [[ -n "$FEATURE_RANGE" ]]; then
|
|
162
|
+
# Parse range F-NNN:F-MMM
|
|
163
|
+
RANGE_START="${FEATURE_RANGE%%:*}"
|
|
164
|
+
RANGE_END="${FEATURE_RANGE##*:}"
|
|
165
|
+
START_NUM=$(echo "$RANGE_START" | sed 's/[Ff]-//' | sed 's/^0*//')
|
|
166
|
+
END_NUM=$(echo "$RANGE_END" | sed 's/[Ff]-//' | sed 's/^0*//')
|
|
167
|
+
|
|
168
|
+
if [[ -z "$START_NUM" || -z "$END_NUM" || "$START_NUM" -gt "$END_NUM" ]]; then
|
|
169
|
+
log_error "Invalid range: $FEATURE_RANGE (start must be <= end)"
|
|
170
|
+
exit 1
|
|
171
|
+
fi
|
|
172
|
+
|
|
173
|
+
for ((i=START_NUM; i<=END_NUM; i++)); do
|
|
174
|
+
FEATURE_IDS+=("F-$(printf '%03d' "$i")")
|
|
175
|
+
done
|
|
176
|
+
log_info "Range $FEATURE_RANGE → ${FEATURE_IDS[*]}"
|
|
177
|
+
|
|
178
|
+
else
|
|
179
|
+
FEATURE_IDS=("$FEATURE_ID")
|
|
180
|
+
fi
|
|
181
|
+
|
|
182
|
+
# --run only works with single feature
|
|
183
|
+
if [[ "$DO_RUN" == true && ${#FEATURE_IDS[@]} -gt 1 ]]; then
|
|
184
|
+
log_warn "--run is only supported for single feature reset. Use './run.sh run' to resume pipeline after batch reset."
|
|
185
|
+
DO_RUN=false
|
|
186
|
+
fi
|
|
187
|
+
|
|
188
|
+
# ============================================================
|
|
189
|
+
# Process each feature
|
|
190
|
+
# ============================================================
|
|
191
|
+
|
|
192
|
+
PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
193
|
+
RESET_COUNT=0
|
|
194
|
+
FAIL_COUNT=0
|
|
195
|
+
|
|
196
|
+
for CUR_FEATURE_ID in "${FEATURE_IDS[@]}"; do
|
|
197
|
+
|
|
198
|
+
# Get feature info from feature list
|
|
199
|
+
FEATURE_INFO=$(python3 -c "
|
|
93
200
|
import json, sys, re
|
|
94
201
|
with open('$FEATURE_LIST') as f:
|
|
95
202
|
data = json.load(f)
|
|
96
203
|
for feat in data.get('features', []):
|
|
97
|
-
if feat.get('id') == '$
|
|
204
|
+
if feat.get('id') == '$CUR_FEATURE_ID':
|
|
98
205
|
title = feat.get('title', '')
|
|
99
206
|
# Compute slug
|
|
100
|
-
numeric = '$
|
|
207
|
+
numeric = '$CUR_FEATURE_ID'.replace('F-', '').replace('f-', '').zfill(3)
|
|
101
208
|
slug = title.lower()
|
|
102
209
|
slug = re.sub(r'[^a-z0-9\s-]', '', slug)
|
|
103
210
|
slug = re.sub(r'[\s]+', '-', slug.strip())
|
|
@@ -107,103 +214,100 @@ for feat in data.get('features', []):
|
|
|
107
214
|
sys.exit(0)
|
|
108
215
|
sys.exit(1)
|
|
109
216
|
" 2>/dev/null) || {
|
|
110
|
-
|
|
111
|
-
|
|
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
|
-
# ============================================================
|
|
217
|
+
log_warn "Feature $CUR_FEATURE_ID not found in $FEATURE_LIST — skipping"
|
|
218
|
+
FAIL_COUNT=$((FAIL_COUNT + 1))
|
|
219
|
+
continue
|
|
220
|
+
}
|
|
121
221
|
|
|
122
|
-
echo ""
|
|
123
|
-
echo -
|
|
124
|
-
echo -e "${BOLD} Reset: $FEATURE_ID — $FEATURE_TITLE${NC}"
|
|
125
|
-
echo -e "${BOLD}════════════════════════════════════════════════════${NC}"
|
|
222
|
+
FEATURE_TITLE=$(echo "$FEATURE_INFO" | python3 -c "import sys,json; print(json.load(sys.stdin)['title'])")
|
|
223
|
+
FEATURE_SLUG=$(echo "$FEATURE_INFO" | python3 -c "import sys,json; print(json.load(sys.stdin)['slug'])")
|
|
126
224
|
|
|
127
|
-
#
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
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
|
|
225
|
+
# ── Show current state ──
|
|
226
|
+
echo ""
|
|
227
|
+
echo -e "${BOLD}════════════════════════════════════════════════════${NC}"
|
|
228
|
+
echo -e "${BOLD} Reset: $CUR_FEATURE_ID — $FEATURE_TITLE${NC}"
|
|
229
|
+
echo -e "${BOLD}════════════════════════════════════════════════════${NC}"
|
|
137
230
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
231
|
+
STATUS_FILE="$STATE_DIR/features/$CUR_FEATURE_ID/status.json"
|
|
232
|
+
if [[ -f "$STATUS_FILE" ]]; then
|
|
233
|
+
CURRENT_STATUS=$(python3 -c "import json; d=json.load(open('$STATUS_FILE')); print(d.get('status','?'))")
|
|
234
|
+
CURRENT_RETRY=$(python3 -c "import json; d=json.load(open('$STATUS_FILE')); print(d.get('retry_count',0))")
|
|
235
|
+
SESSION_COUNT=$(python3 -c "import json; d=json.load(open('$STATUS_FILE')); print(len(d.get('sessions',[])))")
|
|
236
|
+
log_info "Current status: $CURRENT_STATUS (retry $CURRENT_RETRY, $SESSION_COUNT sessions)"
|
|
237
|
+
else
|
|
238
|
+
log_info "No status file found (never executed)"
|
|
239
|
+
fi
|
|
145
240
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
if [[ -d "$
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
fi
|
|
241
|
+
SPECS_DIR="$PROJECT_ROOT/.prizmkit/specs/$FEATURE_SLUG"
|
|
242
|
+
SPECS_COUNT=0
|
|
243
|
+
if [[ -d "$SPECS_DIR" ]]; then
|
|
244
|
+
SPECS_COUNT=$(find "$SPECS_DIR" -type f 2>/dev/null | wc -l | tr -d ' ')
|
|
245
|
+
log_info "PrizmKit artifacts: $SPECS_COUNT files in .prizmkit/specs/$FEATURE_SLUG/"
|
|
246
|
+
fi
|
|
152
247
|
|
|
153
|
-
|
|
248
|
+
SESSIONS_DIR="$STATE_DIR/features/$CUR_FEATURE_ID/sessions"
|
|
249
|
+
SESSIONS_COUNT=0
|
|
250
|
+
if [[ -d "$SESSIONS_DIR" ]]; then
|
|
251
|
+
SESSIONS_COUNT=$(find "$SESSIONS_DIR" -mindepth 1 -maxdepth 1 -type d 2>/dev/null | wc -l | tr -d ' ')
|
|
252
|
+
log_info "Session history: $SESSIONS_COUNT session(s)"
|
|
253
|
+
fi
|
|
154
254
|
|
|
155
|
-
|
|
156
|
-
# Execute reset
|
|
157
|
-
# ============================================================
|
|
255
|
+
echo -e "${BOLD}════════════════════════════════════════════════════${NC}"
|
|
158
256
|
|
|
159
|
-
|
|
257
|
+
# ── Execute reset ──
|
|
258
|
+
if [[ "$DO_CLEAN" == true ]]; then
|
|
259
|
+
log_info "Cleaning $CUR_FEATURE_ID (reset + delete artifacts)..."
|
|
260
|
+
RESULT=$(python3 "$SCRIPTS_DIR/update-feature-status.py" \
|
|
261
|
+
--feature-list "$FEATURE_LIST" \
|
|
262
|
+
--state-dir "$STATE_DIR" \
|
|
263
|
+
--feature-id "$CUR_FEATURE_ID" \
|
|
264
|
+
--feature-slug "$FEATURE_SLUG" \
|
|
265
|
+
--project-root "$PROJECT_ROOT" \
|
|
266
|
+
--action clean 2>&1)
|
|
267
|
+
else
|
|
268
|
+
log_info "Resetting $CUR_FEATURE_ID status..."
|
|
269
|
+
RESULT=$(python3 "$SCRIPTS_DIR/update-feature-status.py" \
|
|
270
|
+
--feature-list "$FEATURE_LIST" \
|
|
271
|
+
--state-dir "$STATE_DIR" \
|
|
272
|
+
--feature-id "$CUR_FEATURE_ID" \
|
|
273
|
+
--action reset 2>&1)
|
|
274
|
+
fi
|
|
160
275
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
--state-dir "$STATE_DIR" \
|
|
175
|
-
--feature-id "$FEATURE_ID" \
|
|
176
|
-
--action reset 2>&1)
|
|
177
|
-
fi
|
|
276
|
+
# Check for errors
|
|
277
|
+
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
|
|
278
|
+
RESET_COUNT=$((RESET_COUNT + 1))
|
|
279
|
+
if [[ "$DO_CLEAN" == true ]]; then
|
|
280
|
+
log_success "$CUR_FEATURE_ID cleaned: status → pending, $SESSIONS_COUNT session(s) deleted, $SPECS_COUNT artifact(s) deleted"
|
|
281
|
+
else
|
|
282
|
+
log_success "$CUR_FEATURE_ID reset: status → pending, retry count → 0"
|
|
283
|
+
fi
|
|
284
|
+
else
|
|
285
|
+
ERROR_MSG=$(echo "$RESULT" | python3 -c "import sys,json; print(json.load(sys.stdin).get('error','unknown'))" 2>/dev/null || echo "$RESULT")
|
|
286
|
+
log_error "Reset $CUR_FEATURE_ID failed: $ERROR_MSG"
|
|
287
|
+
FAIL_COUNT=$((FAIL_COUNT + 1))
|
|
288
|
+
fi
|
|
178
289
|
|
|
179
|
-
|
|
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
|
|
290
|
+
done
|
|
187
291
|
|
|
188
292
|
# ============================================================
|
|
189
293
|
# Summary
|
|
190
294
|
# ============================================================
|
|
191
295
|
|
|
192
296
|
echo ""
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
log_success "$FEATURE_ID reset: status → pending, retry count → 0"
|
|
197
|
-
fi
|
|
297
|
+
echo -e "${BOLD}════════════════════════════════════════════════════${NC}"
|
|
298
|
+
echo -e "${BOLD} Reset complete: $RESET_COUNT succeeded, $FAIL_COUNT failed${NC}"
|
|
299
|
+
echo -e "${BOLD}════════════════════════════════════════════════════${NC}"
|
|
198
300
|
|
|
199
301
|
echo ""
|
|
200
302
|
echo -e "${BOLD}Next steps:${NC}"
|
|
201
|
-
if [[ "$DO_RUN" == true ]]; then
|
|
202
|
-
log_info "Auto-retrying $
|
|
303
|
+
if [[ "$DO_RUN" == true && ${#FEATURE_IDS[@]} -eq 1 ]]; then
|
|
304
|
+
log_info "Auto-retrying ${FEATURE_IDS[0]}..."
|
|
203
305
|
echo ""
|
|
204
|
-
exec "$SCRIPT_DIR/retry-feature.sh" "$
|
|
306
|
+
exec "$SCRIPT_DIR/retry-feature.sh" "${FEATURE_IDS[0]}" "$FEATURE_LIST"
|
|
205
307
|
else
|
|
206
|
-
log_info " ./dev-pipeline/
|
|
207
|
-
|
|
308
|
+
log_info " ./dev-pipeline/run.sh run feature-list.json # Resume pipeline from first pending"
|
|
309
|
+
if [[ ${#FEATURE_IDS[@]} -eq 1 ]]; then
|
|
310
|
+
log_info " ./dev-pipeline/retry-feature.sh ${FEATURE_IDS[0]} # Retry single feature"
|
|
311
|
+
fi
|
|
208
312
|
fi
|
|
209
313
|
echo ""
|
|
@@ -987,6 +987,15 @@ print(count)
|
|
|
987
987
|
fi
|
|
988
988
|
echo -e "${BOLD}────────────────────────────────────────────────────${NC}"
|
|
989
989
|
|
|
990
|
+
# Commit dirty working tree before starting feature
|
|
991
|
+
local _dirty_files=""
|
|
992
|
+
_dirty_files=$(git -C "$_proj_root" status --porcelain 2>/dev/null || true)
|
|
993
|
+
if [[ -n "$_dirty_files" ]]; then
|
|
994
|
+
log_info "Dirty working tree detected — committing before $feature_id..."
|
|
995
|
+
git -C "$_proj_root" add -A 2>/dev/null || true
|
|
996
|
+
git -C "$_proj_root" commit --no-verify -m "ready for run $feature_id" 2>/dev/null || true
|
|
997
|
+
fi
|
|
998
|
+
|
|
990
999
|
# Create per-feature dev branch
|
|
991
1000
|
local _feature_branch="${DEV_BRANCH:-dev/${feature_id}-$(date +%Y%m%d%H%M)}"
|
|
992
1001
|
if branch_create "$_proj_root" "$_feature_branch" "$_ORIGINAL_BRANCH"; then
|