prizmkit 1.1.57 → 1.1.60
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/bin/create-prizmkit.js +8 -6
- package/bundled/VERSION.json +3 -3
- package/bundled/adapters/codex/agent-adapter.js +38 -0
- package/bundled/adapters/codex/paths.js +27 -0
- package/bundled/adapters/codex/rules-adapter.js +30 -0
- package/bundled/adapters/codex/settings-adapter.js +27 -0
- package/bundled/adapters/codex/skill-adapter.js +65 -0
- package/bundled/adapters/codex/team-adapter.js +37 -0
- package/bundled/dev-pipeline/.env.example +2 -1
- package/bundled/dev-pipeline/README.md +10 -7
- package/bundled/dev-pipeline/lib/common.sh +278 -37
- package/bundled/dev-pipeline/run-bugfix.sh +10 -61
- package/bundled/dev-pipeline/run-feature.sh +10 -78
- package/bundled/dev-pipeline/run-recovery.sh +10 -46
- package/bundled/dev-pipeline/run-refactor.sh +10 -61
- package/bundled/dev-pipeline/scripts/generate-bootstrap-prompt.py +17 -7
- package/bundled/dev-pipeline/scripts/generate-bugfix-prompt.py +9 -3
- package/bundled/dev-pipeline/scripts/generate-refactor-prompt.py +9 -3
- package/bundled/dev-pipeline/scripts/utils.py +6 -4
- package/bundled/dev-pipeline-windows/.env.example +28 -0
- package/bundled/dev-pipeline-windows/README.md +30 -0
- package/bundled/dev-pipeline-windows/SCHEMA_ANALYSIS.md +525 -0
- package/bundled/dev-pipeline-windows/assets/feature-list-example.json +146 -0
- package/bundled/dev-pipeline-windows/assets/prizm-dev-team-integration.md +138 -0
- package/bundled/dev-pipeline-windows/launch-bugfix-daemon.ps1 +9 -0
- package/bundled/dev-pipeline-windows/launch-feature-daemon.ps1 +9 -0
- package/bundled/dev-pipeline-windows/launch-refactor-daemon.ps1 +9 -0
- package/bundled/dev-pipeline-windows/lib/common.ps1 +432 -0
- package/bundled/dev-pipeline-windows/lib/daemon.ps1 +140 -0
- package/bundled/dev-pipeline-windows/lib/pipeline.ps1 +446 -0
- package/bundled/dev-pipeline-windows/lib/reset.ps1 +87 -0
- package/bundled/dev-pipeline-windows/reset-bug.ps1 +9 -0
- package/bundled/dev-pipeline-windows/reset-feature.ps1 +9 -0
- package/bundled/dev-pipeline-windows/reset-refactor.ps1 +9 -0
- package/bundled/dev-pipeline-windows/run-bugfix.ps1 +9 -0
- package/bundled/dev-pipeline-windows/run-feature.ps1 +9 -0
- package/bundled/dev-pipeline-windows/run-recovery.ps1 +76 -0
- package/bundled/dev-pipeline-windows/run-refactor.ps1 +9 -0
- package/bundled/dev-pipeline-windows/scripts/check-session-status.py +228 -0
- package/bundled/dev-pipeline-windows/scripts/cleanup-logs.py +192 -0
- package/bundled/dev-pipeline-windows/scripts/detect-stuck.py +530 -0
- package/bundled/dev-pipeline-windows/scripts/generate-bootstrap-prompt.py +1737 -0
- package/bundled/dev-pipeline-windows/scripts/generate-bugfix-prompt.py +685 -0
- package/bundled/dev-pipeline-windows/scripts/generate-recovery-prompt.py +805 -0
- package/bundled/dev-pipeline-windows/scripts/generate-refactor-prompt.py +763 -0
- package/bundled/dev-pipeline-windows/scripts/init-bugfix-pipeline.py +316 -0
- package/bundled/dev-pipeline-windows/scripts/init-dev-team.py +134 -0
- package/bundled/dev-pipeline-windows/scripts/init-pipeline.py +380 -0
- package/bundled/dev-pipeline-windows/scripts/init-refactor-pipeline.py +399 -0
- package/bundled/dev-pipeline-windows/scripts/parse-stream-progress.py +388 -0
- package/bundled/dev-pipeline-windows/scripts/patch-completion-notes.py +191 -0
- package/bundled/dev-pipeline-windows/scripts/update-bug-status.py +864 -0
- package/bundled/dev-pipeline-windows/scripts/update-checkpoint.py +173 -0
- package/bundled/dev-pipeline-windows/scripts/update-feature-status.py +1501 -0
- package/bundled/dev-pipeline-windows/scripts/update-refactor-status.py +1073 -0
- package/bundled/dev-pipeline-windows/scripts/utils.py +542 -0
- package/bundled/dev-pipeline-windows/templates/agent-prompts/critic-plan-challenge.md +7 -0
- package/bundled/dev-pipeline-windows/templates/agent-prompts/dev-fix.md +7 -0
- package/bundled/dev-pipeline-windows/templates/agent-prompts/dev-implement.md +30 -0
- package/bundled/dev-pipeline-windows/templates/agent-prompts/dev-resume.md +5 -0
- package/bundled/dev-pipeline-windows/templates/agent-prompts/reviewer-review.md +7 -0
- package/bundled/dev-pipeline-windows/templates/bootstrap-prompt.md +46 -0
- package/bundled/dev-pipeline-windows/templates/bootstrap-tier1.md +43 -0
- package/bundled/dev-pipeline-windows/templates/bootstrap-tier2.md +43 -0
- package/bundled/dev-pipeline-windows/templates/bootstrap-tier3.md +43 -0
- package/bundled/dev-pipeline-windows/templates/bug-fix-list-schema.json +263 -0
- package/bundled/dev-pipeline-windows/templates/bugfix-bootstrap-prompt.md +320 -0
- package/bundled/dev-pipeline-windows/templates/feature-list-schema.json +237 -0
- package/bundled/dev-pipeline-windows/templates/refactor-bootstrap-prompt.md +331 -0
- package/bundled/dev-pipeline-windows/templates/refactor-list-schema.json +270 -0
- package/bundled/dev-pipeline-windows/templates/sections/ac-verification-checklist.md +13 -0
- package/bundled/dev-pipeline-windows/templates/sections/checkpoint-system.md +91 -0
- package/bundled/dev-pipeline-windows/templates/sections/context-budget-rules.md +33 -0
- package/bundled/dev-pipeline-windows/templates/sections/critical-paths-agent.md +10 -0
- package/bundled/dev-pipeline-windows/templates/sections/critical-paths-full.md +12 -0
- package/bundled/dev-pipeline-windows/templates/sections/critical-paths-lite.md +7 -0
- package/bundled/dev-pipeline-windows/templates/sections/directory-convention-agent.md +8 -0
- package/bundled/dev-pipeline-windows/templates/sections/directory-convention-full.md +9 -0
- package/bundled/dev-pipeline-windows/templates/sections/directory-convention-lite.md +6 -0
- package/bundled/dev-pipeline-windows/templates/sections/failure-capture.md +21 -0
- package/bundled/dev-pipeline-windows/templates/sections/feature-context.md +31 -0
- package/bundled/dev-pipeline-windows/templates/sections/phase-browser-verification-auto.md +72 -0
- package/bundled/dev-pipeline-windows/templates/sections/phase-browser-verification-opencli.md +63 -0
- package/bundled/dev-pipeline-windows/templates/sections/phase-browser-verification.md +62 -0
- package/bundled/dev-pipeline-windows/templates/sections/phase-commit-full.md +71 -0
- package/bundled/dev-pipeline-windows/templates/sections/phase-commit.md +64 -0
- package/bundled/dev-pipeline-windows/templates/sections/phase-context-snapshot-agent-suffix.md +23 -0
- package/bundled/dev-pipeline-windows/templates/sections/phase-context-snapshot-base.md +24 -0
- package/bundled/dev-pipeline-windows/templates/sections/phase-context-snapshot-lite-suffix.md +12 -0
- package/bundled/dev-pipeline-windows/templates/sections/phase-critic-plan-full.md +53 -0
- package/bundled/dev-pipeline-windows/templates/sections/phase-critic-plan.md +32 -0
- package/bundled/dev-pipeline-windows/templates/sections/phase-implement-agent.md +37 -0
- package/bundled/dev-pipeline-windows/templates/sections/phase-implement-full.md +50 -0
- package/bundled/dev-pipeline-windows/templates/sections/phase-implement-lite.md +52 -0
- package/bundled/dev-pipeline-windows/templates/sections/phase-plan-agent.md +27 -0
- package/bundled/dev-pipeline-windows/templates/sections/phase-plan-lite.md +27 -0
- package/bundled/dev-pipeline-windows/templates/sections/phase-review-agent.md +27 -0
- package/bundled/dev-pipeline-windows/templates/sections/phase-review-full.md +29 -0
- package/bundled/dev-pipeline-windows/templates/sections/phase-specify-plan-full.md +77 -0
- package/bundled/dev-pipeline-windows/templates/sections/phase0-init.md +13 -0
- package/bundled/dev-pipeline-windows/templates/sections/phase0-test-baseline.md +23 -0
- package/bundled/dev-pipeline-windows/templates/sections/session-context.md +5 -0
- package/bundled/dev-pipeline-windows/templates/sections/subagent-timeout-recovery.md +6 -0
- package/bundled/dev-pipeline-windows/templates/sections/test-failure-recovery-agent.md +67 -0
- package/bundled/dev-pipeline-windows/templates/sections/test-failure-recovery-lite.md +58 -0
- package/bundled/dev-pipeline-windows/templates/session-status-schema.json +83 -0
- package/bundled/skills/_metadata.json +1 -1
- package/bundled/skills/app-planner/SKILL.md +26 -18
- package/bundled/skills/app-planner/references/architecture-decisions.md +9 -5
- package/bundled/skills/app-planner/references/frontend-design-guide.md +1 -1
- package/bundled/skills/feature-planner/SKILL.md +9 -2
- package/bundled/skills/prizmkit-init/SKILL.md +7 -6
- package/bundled/skills/recovery-workflow/scripts/detect-recovery-state.py +2 -0
- package/bundled/skills-windows/app-planner/SKILL.md +639 -0
- package/bundled/skills-windows/app-planner/assets/app-design-guide.md +101 -0
- package/bundled/skills-windows/app-planner/references/architecture-decisions.md +52 -0
- package/bundled/skills-windows/app-planner/references/brainstorm-guide.md +101 -0
- package/bundled/skills-windows/app-planner/references/frontend-design-guide.md +71 -0
- package/bundled/skills-windows/app-planner/references/project-brief-guide.md +82 -0
- package/bundled/skills-windows/app-planner/references/red-team-checklist.md +40 -0
- package/bundled/skills-windows/app-planner/references/rules/backend/derivation-rules.md +609 -0
- package/bundled/skills-windows/app-planner/references/rules/backend/fixed-rules.md +285 -0
- package/bundled/skills-windows/app-planner/references/rules/backend/question-bank.md +249 -0
- package/bundled/skills-windows/app-planner/references/rules/backend/template.md +173 -0
- package/bundled/skills-windows/app-planner/references/rules/database/derivation-rules.md +373 -0
- package/bundled/skills-windows/app-planner/references/rules/database/fixed-rules.md +211 -0
- package/bundled/skills-windows/app-planner/references/rules/database/question-bank.md +184 -0
- package/bundled/skills-windows/app-planner/references/rules/database/template.md +158 -0
- package/bundled/skills-windows/app-planner/references/rules/frontend/derivation-rules.md +810 -0
- package/bundled/skills-windows/app-planner/references/rules/frontend/fixed-rules.md +188 -0
- package/bundled/skills-windows/app-planner/references/rules/frontend/question-bank.md +302 -0
- package/bundled/skills-windows/app-planner/references/rules/frontend/template.md +320 -0
- package/bundled/skills-windows/app-planner/references/rules/mobile/derivation-rules.md +639 -0
- package/bundled/skills-windows/app-planner/references/rules/mobile/fixed-rules.md +290 -0
- package/bundled/skills-windows/app-planner/references/rules/mobile/question-bank.md +232 -0
- package/bundled/skills-windows/app-planner/references/rules/mobile/template.md +175 -0
- package/bundled/skills-windows/bug-fix-workflow/SKILL.md +415 -0
- package/bundled/skills-windows/bug-planner/SKILL.md +395 -0
- package/bundled/skills-windows/bug-planner/assets/bug-confirmation-template.md +43 -0
- package/bundled/skills-windows/bug-planner/references/critic-and-verification.md +44 -0
- package/bundled/skills-windows/bug-planner/references/error-recovery.md +73 -0
- package/bundled/skills-windows/bug-planner/references/input-formats.md +53 -0
- package/bundled/skills-windows/bug-planner/references/schema-validation.md +25 -0
- package/bundled/skills-windows/bug-planner/references/severity-rules.md +16 -0
- package/bundled/skills-windows/bug-planner/scripts/validate-bug-list.py +322 -0
- package/bundled/skills-windows/bugfix-pipeline-launcher/SKILL.md +380 -0
- package/bundled/skills-windows/feature-pipeline-launcher/SKILL.md +441 -0
- package/bundled/skills-windows/feature-pipeline-launcher/scripts/preflight-check.py +462 -0
- package/bundled/skills-windows/feature-planner/SKILL.md +401 -0
- package/bundled/skills-windows/feature-planner/assets/evaluation-guide.md +64 -0
- package/bundled/skills-windows/feature-planner/assets/planning-guide.md +214 -0
- package/bundled/skills-windows/feature-planner/references/browser-interaction.md +59 -0
- package/bundled/skills-windows/feature-planner/references/completeness-review.md +57 -0
- package/bundled/skills-windows/feature-planner/references/decomposition-patterns.md +75 -0
- package/bundled/skills-windows/feature-planner/references/error-recovery.md +90 -0
- package/bundled/skills-windows/feature-planner/references/incremental-feature-planning.md +112 -0
- package/bundled/skills-windows/feature-planner/references/new-project-planning.md +85 -0
- package/bundled/skills-windows/feature-planner/scripts/validate-and-generate.py +1029 -0
- package/bundled/skills-windows/feature-workflow/SKILL.md +531 -0
- package/bundled/skills-windows/prizmkit-init/SKILL.md +356 -0
- package/bundled/skills-windows/prizmkit-init/assets/project-brief-template.md +82 -0
- package/bundled/skills-windows/prizmkit-init/references/config-schema.md +68 -0
- package/bundled/skills-windows/prizmkit-init/references/rules/layer-detection.md +41 -0
- package/bundled/skills-windows/prizmkit-init/references/tech-stack-catalog.md +13 -0
- package/bundled/skills-windows/prizmkit-init/references/update-supplement.md +9 -0
- package/bundled/skills-windows/recovery-workflow/SKILL.md +456 -0
- package/bundled/skills-windows/recovery-workflow/evals/evals.json +46 -0
- package/bundled/skills-windows/recovery-workflow/scripts/detect-recovery-state.py +544 -0
- package/bundled/skills-windows/refactor-pipeline-launcher/SKILL.md +406 -0
- package/bundled/skills-windows/refactor-planner/SKILL.md +540 -0
- package/bundled/skills-windows/refactor-planner/assets/planning-guide.md +292 -0
- package/bundled/skills-windows/refactor-planner/references/behavior-preservation.md +301 -0
- package/bundled/skills-windows/refactor-planner/references/refactor-scoping-guide.md +221 -0
- package/bundled/skills-windows/refactor-planner/scripts/validate-and-generate-refactor.py +858 -0
- package/bundled/skills-windows/refactor-workflow/SKILL.md +503 -0
- package/package.json +3 -2
- package/src/clean.js +73 -2
- package/src/config.js +159 -50
- package/src/detect-platform.js +16 -8
- package/src/external-skills.js +26 -19
- package/src/index.js +31 -9
- package/src/manifest.js +6 -2
- package/src/metadata.js +43 -5
- package/src/platforms.js +36 -0
- package/src/prompts.js +31 -6
- package/src/runtimes.js +20 -0
- package/src/scaffold.js +314 -110
- package/src/upgrade.js +81 -41
|
@@ -0,0 +1,544 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
detect-recovery-state.py — Universal workflow recovery detector.
|
|
4
|
+
|
|
5
|
+
Auto-detects which interactive workflow (feature-workflow, bug-fix-workflow,
|
|
6
|
+
refactor-workflow) was interrupted and what phase it reached, by inspecting
|
|
7
|
+
the workspace: git branch names, characteristic artifacts, and pipeline state.
|
|
8
|
+
|
|
9
|
+
Does NOT run tests — that's left to the skill so the user sees output in real time.
|
|
10
|
+
|
|
11
|
+
Usage:
|
|
12
|
+
python3 detect-recovery-state.py [--project-root .]
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
import argparse
|
|
16
|
+
import json
|
|
17
|
+
import os
|
|
18
|
+
import re
|
|
19
|
+
import subprocess
|
|
20
|
+
import sys
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
# ---------------------------------------------------------------------------
|
|
24
|
+
# Git helper
|
|
25
|
+
# ---------------------------------------------------------------------------
|
|
26
|
+
|
|
27
|
+
def run_git(args, cwd=None):
|
|
28
|
+
"""Run a git command and return stdout, or empty string on failure."""
|
|
29
|
+
try:
|
|
30
|
+
result = subprocess.run(
|
|
31
|
+
["git"] + args,
|
|
32
|
+
capture_output=True,
|
|
33
|
+
text=True,
|
|
34
|
+
cwd=cwd,
|
|
35
|
+
timeout=10,
|
|
36
|
+
)
|
|
37
|
+
return result.stdout.strip()
|
|
38
|
+
except (subprocess.SubprocessError, FileNotFoundError):
|
|
39
|
+
return ""
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def detect_main_branch(project_root):
|
|
43
|
+
"""Detect the default branch name (main or master)."""
|
|
44
|
+
for candidate in ["main", "master"]:
|
|
45
|
+
check = run_git(["rev-parse", "--verify", candidate], cwd=project_root)
|
|
46
|
+
if check:
|
|
47
|
+
return candidate
|
|
48
|
+
return "main"
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
# ---------------------------------------------------------------------------
|
|
52
|
+
# Workflow signature detection (priority-ordered)
|
|
53
|
+
# ---------------------------------------------------------------------------
|
|
54
|
+
|
|
55
|
+
def extract_bug_id_from_branch(branch):
|
|
56
|
+
"""Extract bug ID from branch name like fix/B-001-login-crash → B-001."""
|
|
57
|
+
match = re.match(r"fix/(B-\d+)", branch)
|
|
58
|
+
return match.group(1) if match else None
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def detect_workflow_type(project_root):
|
|
62
|
+
"""Priority-ordered signature matching.
|
|
63
|
+
|
|
64
|
+
Returns (workflow_type, context_dict) or (None, None).
|
|
65
|
+
"""
|
|
66
|
+
branch = run_git(["branch", "--show-current"], cwd=project_root)
|
|
67
|
+
|
|
68
|
+
# Priority 1: fix/* branch → bug-fix-workflow
|
|
69
|
+
if branch.startswith("fix/"):
|
|
70
|
+
bug_id = extract_bug_id_from_branch(branch)
|
|
71
|
+
return ("bug-fix-workflow", {"bug_id": bug_id, "branch": branch})
|
|
72
|
+
|
|
73
|
+
# Priority 2: .prizmkit/bugfix/ has content → bug-fix-workflow
|
|
74
|
+
bugfix_dir = os.path.join(project_root, ".prizmkit", "bugfix")
|
|
75
|
+
if os.path.isdir(bugfix_dir):
|
|
76
|
+
bug_ids = sorted(
|
|
77
|
+
d for d in os.listdir(bugfix_dir)
|
|
78
|
+
if os.path.isdir(os.path.join(bugfix_dir, d))
|
|
79
|
+
)
|
|
80
|
+
if bug_ids:
|
|
81
|
+
return ("bug-fix-workflow", {"bug_id": bug_ids[-1], "branch": branch})
|
|
82
|
+
|
|
83
|
+
# Priority 3: refactor/* branch → refactor-workflow
|
|
84
|
+
if branch.startswith("refactor/"):
|
|
85
|
+
return ("refactor-workflow", {"branch": branch})
|
|
86
|
+
|
|
87
|
+
# Priority 4: refactor-list.json exists → refactor-workflow
|
|
88
|
+
# Check both new and old paths for backward compatibility
|
|
89
|
+
new_refactor = os.path.join(project_root, ".prizmkit", "plans", "refactor-list.json")
|
|
90
|
+
old_refactor = os.path.join(project_root, "refactor-list.json")
|
|
91
|
+
if os.path.isfile(new_refactor):
|
|
92
|
+
return ("refactor-workflow", {"branch": branch})
|
|
93
|
+
elif os.path.isfile(old_refactor):
|
|
94
|
+
print(f"⚠️ Migration notice: refactor-list.json found in root. "
|
|
95
|
+
f"Please move to .prizmkit/plans/refactor-list.json", file=sys.stderr)
|
|
96
|
+
return ("refactor-workflow", {"branch": branch})
|
|
97
|
+
|
|
98
|
+
# Priority 5: feat/* branch → feature-workflow
|
|
99
|
+
if branch.startswith("feat/"):
|
|
100
|
+
return ("feature-workflow", {"branch": branch})
|
|
101
|
+
|
|
102
|
+
# Priority 6: feature-list.json exists → feature-workflow
|
|
103
|
+
# Check both new and old paths for backward compatibility
|
|
104
|
+
new_feature = os.path.join(project_root, ".prizmkit", "plans", "feature-list.json")
|
|
105
|
+
old_feature = os.path.join(project_root, "feature-list.json")
|
|
106
|
+
if os.path.isfile(new_feature):
|
|
107
|
+
return ("feature-workflow", {"branch": branch})
|
|
108
|
+
elif os.path.isfile(old_feature):
|
|
109
|
+
print(f"⚠️ Migration notice: feature-list.json found in root. "
|
|
110
|
+
f"Please move to .prizmkit/plans/feature-list.json", file=sys.stderr)
|
|
111
|
+
return ("feature-workflow", {"branch": branch})
|
|
112
|
+
|
|
113
|
+
# No match
|
|
114
|
+
return (None, None)
|
|
115
|
+
|
|
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
|
+
|
|
169
|
+
# ---------------------------------------------------------------------------
|
|
170
|
+
# Phase inference — one function per workflow
|
|
171
|
+
# ---------------------------------------------------------------------------
|
|
172
|
+
|
|
173
|
+
def infer_bugfix_phase(project_root, bug_id, code_changes, commits_ahead):
|
|
174
|
+
"""Infer bug-fix-workflow phase from artifacts and git state.
|
|
175
|
+
|
|
176
|
+
Detection table (from bug-fix-workflow SKILL.md):
|
|
177
|
+
(nothing) → Phase 1: Deep Bug Diagnosis
|
|
178
|
+
fix-plan.md only → Phase 4: Fix
|
|
179
|
+
fix-plan.md + code changes → Phase 5: Review
|
|
180
|
+
all docs + review passed → Phase 6: User Verification
|
|
181
|
+
all docs + committed → Phase 7: Merge Decision
|
|
182
|
+
"""
|
|
183
|
+
bugfix_dir = os.path.join(project_root, ".prizmkit", "bugfix", bug_id) if bug_id else ""
|
|
184
|
+
has_fix_plan = bugfix_dir and os.path.isfile(os.path.join(bugfix_dir, "fix-plan.md"))
|
|
185
|
+
has_fix_report = bugfix_dir and os.path.isfile(os.path.join(bugfix_dir, "fix-report.md"))
|
|
186
|
+
|
|
187
|
+
artifacts = {
|
|
188
|
+
"fix_plan_exists": has_fix_plan,
|
|
189
|
+
"fix_report_exists": has_fix_report,
|
|
190
|
+
}
|
|
191
|
+
if has_fix_plan:
|
|
192
|
+
artifacts["fix_plan_path"] = os.path.relpath(
|
|
193
|
+
os.path.join(bugfix_dir, "fix-plan.md"), project_root
|
|
194
|
+
)
|
|
195
|
+
if has_fix_report:
|
|
196
|
+
artifacts["fix_report_path"] = os.path.relpath(
|
|
197
|
+
os.path.join(bugfix_dir, "fix-report.md"), project_root
|
|
198
|
+
)
|
|
199
|
+
|
|
200
|
+
if commits_ahead > 0:
|
|
201
|
+
return 7, "Merge Decision", artifacts, \
|
|
202
|
+
f"{commits_ahead} commit(s) ahead — fix likely committed", \
|
|
203
|
+
"merge decision only"
|
|
204
|
+
|
|
205
|
+
if has_fix_report:
|
|
206
|
+
return 6, "User Verification", artifacts, \
|
|
207
|
+
"fix-report.md exists — review completed", \
|
|
208
|
+
"user verification → commit & merge"
|
|
209
|
+
|
|
210
|
+
if has_fix_plan and code_changes["has_changes"]:
|
|
211
|
+
return 5, "Review", artifacts, \
|
|
212
|
+
"fix-plan.md exists + code changes present", \
|
|
213
|
+
"code review → user verification → commit & merge"
|
|
214
|
+
|
|
215
|
+
if has_fix_plan:
|
|
216
|
+
return 4, "Fix", artifacts, \
|
|
217
|
+
"fix-plan.md exists, no code changes yet", \
|
|
218
|
+
"implement fix → review → user verification → commit & merge"
|
|
219
|
+
|
|
220
|
+
# On fix branch but no artifacts
|
|
221
|
+
return 1, "Deep Bug Diagnosis", artifacts, \
|
|
222
|
+
"on fix branch but no artifacts found", \
|
|
223
|
+
"diagnosis → triage → reproduce → fix → review → commit & merge"
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
def _infer_pipeline_workflow_phase(project_root, list_filename, state_subdir, workflow_label):
|
|
227
|
+
"""Infer phase for pipeline-driven workflows (feature-workflow, refactor-workflow).
|
|
228
|
+
|
|
229
|
+
Both follow the same structure:
|
|
230
|
+
No list file → Phase 1: Brainstorm
|
|
231
|
+
List file, no pipeline state → Phase 3: Launch
|
|
232
|
+
List file + pipeline state → Phase 4: Monitor
|
|
233
|
+
|
|
234
|
+
Checks new path (.prizmkit/plans/<list_filename>) first, then falls back
|
|
235
|
+
to old root-level path with a migration warning.
|
|
236
|
+
"""
|
|
237
|
+
# Check new path first, then old path with fallback warning
|
|
238
|
+
new_list_path = os.path.join(project_root, ".prizmkit", "plans", list_filename)
|
|
239
|
+
old_list_path = os.path.join(project_root, list_filename)
|
|
240
|
+
has_list = os.path.isfile(new_list_path)
|
|
241
|
+
if not has_list and os.path.isfile(old_list_path):
|
|
242
|
+
has_list = True
|
|
243
|
+
print(f"⚠️ Migration notice: {list_filename} found in root. "
|
|
244
|
+
f"Please move to .prizmkit/plans/{list_filename}", file=sys.stderr)
|
|
245
|
+
|
|
246
|
+
# Check new state path first, then old path with fallback warning
|
|
247
|
+
new_state_dir = os.path.join(project_root, ".prizmkit", "state", state_subdir)
|
|
248
|
+
old_state_dir = os.path.join(project_root, "dev-pipeline", "state", state_subdir)
|
|
249
|
+
has_pipeline_state = os.path.isdir(new_state_dir) and bool(os.listdir(new_state_dir))
|
|
250
|
+
if not has_pipeline_state and os.path.isdir(old_state_dir) and bool(os.listdir(old_state_dir)):
|
|
251
|
+
has_pipeline_state = True
|
|
252
|
+
print(f"⚠️ Migration notice: pipeline state found at dev-pipeline/state/{state_subdir}. "
|
|
253
|
+
f"Please move to .prizmkit/state/{state_subdir}", file=sys.stderr)
|
|
254
|
+
|
|
255
|
+
artifacts = {
|
|
256
|
+
f"{workflow_label}_list_exists": has_list,
|
|
257
|
+
"pipeline_state_exists": has_pipeline_state,
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
if has_list and has_pipeline_state:
|
|
261
|
+
return 4, "Monitor", artifacts, \
|
|
262
|
+
f".prizmkit/plans/{list_filename} + pipeline state exist", \
|
|
263
|
+
"check pipeline status and report results"
|
|
264
|
+
|
|
265
|
+
if has_list:
|
|
266
|
+
return 3, "Launch", artifacts, \
|
|
267
|
+
f".prizmkit/plans/{list_filename} exists, no pipeline state", \
|
|
268
|
+
"launch pipeline → monitor progress"
|
|
269
|
+
|
|
270
|
+
return 1, "Brainstorm", artifacts, \
|
|
271
|
+
f"no .prizmkit/plans/{list_filename} found", \
|
|
272
|
+
f"{workflow_label} goal clarification → plan → launch → monitor"
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
def infer_feature_phase(project_root):
|
|
276
|
+
"""Infer feature-workflow phase from artifacts and pipeline state."""
|
|
277
|
+
return _infer_pipeline_workflow_phase(
|
|
278
|
+
project_root, "feature-list.json", "features", "feature"
|
|
279
|
+
)
|
|
280
|
+
|
|
281
|
+
|
|
282
|
+
def infer_refactor_phase(project_root):
|
|
283
|
+
"""Infer refactor-workflow phase from artifacts and pipeline state."""
|
|
284
|
+
return _infer_pipeline_workflow_phase(
|
|
285
|
+
project_root, "refactor-list.json", "refactor", "refactor"
|
|
286
|
+
)
|
|
287
|
+
|
|
288
|
+
|
|
289
|
+
# ---------------------------------------------------------------------------
|
|
290
|
+
# Git state helpers
|
|
291
|
+
# ---------------------------------------------------------------------------
|
|
292
|
+
|
|
293
|
+
def detect_commits_ahead(project_root, main_branch="main"):
|
|
294
|
+
"""Count commits ahead of main branch."""
|
|
295
|
+
log_output = run_git(
|
|
296
|
+
["log", f"{main_branch}..HEAD", "--oneline"], cwd=project_root
|
|
297
|
+
)
|
|
298
|
+
if log_output:
|
|
299
|
+
return len(log_output.strip().split("\n"))
|
|
300
|
+
return 0
|
|
301
|
+
|
|
302
|
+
|
|
303
|
+
def detect_git_state(project_root, main_branch="main", cached_branch=None):
|
|
304
|
+
"""Detect git branch and change state."""
|
|
305
|
+
current = cached_branch or run_git(["branch", "--show-current"], cwd=project_root)
|
|
306
|
+
|
|
307
|
+
# Uncommitted changes (working tree)
|
|
308
|
+
uncommitted = 0
|
|
309
|
+
diff_stat = run_git(["diff", "--stat"], cwd=project_root)
|
|
310
|
+
if diff_stat:
|
|
311
|
+
lines = diff_stat.strip().split("\n")
|
|
312
|
+
uncommitted = max(0, len(lines) - 1)
|
|
313
|
+
|
|
314
|
+
# Staged changes
|
|
315
|
+
staged = 0
|
|
316
|
+
staged_stat = run_git(["diff", "--cached", "--stat"], cwd=project_root)
|
|
317
|
+
if staged_stat:
|
|
318
|
+
lines = staged_stat.strip().split("\n")
|
|
319
|
+
staged = max(0, len(lines) - 1)
|
|
320
|
+
|
|
321
|
+
commits_ahead = detect_commits_ahead(project_root, main_branch)
|
|
322
|
+
|
|
323
|
+
return {
|
|
324
|
+
"current_branch": current,
|
|
325
|
+
"uncommitted_files": uncommitted,
|
|
326
|
+
"staged_files": staged,
|
|
327
|
+
"commits_ahead_of_main": commits_ahead,
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
|
|
331
|
+
# ---------------------------------------------------------------------------
|
|
332
|
+
# Code change detection (reused from original, workflow-agnostic)
|
|
333
|
+
# ---------------------------------------------------------------------------
|
|
334
|
+
|
|
335
|
+
def detect_code_changes(project_root, main_branch="main"):
|
|
336
|
+
"""Analyze code changes relative to main branch.
|
|
337
|
+
|
|
338
|
+
Filters out pipeline/config files that aren't source code — only counts
|
|
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.
|
|
343
|
+
"""
|
|
344
|
+
IGNORED_FILES = {
|
|
345
|
+
# Basename-matched list files (root-level legacy paths)
|
|
346
|
+
"feature-list.json",
|
|
347
|
+
"bug-fix-list.json",
|
|
348
|
+
"refactor-list.json",
|
|
349
|
+
# Lock files
|
|
350
|
+
"package-lock.json",
|
|
351
|
+
"yarn.lock",
|
|
352
|
+
"pnpm-lock.yaml",
|
|
353
|
+
}
|
|
354
|
+
# Note: .prizmkit/plans/*.json paths are caught by IGNORED_PREFIXES below
|
|
355
|
+
IGNORED_PREFIXES = (
|
|
356
|
+
".prizmkit/",
|
|
357
|
+
".agents/",
|
|
358
|
+
".codex/",
|
|
359
|
+
".claude/",
|
|
360
|
+
".codebuddy/",
|
|
361
|
+
)
|
|
362
|
+
|
|
363
|
+
def is_source_file(filepath):
|
|
364
|
+
"""Return True if this file represents implementation code."""
|
|
365
|
+
basename = os.path.basename(filepath)
|
|
366
|
+
if basename in IGNORED_FILES:
|
|
367
|
+
return False
|
|
368
|
+
for prefix in IGNORED_PREFIXES:
|
|
369
|
+
if filepath.startswith(prefix):
|
|
370
|
+
return False
|
|
371
|
+
return True
|
|
372
|
+
|
|
373
|
+
# Track unique file → status to avoid double-counting.
|
|
374
|
+
# Later sources (uncommitted, untracked) update the status if the file
|
|
375
|
+
# was already seen in a committed diff.
|
|
376
|
+
file_statuses = {} # filepath → "M" | "A" | "D"
|
|
377
|
+
|
|
378
|
+
# Diff relative to main (committed changes on branch)
|
|
379
|
+
diff_output = run_git(
|
|
380
|
+
["diff", main_branch, "--name-status"], cwd=project_root
|
|
381
|
+
)
|
|
382
|
+
|
|
383
|
+
if diff_output:
|
|
384
|
+
for line in diff_output.strip().split("\n"):
|
|
385
|
+
if not line.strip():
|
|
386
|
+
continue
|
|
387
|
+
parts = line.split("\t", 1)
|
|
388
|
+
if len(parts) < 2:
|
|
389
|
+
continue
|
|
390
|
+
status, filepath = parts[0][0], parts[1] # first char of status
|
|
391
|
+
if not is_source_file(filepath):
|
|
392
|
+
continue
|
|
393
|
+
file_statuses[filepath] = status
|
|
394
|
+
|
|
395
|
+
# Uncommitted working tree changes — update status for already-seen files
|
|
396
|
+
uncommitted = run_git(["diff", "--name-status"], cwd=project_root)
|
|
397
|
+
if uncommitted:
|
|
398
|
+
for line in uncommitted.strip().split("\n"):
|
|
399
|
+
if not line.strip():
|
|
400
|
+
continue
|
|
401
|
+
parts = line.split("\t", 1)
|
|
402
|
+
if len(parts) < 2:
|
|
403
|
+
continue
|
|
404
|
+
status, filepath = parts[0][0], parts[1]
|
|
405
|
+
if not is_source_file(filepath):
|
|
406
|
+
continue
|
|
407
|
+
if filepath not in file_statuses:
|
|
408
|
+
file_statuses[filepath] = "M" # uncommitted change = modified
|
|
409
|
+
# If already tracked from branch diff, keep the branch-level status
|
|
410
|
+
|
|
411
|
+
# Untracked files
|
|
412
|
+
untracked = run_git(
|
|
413
|
+
["ls-files", "--others", "--exclude-standard"], cwd=project_root
|
|
414
|
+
)
|
|
415
|
+
if untracked:
|
|
416
|
+
for filepath in untracked.strip().split("\n"):
|
|
417
|
+
filepath = filepath.strip()
|
|
418
|
+
if filepath and is_source_file(filepath):
|
|
419
|
+
if filepath not in file_statuses:
|
|
420
|
+
file_statuses[filepath] = "A" # untracked = added
|
|
421
|
+
|
|
422
|
+
# Count by status
|
|
423
|
+
result = {
|
|
424
|
+
"files_modified": 0,
|
|
425
|
+
"files_added": 0,
|
|
426
|
+
"files_deleted": 0,
|
|
427
|
+
"test_files_touched": 0,
|
|
428
|
+
"directories_touched": [],
|
|
429
|
+
"has_changes": False,
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
test_patterns = re.compile(
|
|
433
|
+
r"(test|spec|__tests__|\.test\.|\.spec\.)", re.IGNORECASE
|
|
434
|
+
)
|
|
435
|
+
dirs = set()
|
|
436
|
+
|
|
437
|
+
for filepath, status in file_statuses.items():
|
|
438
|
+
if status == "M":
|
|
439
|
+
result["files_modified"] += 1
|
|
440
|
+
elif status == "A":
|
|
441
|
+
result["files_added"] += 1
|
|
442
|
+
elif status == "D":
|
|
443
|
+
result["files_deleted"] += 1
|
|
444
|
+
|
|
445
|
+
if test_patterns.search(filepath):
|
|
446
|
+
result["test_files_touched"] += 1
|
|
447
|
+
parent = os.path.dirname(filepath)
|
|
448
|
+
if parent:
|
|
449
|
+
parts = parent.split(os.sep)
|
|
450
|
+
dirs.add(os.sep.join(parts[:2]) + "/")
|
|
451
|
+
|
|
452
|
+
result["directories_touched"] = sorted(dirs)
|
|
453
|
+
result["has_changes"] = len(file_statuses) > 0
|
|
454
|
+
|
|
455
|
+
return result
|
|
456
|
+
|
|
457
|
+
|
|
458
|
+
# ---------------------------------------------------------------------------
|
|
459
|
+
# Main
|
|
460
|
+
# ---------------------------------------------------------------------------
|
|
461
|
+
|
|
462
|
+
def main():
|
|
463
|
+
parser = argparse.ArgumentParser(
|
|
464
|
+
description="Auto-detect interrupted workflow state for recovery"
|
|
465
|
+
)
|
|
466
|
+
parser.add_argument(
|
|
467
|
+
"--project-root",
|
|
468
|
+
default=None,
|
|
469
|
+
help="Project root directory (default: auto-detect from git)",
|
|
470
|
+
)
|
|
471
|
+
|
|
472
|
+
args = parser.parse_args()
|
|
473
|
+
|
|
474
|
+
# Resolve project root
|
|
475
|
+
if args.project_root:
|
|
476
|
+
project_root = os.path.abspath(args.project_root)
|
|
477
|
+
else:
|
|
478
|
+
git_root = run_git(["rev-parse", "--show-toplevel"])
|
|
479
|
+
project_root = git_root if git_root else os.getcwd()
|
|
480
|
+
|
|
481
|
+
main_branch = detect_main_branch(project_root)
|
|
482
|
+
|
|
483
|
+
# Step 1: Detect workflow type (also caches branch name)
|
|
484
|
+
workflow_type, context = detect_workflow_type(project_root)
|
|
485
|
+
|
|
486
|
+
if workflow_type is None:
|
|
487
|
+
report = {
|
|
488
|
+
"detected": False,
|
|
489
|
+
"message": (
|
|
490
|
+
"No interrupted workflow detected. "
|
|
491
|
+
"Use /feature-workflow, /bug-fix-workflow, or /refactor-workflow to start."
|
|
492
|
+
),
|
|
493
|
+
}
|
|
494
|
+
print(json.dumps(report, indent=2))
|
|
495
|
+
sys.exit(0)
|
|
496
|
+
|
|
497
|
+
# Check for other interrupted workflows (informational)
|
|
498
|
+
other_workflows = detect_other_workflows(project_root, workflow_type)
|
|
499
|
+
|
|
500
|
+
# Step 2: Collect git state and code changes once (shared across phase inference + report)
|
|
501
|
+
cached_branch = context.get("branch")
|
|
502
|
+
git_state = detect_git_state(project_root, main_branch, cached_branch=cached_branch)
|
|
503
|
+
code_changes = detect_code_changes(project_root, main_branch)
|
|
504
|
+
|
|
505
|
+
# Step 3: Infer phase within the detected workflow
|
|
506
|
+
if workflow_type == "bug-fix-workflow":
|
|
507
|
+
phase, phase_name, artifacts, reason, remaining = \
|
|
508
|
+
infer_bugfix_phase(project_root, context.get("bug_id"),
|
|
509
|
+
code_changes, git_state["commits_ahead_of_main"])
|
|
510
|
+
elif workflow_type == "feature-workflow":
|
|
511
|
+
phase, phase_name, artifacts, reason, remaining = \
|
|
512
|
+
infer_feature_phase(project_root)
|
|
513
|
+
elif workflow_type == "refactor-workflow":
|
|
514
|
+
phase, phase_name, artifacts, reason, remaining = \
|
|
515
|
+
infer_refactor_phase(project_root)
|
|
516
|
+
else:
|
|
517
|
+
# Should never reach here
|
|
518
|
+
print(json.dumps({"detected": False, "message": "Unknown workflow type"}), file=sys.stderr)
|
|
519
|
+
sys.exit(1)
|
|
520
|
+
|
|
521
|
+
# Step 4: Build report
|
|
522
|
+
report = {
|
|
523
|
+
"detected": True,
|
|
524
|
+
"workflow_type": workflow_type,
|
|
525
|
+
"phase": phase,
|
|
526
|
+
"phase_name": phase_name,
|
|
527
|
+
"context": context,
|
|
528
|
+
"artifacts": artifacts,
|
|
529
|
+
"git": git_state,
|
|
530
|
+
"code": code_changes,
|
|
531
|
+
"recovery": {
|
|
532
|
+
"reason": reason,
|
|
533
|
+
"remaining_work": remaining,
|
|
534
|
+
},
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
if other_workflows:
|
|
538
|
+
report["other_interrupted_workflows"] = other_workflows
|
|
539
|
+
|
|
540
|
+
print(json.dumps(report, indent=2))
|
|
541
|
+
|
|
542
|
+
|
|
543
|
+
if __name__ == "__main__":
|
|
544
|
+
main()
|