prizmkit 1.1.27 → 1.1.30

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
- "version": "1.1.27",
2
+ "version": "1.1.30",
3
3
  "skills": {
4
4
  "prizm-kit": {
5
5
  "description": "Full-lifecycle dev toolkit. Covers spec-driven development, Prizm context docs, code quality, debugging, deployment, and knowledge management.",
@@ -20,6 +20,29 @@ User says:
20
20
  - User wants a clean restart → use the original workflow skill directly (`/feature-workflow`, `/bug-fix-workflow`, `/refactor-workflow`)
21
21
  - Nothing was ever started → use the original workflow skill
22
22
 
23
+ ## Pipeline Recovery (Recommended)
24
+
25
+ **IMPORTANT**: In Phase 1.3, you MUST present the user with a choice between pipeline recovery (`run-recovery.sh`) and interactive recovery. **NEVER skip this choice. NEVER decide for the user.** The pipeline approach is recommended because it generates a comprehensive bootstrap prompt that explicitly lists every remaining phase with full instructions, ensuring the AI completes the full workflow — not just the implementation part.
26
+
27
+ Pipeline commands (for reference — Phase 1.3 will present these as a selectable option):
28
+
29
+ ```bash
30
+ ./dev-pipeline/run-recovery.sh # Auto-detect and recover
31
+ ./dev-pipeline/run-recovery.sh detect # Detection report only
32
+ ./dev-pipeline/run-recovery.sh run --dry-run # Generate prompt, don't execute
33
+ ./dev-pipeline/run-recovery.sh run --yes # Skip confirmation
34
+ ./dev-pipeline/run-recovery.sh run --model <model> # Override AI model
35
+ ```
36
+
37
+ ### When to use pipeline vs interactive recovery
38
+
39
+ | Scenario | Approach |
40
+ |----------|----------|
41
+ | Pipeline session timed out / crashed | `./run-recovery.sh` — autonomous, completes all phases reliably |
42
+ | Interactive session interrupted | This skill (`/recovery-workflow`) — for in-session interactive use |
43
+ | Want to inspect before recovering | `./run-recovery.sh detect` or `./run-recovery.sh run --dry-run` |
44
+ | Daemon/scripted use | `./run-recovery.sh run --yes` — no user confirmation needed |
45
+
23
46
  ## Supported Workflows
24
47
 
25
48
  | Workflow | Branch Pattern | Key Artifacts |
@@ -42,10 +65,10 @@ recovery-workflow
42
65
  │ ├── Based on artifact presence → infer current phase
43
66
  │ └── No match → reject and guide user
44
67
 
45
- ├── Phase 1: Report + user confirmation
68
+ ├── Phase 1: Report + user choice
46
69
  │ ├── Display detection results
47
70
  │ ├── If code changes exist → run test suite
48
- │ └── User confirms "continue" proceed
71
+ │ └── User chooses: run-recovery.sh (recommended) | interactive | start fresh
49
72
 
50
73
  └── Phase 2: Execute remaining steps
51
74
  ├── Read target workflow's SKILL.md
@@ -137,18 +160,55 @@ Include test results in the report:
137
160
  - How many tests pass/fail
138
161
  - If failures exist — which tests and why
139
162
 
140
- ### 1.3 Ask User to Confirm
163
+ ### 1.3 Ask User to Choose Recovery Approach
164
+
165
+ **User choice required (mandatory)** — Use `AskUserQuestion` to present interactive selectable options. **NEVER skip this step. NEVER choose for the user.**
166
+
167
+ ```
168
+ AskUserQuestion:
169
+ question: "Interrupted {workflow_type} detected at Phase {N} ({phase_name}). How would you like to recover?"
170
+ header: "Recovery"
171
+ options:
172
+ - label: "Run recovery script (Recommended)"
173
+ description: "Execute ./dev-pipeline/run-recovery.sh — autonomously completes ALL remaining phases (review, commit, merge, etc.) via a dedicated AI session with explicit phase instructions"
174
+ - label: "Copy command and run manually"
175
+ description: "I'll give you the exact shell command to paste into your terminal — you run it yourself outside this session"
176
+ - label: "Resume interactively in this session"
177
+ description: "Continue from Phase {N} within this conversation — more control, but may not complete all phases if session is interrupted again"
178
+ - label: "Start fresh"
179
+ description: "Discard interrupted work and restart the original workflow from scratch"
180
+ ```
181
+
182
+ **If "Run recovery script"** → Execute the pipeline recovery:
183
+ ```bash
184
+ ./dev-pipeline/run-recovery.sh
185
+ ```
186
+ The script handles everything: detection, confirmation, prompt generation, session spawn, and post-session validation. **End this skill after launching the script** — do not proceed to Phase 2.
141
187
 
188
+ **If "Copy command and run manually"** → Output the command for the user to copy and run in their own terminal:
142
189
  ```
143
- Ready to resume from Phase 5 (Review). Continue?
190
+ To recover, run this command in your project root:
191
+
192
+ ./dev-pipeline/run-recovery.sh
193
+
194
+ Or with options:
195
+ ./dev-pipeline/run-recovery.sh run --dry-run # Preview the recovery prompt first
196
+ ./dev-pipeline/run-recovery.sh run --yes # Skip confirmation
197
+ ./dev-pipeline/run-recovery.sh run --model <model> # Specify AI model
144
198
  ```
199
+ **End this skill** — do not proceed to Phase 2. The user will run the command themselves.
200
+
201
+ **If "Resume interactively"** → Continue to Phase 2 below (execute remaining steps in this session).
145
202
 
146
- If the user declines, suggest alternatives:
147
- - "Use `/bug-fix-workflow` to start fresh"
148
- - "Use `/feature-workflow` to start fresh"
149
- - "Use `/refactor-workflow` to start fresh"
203
+ **If "Start fresh"** → Suggest the appropriate original workflow skill:
204
+ - bug-fix-workflow `/bug-fix-workflow`
205
+ - feature-workflow `/feature-workflow`
206
+ - refactor-workflow `/refactor-workflow`
207
+ End this skill.
150
208
 
151
- **CHECKPOINT CP-REC-1**: User confirmed recovery.
209
+ **NEVER proceed to Phase 2 without explicit user selection via `AskUserQuestion`. Do NOT render options as plain text — the user must be able to click/select.**
210
+
211
+ **CHECKPOINT CP-REC-1**: User chose recovery approach.
152
212
 
153
213
  ---
154
214
 
@@ -181,6 +241,8 @@ Phase inference table:
181
241
  | All docs + review passed | Phase 6: User Verification | Ask user to verify the fix works |
182
242
  | All docs + committed | Phase 7: Merge Decision | Ask merge vs keep branch |
183
243
 
244
+ **Note**: Bug-fix Phases 1-3 (Diagnosis, Triage, Reproduce) collapse to Phase 1 for detection purposes because these phases don't produce persistent artifacts. If interrupted during these phases, recovery restarts from Phase 1 (diagnosis), which re-derives understanding from available inputs (bug description, code) without interactive Q&A.
245
+
184
246
  **Execution for each remaining phase**: Follow the bug-fix-workflow SKILL.md instructions exactly. Call the same prizmkit sub-commands (`/prizmkit-code-review`, `/prizmkit-committer`) at the same points.
185
247
 
186
248
  **Special handling**:
@@ -259,6 +321,7 @@ Recovery complete.
259
321
  | `feature-pipeline-launcher` | **Called in Phase 2.2** — launches or checks pipeline status for feature recovery |
260
322
  | `reset-feature.sh --clean --run` | **Alternative** — full clean retry for pipeline failures; this skill is the smart interactive alternative |
261
323
  | `reset-bug.sh --clean --run` | **Alternative** — full clean retry for bugfix pipeline failures |
324
+ | `run-recovery.sh` | **Pipeline counterpart** — shell-driven recovery that generates bootstrap prompt and spawns AI CLI session for autonomous completion |
262
325
  | `/prizmkit-code-review` | **Called in Phase 2.1** — reviews recovered bug-fix code |
263
326
  | `/prizmkit-committer` | **Called in Phase 2.1** — commits the recovered result |
264
327
 
@@ -114,6 +114,58 @@ def detect_workflow_type(project_root):
114
114
  return (None, None)
115
115
 
116
116
 
117
+ def detect_other_workflows(project_root, primary_type):
118
+ """Scan for other interrupted workflow signals beyond the primary match.
119
+
120
+ Returns a list of workflow type strings that also have signals present,
121
+ excluding the primary_type already detected.
122
+ """
123
+ others = []
124
+ branch = run_git(["branch", "--show-current"], cwd=project_root)
125
+
126
+ # Bug-fix signals
127
+ if primary_type != "bug-fix-workflow":
128
+ if branch.startswith("fix/"):
129
+ others.append("bug-fix-workflow")
130
+ else:
131
+ bugfix_dir = os.path.join(project_root, ".prizmkit", "bugfix")
132
+ if os.path.isdir(bugfix_dir):
133
+ bug_ids = [
134
+ d for d in os.listdir(bugfix_dir)
135
+ if os.path.isdir(os.path.join(bugfix_dir, d))
136
+ ]
137
+ if bug_ids:
138
+ others.append("bug-fix-workflow")
139
+
140
+ # Refactor signals
141
+ if primary_type != "refactor-workflow":
142
+ if branch.startswith("refactor/"):
143
+ others.append("refactor-workflow")
144
+ else:
145
+ for path in [
146
+ os.path.join(project_root, ".prizmkit", "plans", "refactor-list.json"),
147
+ os.path.join(project_root, "refactor-list.json"),
148
+ ]:
149
+ if os.path.isfile(path):
150
+ others.append("refactor-workflow")
151
+ break
152
+
153
+ # Feature signals
154
+ if primary_type != "feature-workflow":
155
+ if branch.startswith("feat/"):
156
+ others.append("feature-workflow")
157
+ else:
158
+ for path in [
159
+ os.path.join(project_root, ".prizmkit", "plans", "feature-list.json"),
160
+ os.path.join(project_root, "feature-list.json"),
161
+ ]:
162
+ if os.path.isfile(path):
163
+ others.append("feature-workflow")
164
+ break
165
+
166
+ return others
167
+
168
+
117
169
  # ---------------------------------------------------------------------------
118
170
  # Phase inference — one function per workflow
119
171
  # ---------------------------------------------------------------------------
@@ -285,18 +337,21 @@ def detect_code_changes(project_root, main_branch="main"):
285
337
 
286
338
  Filters out pipeline/config files that aren't source code — only counts
287
339
  files that represent actual implementation work.
340
+
341
+ Uses a file_statuses dict keyed by filepath to avoid double-counting
342
+ files that appear in both committed diff and uncommitted changes.
288
343
  """
289
344
  IGNORED_FILES = {
290
- ".prizmkit/plans/feature-list.json",
291
- ".prizmkit/plans/bug-fix-list.json",
292
- ".prizmkit/plans/refactor-list.json",
345
+ # Basename-matched list files (root-level legacy paths)
293
346
  "feature-list.json",
294
347
  "bug-fix-list.json",
295
348
  "refactor-list.json",
349
+ # Lock files
296
350
  "package-lock.json",
297
351
  "yarn.lock",
298
352
  "pnpm-lock.yaml",
299
353
  }
354
+ # Note: .prizmkit/plans/*.json paths are caught by IGNORED_PREFIXES below
300
355
  IGNORED_PREFIXES = (
301
356
  ".prizmkit/",
302
357
  ".prizm-docs/",
@@ -314,29 +369,16 @@ def detect_code_changes(project_root, main_branch="main"):
314
369
  return False
315
370
  return True
316
371
 
317
- result = {
318
- "files_modified": 0,
319
- "files_added": 0,
320
- "files_deleted": 0,
321
- "test_files_touched": 0,
322
- "directories_touched": [],
323
- "has_changes": False,
324
- }
372
+ # Track unique file → status to avoid double-counting.
373
+ # Later sources (uncommitted, untracked) update the status if the file
374
+ # was already seen in a committed diff.
375
+ file_statuses = {} # filepath → "M" | "A" | "D"
325
376
 
326
- # Diff relative to main
377
+ # Diff relative to main (committed changes on branch)
327
378
  diff_output = run_git(
328
379
  ["diff", main_branch, "--name-status"], cwd=project_root
329
380
  )
330
381
 
331
- # Also include uncommitted changes
332
- uncommitted = run_git(["diff", "--name-status"], cwd=project_root)
333
- untracked = run_git(
334
- ["ls-files", "--others", "--exclude-standard"], cwd=project_root
335
- )
336
-
337
- all_files = set()
338
- dirs = set()
339
-
340
382
  if diff_output:
341
383
  for line in diff_output.strip().split("\n"):
342
384
  if not line.strip():
@@ -344,40 +386,61 @@ def detect_code_changes(project_root, main_branch="main"):
344
386
  parts = line.split("\t", 1)
345
387
  if len(parts) < 2:
346
388
  continue
347
- status, filepath = parts[0], parts[1]
389
+ status, filepath = parts[0][0], parts[1] # first char of status
348
390
  if not is_source_file(filepath):
349
391
  continue
350
- all_files.add(filepath)
351
- if status.startswith("M"):
352
- result["files_modified"] += 1
353
- elif status.startswith("A"):
354
- result["files_added"] += 1
355
- elif status.startswith("D"):
356
- result["files_deleted"] += 1
392
+ file_statuses[filepath] = status
357
393
 
394
+ # Uncommitted working tree changes — update status for already-seen files
395
+ uncommitted = run_git(["diff", "--name-status"], cwd=project_root)
358
396
  if uncommitted:
359
397
  for line in uncommitted.strip().split("\n"):
360
398
  if not line.strip():
361
399
  continue
362
400
  parts = line.split("\t", 1)
363
- if len(parts) >= 2:
364
- filepath = parts[1]
365
- if not is_source_file(filepath):
366
- continue
367
- all_files.add(filepath)
368
- result["files_modified"] += 1
401
+ if len(parts) < 2:
402
+ continue
403
+ status, filepath = parts[0][0], parts[1]
404
+ if not is_source_file(filepath):
405
+ continue
406
+ if filepath not in file_statuses:
407
+ file_statuses[filepath] = "M" # uncommitted change = modified
408
+ # If already tracked from branch diff, keep the branch-level status
369
409
 
410
+ # Untracked files
411
+ untracked = run_git(
412
+ ["ls-files", "--others", "--exclude-standard"], cwd=project_root
413
+ )
370
414
  if untracked:
371
415
  for filepath in untracked.strip().split("\n"):
372
- if filepath.strip() and is_source_file(filepath.strip()):
373
- all_files.add(filepath.strip())
374
- result["files_added"] += 1
416
+ filepath = filepath.strip()
417
+ if filepath and is_source_file(filepath):
418
+ if filepath not in file_statuses:
419
+ file_statuses[filepath] = "A" # untracked = added
420
+
421
+ # Count by status
422
+ result = {
423
+ "files_modified": 0,
424
+ "files_added": 0,
425
+ "files_deleted": 0,
426
+ "test_files_touched": 0,
427
+ "directories_touched": [],
428
+ "has_changes": False,
429
+ }
375
430
 
376
- # Analyze file set
377
431
  test_patterns = re.compile(
378
432
  r"(test|spec|__tests__|\.test\.|\.spec\.)", re.IGNORECASE
379
433
  )
380
- for filepath in all_files:
434
+ dirs = set()
435
+
436
+ for filepath, status in file_statuses.items():
437
+ if status == "M":
438
+ result["files_modified"] += 1
439
+ elif status == "A":
440
+ result["files_added"] += 1
441
+ elif status == "D":
442
+ result["files_deleted"] += 1
443
+
381
444
  if test_patterns.search(filepath):
382
445
  result["test_files_touched"] += 1
383
446
  parent = os.path.dirname(filepath)
@@ -386,7 +449,7 @@ def detect_code_changes(project_root, main_branch="main"):
386
449
  dirs.add(os.sep.join(parts[:2]) + "/")
387
450
 
388
451
  result["directories_touched"] = sorted(dirs)
389
- result["has_changes"] = len(all_files) > 0
452
+ result["has_changes"] = len(file_statuses) > 0
390
453
 
391
454
  return result
392
455
 
@@ -428,7 +491,10 @@ def main():
428
491
  ),
429
492
  }
430
493
  print(json.dumps(report, indent=2))
431
- sys.exit(1)
494
+ sys.exit(0)
495
+
496
+ # Check for other interrupted workflows (informational)
497
+ other_workflows = detect_other_workflows(project_root, workflow_type)
432
498
 
433
499
  # Step 2: Collect git state and code changes once (shared across phase inference + report)
434
500
  cached_branch = context.get("branch")
@@ -467,6 +533,9 @@ def main():
467
533
  },
468
534
  }
469
535
 
536
+ if other_workflows:
537
+ report["other_interrupted_workflows"] = other_workflows
538
+
470
539
  print(json.dumps(report, indent=2))
471
540
 
472
541
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "prizmkit",
3
- "version": "1.1.27",
3
+ "version": "1.1.30",
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": {