prizmkit 1.1.1 → 1.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bundled/VERSION.json +3 -3
- package/bundled/adapters/claude/agent-adapter.js +18 -0
- package/bundled/adapters/claude/command-adapter.js +1 -27
- package/bundled/agents/prizm-dev-team-critic.md +2 -0
- package/bundled/agents/prizm-dev-team-dev.md +2 -0
- package/bundled/agents/prizm-dev-team-reviewer.md +2 -0
- package/bundled/dev-pipeline/README.md +63 -63
- package/bundled/dev-pipeline/assets/feature-list-example.json +1 -1
- package/bundled/dev-pipeline/assets/prizm-dev-team-integration.md +1 -1
- package/bundled/dev-pipeline/{launch-daemon.sh → launch-feature-daemon.sh} +33 -33
- package/bundled/dev-pipeline/launch-refactor-daemon.sh +454 -0
- package/bundled/dev-pipeline/lib/branch.sh +1 -1
- package/bundled/dev-pipeline/reset-feature.sh +3 -3
- package/bundled/dev-pipeline/reset-refactor.sh +312 -0
- package/bundled/dev-pipeline/{retry-bug.sh → retry-bugfix.sh} +47 -59
- package/bundled/dev-pipeline/retry-feature.sh +41 -54
- package/bundled/dev-pipeline/retry-refactor.sh +358 -0
- package/bundled/dev-pipeline/run-bugfix.sh +41 -0
- package/bundled/dev-pipeline/{run.sh → run-feature.sh} +64 -31
- package/bundled/dev-pipeline/run-refactor.sh +787 -0
- package/bundled/dev-pipeline/scripts/generate-bootstrap-prompt.py +398 -10
- package/bundled/dev-pipeline/scripts/generate-bugfix-prompt.py +124 -0
- package/bundled/dev-pipeline/scripts/generate-refactor-prompt.py +419 -0
- package/bundled/dev-pipeline/scripts/init-refactor-pipeline.py +393 -0
- package/bundled/dev-pipeline/scripts/update-refactor-status.py +726 -0
- package/bundled/dev-pipeline/templates/agent-prompts/critic-code-challenge.md +13 -0
- package/bundled/dev-pipeline/templates/agent-prompts/critic-plan-challenge.md +7 -0
- package/bundled/dev-pipeline/templates/agent-prompts/dev-fix.md +7 -0
- package/bundled/dev-pipeline/templates/agent-prompts/dev-implement.md +27 -0
- package/bundled/dev-pipeline/templates/agent-prompts/dev-resume.md +5 -0
- package/bundled/dev-pipeline/templates/agent-prompts/reviewer-analyze.md +5 -0
- package/bundled/dev-pipeline/templates/agent-prompts/reviewer-review.md +12 -0
- package/bundled/dev-pipeline/templates/bootstrap-tier1.md +33 -2
- package/bundled/dev-pipeline/templates/bootstrap-tier2.md +13 -9
- package/bundled/dev-pipeline/templates/bootstrap-tier3.md +16 -12
- package/bundled/dev-pipeline/templates/bugfix-bootstrap-prompt.md +22 -4
- package/bundled/dev-pipeline/templates/feature-list-schema.json +1 -1
- package/bundled/dev-pipeline/templates/refactor-list-schema.json +159 -0
- package/bundled/dev-pipeline/templates/sections/ac-verification-checklist.md +13 -0
- package/bundled/dev-pipeline/templates/sections/checkpoint-system.md +36 -0
- package/bundled/dev-pipeline/templates/sections/failure-log-check.md +2 -1
- package/bundled/dev-pipeline/templates/sections/feature-context.md +1 -1
- package/bundled/dev-pipeline/templates/sections/phase-analyze-agent.md +11 -7
- package/bundled/dev-pipeline/templates/sections/phase-analyze-full.md +11 -7
- package/bundled/dev-pipeline/templates/sections/phase-browser-verification.md +5 -1
- package/bundled/dev-pipeline/templates/sections/phase-commit-full.md +3 -0
- package/bundled/dev-pipeline/templates/sections/phase-commit.md +3 -0
- package/bundled/dev-pipeline/templates/sections/phase-context-snapshot-agent-suffix.md +3 -0
- package/bundled/dev-pipeline/templates/sections/phase-context-snapshot-lite-suffix.md +3 -0
- package/bundled/dev-pipeline/templates/sections/phase-critic-code.md +11 -10
- package/bundled/dev-pipeline/templates/sections/phase-critic-plan-full.md +12 -10
- package/bundled/dev-pipeline/templates/sections/phase-critic-plan.md +11 -9
- package/bundled/dev-pipeline/templates/sections/phase-deploy-verification.md +3 -0
- package/bundled/dev-pipeline/templates/sections/phase-implement-agent.md +10 -10
- package/bundled/dev-pipeline/templates/sections/phase-implement-full.md +12 -16
- package/bundled/dev-pipeline/templates/sections/phase-implement-lite.md +3 -0
- package/bundled/dev-pipeline/templates/sections/phase-plan-agent.md +3 -0
- package/bundled/dev-pipeline/templates/sections/phase-plan-lite.md +3 -0
- package/bundled/dev-pipeline/templates/sections/phase-review-agent.md +11 -13
- package/bundled/dev-pipeline/templates/sections/phase-review-full.md +12 -20
- package/bundled/dev-pipeline/templates/sections/phase-specify-plan-full.md +3 -0
- package/bundled/dev-pipeline/templates/sections/phase0-init.md +3 -0
- package/bundled/dev-pipeline/templates/sections/phase0-test-baseline.md +3 -0
- package/bundled/dev-pipeline/templates/sections/resume-header.md +4 -1
- package/bundled/dev-pipeline/templates/sections/test-failure-recovery.md +75 -0
- package/bundled/rules/prizm/prizm-commit-workflow.md +1 -0
- package/bundled/rules/prizm/prizm-documentation.md +15 -15
- package/bundled/rules/prizm/prizm-progressive-loading.md +2 -1
- package/bundled/skills/_metadata.json +33 -6
- package/bundled/skills/app-planner/SKILL.md +105 -320
- package/bundled/skills/app-planner/assets/app-design-guide.md +101 -0
- package/bundled/skills/app-planner/references/frontend-design-guide.md +1 -1
- package/bundled/skills/app-planner/references/project-brief-guide.md +49 -80
- package/bundled/skills/bug-fix-workflow/SKILL.md +2 -2
- package/bundled/skills/bug-planner/SKILL.md +68 -5
- package/bundled/skills/bug-planner/scripts/validate-bug-list.py +3 -2
- package/bundled/skills/bugfix-pipeline-launcher/SKILL.md +19 -5
- package/bundled/skills/{dev-pipeline-launcher → feature-pipeline-launcher}/SKILL.md +32 -32
- package/bundled/skills/feature-planner/SKILL.md +337 -0
- package/bundled/skills/{app-planner → feature-planner}/assets/evaluation-guide.md +4 -4
- package/bundled/skills/{app-planner → feature-planner}/assets/planning-guide.md +3 -171
- package/bundled/skills/{app-planner → feature-planner}/references/browser-interaction.md +6 -5
- package/bundled/skills/feature-planner/references/decomposition-patterns.md +75 -0
- package/bundled/skills/{app-planner → feature-planner}/references/error-recovery.md +8 -8
- package/bundled/skills/{app-planner → feature-planner}/references/incremental-feature-planning.md +1 -1
- package/bundled/skills/{app-planner/references/new-app-planning.md → feature-planner/references/new-project-planning.md} +1 -1
- package/bundled/skills/{app-planner → feature-planner}/scripts/validate-and-generate.py +4 -4
- package/bundled/skills/feature-workflow/SKILL.md +23 -23
- package/bundled/skills/prizm-kit/SKILL.md +1 -3
- package/bundled/skills/prizm-kit/assets/project-memory-template.md +4 -2
- package/bundled/skills/prizmkit-analyze/SKILL.md +2 -5
- package/bundled/skills/prizmkit-code-review/SKILL.md +2 -2
- package/bundled/skills/prizmkit-committer/SKILL.md +32 -8
- package/bundled/skills/prizmkit-deploy/SKILL.md +1 -5
- package/bundled/skills/prizmkit-implement/SKILL.md +5 -51
- package/bundled/skills/prizmkit-init/SKILL.md +7 -78
- package/bundled/skills/prizmkit-plan/SKILL.md +1 -12
- package/bundled/skills/prizmkit-prizm-docs/SKILL.md +13 -28
- package/bundled/skills/prizmkit-prizm-docs/assets/PRIZM-SPEC.md +52 -1
- package/bundled/skills/prizmkit-retrospective/SKILL.md +12 -117
- package/bundled/skills/recovery-workflow/SKILL.md +168 -316
- package/bundled/skills/recovery-workflow/evals/evals.json +29 -13
- package/bundled/skills/recovery-workflow/scripts/detect-recovery-state.py +232 -274
- package/bundled/skills/refactor-pipeline-launcher/SKILL.md +352 -0
- package/bundled/skills/refactor-planner/SKILL.md +436 -0
- package/bundled/skills/refactor-planner/assets/planning-guide.md +292 -0
- package/bundled/skills/refactor-planner/references/behavior-preservation.md +301 -0
- package/bundled/skills/refactor-planner/references/refactor-scoping-guide.md +221 -0
- package/bundled/skills/refactor-planner/scripts/validate-and-generate-refactor.py +786 -0
- package/bundled/skills/refactor-workflow/SKILL.md +299 -319
- package/bundled/team/prizm-dev-team.json +1 -1
- package/package.json +1 -1
- package/src/clean.js +3 -3
- package/src/scaffold.js +6 -6
- package/bundled/skills/prizmkit-plan/assets/spec-template.md +0 -56
- package/bundled/skills/prizmkit-plan/references/clarify-guide.md +0 -67
- package/src/config.js +0 -504
- package/src/prompts.js +0 -210
- /package/bundled/skills/{dev-pipeline-launcher → feature-pipeline-launcher}/scripts/preflight-check.py +0 -0
|
@@ -0,0 +1,393 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Initialize the refactor pipeline state directory from a refactor-list.json file.
|
|
3
|
+
|
|
4
|
+
Validates the refactor list schema, sorts by priority/complexity, checks for
|
|
5
|
+
circular dependencies, and creates the state directory structure with pipeline
|
|
6
|
+
and per-refactor status files.
|
|
7
|
+
|
|
8
|
+
Usage:
|
|
9
|
+
python3 init-refactor-pipeline.py --refactor-list <path> --state-dir <path>
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
import argparse
|
|
13
|
+
import json
|
|
14
|
+
import os
|
|
15
|
+
import re
|
|
16
|
+
import sys
|
|
17
|
+
from datetime import datetime, timezone
|
|
18
|
+
|
|
19
|
+
from utils import load_json_file
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
EXPECTED_SCHEMA = "dev-pipeline-refactor-list-v1"
|
|
23
|
+
REFACTOR_ID_PATTERN = re.compile(r"^R-\d{3}$")
|
|
24
|
+
|
|
25
|
+
REQUIRED_REFACTOR_FIELDS = [
|
|
26
|
+
"id",
|
|
27
|
+
"title",
|
|
28
|
+
"description",
|
|
29
|
+
"scope",
|
|
30
|
+
"type",
|
|
31
|
+
"priority",
|
|
32
|
+
"complexity",
|
|
33
|
+
"behavior_preservation",
|
|
34
|
+
"acceptance_criteria",
|
|
35
|
+
"dependencies",
|
|
36
|
+
"status",
|
|
37
|
+
]
|
|
38
|
+
|
|
39
|
+
VALID_TYPES = ["extract", "rename", "restructure", "simplify", "decouple", "migrate"]
|
|
40
|
+
VALID_PRIORITIES = ["critical", "high", "medium", "low"]
|
|
41
|
+
VALID_COMPLEXITIES = ["low", "medium", "high"]
|
|
42
|
+
VALID_STATUSES = [
|
|
43
|
+
"pending", "in_progress", "completed", "failed", "skipped",
|
|
44
|
+
]
|
|
45
|
+
VALID_BEHAVIOR_STRATEGIES = ["test-gate", "snapshot", "manual"]
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def parse_args():
|
|
49
|
+
parser = argparse.ArgumentParser(
|
|
50
|
+
description="Initialize refactor pipeline state from a refactor-list.json file."
|
|
51
|
+
)
|
|
52
|
+
parser.add_argument(
|
|
53
|
+
"--refactor-list",
|
|
54
|
+
required=True,
|
|
55
|
+
help="Path to the refactor-list.json file",
|
|
56
|
+
)
|
|
57
|
+
parser.add_argument(
|
|
58
|
+
"--state-dir",
|
|
59
|
+
required=True,
|
|
60
|
+
help="Path to the state directory to create/initialize",
|
|
61
|
+
)
|
|
62
|
+
return parser.parse_args()
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def load_refactor_list(path):
|
|
66
|
+
"""Load and return the parsed JSON from the refactor list file."""
|
|
67
|
+
data, err = load_json_file(path)
|
|
68
|
+
if err:
|
|
69
|
+
return None, [err]
|
|
70
|
+
return data, []
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def validate_schema(data):
|
|
74
|
+
"""Validate the top-level schema and structure of the refactor list."""
|
|
75
|
+
errors = []
|
|
76
|
+
|
|
77
|
+
# Check $schema
|
|
78
|
+
schema = data.get("$schema")
|
|
79
|
+
if schema != EXPECTED_SCHEMA:
|
|
80
|
+
errors.append(
|
|
81
|
+
"Invalid $schema: expected '{}', got '{}'".format(EXPECTED_SCHEMA, schema)
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
# Check project_name
|
|
85
|
+
if "project_name" not in data:
|
|
86
|
+
errors.append("Missing required field: project_name")
|
|
87
|
+
elif not isinstance(data["project_name"], str) or not data["project_name"].strip():
|
|
88
|
+
errors.append("project_name must be a non-empty string")
|
|
89
|
+
|
|
90
|
+
# Check refactors array
|
|
91
|
+
if "refactors" not in data:
|
|
92
|
+
errors.append("Missing required field: refactors")
|
|
93
|
+
elif not isinstance(data["refactors"], list):
|
|
94
|
+
errors.append("refactors must be an array")
|
|
95
|
+
elif len(data["refactors"]) == 0:
|
|
96
|
+
errors.append("refactors array must contain at least one refactor")
|
|
97
|
+
|
|
98
|
+
return errors
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def validate_refactors(refactors):
|
|
102
|
+
"""Validate each refactor object in the list."""
|
|
103
|
+
errors = []
|
|
104
|
+
seen_ids = set()
|
|
105
|
+
|
|
106
|
+
for i, refactor in enumerate(refactors):
|
|
107
|
+
if not isinstance(refactor, dict):
|
|
108
|
+
errors.append("Refactor at index {} is not an object".format(i))
|
|
109
|
+
continue
|
|
110
|
+
|
|
111
|
+
# Check required fields
|
|
112
|
+
for field in REQUIRED_REFACTOR_FIELDS:
|
|
113
|
+
if field not in refactor:
|
|
114
|
+
errors.append(
|
|
115
|
+
"Refactor at index {} missing required field: {}".format(i, field)
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
# Validate refactor ID format
|
|
119
|
+
rid = refactor.get("id")
|
|
120
|
+
if rid is not None:
|
|
121
|
+
if not isinstance(rid, str) or not REFACTOR_ID_PATTERN.match(rid):
|
|
122
|
+
errors.append(
|
|
123
|
+
"Refactor at index {} has invalid id '{}' "
|
|
124
|
+
"(must match R-NNN pattern)".format(i, rid)
|
|
125
|
+
)
|
|
126
|
+
elif rid in seen_ids:
|
|
127
|
+
errors.append("Duplicate refactor id: {}".format(rid))
|
|
128
|
+
else:
|
|
129
|
+
seen_ids.add(rid)
|
|
130
|
+
|
|
131
|
+
# Validate type
|
|
132
|
+
rtype = refactor.get("type")
|
|
133
|
+
if rtype is not None and rtype not in VALID_TYPES:
|
|
134
|
+
errors.append(
|
|
135
|
+
"Refactor '{}' has invalid type '{}' "
|
|
136
|
+
"(must be one of {})".format(
|
|
137
|
+
rid or "index {}".format(i), rtype, VALID_TYPES
|
|
138
|
+
)
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
# Validate priority
|
|
142
|
+
priority = refactor.get("priority")
|
|
143
|
+
if priority is not None and priority not in VALID_PRIORITIES:
|
|
144
|
+
errors.append(
|
|
145
|
+
"Refactor '{}' has invalid priority '{}' "
|
|
146
|
+
"(must be one of {})".format(
|
|
147
|
+
rid or "index {}".format(i), priority, VALID_PRIORITIES
|
|
148
|
+
)
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
# Validate complexity
|
|
152
|
+
complexity = refactor.get("complexity")
|
|
153
|
+
if complexity is not None and complexity not in VALID_COMPLEXITIES:
|
|
154
|
+
errors.append(
|
|
155
|
+
"Refactor '{}' has invalid complexity '{}' "
|
|
156
|
+
"(must be one of {})".format(
|
|
157
|
+
rid or "index {}".format(i), complexity, VALID_COMPLEXITIES
|
|
158
|
+
)
|
|
159
|
+
)
|
|
160
|
+
|
|
161
|
+
# Validate status
|
|
162
|
+
status = refactor.get("status")
|
|
163
|
+
if status is not None and status not in VALID_STATUSES:
|
|
164
|
+
errors.append(
|
|
165
|
+
"Refactor '{}' has invalid status '{}' "
|
|
166
|
+
"(must be one of {})".format(
|
|
167
|
+
rid or "index {}".format(i), status, VALID_STATUSES
|
|
168
|
+
)
|
|
169
|
+
)
|
|
170
|
+
|
|
171
|
+
# Validate scope is an object
|
|
172
|
+
scope = refactor.get("scope")
|
|
173
|
+
if scope is not None:
|
|
174
|
+
if not isinstance(scope, dict):
|
|
175
|
+
errors.append(
|
|
176
|
+
"Refactor '{}' scope must be an object".format(
|
|
177
|
+
rid or "index {}".format(i)
|
|
178
|
+
)
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
# Validate behavior_preservation
|
|
182
|
+
bp = refactor.get("behavior_preservation")
|
|
183
|
+
if bp is not None:
|
|
184
|
+
if not isinstance(bp, dict):
|
|
185
|
+
errors.append(
|
|
186
|
+
"Refactor '{}' behavior_preservation must be an object".format(
|
|
187
|
+
rid or "index {}".format(i)
|
|
188
|
+
)
|
|
189
|
+
)
|
|
190
|
+
else:
|
|
191
|
+
strategy = bp.get("strategy")
|
|
192
|
+
if strategy is not None and strategy not in VALID_BEHAVIOR_STRATEGIES:
|
|
193
|
+
errors.append(
|
|
194
|
+
"Refactor '{}' behavior_preservation.strategy '{}' "
|
|
195
|
+
"must be one of {}".format(
|
|
196
|
+
rid or "index {}".format(i), strategy, VALID_BEHAVIOR_STRATEGIES
|
|
197
|
+
)
|
|
198
|
+
)
|
|
199
|
+
|
|
200
|
+
# Validate acceptance_criteria is a list
|
|
201
|
+
ac = refactor.get("acceptance_criteria")
|
|
202
|
+
if ac is not None and not isinstance(ac, list):
|
|
203
|
+
errors.append(
|
|
204
|
+
"Refactor '{}' acceptance_criteria must be an array".format(
|
|
205
|
+
rid or "index {}".format(i)
|
|
206
|
+
)
|
|
207
|
+
)
|
|
208
|
+
|
|
209
|
+
# Validate dependencies is a list
|
|
210
|
+
deps = refactor.get("dependencies")
|
|
211
|
+
if deps is not None and not isinstance(deps, list):
|
|
212
|
+
errors.append(
|
|
213
|
+
"Refactor '{}' dependencies must be an array".format(
|
|
214
|
+
rid or "index {}".format(i)
|
|
215
|
+
)
|
|
216
|
+
)
|
|
217
|
+
|
|
218
|
+
return errors, seen_ids
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
def check_circular_dependencies(refactors):
|
|
222
|
+
"""Check for circular dependencies among refactors. Returns list of error strings."""
|
|
223
|
+
errors = []
|
|
224
|
+
|
|
225
|
+
# Build adjacency map: id -> list of dependency ids
|
|
226
|
+
dep_map = {}
|
|
227
|
+
valid_ids = set()
|
|
228
|
+
for refactor in refactors:
|
|
229
|
+
if not isinstance(refactor, dict):
|
|
230
|
+
continue
|
|
231
|
+
rid = refactor.get("id")
|
|
232
|
+
if not rid:
|
|
233
|
+
continue
|
|
234
|
+
valid_ids.add(rid)
|
|
235
|
+
deps = refactor.get("dependencies", [])
|
|
236
|
+
if isinstance(deps, list):
|
|
237
|
+
dep_map[rid] = [d for d in deps if isinstance(d, str)]
|
|
238
|
+
else:
|
|
239
|
+
dep_map[rid] = []
|
|
240
|
+
|
|
241
|
+
# Check that all dependency references point to valid IDs
|
|
242
|
+
for rid, deps in dep_map.items():
|
|
243
|
+
for dep_id in deps:
|
|
244
|
+
if dep_id not in valid_ids:
|
|
245
|
+
errors.append(
|
|
246
|
+
"Refactor '{}' depends on '{}' which does not exist".format(rid, dep_id)
|
|
247
|
+
)
|
|
248
|
+
|
|
249
|
+
# DFS cycle detection
|
|
250
|
+
WHITE, GRAY, BLACK = 0, 1, 2
|
|
251
|
+
color = {rid: WHITE for rid in valid_ids}
|
|
252
|
+
|
|
253
|
+
def dfs(node, path):
|
|
254
|
+
color[node] = GRAY
|
|
255
|
+
path.append(node)
|
|
256
|
+
for neighbor in dep_map.get(node, []):
|
|
257
|
+
if neighbor not in color:
|
|
258
|
+
continue
|
|
259
|
+
if color[neighbor] == GRAY:
|
|
260
|
+
# Found a cycle
|
|
261
|
+
cycle_start = path.index(neighbor)
|
|
262
|
+
cycle = path[cycle_start:] + [neighbor]
|
|
263
|
+
errors.append(
|
|
264
|
+
"Circular dependency detected: {}".format(" -> ".join(cycle))
|
|
265
|
+
)
|
|
266
|
+
return
|
|
267
|
+
if color[neighbor] == WHITE:
|
|
268
|
+
dfs(neighbor, path)
|
|
269
|
+
path.pop()
|
|
270
|
+
color[node] = BLACK
|
|
271
|
+
|
|
272
|
+
for rid in valid_ids:
|
|
273
|
+
if color[rid] == WHITE:
|
|
274
|
+
dfs(rid, [])
|
|
275
|
+
|
|
276
|
+
return errors
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+
def create_state_directory(state_dir, refactor_list_path, refactors):
|
|
280
|
+
"""Create the state directory structure with pipeline.json and per-refactor status files."""
|
|
281
|
+
abs_state_dir = os.path.abspath(state_dir)
|
|
282
|
+
abs_refactor_list_path = os.path.abspath(refactor_list_path)
|
|
283
|
+
# Store as relative path from state_dir so pipeline.json is portable across machines
|
|
284
|
+
rel_refactor_list_path = os.path.relpath(abs_refactor_list_path, abs_state_dir)
|
|
285
|
+
refactors_dir = os.path.join(abs_state_dir, "refactors")
|
|
286
|
+
|
|
287
|
+
now = datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
|
|
288
|
+
run_id = "refactor-run-" + datetime.now(timezone.utc).strftime("%Y%m%d%H%M")
|
|
289
|
+
|
|
290
|
+
# Create top-level state directory
|
|
291
|
+
os.makedirs(abs_state_dir, exist_ok=True)
|
|
292
|
+
os.makedirs(refactors_dir, exist_ok=True)
|
|
293
|
+
|
|
294
|
+
# Write pipeline.json
|
|
295
|
+
pipeline_state = {
|
|
296
|
+
"run_id": run_id,
|
|
297
|
+
"pipeline_type": "refactor",
|
|
298
|
+
"status": "initialized",
|
|
299
|
+
"refactor_list_path": rel_refactor_list_path,
|
|
300
|
+
"created_at": now,
|
|
301
|
+
"total_refactors": len(refactors),
|
|
302
|
+
"completed_refactors": 0,
|
|
303
|
+
}
|
|
304
|
+
pipeline_path = os.path.join(abs_state_dir, "pipeline.json")
|
|
305
|
+
with open(pipeline_path, "w", encoding="utf-8") as f:
|
|
306
|
+
json.dump(pipeline_state, f, indent=2, ensure_ascii=False)
|
|
307
|
+
f.write("\n")
|
|
308
|
+
|
|
309
|
+
# Write per-refactor status.json and create sessions directory
|
|
310
|
+
for refactor in refactors:
|
|
311
|
+
if not isinstance(refactor, dict):
|
|
312
|
+
continue
|
|
313
|
+
rid = refactor.get("id")
|
|
314
|
+
if rid is None:
|
|
315
|
+
continue
|
|
316
|
+
|
|
317
|
+
refactor_dir = os.path.join(refactors_dir, rid)
|
|
318
|
+
sessions_dir = os.path.join(refactor_dir, "sessions")
|
|
319
|
+
os.makedirs(sessions_dir, exist_ok=True)
|
|
320
|
+
|
|
321
|
+
refactor_status = {
|
|
322
|
+
"refactor_id": rid,
|
|
323
|
+
"status": "pending",
|
|
324
|
+
"retry_count": 0,
|
|
325
|
+
"max_retries": 3,
|
|
326
|
+
"sessions": [],
|
|
327
|
+
"last_session_id": None,
|
|
328
|
+
"resume_from_phase": None,
|
|
329
|
+
"created_at": now,
|
|
330
|
+
"updated_at": now,
|
|
331
|
+
}
|
|
332
|
+
status_path = os.path.join(refactor_dir, "status.json")
|
|
333
|
+
with open(status_path, "w", encoding="utf-8") as f:
|
|
334
|
+
json.dump(refactor_status, f, indent=2, ensure_ascii=False)
|
|
335
|
+
f.write("\n")
|
|
336
|
+
|
|
337
|
+
return abs_state_dir
|
|
338
|
+
|
|
339
|
+
|
|
340
|
+
def main():
|
|
341
|
+
args = parse_args()
|
|
342
|
+
|
|
343
|
+
# Load refactor list
|
|
344
|
+
data, load_errors = load_refactor_list(args.refactor_list)
|
|
345
|
+
if load_errors:
|
|
346
|
+
output = {"valid": False, "errors": load_errors}
|
|
347
|
+
print(json.dumps(output, indent=2, ensure_ascii=False))
|
|
348
|
+
sys.exit(1)
|
|
349
|
+
|
|
350
|
+
# Validate schema
|
|
351
|
+
schema_errors = validate_schema(data)
|
|
352
|
+
if schema_errors:
|
|
353
|
+
output = {"valid": False, "errors": schema_errors}
|
|
354
|
+
print(json.dumps(output, indent=2, ensure_ascii=False))
|
|
355
|
+
sys.exit(1)
|
|
356
|
+
|
|
357
|
+
# Validate refactors
|
|
358
|
+
refactors = data["refactors"]
|
|
359
|
+
refactor_errors, refactor_ids = validate_refactors(refactors)
|
|
360
|
+
if refactor_errors:
|
|
361
|
+
output = {"valid": False, "errors": refactor_errors}
|
|
362
|
+
print(json.dumps(output, indent=2, ensure_ascii=False))
|
|
363
|
+
sys.exit(1)
|
|
364
|
+
|
|
365
|
+
# Check for circular dependencies
|
|
366
|
+
dep_errors = check_circular_dependencies(refactors)
|
|
367
|
+
if dep_errors:
|
|
368
|
+
output = {"valid": False, "errors": dep_errors}
|
|
369
|
+
print(json.dumps(output, indent=2, ensure_ascii=False))
|
|
370
|
+
sys.exit(1)
|
|
371
|
+
|
|
372
|
+
# Create state directory
|
|
373
|
+
try:
|
|
374
|
+
abs_state_dir = create_state_directory(
|
|
375
|
+
args.state_dir, args.refactor_list, refactors
|
|
376
|
+
)
|
|
377
|
+
except (IOError, OSError) as e:
|
|
378
|
+
output = {"valid": False, "errors": ["Failed to create state directory: {}".format(str(e))]}
|
|
379
|
+
print(json.dumps(output, indent=2, ensure_ascii=False))
|
|
380
|
+
sys.exit(1)
|
|
381
|
+
|
|
382
|
+
# Success output
|
|
383
|
+
output = {
|
|
384
|
+
"valid": True,
|
|
385
|
+
"refactors_count": len(refactors),
|
|
386
|
+
"state_dir": abs_state_dir,
|
|
387
|
+
}
|
|
388
|
+
print(json.dumps(output, indent=2, ensure_ascii=False))
|
|
389
|
+
sys.exit(0)
|
|
390
|
+
|
|
391
|
+
|
|
392
|
+
if __name__ == "__main__":
|
|
393
|
+
main()
|