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.
@@ -1,5 +1,5 @@
1
1
  {
2
- "frameworkVersion": "1.0.151",
3
- "bundledAt": "2026-04-01T01:52:30.515Z",
4
- "bundledFrom": "21236ab"
2
+ "frameworkVersion": "1.0.152",
3
+ "bundledAt": "2026-04-01T14:11:50.110Z",
4
+ "bundledFrom": "7fa7136"
5
5
  }
@@ -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 the feature (calls retry-feature.sh)
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) DO_CLEAN=true ;;
53
- --run) DO_RUN=true ;;
54
- -h|--help) ;;
55
- F-*) FEATURE_ID="$arg" ;;
56
- *) FEATURE_LIST="$arg" ;;
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 Feature to reset (e.g. F-007)"
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 the feature immediately after reset"
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
- # Get feature info from feature list
92
- FEATURE_INFO=$(python3 -c "
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') == '$FEATURE_ID':
204
+ if feat.get('id') == '$CUR_FEATURE_ID':
98
205
  title = feat.get('title', '')
99
206
  # Compute slug
100
- numeric = '$FEATURE_ID'.replace('F-', '').replace('f-', '').zfill(3)
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
- 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
- # ============================================================
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 -e "${BOLD}════════════════════════════════════════════════════${NC}"
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
- # 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
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
- # 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
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
- 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
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
- echo -e "${BOLD}════════════════════════════════════════════════════${NC}"
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
- PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
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
- 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
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
- # 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
290
+ done
187
291
 
188
292
  # ============================================================
189
293
  # Summary
190
294
  # ============================================================
191
295
 
192
296
  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
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 $FEATURE_ID..."
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" "$FEATURE_ID" "$FEATURE_LIST"
306
+ exec "$SCRIPT_DIR/retry-feature.sh" "${FEATURE_IDS[0]}" "$FEATURE_LIST"
205
307
  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"
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
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "prizmkit",
3
- "version": "1.0.151",
3
+ "version": "1.0.152",
4
4
  "description": "Create a new PrizmKit-powered project with clean initialization — no framework dev files, just what you need.",
5
5
  "type": "module",
6
6
  "bin": {