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,1073 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Core state machine for updating refactor status in the refactor pipeline.
|
|
3
|
+
|
|
4
|
+
Handles eight actions:
|
|
5
|
+
- get_next: Find the next refactor to process based on dependency order, priority, complexity
|
|
6
|
+
- start: Mark a refactor as in_progress when a session starts
|
|
7
|
+
- update: Update a refactor's status based on session outcome
|
|
8
|
+
- status: Print a formatted overview of all refactors
|
|
9
|
+
- pause: Save pipeline state for graceful shutdown
|
|
10
|
+
- reset: Reset a refactor to pending (status + retry count)
|
|
11
|
+
- clean: Reset + delete session history + delete refactor artifacts
|
|
12
|
+
- unskip: Reset skipped refactors and their downstream dependents back to pending
|
|
13
|
+
|
|
14
|
+
Usage:
|
|
15
|
+
python3 update-refactor-status.py \
|
|
16
|
+
--refactor-list <path> --state-dir <path> \
|
|
17
|
+
--action <get_next|start|update|status|pause|reset|clean|unskip> \
|
|
18
|
+
[--refactor-id <id>] [--session-status <status>] \
|
|
19
|
+
[--session-id <id>] [--max-retries <n>]
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
import argparse
|
|
23
|
+
import json
|
|
24
|
+
import os
|
|
25
|
+
import shutil
|
|
26
|
+
import sys
|
|
27
|
+
from datetime import datetime, timezone
|
|
28
|
+
|
|
29
|
+
from utils import (
|
|
30
|
+
load_json_file,
|
|
31
|
+
write_json_file,
|
|
32
|
+
error_out,
|
|
33
|
+
pad_right,
|
|
34
|
+
_build_progress_bar,
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
SESSION_STATUS_VALUES = [
|
|
39
|
+
"success",
|
|
40
|
+
"partial_resumable",
|
|
41
|
+
"partial_not_resumable",
|
|
42
|
+
"failed",
|
|
43
|
+
"crashed",
|
|
44
|
+
"timed_out",
|
|
45
|
+
"commit_missing",
|
|
46
|
+
"docs_missing",
|
|
47
|
+
"merge_conflict",
|
|
48
|
+
]
|
|
49
|
+
|
|
50
|
+
TERMINAL_STATUSES = {"completed", "failed", "skipped", "auto_skipped"}
|
|
51
|
+
|
|
52
|
+
# Artifact directory names (relative to project root)
|
|
53
|
+
REFACTOR_ARTIFACTS_REL = os.path.join(".prizmkit", "refactor")
|
|
54
|
+
DEV_TEAM_DIR_NAME = ".dev-team"
|
|
55
|
+
|
|
56
|
+
# Priority ordering (lower number = higher priority)
|
|
57
|
+
PRIORITY_ORDER = {
|
|
58
|
+
"critical": 0,
|
|
59
|
+
"high": 1,
|
|
60
|
+
"medium": 2,
|
|
61
|
+
"low": 3,
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
# Complexity ordering (lower number = simpler, processed first)
|
|
65
|
+
COMPLEXITY_ORDER = {
|
|
66
|
+
"low": 0,
|
|
67
|
+
"medium": 1,
|
|
68
|
+
"high": 2,
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def parse_args():
|
|
73
|
+
parser = argparse.ArgumentParser(
|
|
74
|
+
description="Core state machine for refactor pipeline status management."
|
|
75
|
+
)
|
|
76
|
+
parser.add_argument("--refactor-list", required=True, help="Path to the .prizmkit/plans/refactor-list.json file")
|
|
77
|
+
parser.add_argument("--state-dir", required=True, help="Path to the state directory (default: .prizmkit/state/refactor)")
|
|
78
|
+
parser.add_argument(
|
|
79
|
+
"--action", required=True,
|
|
80
|
+
choices=["get_next", "start", "update", "status", "pause", "reset", "clean", "unskip", "complete"],
|
|
81
|
+
help="Action to perform",
|
|
82
|
+
)
|
|
83
|
+
parser.add_argument("--refactor-id", default=None, help="Refactor ID (required for 'update'/'reset'/'clean' actions)")
|
|
84
|
+
parser.add_argument(
|
|
85
|
+
"--session-status", default=None, choices=SESSION_STATUS_VALUES,
|
|
86
|
+
help="Session outcome status (required for 'update' action)",
|
|
87
|
+
)
|
|
88
|
+
parser.add_argument("--session-id", default=None, help="Session ID (optional, for 'update' action)")
|
|
89
|
+
parser.add_argument("--max-retries", type=int, default=3, help="Maximum retry count (default: 3)")
|
|
90
|
+
parser.add_argument("--project-root", default=None, help="Project root directory. Required for 'clean' action.")
|
|
91
|
+
return parser.parse_args()
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def now_iso():
|
|
95
|
+
return datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def _default_status(refactor_id):
|
|
99
|
+
"""Create a default refactor runtime status object (no status field)."""
|
|
100
|
+
now = now_iso()
|
|
101
|
+
return {
|
|
102
|
+
"refactor_id": refactor_id,
|
|
103
|
+
"retry_count": 0,
|
|
104
|
+
"max_retries": 3,
|
|
105
|
+
"sessions": [],
|
|
106
|
+
"last_session_id": None,
|
|
107
|
+
"resume_from_phase": None,
|
|
108
|
+
"created_at": now,
|
|
109
|
+
"updated_at": now,
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def load_refactor_status(state_dir, refactor_id):
|
|
114
|
+
"""Load runtime state from status.json for a refactor.
|
|
115
|
+
|
|
116
|
+
Returns runtime fields only (retry_count, sessions, etc.).
|
|
117
|
+
The 'status' field is NOT included — status lives exclusively
|
|
118
|
+
in refactor-list.json.
|
|
119
|
+
"""
|
|
120
|
+
status_path = os.path.join(state_dir, "refactors", refactor_id, "status.json")
|
|
121
|
+
if not os.path.isfile(status_path):
|
|
122
|
+
return _default_status(refactor_id)
|
|
123
|
+
data, err = load_json_file(status_path)
|
|
124
|
+
if err:
|
|
125
|
+
return _default_status(refactor_id)
|
|
126
|
+
# Defensively remove status if present (legacy data)
|
|
127
|
+
data.pop("status", None)
|
|
128
|
+
return data
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def save_refactor_status(state_dir, refactor_id, status_data):
|
|
132
|
+
"""Write the status.json for a refactor (runtime fields only)."""
|
|
133
|
+
# Defensively strip status — it belongs in refactor-list.json
|
|
134
|
+
status_data.pop("status", None)
|
|
135
|
+
status_path = os.path.join(state_dir, "refactors", refactor_id, "status.json")
|
|
136
|
+
return write_json_file(status_path, status_data)
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def get_refactor_status_from_list(refactor_list_path, refactor_id):
|
|
140
|
+
"""Read a single refactor's status from refactor-list.json."""
|
|
141
|
+
data, err = load_json_file(refactor_list_path)
|
|
142
|
+
if err:
|
|
143
|
+
return "pending"
|
|
144
|
+
for r in data.get("refactors", []):
|
|
145
|
+
if isinstance(r, dict) and r.get("id") == refactor_id:
|
|
146
|
+
return r.get("status", "pending")
|
|
147
|
+
return "pending"
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def update_refactor_in_list(refactor_list_path, refactor_id, new_status):
|
|
151
|
+
data, err = load_json_file(refactor_list_path)
|
|
152
|
+
if err:
|
|
153
|
+
return err
|
|
154
|
+
refactors = data.get("refactors", [])
|
|
155
|
+
found = False
|
|
156
|
+
for refactor in refactors:
|
|
157
|
+
if isinstance(refactor, dict) and refactor.get("id") == refactor_id:
|
|
158
|
+
refactor["status"] = new_status
|
|
159
|
+
found = True
|
|
160
|
+
break
|
|
161
|
+
if not found:
|
|
162
|
+
return "Refactor '{}' not found in .prizmkit/plans/refactor-list.json".format(refactor_id)
|
|
163
|
+
return write_json_file(refactor_list_path, data)
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
# ---------------------------------------------------------------------------
|
|
167
|
+
# Action: get_next
|
|
168
|
+
# ---------------------------------------------------------------------------
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
def _dependencies_met(refactor, completed_set):
|
|
172
|
+
"""Check if all dependencies for a refactor are in terminal (completed) status."""
|
|
173
|
+
deps = refactor.get("dependencies", [])
|
|
174
|
+
if not deps or not isinstance(deps, list):
|
|
175
|
+
return True
|
|
176
|
+
return all(dep in completed_set for dep in deps)
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
def _count_unmet_deps(refactor, completed_set):
|
|
180
|
+
"""Count how many dependencies are not yet completed."""
|
|
181
|
+
deps = refactor.get("dependencies", [])
|
|
182
|
+
if not deps or not isinstance(deps, list):
|
|
183
|
+
return 0
|
|
184
|
+
return sum(1 for dep in deps if dep not in completed_set)
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
def action_get_next(refactor_list_data, state_dir):
|
|
188
|
+
"""Find the next refactor to process.
|
|
189
|
+
|
|
190
|
+
Priority logic:
|
|
191
|
+
1. Skip terminal statuses (completed, failed, skipped)
|
|
192
|
+
2. Only consider refactors whose dependencies are all completed
|
|
193
|
+
3. Prefer in_progress refactors (interrupted session resume) over pending
|
|
194
|
+
4. Sort by: dependency order (no-dependency items first),
|
|
195
|
+
then priority (critical > high > medium > low),
|
|
196
|
+
then complexity (low first)
|
|
197
|
+
"""
|
|
198
|
+
refactors = refactor_list_data.get("refactors", [])
|
|
199
|
+
if not refactors:
|
|
200
|
+
print("PIPELINE_COMPLETE")
|
|
201
|
+
return
|
|
202
|
+
|
|
203
|
+
# Build status map from refactor-list.json (single source of truth)
|
|
204
|
+
status_map = {}
|
|
205
|
+
status_data_map = {}
|
|
206
|
+
for r in refactors:
|
|
207
|
+
if not isinstance(r, dict):
|
|
208
|
+
continue
|
|
209
|
+
rid = r.get("id")
|
|
210
|
+
if not rid:
|
|
211
|
+
continue
|
|
212
|
+
status_map[rid] = r.get("status", "pending")
|
|
213
|
+
rs = load_refactor_status(state_dir, rid)
|
|
214
|
+
status_data_map[rid] = rs
|
|
215
|
+
|
|
216
|
+
completed_set = {rid for rid, st in status_map.items() if st in TERMINAL_STATUSES}
|
|
217
|
+
|
|
218
|
+
# Check if all refactors are terminal
|
|
219
|
+
non_terminal = [
|
|
220
|
+
r for r in refactors
|
|
221
|
+
if isinstance(r, dict) and r.get("id")
|
|
222
|
+
and status_map.get(r["id"], "pending") not in TERMINAL_STATUSES
|
|
223
|
+
]
|
|
224
|
+
if not non_terminal:
|
|
225
|
+
print("PIPELINE_COMPLETE")
|
|
226
|
+
return
|
|
227
|
+
|
|
228
|
+
# Filter to only those with met dependencies
|
|
229
|
+
eligible = [r for r in non_terminal if _dependencies_met(r, completed_set)]
|
|
230
|
+
if not eligible:
|
|
231
|
+
print("PIPELINE_BLOCKED")
|
|
232
|
+
return
|
|
233
|
+
|
|
234
|
+
# Separate in_progress from pending
|
|
235
|
+
in_progress_refactors = []
|
|
236
|
+
pending_refactors = []
|
|
237
|
+
for r in eligible:
|
|
238
|
+
rid = r.get("id")
|
|
239
|
+
rstatus = status_map.get(rid, "pending")
|
|
240
|
+
if rstatus == "in_progress":
|
|
241
|
+
in_progress_refactors.append(r)
|
|
242
|
+
elif rstatus == "pending":
|
|
243
|
+
pending_refactors.append(r)
|
|
244
|
+
|
|
245
|
+
def sort_key(r):
|
|
246
|
+
unmet = _count_unmet_deps(r, completed_set)
|
|
247
|
+
priority = PRIORITY_ORDER.get(r.get("priority", "medium"), 2)
|
|
248
|
+
complexity = COMPLEXITY_ORDER.get(r.get("complexity", "medium"), 1)
|
|
249
|
+
return (unmet, priority, complexity)
|
|
250
|
+
|
|
251
|
+
if in_progress_refactors:
|
|
252
|
+
candidates = sorted(in_progress_refactors, key=sort_key)
|
|
253
|
+
elif pending_refactors:
|
|
254
|
+
candidates = sorted(pending_refactors, key=sort_key)
|
|
255
|
+
else:
|
|
256
|
+
print("PIPELINE_BLOCKED")
|
|
257
|
+
return
|
|
258
|
+
|
|
259
|
+
chosen = candidates[0]
|
|
260
|
+
chosen_id = chosen["id"]
|
|
261
|
+
chosen_status_data = status_data_map.get(chosen_id, {})
|
|
262
|
+
|
|
263
|
+
result = {
|
|
264
|
+
"refactor_id": chosen_id,
|
|
265
|
+
"title": chosen.get("title", ""),
|
|
266
|
+
"type": chosen.get("type", "restructure"),
|
|
267
|
+
"priority": chosen.get("priority", "medium"),
|
|
268
|
+
"complexity": chosen.get("complexity", "medium"),
|
|
269
|
+
"retry_count": chosen_status_data.get("retry_count", 0),
|
|
270
|
+
"resume_from_phase": chosen_status_data.get("resume_from_phase", None),
|
|
271
|
+
}
|
|
272
|
+
print(json.dumps(result, indent=2, ensure_ascii=False))
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
# ---------------------------------------------------------------------------
|
|
276
|
+
# Action: update
|
|
277
|
+
# ---------------------------------------------------------------------------
|
|
278
|
+
|
|
279
|
+
def action_update(args, refactor_list_path, state_dir):
|
|
280
|
+
refactor_id = args.refactor_id
|
|
281
|
+
session_status = args.session_status
|
|
282
|
+
session_id = args.session_id
|
|
283
|
+
max_retries = args.max_retries
|
|
284
|
+
|
|
285
|
+
if not refactor_id:
|
|
286
|
+
error_out("--refactor-id is required for 'update' action")
|
|
287
|
+
return
|
|
288
|
+
if not session_status:
|
|
289
|
+
error_out("--session-status is required for 'update' action")
|
|
290
|
+
return
|
|
291
|
+
|
|
292
|
+
rs = load_refactor_status(state_dir, refactor_id)
|
|
293
|
+
|
|
294
|
+
# Track what status we write to refactor-list.json
|
|
295
|
+
new_status = get_refactor_status_from_list(refactor_list_path, refactor_id)
|
|
296
|
+
|
|
297
|
+
if session_status == "success":
|
|
298
|
+
new_status = "completed"
|
|
299
|
+
rs["resume_from_phase"] = None
|
|
300
|
+
err = update_refactor_in_list(refactor_list_path, refactor_id, "completed")
|
|
301
|
+
if err:
|
|
302
|
+
error_out("Failed to update .prizmkit/plans/refactor-list.json: {}".format(err))
|
|
303
|
+
return
|
|
304
|
+
elif session_status in ("commit_missing", "docs_missing", "merge_conflict"):
|
|
305
|
+
rs["retry_count"] = rs.get("retry_count", 0) + 1
|
|
306
|
+
|
|
307
|
+
if rs["retry_count"] >= max_retries:
|
|
308
|
+
new_status = "failed"
|
|
309
|
+
else:
|
|
310
|
+
new_status = "pending"
|
|
311
|
+
|
|
312
|
+
rs["degraded_reason"] = session_status
|
|
313
|
+
rs["resume_from_phase"] = None
|
|
314
|
+
rs["sessions"] = []
|
|
315
|
+
rs["last_session_id"] = None
|
|
316
|
+
|
|
317
|
+
err = update_refactor_in_list(refactor_list_path, refactor_id, new_status)
|
|
318
|
+
if err:
|
|
319
|
+
error_out("Failed to update .prizmkit/plans/refactor-list.json: {}".format(err))
|
|
320
|
+
return
|
|
321
|
+
else:
|
|
322
|
+
rs["retry_count"] = rs.get("retry_count", 0) + 1
|
|
323
|
+
|
|
324
|
+
cleaned = cleanup_refactor_artifacts(
|
|
325
|
+
state_dir=state_dir,
|
|
326
|
+
refactor_id=refactor_id,
|
|
327
|
+
project_root=args.project_root,
|
|
328
|
+
)
|
|
329
|
+
|
|
330
|
+
if rs["retry_count"] >= max_retries:
|
|
331
|
+
new_status = "failed"
|
|
332
|
+
else:
|
|
333
|
+
new_status = "pending"
|
|
334
|
+
|
|
335
|
+
rs["resume_from_phase"] = None
|
|
336
|
+
rs["sessions"] = []
|
|
337
|
+
rs["last_session_id"] = None
|
|
338
|
+
|
|
339
|
+
err = update_refactor_in_list(refactor_list_path, refactor_id, new_status)
|
|
340
|
+
if err:
|
|
341
|
+
error_out("Failed to update .prizmkit/plans/refactor-list.json: {}".format(err))
|
|
342
|
+
return
|
|
343
|
+
|
|
344
|
+
if session_status == "success" and session_id:
|
|
345
|
+
sessions = rs.get("sessions", [])
|
|
346
|
+
if session_id not in sessions:
|
|
347
|
+
sessions.append(session_id)
|
|
348
|
+
rs["sessions"] = sessions
|
|
349
|
+
rs["last_session_id"] = session_id
|
|
350
|
+
|
|
351
|
+
rs["updated_at"] = now_iso()
|
|
352
|
+
|
|
353
|
+
err = save_refactor_status(state_dir, refactor_id, rs)
|
|
354
|
+
if err:
|
|
355
|
+
error_out("Failed to save refactor status: {}".format(err))
|
|
356
|
+
return
|
|
357
|
+
|
|
358
|
+
# Auto-skip downstream refactors when this refactor is marked as failed or skipped
|
|
359
|
+
auto_skipped_refactors = []
|
|
360
|
+
if new_status in ("failed", "skipped"):
|
|
361
|
+
auto_skipped_refactors = auto_skip_blocked_refactors(
|
|
362
|
+
refactor_list_path, state_dir, refactor_id
|
|
363
|
+
)
|
|
364
|
+
|
|
365
|
+
summary = {
|
|
366
|
+
"action": "update",
|
|
367
|
+
"refactor_id": refactor_id,
|
|
368
|
+
"session_status": session_status,
|
|
369
|
+
"new_status": new_status,
|
|
370
|
+
"retry_count": rs["retry_count"],
|
|
371
|
+
"resume_from_phase": rs.get("resume_from_phase"),
|
|
372
|
+
"updated_at": rs["updated_at"],
|
|
373
|
+
}
|
|
374
|
+
if auto_skipped_refactors:
|
|
375
|
+
summary["auto_skipped"] = [info["refactor_id"] for info in auto_skipped_refactors]
|
|
376
|
+
if session_status in ("commit_missing", "docs_missing", "merge_conflict"):
|
|
377
|
+
summary["degraded_reason"] = session_status
|
|
378
|
+
summary["restart_policy"] = "finalization_retry"
|
|
379
|
+
elif session_status != "success":
|
|
380
|
+
summary["restart_policy"] = "full_restart"
|
|
381
|
+
summary["cleanup_performed"] = cleaned
|
|
382
|
+
|
|
383
|
+
print(json.dumps(summary, indent=2, ensure_ascii=False))
|
|
384
|
+
|
|
385
|
+
|
|
386
|
+
def _default_project_root():
|
|
387
|
+
env = os.environ.get("PROJECT_ROOT")
|
|
388
|
+
if env:
|
|
389
|
+
return os.path.abspath(env)
|
|
390
|
+
script_dir = os.path.dirname(os.path.abspath(__file__))
|
|
391
|
+
pipeline_dir = os.path.dirname(script_dir)
|
|
392
|
+
pipeline_parent = os.path.dirname(pipeline_dir)
|
|
393
|
+
if os.path.basename(pipeline_parent) == ".prizmkit":
|
|
394
|
+
return os.path.dirname(pipeline_parent)
|
|
395
|
+
return pipeline_parent
|
|
396
|
+
|
|
397
|
+
|
|
398
|
+
def cleanup_refactor_artifacts(state_dir, refactor_id, project_root=None):
|
|
399
|
+
"""Delete intermediate artifacts for a failed refactor run."""
|
|
400
|
+
if not project_root:
|
|
401
|
+
project_root = _default_project_root()
|
|
402
|
+
|
|
403
|
+
cleaned = []
|
|
404
|
+
|
|
405
|
+
# 1) Remove all session history
|
|
406
|
+
sessions_dir = os.path.join(state_dir, "refactors", refactor_id, "sessions")
|
|
407
|
+
sessions_deleted = 0
|
|
408
|
+
if os.path.isdir(sessions_dir):
|
|
409
|
+
for entry in os.listdir(sessions_dir):
|
|
410
|
+
entry_path = os.path.join(sessions_dir, entry)
|
|
411
|
+
if os.path.isdir(entry_path):
|
|
412
|
+
shutil.rmtree(entry_path)
|
|
413
|
+
sessions_deleted += 1
|
|
414
|
+
cleaned.append("Deleted {} session(s) from {}".format(sessions_deleted, sessions_dir))
|
|
415
|
+
|
|
416
|
+
# 2) Remove transient files under refactor dir (keep status.json)
|
|
417
|
+
refactor_dir = os.path.join(state_dir, "refactors", refactor_id)
|
|
418
|
+
if os.path.isdir(refactor_dir):
|
|
419
|
+
for entry in os.listdir(refactor_dir):
|
|
420
|
+
if entry == "status.json" or entry == "sessions":
|
|
421
|
+
continue
|
|
422
|
+
entry_path = os.path.join(refactor_dir, entry)
|
|
423
|
+
if os.path.isdir(entry_path):
|
|
424
|
+
shutil.rmtree(entry_path)
|
|
425
|
+
cleaned.append("Deleted directory {}".format(entry_path))
|
|
426
|
+
elif os.path.isfile(entry_path):
|
|
427
|
+
os.remove(entry_path)
|
|
428
|
+
cleaned.append("Deleted file {}".format(entry_path))
|
|
429
|
+
|
|
430
|
+
# 3) Remove refactor artifacts
|
|
431
|
+
refactor_artifact_dir = os.path.join(project_root, REFACTOR_ARTIFACTS_REL, refactor_id)
|
|
432
|
+
if os.path.isdir(refactor_artifact_dir):
|
|
433
|
+
shutil.rmtree(refactor_artifact_dir)
|
|
434
|
+
cleaned.append("Deleted {}".format(refactor_artifact_dir))
|
|
435
|
+
|
|
436
|
+
# 4) Remove shared dev-team workspace
|
|
437
|
+
dev_team_dir = os.path.join(project_root, DEV_TEAM_DIR_NAME)
|
|
438
|
+
if os.path.isdir(dev_team_dir):
|
|
439
|
+
shutil.rmtree(dev_team_dir)
|
|
440
|
+
cleaned.append("Deleted {}".format(dev_team_dir))
|
|
441
|
+
|
|
442
|
+
return cleaned
|
|
443
|
+
|
|
444
|
+
|
|
445
|
+
def load_session_status(state_dir, refactor_id, session_id):
|
|
446
|
+
session_status_path = os.path.join(
|
|
447
|
+
state_dir, "refactors", refactor_id, "sessions",
|
|
448
|
+
session_id, "session-status.json"
|
|
449
|
+
)
|
|
450
|
+
data, err = load_json_file(session_status_path)
|
|
451
|
+
if err:
|
|
452
|
+
return None, err
|
|
453
|
+
return data, None
|
|
454
|
+
|
|
455
|
+
|
|
456
|
+
# ---------------------------------------------------------------------------
|
|
457
|
+
# Auto-skip: cascade failure to blocked downstream refactors
|
|
458
|
+
# ---------------------------------------------------------------------------
|
|
459
|
+
|
|
460
|
+
def auto_skip_blocked_refactors(refactor_list_path, state_dir, failed_refactor_id):
|
|
461
|
+
"""Recursively mark all downstream refactors blocked by a failed refactor as auto_skipped.
|
|
462
|
+
|
|
463
|
+
When a refactor is marked as failed, any refactor whose dependency chain includes
|
|
464
|
+
the failed refactor can never be executed. This function propagates the failure
|
|
465
|
+
by marking those blocked refactors as auto_skipped, allowing the pipeline to
|
|
466
|
+
continue processing unblocked refactors and eventually reach PIPELINE_COMPLETE.
|
|
467
|
+
|
|
468
|
+
Re-reads .prizmkit/plans/refactor-list.json from disk to get the latest state (including the
|
|
469
|
+
just-written failed status from update_refactor_in_list).
|
|
470
|
+
|
|
471
|
+
NOTE: This function performs a read-modify-write on .prizmkit/plans/refactor-list.json without
|
|
472
|
+
file locking. The caller (action_update) also writes to .prizmkit/plans/refactor-list.json
|
|
473
|
+
immediately before calling this. Safe for single-pipeline execution, but if
|
|
474
|
+
multiple pipeline instances share the same .prizmkit/plans/refactor-list.json concurrently,
|
|
475
|
+
a race condition may cause lost writes. Add file locking if parallel pipelines
|
|
476
|
+
are introduced.
|
|
477
|
+
"""
|
|
478
|
+
data, err = load_json_file(refactor_list_path)
|
|
479
|
+
if err:
|
|
480
|
+
return []
|
|
481
|
+
refactors = data.get("refactors", [])
|
|
482
|
+
|
|
483
|
+
# Build current status map
|
|
484
|
+
status_map = {}
|
|
485
|
+
for r in refactors:
|
|
486
|
+
if isinstance(r, dict) and r.get("id"):
|
|
487
|
+
status_map[r["id"]] = r.get("status", "pending")
|
|
488
|
+
|
|
489
|
+
# Collect all refactors to auto-skip (recursive propagation)
|
|
490
|
+
to_skip = set()
|
|
491
|
+
changed = True
|
|
492
|
+
while changed:
|
|
493
|
+
changed = False
|
|
494
|
+
for r in refactors:
|
|
495
|
+
if not isinstance(r, dict):
|
|
496
|
+
continue
|
|
497
|
+
rid = r.get("id")
|
|
498
|
+
if not rid or rid in to_skip:
|
|
499
|
+
continue
|
|
500
|
+
current = status_map.get(rid, "pending")
|
|
501
|
+
if current in TERMINAL_STATUSES:
|
|
502
|
+
continue
|
|
503
|
+
deps = r.get("dependencies", [])
|
|
504
|
+
for dep_id in deps:
|
|
505
|
+
dep_status = status_map.get(dep_id, "pending")
|
|
506
|
+
if dep_status in ("failed", "skipped", "auto_skipped") or dep_id in to_skip:
|
|
507
|
+
to_skip.add(rid)
|
|
508
|
+
status_map[rid] = "auto_skipped"
|
|
509
|
+
changed = True
|
|
510
|
+
break
|
|
511
|
+
|
|
512
|
+
if not to_skip:
|
|
513
|
+
return []
|
|
514
|
+
|
|
515
|
+
# Batch-write to .prizmkit/plans/refactor-list.json
|
|
516
|
+
for r in refactors:
|
|
517
|
+
if isinstance(r, dict) and r.get("id") in to_skip:
|
|
518
|
+
r["status"] = "auto_skipped"
|
|
519
|
+
write_json_file(refactor_list_path, data)
|
|
520
|
+
|
|
521
|
+
# Update timestamps in status.json for each auto-skipped refactor
|
|
522
|
+
for rid in to_skip:
|
|
523
|
+
rs = load_refactor_status(state_dir, rid)
|
|
524
|
+
rs["updated_at"] = now_iso()
|
|
525
|
+
save_refactor_status(state_dir, rid, rs)
|
|
526
|
+
|
|
527
|
+
# Build blocking reason map for logging
|
|
528
|
+
skipped_info = []
|
|
529
|
+
for r in refactors:
|
|
530
|
+
if not isinstance(r, dict):
|
|
531
|
+
continue
|
|
532
|
+
rid = r.get("id")
|
|
533
|
+
if rid not in to_skip:
|
|
534
|
+
continue
|
|
535
|
+
deps = r.get("dependencies", [])
|
|
536
|
+
blockers = [
|
|
537
|
+
d for d in deps
|
|
538
|
+
if d == failed_refactor_id or d in to_skip
|
|
539
|
+
]
|
|
540
|
+
skipped_info.append({
|
|
541
|
+
"refactor_id": rid,
|
|
542
|
+
"title": r.get("title", ""),
|
|
543
|
+
"blocked_by": blockers,
|
|
544
|
+
})
|
|
545
|
+
|
|
546
|
+
print(
|
|
547
|
+
"[auto-skip] {} refactor(s) auto-skipped due to failed {}:".format(
|
|
548
|
+
len(skipped_info), failed_refactor_id
|
|
549
|
+
),
|
|
550
|
+
file=sys.stderr,
|
|
551
|
+
)
|
|
552
|
+
for info in skipped_info:
|
|
553
|
+
print(
|
|
554
|
+
" {} ({}) — blocked by {}".format(
|
|
555
|
+
info["refactor_id"],
|
|
556
|
+
info["title"],
|
|
557
|
+
", ".join(info["blocked_by"]),
|
|
558
|
+
),
|
|
559
|
+
file=sys.stderr,
|
|
560
|
+
)
|
|
561
|
+
|
|
562
|
+
return skipped_info
|
|
563
|
+
|
|
564
|
+
|
|
565
|
+
# ---------------------------------------------------------------------------
|
|
566
|
+
# Action: status
|
|
567
|
+
# ---------------------------------------------------------------------------
|
|
568
|
+
|
|
569
|
+
COLOR_GREEN = "\033[92m"
|
|
570
|
+
COLOR_YELLOW = "\033[93m"
|
|
571
|
+
COLOR_RED = "\033[91m"
|
|
572
|
+
COLOR_GRAY = "\033[90m"
|
|
573
|
+
COLOR_MAGENTA = "\033[95m"
|
|
574
|
+
COLOR_CYAN = "\033[96m"
|
|
575
|
+
COLOR_BOLD = "\033[1m"
|
|
576
|
+
COLOR_RESET = "\033[0m"
|
|
577
|
+
|
|
578
|
+
BOX_WIDTH = 72
|
|
579
|
+
|
|
580
|
+
TYPE_ICONS = {
|
|
581
|
+
"extract": "📦",
|
|
582
|
+
"rename": "🏷️",
|
|
583
|
+
"restructure": "🏗️",
|
|
584
|
+
"simplify": "✂️",
|
|
585
|
+
"decouple": "🔗",
|
|
586
|
+
"migrate": "🚀",
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
COMPLEXITY_BADGES = {
|
|
590
|
+
"low": COLOR_GREEN + "[LOW]" + COLOR_RESET,
|
|
591
|
+
"medium": COLOR_YELLOW + "[MED]" + COLOR_RESET,
|
|
592
|
+
"high": COLOR_RED + "[HI]" + COLOR_RESET,
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
|
|
596
|
+
def action_status(refactor_list_data, state_dir):
|
|
597
|
+
refactors = refactor_list_data.get("refactors", [])
|
|
598
|
+
project_name = refactor_list_data.get("project_name", "Unknown")
|
|
599
|
+
|
|
600
|
+
counts = {"completed": 0, "in_progress": 0, "failed": 0, "pending": 0, "skipped": 0, "auto_skipped": 0}
|
|
601
|
+
refactor_lines = []
|
|
602
|
+
|
|
603
|
+
for r in refactors:
|
|
604
|
+
if not isinstance(r, dict):
|
|
605
|
+
continue
|
|
606
|
+
rid = r.get("id")
|
|
607
|
+
title = r.get("title", "Untitled")
|
|
608
|
+
rtype = r.get("type", "restructure")
|
|
609
|
+
complexity = r.get("complexity", "medium")
|
|
610
|
+
if not rid:
|
|
611
|
+
continue
|
|
612
|
+
|
|
613
|
+
rstatus = r.get("status", "pending")
|
|
614
|
+
rs = load_refactor_status(state_dir, rid)
|
|
615
|
+
retry_count = rs.get("retry_count", 0)
|
|
616
|
+
max_retries_val = rs.get("max_retries", 3)
|
|
617
|
+
resume_phase = rs.get("resume_from_phase")
|
|
618
|
+
|
|
619
|
+
if rstatus in counts:
|
|
620
|
+
counts[rstatus] += 1
|
|
621
|
+
else:
|
|
622
|
+
counts["pending"] += 1
|
|
623
|
+
|
|
624
|
+
# Status icon
|
|
625
|
+
if rstatus == "completed":
|
|
626
|
+
icon = COLOR_GREEN + "[✓]" + COLOR_RESET
|
|
627
|
+
elif rstatus == "in_progress":
|
|
628
|
+
icon = COLOR_YELLOW + "[→]" + COLOR_RESET
|
|
629
|
+
elif rstatus == "failed":
|
|
630
|
+
icon = COLOR_RED + "[✗]" + COLOR_RESET
|
|
631
|
+
elif rstatus == "skipped":
|
|
632
|
+
icon = COLOR_GRAY + "[—]" + COLOR_RESET
|
|
633
|
+
elif rstatus == "auto_skipped":
|
|
634
|
+
icon = COLOR_GRAY + "[⊘]" + COLOR_RESET
|
|
635
|
+
else:
|
|
636
|
+
icon = COLOR_GRAY + "[ ]" + COLOR_RESET
|
|
637
|
+
|
|
638
|
+
# Type badge
|
|
639
|
+
type_icon = TYPE_ICONS.get(rtype, "🔧")
|
|
640
|
+
type_badge = "[{}]".format(rtype[:6].upper())
|
|
641
|
+
|
|
642
|
+
# Complexity badge
|
|
643
|
+
cmplx_badge = COMPLEXITY_BADGES.get(complexity, "[MED]")
|
|
644
|
+
|
|
645
|
+
# Detail
|
|
646
|
+
detail = ""
|
|
647
|
+
if rstatus == "in_progress":
|
|
648
|
+
parts = []
|
|
649
|
+
if retry_count > 0:
|
|
650
|
+
parts.append("retry {}/{}".format(retry_count, max_retries_val))
|
|
651
|
+
if resume_phase is not None:
|
|
652
|
+
parts.append("CP-RF-{}".format(resume_phase))
|
|
653
|
+
if parts:
|
|
654
|
+
detail = " ({})".format(", ".join(parts))
|
|
655
|
+
elif rstatus == "failed":
|
|
656
|
+
detail = " (failed after {} retries)".format(retry_count)
|
|
657
|
+
|
|
658
|
+
# Colorize title based on status
|
|
659
|
+
if rstatus == "completed":
|
|
660
|
+
colored_title = COLOR_GREEN + title + COLOR_RESET
|
|
661
|
+
elif rstatus == "in_progress":
|
|
662
|
+
colored_title = COLOR_YELLOW + title + COLOR_RESET
|
|
663
|
+
elif rstatus == "failed":
|
|
664
|
+
colored_title = COLOR_RED + title + COLOR_RESET
|
|
665
|
+
else:
|
|
666
|
+
colored_title = COLOR_GRAY + title + COLOR_RESET
|
|
667
|
+
|
|
668
|
+
line_content = "{} {} {} {} {} {} {}{}".format(
|
|
669
|
+
rid, icon, type_badge, cmplx_badge, type_icon, colored_title, "", detail
|
|
670
|
+
)
|
|
671
|
+
|
|
672
|
+
refactor_lines.append(line_content)
|
|
673
|
+
|
|
674
|
+
total = len(refactors)
|
|
675
|
+
completed = counts["completed"]
|
|
676
|
+
percent = round(completed / total * 100, 1) if total > 0 else 0.0
|
|
677
|
+
progress_bar = _build_progress_bar(percent, width=24)
|
|
678
|
+
|
|
679
|
+
summary_line = "Total: {} refactors | Completed: {} | In Progress: {}".format(
|
|
680
|
+
total, completed, counts["in_progress"]
|
|
681
|
+
)
|
|
682
|
+
summary_line2 = "Failed: {} | Pending: {} | Skipped: {} | Auto-skipped: {}".format(
|
|
683
|
+
counts["failed"], counts["pending"], counts["skipped"], counts["auto_skipped"]
|
|
684
|
+
)
|
|
685
|
+
|
|
686
|
+
inner = BOX_WIDTH - 2
|
|
687
|
+
print("╔" + "═" * BOX_WIDTH + "╗")
|
|
688
|
+
print("║" + pad_right(COLOR_BOLD + " Refactor Pipeline Status" + COLOR_RESET, inner) + " ║")
|
|
689
|
+
print("╠" + "═" * BOX_WIDTH + "╣")
|
|
690
|
+
print("║" + pad_right(" Project: {}".format(project_name), inner) + " ║")
|
|
691
|
+
print("║" + pad_right(" {}".format(summary_line), inner) + " ║")
|
|
692
|
+
print("║" + pad_right(" {}".format(summary_line2), inner) + " ║")
|
|
693
|
+
print("╠" + "─" * BOX_WIDTH + "╣")
|
|
694
|
+
print("║" + pad_right(" Progress: {}".format(progress_bar), inner) + " ║")
|
|
695
|
+
print("╠" + "═" * BOX_WIDTH + "╣")
|
|
696
|
+
for line in refactor_lines:
|
|
697
|
+
print("║" + pad_right(" {}".format(line), inner) + " ║")
|
|
698
|
+
print("╚" + "═" * BOX_WIDTH + "╝")
|
|
699
|
+
|
|
700
|
+
|
|
701
|
+
# ---------------------------------------------------------------------------
|
|
702
|
+
# Action: reset
|
|
703
|
+
# ---------------------------------------------------------------------------
|
|
704
|
+
|
|
705
|
+
def action_reset(args, refactor_list_path, state_dir):
|
|
706
|
+
refactor_id = args.refactor_id
|
|
707
|
+
if not refactor_id:
|
|
708
|
+
error_out("--refactor-id is required for 'reset' action")
|
|
709
|
+
return
|
|
710
|
+
|
|
711
|
+
rs = load_refactor_status(state_dir, refactor_id)
|
|
712
|
+
old_status = get_refactor_status_from_list(refactor_list_path, refactor_id)
|
|
713
|
+
old_retry = rs.get("retry_count", 0)
|
|
714
|
+
|
|
715
|
+
rs["retry_count"] = 0
|
|
716
|
+
rs["sessions"] = []
|
|
717
|
+
rs["last_session_id"] = None
|
|
718
|
+
rs["resume_from_phase"] = None
|
|
719
|
+
rs["updated_at"] = now_iso()
|
|
720
|
+
|
|
721
|
+
err = save_refactor_status(state_dir, refactor_id, rs)
|
|
722
|
+
if err:
|
|
723
|
+
error_out("Failed to save refactor status: {}".format(err))
|
|
724
|
+
return
|
|
725
|
+
|
|
726
|
+
err = update_refactor_in_list(refactor_list_path, refactor_id, "pending")
|
|
727
|
+
if err:
|
|
728
|
+
error_out("Failed to update .prizmkit/plans/refactor-list.json: {}".format(err))
|
|
729
|
+
return
|
|
730
|
+
|
|
731
|
+
result = {
|
|
732
|
+
"action": "reset",
|
|
733
|
+
"refactor_id": refactor_id,
|
|
734
|
+
"old_status": old_status,
|
|
735
|
+
"old_retry_count": old_retry,
|
|
736
|
+
"new_status": "pending",
|
|
737
|
+
}
|
|
738
|
+
print(json.dumps(result, indent=2, ensure_ascii=False))
|
|
739
|
+
|
|
740
|
+
|
|
741
|
+
# ---------------------------------------------------------------------------
|
|
742
|
+
# Action: clean
|
|
743
|
+
# ---------------------------------------------------------------------------
|
|
744
|
+
|
|
745
|
+
def action_clean(args, refactor_list_path, state_dir):
|
|
746
|
+
refactor_id = args.refactor_id
|
|
747
|
+
project_root = args.project_root
|
|
748
|
+
|
|
749
|
+
if not refactor_id:
|
|
750
|
+
error_out("--refactor-id is required for 'clean' action")
|
|
751
|
+
return
|
|
752
|
+
if not project_root:
|
|
753
|
+
error_out("--project-root is required for 'clean' action")
|
|
754
|
+
return
|
|
755
|
+
|
|
756
|
+
cleaned = []
|
|
757
|
+
|
|
758
|
+
# 1. Delete session history
|
|
759
|
+
sessions_dir = os.path.join(state_dir, "refactors", refactor_id, "sessions")
|
|
760
|
+
sessions_deleted = 0
|
|
761
|
+
if os.path.isdir(sessions_dir):
|
|
762
|
+
for entry in os.listdir(sessions_dir):
|
|
763
|
+
entry_path = os.path.join(sessions_dir, entry)
|
|
764
|
+
if os.path.isdir(entry_path):
|
|
765
|
+
shutil.rmtree(entry_path)
|
|
766
|
+
sessions_deleted += 1
|
|
767
|
+
cleaned.append("Deleted {} session(s) from {}".format(sessions_deleted, sessions_dir))
|
|
768
|
+
|
|
769
|
+
# 2. Delete refactor artifacts for this refactor
|
|
770
|
+
refactor_artifact_dir = os.path.join(project_root, REFACTOR_ARTIFACTS_REL, refactor_id)
|
|
771
|
+
if os.path.isdir(refactor_artifact_dir):
|
|
772
|
+
shutil.rmtree(refactor_artifact_dir)
|
|
773
|
+
cleaned.append("Deleted {}".format(refactor_artifact_dir))
|
|
774
|
+
|
|
775
|
+
# 3. Delete shared dev-team workspace
|
|
776
|
+
dev_team_dir = os.path.join(project_root, DEV_TEAM_DIR_NAME)
|
|
777
|
+
if os.path.isdir(dev_team_dir):
|
|
778
|
+
shutil.rmtree(dev_team_dir)
|
|
779
|
+
cleaned.append("Deleted {}".format(dev_team_dir))
|
|
780
|
+
|
|
781
|
+
# 4. Reset status
|
|
782
|
+
rs = load_refactor_status(state_dir, refactor_id)
|
|
783
|
+
old_status = get_refactor_status_from_list(refactor_list_path, refactor_id)
|
|
784
|
+
old_retry = rs.get("retry_count", 0)
|
|
785
|
+
|
|
786
|
+
rs["retry_count"] = 0
|
|
787
|
+
rs["sessions"] = []
|
|
788
|
+
rs["last_session_id"] = None
|
|
789
|
+
rs["resume_from_phase"] = None
|
|
790
|
+
rs["updated_at"] = now_iso()
|
|
791
|
+
|
|
792
|
+
err = save_refactor_status(state_dir, refactor_id, rs)
|
|
793
|
+
if err:
|
|
794
|
+
error_out("Failed to save refactor status: {}".format(err))
|
|
795
|
+
return
|
|
796
|
+
|
|
797
|
+
err = update_refactor_in_list(refactor_list_path, refactor_id, "pending")
|
|
798
|
+
if err:
|
|
799
|
+
error_out("Failed to update .prizmkit/plans/refactor-list.json: {}".format(err))
|
|
800
|
+
return
|
|
801
|
+
|
|
802
|
+
result = {
|
|
803
|
+
"action": "clean",
|
|
804
|
+
"refactor_id": refactor_id,
|
|
805
|
+
"old_status": old_status,
|
|
806
|
+
"old_retry_count": old_retry,
|
|
807
|
+
"new_status": "pending",
|
|
808
|
+
"sessions_deleted": sessions_deleted,
|
|
809
|
+
"cleaned": cleaned,
|
|
810
|
+
}
|
|
811
|
+
print(json.dumps(result, indent=2, ensure_ascii=False))
|
|
812
|
+
|
|
813
|
+
|
|
814
|
+
# ---------------------------------------------------------------------------
|
|
815
|
+
# Action: pause
|
|
816
|
+
# ---------------------------------------------------------------------------
|
|
817
|
+
|
|
818
|
+
def action_pause(state_dir):
|
|
819
|
+
pipeline_path = os.path.join(state_dir, "pipeline.json")
|
|
820
|
+
data, err = load_json_file(pipeline_path)
|
|
821
|
+
if err:
|
|
822
|
+
data = {"status": "paused", "paused_at": now_iso()}
|
|
823
|
+
else:
|
|
824
|
+
data["status"] = "paused"
|
|
825
|
+
data["paused_at"] = now_iso()
|
|
826
|
+
|
|
827
|
+
err = write_json_file(pipeline_path, data)
|
|
828
|
+
if err:
|
|
829
|
+
error_out("Failed to write pipeline.json: {}".format(err))
|
|
830
|
+
return
|
|
831
|
+
|
|
832
|
+
result = {
|
|
833
|
+
"action": "pause",
|
|
834
|
+
"status": "paused",
|
|
835
|
+
"paused_at": data["paused_at"],
|
|
836
|
+
}
|
|
837
|
+
print(json.dumps(result, indent=2, ensure_ascii=False))
|
|
838
|
+
|
|
839
|
+
|
|
840
|
+
# ---------------------------------------------------------------------------
|
|
841
|
+
# Action: start
|
|
842
|
+
# ---------------------------------------------------------------------------
|
|
843
|
+
|
|
844
|
+
def action_start(args, refactor_list_path, state_dir):
|
|
845
|
+
"""Mark a refactor as in_progress when a session starts.
|
|
846
|
+
|
|
847
|
+
This keeps refactor-list.json/state status in sync during execution,
|
|
848
|
+
instead of only updating after session end.
|
|
849
|
+
"""
|
|
850
|
+
refactor_id = args.refactor_id
|
|
851
|
+
if not refactor_id:
|
|
852
|
+
error_out("--refactor-id is required for 'start' action")
|
|
853
|
+
return
|
|
854
|
+
|
|
855
|
+
rs = load_refactor_status(state_dir, refactor_id)
|
|
856
|
+
old_status = get_refactor_status_from_list(refactor_list_path, refactor_id)
|
|
857
|
+
|
|
858
|
+
rs["updated_at"] = now_iso()
|
|
859
|
+
|
|
860
|
+
err = save_refactor_status(state_dir, refactor_id, rs)
|
|
861
|
+
if err:
|
|
862
|
+
error_out("Failed to save refactor status: {}".format(err))
|
|
863
|
+
return
|
|
864
|
+
|
|
865
|
+
err = update_refactor_in_list(refactor_list_path, refactor_id, "in_progress")
|
|
866
|
+
if err:
|
|
867
|
+
error_out("Failed to update .prizmkit/plans/refactor-list.json: {}".format(err))
|
|
868
|
+
return
|
|
869
|
+
|
|
870
|
+
result = {
|
|
871
|
+
"action": "start",
|
|
872
|
+
"refactor_id": refactor_id,
|
|
873
|
+
"old_status": old_status,
|
|
874
|
+
"new_status": "in_progress",
|
|
875
|
+
"updated_at": rs["updated_at"],
|
|
876
|
+
}
|
|
877
|
+
print(json.dumps(result, indent=2, ensure_ascii=False))
|
|
878
|
+
|
|
879
|
+
|
|
880
|
+
# ---------------------------------------------------------------------------
|
|
881
|
+
# Action: unskip
|
|
882
|
+
# ---------------------------------------------------------------------------
|
|
883
|
+
|
|
884
|
+
def action_unskip(args, refactor_list_path, state_dir):
|
|
885
|
+
"""Recover skipped/auto_skipped/failed refactors by resetting them and their failed upstream.
|
|
886
|
+
|
|
887
|
+
Two modes:
|
|
888
|
+
- --refactor-id R-001: Reset the specified failed/skipped/auto_skipped refactor + all
|
|
889
|
+
downstream auto_skipped refactors whose dependency chain includes it.
|
|
890
|
+
If the target is auto_skipped, also walk upstream to find and reset the
|
|
891
|
+
failed/skipped ancestor that caused the cascade.
|
|
892
|
+
- No --refactor-id: Reset ALL failed, skipped, and auto_skipped refactors to pending.
|
|
893
|
+
"""
|
|
894
|
+
refactor_id = args.refactor_id
|
|
895
|
+
|
|
896
|
+
data, err = load_json_file(refactor_list_path)
|
|
897
|
+
if err:
|
|
898
|
+
error_out("Cannot load refactor list: {}".format(err))
|
|
899
|
+
return
|
|
900
|
+
refactors = data.get("refactors", [])
|
|
901
|
+
|
|
902
|
+
to_reset = set()
|
|
903
|
+
|
|
904
|
+
if refactor_id:
|
|
905
|
+
# Find the target refactor
|
|
906
|
+
target = None
|
|
907
|
+
for r in refactors:
|
|
908
|
+
if isinstance(r, dict) and r.get("id") == refactor_id:
|
|
909
|
+
target = r
|
|
910
|
+
break
|
|
911
|
+
if not target:
|
|
912
|
+
error_out("Refactor '{}' not found in .prizmkit/plans/refactor-list.json".format(refactor_id))
|
|
913
|
+
return
|
|
914
|
+
if target.get("status") not in ("failed", "skipped", "auto_skipped"):
|
|
915
|
+
error_out(
|
|
916
|
+
"Refactor '{}' has status '{}', expected 'failed', 'skipped', or 'auto_skipped'".format(
|
|
917
|
+
refactor_id, target.get("status", "unknown")
|
|
918
|
+
)
|
|
919
|
+
)
|
|
920
|
+
return
|
|
921
|
+
|
|
922
|
+
# If target is failed or skipped, reset it and find all auto_skipped descendants
|
|
923
|
+
if target.get("status") in ("failed", "skipped"):
|
|
924
|
+
to_reset.add(refactor_id)
|
|
925
|
+
# Find all auto_skipped refactors that depend (transitively) on this one
|
|
926
|
+
changed = True
|
|
927
|
+
while changed:
|
|
928
|
+
changed = False
|
|
929
|
+
for r in refactors:
|
|
930
|
+
if not isinstance(r, dict):
|
|
931
|
+
continue
|
|
932
|
+
rid = r.get("id")
|
|
933
|
+
if not rid or rid in to_reset:
|
|
934
|
+
continue
|
|
935
|
+
if r.get("status") != "auto_skipped":
|
|
936
|
+
continue
|
|
937
|
+
deps = r.get("dependencies", [])
|
|
938
|
+
if any(d in to_reset for d in deps):
|
|
939
|
+
to_reset.add(rid)
|
|
940
|
+
changed = True
|
|
941
|
+
|
|
942
|
+
# If target is auto_skipped, reset it and its failed upstream + siblings
|
|
943
|
+
elif target.get("status") == "auto_skipped":
|
|
944
|
+
to_reset.add(refactor_id)
|
|
945
|
+
# Transitively walk upstream to find ALL failed/auto_skipped ancestors
|
|
946
|
+
# (e.g., R-001 failed → R-002 auto_skipped → R-003 auto_skipped;
|
|
947
|
+
# unskip R-003 must also find and reset R-001)
|
|
948
|
+
upstream_changed = True
|
|
949
|
+
while upstream_changed:
|
|
950
|
+
upstream_changed = False
|
|
951
|
+
for r in refactors:
|
|
952
|
+
if not isinstance(r, dict):
|
|
953
|
+
continue
|
|
954
|
+
rid = r.get("id")
|
|
955
|
+
if not rid or rid not in to_reset:
|
|
956
|
+
continue
|
|
957
|
+
for dep_id in r.get("dependencies", []):
|
|
958
|
+
if dep_id in to_reset:
|
|
959
|
+
continue
|
|
960
|
+
for dep_r in refactors:
|
|
961
|
+
if isinstance(dep_r, dict) and dep_r.get("id") == dep_id:
|
|
962
|
+
if dep_r.get("status") in ("failed", "skipped", "auto_skipped"):
|
|
963
|
+
to_reset.add(dep_id)
|
|
964
|
+
upstream_changed = True
|
|
965
|
+
# Also reset downstream auto_skipped refactors blocked by the same upstreams
|
|
966
|
+
changed = True
|
|
967
|
+
while changed:
|
|
968
|
+
changed = False
|
|
969
|
+
for r in refactors:
|
|
970
|
+
if not isinstance(r, dict):
|
|
971
|
+
continue
|
|
972
|
+
rid = r.get("id")
|
|
973
|
+
if not rid or rid in to_reset:
|
|
974
|
+
continue
|
|
975
|
+
if r.get("status") != "auto_skipped":
|
|
976
|
+
continue
|
|
977
|
+
rdeps = r.get("dependencies", [])
|
|
978
|
+
if any(d in to_reset for d in rdeps):
|
|
979
|
+
to_reset.add(rid)
|
|
980
|
+
changed = True
|
|
981
|
+
else:
|
|
982
|
+
# No refactor-id: reset ALL failed + skipped + auto_skipped
|
|
983
|
+
for r in refactors:
|
|
984
|
+
if isinstance(r, dict) and r.get("id"):
|
|
985
|
+
if r.get("status") in ("failed", "skipped", "auto_skipped"):
|
|
986
|
+
to_reset.add(r["id"])
|
|
987
|
+
|
|
988
|
+
if not to_reset:
|
|
989
|
+
error_out("No refactors to unskip")
|
|
990
|
+
return
|
|
991
|
+
|
|
992
|
+
# Reset all collected refactors in refactor-list.json
|
|
993
|
+
reset_details = []
|
|
994
|
+
for r in refactors:
|
|
995
|
+
if isinstance(r, dict) and r.get("id") in to_reset:
|
|
996
|
+
old_status = r.get("status", "unknown")
|
|
997
|
+
r["status"] = "pending"
|
|
998
|
+
reset_details.append({
|
|
999
|
+
"refactor_id": r["id"],
|
|
1000
|
+
"title": r.get("title", ""),
|
|
1001
|
+
"old_status": old_status,
|
|
1002
|
+
})
|
|
1003
|
+
|
|
1004
|
+
err = write_json_file(refactor_list_path, data)
|
|
1005
|
+
if err:
|
|
1006
|
+
error_out("Failed to write .prizmkit/plans/refactor-list.json: {}".format(err))
|
|
1007
|
+
return
|
|
1008
|
+
|
|
1009
|
+
# Reset runtime fields in status.json for each refactor
|
|
1010
|
+
for rid in to_reset:
|
|
1011
|
+
rs = load_refactor_status(state_dir, rid)
|
|
1012
|
+
rs["retry_count"] = 0
|
|
1013
|
+
rs["sessions"] = []
|
|
1014
|
+
rs["last_session_id"] = None
|
|
1015
|
+
rs["resume_from_phase"] = None
|
|
1016
|
+
rs["updated_at"] = now_iso()
|
|
1017
|
+
save_refactor_status(state_dir, rid, rs)
|
|
1018
|
+
|
|
1019
|
+
result = {
|
|
1020
|
+
"action": "unskip",
|
|
1021
|
+
"reset_count": len(to_reset),
|
|
1022
|
+
"refactors": reset_details,
|
|
1023
|
+
}
|
|
1024
|
+
print(json.dumps(result, indent=2, ensure_ascii=False))
|
|
1025
|
+
|
|
1026
|
+
|
|
1027
|
+
# ---------------------------------------------------------------------------
|
|
1028
|
+
# Main
|
|
1029
|
+
# ---------------------------------------------------------------------------
|
|
1030
|
+
|
|
1031
|
+
def main():
|
|
1032
|
+
args = parse_args()
|
|
1033
|
+
|
|
1034
|
+
if args.action == "update":
|
|
1035
|
+
if not args.refactor_id:
|
|
1036
|
+
error_out("--refactor-id is required for 'update' action")
|
|
1037
|
+
if not args.session_status:
|
|
1038
|
+
error_out("--session-status is required for 'update' action")
|
|
1039
|
+
if args.action in ("start", "reset", "clean", "complete"):
|
|
1040
|
+
if not args.refactor_id:
|
|
1041
|
+
error_out("--refactor-id is required for '{}' action".format(args.action))
|
|
1042
|
+
if args.action == "clean":
|
|
1043
|
+
if not args.project_root:
|
|
1044
|
+
error_out("--project-root is required for 'clean' action")
|
|
1045
|
+
|
|
1046
|
+
refactor_list_data, err = load_json_file(args.refactor_list)
|
|
1047
|
+
if err:
|
|
1048
|
+
error_out("Cannot load refactor list: {}".format(err))
|
|
1049
|
+
|
|
1050
|
+
if args.action == "get_next":
|
|
1051
|
+
action_get_next(refactor_list_data, args.state_dir)
|
|
1052
|
+
elif args.action == "update":
|
|
1053
|
+
action_update(args, args.refactor_list, args.state_dir)
|
|
1054
|
+
elif args.action == "status":
|
|
1055
|
+
action_status(refactor_list_data, args.state_dir)
|
|
1056
|
+
elif args.action == "reset":
|
|
1057
|
+
action_reset(args, args.refactor_list, args.state_dir)
|
|
1058
|
+
elif args.action == "clean":
|
|
1059
|
+
action_clean(args, args.refactor_list, args.state_dir)
|
|
1060
|
+
elif args.action == "start":
|
|
1061
|
+
action_start(args, args.refactor_list, args.state_dir)
|
|
1062
|
+
elif args.action == "pause":
|
|
1063
|
+
action_pause(args.state_dir)
|
|
1064
|
+
elif args.action == "unskip":
|
|
1065
|
+
action_unskip(args, args.refactor_list, args.state_dir)
|
|
1066
|
+
elif args.action == "complete":
|
|
1067
|
+
# Shortcut: 'complete' is equivalent to 'update --session-status success'
|
|
1068
|
+
args.session_status = "success"
|
|
1069
|
+
action_update(args, args.refactor_list, args.state_dir)
|
|
1070
|
+
|
|
1071
|
+
|
|
1072
|
+
if __name__ == "__main__":
|
|
1073
|
+
main()
|