super-engineer-workflow 0.1.0

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.
Files changed (53) hide show
  1. package/CHANGELOG.md +9 -0
  2. package/CONTRIBUTING.md +34 -0
  3. package/LICENSE +21 -0
  4. package/README.md +300 -0
  5. package/SECURITY.md +21 -0
  6. package/bin/super-engineer.js +19 -0
  7. package/docs/se/345/221/275/344/273/244/345/215/217/350/256/256.md +335 -0
  8. package/docs//344/270/255/346/226/207/344/275/277/347/224/250/346/211/213/345/206/214.md +707 -0
  9. package/docs//345/205/254/345/274/200/345/217/221/345/270/203/346/243/200/346/237/245/346/270/205/345/215/225.md +43 -0
  10. package/docs//345/277/253/351/200/237/345/210/235/345/247/213/345/214/226/345/267/245/344/275/234/345/214/272.md +419 -0
  11. package/docs//351/241/271/347/233/256/346/236/266/346/236/204/344/270/216/350/256/276/350/256/241/350/257/264/346/230/216.md +657 -0
  12. package/package.json +55 -0
  13. package/scripts/se-cli.py +301 -0
  14. package/scripts/se-setup.py +331 -0
  15. package/scripts/se-smoke-test.py +86 -0
  16. package/super-engineer-workflow/SKILL.md +439 -0
  17. package/super-engineer-workflow/adapters/go.yml +8 -0
  18. package/super-engineer-workflow/adapters/java-gradle.yml +8 -0
  19. package/super-engineer-workflow/adapters/java-maven.yml +8 -0
  20. package/super-engineer-workflow/adapters/node-react.yml +8 -0
  21. package/super-engineer-workflow/adapters/node-vue.yml +8 -0
  22. package/super-engineer-workflow/adapters/python.yml +8 -0
  23. package/super-engineer-workflow/agents/openai.yaml +4 -0
  24. package/super-engineer-workflow/assets/config-schema.json +100 -0
  25. package/super-engineer-workflow/assets/config.example.yml +12 -0
  26. package/super-engineer-workflow/assets/plan-schema.json +362 -0
  27. package/super-engineer-workflow/assets/status-schema.json +83 -0
  28. package/super-engineer-workflow/assets/workspace.example.yml +25 -0
  29. package/super-engineer-workflow/config.example.yml +12 -0
  30. package/super-engineer-workflow/references/contracts.md +39 -0
  31. package/super-engineer-workflow/references/execution-modes.md +38 -0
  32. package/super-engineer-workflow/references/java.md +21 -0
  33. package/super-engineer-workflow/references/planning.md +45 -0
  34. package/super-engineer-workflow/references/platform-openclaw.md +10 -0
  35. package/super-engineer-workflow/references/project-docs.md +7 -0
  36. package/super-engineer-workflow/references/review-checklist.md +26 -0
  37. package/super-engineer-workflow/references/se-commands.md +582 -0
  38. package/super-engineer-workflow/references/verify-checklist.md +45 -0
  39. package/super-engineer-workflow/references/workflow.md +208 -0
  40. package/super-engineer-workflow/scripts/archive-openspec.py +110 -0
  41. package/super-engineer-workflow/scripts/bootstrap-openspec.py +42 -0
  42. package/super-engineer-workflow/scripts/common.py +3285 -0
  43. package/super-engineer-workflow/scripts/generate-discovery.py +185 -0
  44. package/super-engineer-workflow/scripts/generate-review-report.py +296 -0
  45. package/super-engineer-workflow/scripts/generate-self-check.py +185 -0
  46. package/super-engineer-workflow/scripts/generate-smart-plan.py +429 -0
  47. package/super-engineer-workflow/scripts/init-workspace.py +68 -0
  48. package/super-engineer-workflow/scripts/prepare-archive-openspec.py +186 -0
  49. package/super-engineer-workflow/scripts/propose-openspec.py +170 -0
  50. package/super-engineer-workflow/scripts/run-verify-and-report.py +399 -0
  51. package/super-engineer-workflow/scripts/run-workflow.py +506 -0
  52. package/super-engineer-workflow/scripts/update-status.py +43 -0
  53. package/super-engineer-workflow/scripts/writeback-openspec.py +311 -0
@@ -0,0 +1,311 @@
1
+ #!/usr/bin/env python3
2
+ from __future__ import annotations
3
+
4
+ import argparse
5
+ from pathlib import Path
6
+
7
+ from common import (
8
+ current_session_meta,
9
+ data_artifact_path,
10
+ format_duration,
11
+ load_workspace_config,
12
+ now_iso,
13
+ openspec_bridge_context_path,
14
+ openspec_change_dir,
15
+ openspec_writeback_dir,
16
+ read_json,
17
+ read_text,
18
+ report_artifact_path,
19
+ todo_path,
20
+ workflow_source,
21
+ workspace_root,
22
+ write_managed_json,
23
+ write_managed_text,
24
+ )
25
+
26
+
27
+ def list_items(value: object) -> list[object]:
28
+ if isinstance(value, list):
29
+ return value
30
+ return []
31
+
32
+
33
+ def summarize_review(review: dict) -> list[str]:
34
+ findings = list_items(review.get("findings", []))
35
+ if not findings:
36
+ return ["暂无 review finding。"]
37
+ lines: list[str] = []
38
+ for item in findings[:8]:
39
+ if isinstance(item, dict):
40
+ lines.append(f"[{item.get('severity', 'info')}] {item.get('title', '')}:{item.get('detail', '')}")
41
+ else:
42
+ lines.append(f"[info] {str(item)}")
43
+ return lines
44
+
45
+
46
+ def summarize_verify(verify: dict) -> list[str]:
47
+ sections = list_items(verify.get("sections", []))
48
+ if not sections:
49
+ return ["暂无 verify 明细。"]
50
+ lines: list[str] = []
51
+ for section in sections[:8]:
52
+ if not isinstance(section, dict):
53
+ lines.append(str(section))
54
+ continue
55
+ duration = section.get("duration", 0)
56
+ try:
57
+ duration_text = f"{float(duration):.2f}"
58
+ except (TypeError, ValueError):
59
+ duration_text = "0.00"
60
+ lines.append(
61
+ f"{section.get('name', 'unknown')}:{section.get('result', '')},退出码 {section.get('exit_code', '')},耗时 {duration_text} 秒"
62
+ )
63
+ return lines
64
+
65
+
66
+ def summarize_acceptance(plan: dict, verify: dict) -> list[dict[str, object]]:
67
+ verify_result = str(verify.get("result", "missing"))
68
+ status = "passed" if verify_result == "通过" else "pending"
69
+ items: list[dict[str, object]] = []
70
+ for criterion in list_items(plan.get("acceptance_criteria", [])):
71
+ if isinstance(criterion, dict):
72
+ task_title = str(criterion.get("task_title") or criterion.get("title") or criterion.get("name") or "")
73
+ checks = [str(item) for item in list_items(criterion.get("checks", []))]
74
+ else:
75
+ task_title = str(criterion)
76
+ checks = []
77
+ if not task_title.strip():
78
+ task_title = "未命名验收项"
79
+ items.append(
80
+ {
81
+ "task_title": task_title,
82
+ "status": status,
83
+ "checks": checks,
84
+ }
85
+ )
86
+ return items
87
+
88
+
89
+ def infer_spec_impacts(plan: dict, bridge_context: dict) -> list[str]:
90
+ impacts = [str(item) for item in list_items(bridge_context.get("spec_reference_files", [])) if str(item).strip()]
91
+ if impacts:
92
+ return impacts
93
+ paths: list[str] = []
94
+ for item in list_items(plan.get("target_codebases", [])):
95
+ if isinstance(item, dict):
96
+ path = str(item.get("path", "")).strip()
97
+ else:
98
+ path = str(item).strip()
99
+ if path:
100
+ paths.append(path)
101
+ return paths
102
+
103
+
104
+ def residual_risks(plan: dict, review: dict, verify: dict) -> list[str]:
105
+ risks = [str(item) for item in list_items(plan.get("risks", [])) if str(item).strip()]
106
+ for finding in list_items(review.get("findings", [])):
107
+ if isinstance(finding, dict):
108
+ if str(finding.get("severity", "")) in ("warning", "blocker"):
109
+ risks.append(str(finding.get("detail", "")))
110
+ elif str(finding).strip():
111
+ risks.append(str(finding))
112
+ if verify.get("result") not in ("通过", "", None):
113
+ risks.append(f"验证结果未通过:{verify.get('result')}")
114
+ return risks[:12]
115
+
116
+
117
+ def task_mapping(plan: dict, verify: dict, todo_text: str) -> list[dict[str, object]]:
118
+ verify_result = str(verify.get("result", "")).strip()
119
+ default_status = "verified" if verify_result == "通过" else "implemented"
120
+ mappings: list[dict[str, object]] = []
121
+ for module in list_items(plan.get("task_modules", [])):
122
+ if not isinstance(module, dict):
123
+ continue
124
+ module_title = str(module.get("title", "")).strip()
125
+ for task in list_items(module.get("tasks", [])):
126
+ if not isinstance(task, dict):
127
+ continue
128
+ title = str(task.get("title", "")).strip()
129
+ if not title:
130
+ continue
131
+ mappings.append(
132
+ {
133
+ "openspec_task": title,
134
+ "todo_task": title,
135
+ "module": module_title,
136
+ "status": default_status,
137
+ "evidence": {
138
+ "impacted_files": list_items(plan.get("impacted_files", [])),
139
+ "review_result": str(plan.get("review_result", "")),
140
+ "verify_result": verify_result,
141
+ },
142
+ }
143
+ )
144
+ if mappings:
145
+ return mappings
146
+ for line in todo_text.splitlines():
147
+ stripped = line.strip()
148
+ if not stripped.startswith("- ["):
149
+ continue
150
+ title = stripped.split("]", 1)[-1].strip()
151
+ if title:
152
+ mappings.append(
153
+ {
154
+ "openspec_task": title,
155
+ "todo_task": title,
156
+ "module": "",
157
+ "status": default_status,
158
+ "evidence": {
159
+ "impacted_files": list_items(plan.get("impacted_files", [])),
160
+ "verify_result": verify_result,
161
+ },
162
+ }
163
+ )
164
+ return mappings
165
+
166
+
167
+ def build_markdown(payload: dict) -> str:
168
+ lines = [
169
+ "# Super Engineer Execution Summary",
170
+ "",
171
+ "## Change",
172
+ f"- change: {payload.get('change_name', '')}",
173
+ f"- change_dir: {payload.get('change_dir', '')}",
174
+ f"- session_id: {payload.get('session_id', '')}",
175
+ f"- updated_at: {payload.get('updated_at', '')}",
176
+ "",
177
+ "## Plan",
178
+ f"- requirement_summary: {payload.get('plan', {}).get('requirement_summary', '')}",
179
+ f"- confidence: {payload.get('plan', {}).get('confidence', '')}",
180
+ "",
181
+ "## Repositories",
182
+ ]
183
+ repos = list_items(payload.get("plan", {}).get("target_codebases", []))
184
+ if repos:
185
+ for repo in repos:
186
+ if isinstance(repo, dict):
187
+ lines.append(f"- {repo.get('name', '')}: {repo.get('path', '')}")
188
+ else:
189
+ lines.append(f"- {str(repo)}")
190
+ else:
191
+ lines.append("- 暂无")
192
+ lines.extend([
193
+ "",
194
+ "## Review",
195
+ f"- result: {payload.get('review', {}).get('result', 'missing')}",
196
+ ])
197
+ lines.extend(f"- {item}" for item in payload.get("review_summary", []))
198
+ lines.extend([
199
+ "",
200
+ "## Verify",
201
+ f"- result: {payload.get('verify', {}).get('result', 'missing')}",
202
+ ])
203
+ lines.extend(f"- {item}" for item in payload.get("verify_summary", []))
204
+ lines.extend([
205
+ "",
206
+ "## Task Mapping",
207
+ ])
208
+ mapping = list_items(payload.get("task_mapping", []))
209
+ if mapping:
210
+ for item in mapping[:30]:
211
+ if isinstance(item, dict):
212
+ lines.append(f"- {item.get('todo_task', '')}: {item.get('status', '')}")
213
+ else:
214
+ lines.append("- 暂无")
215
+ lines.extend([
216
+ "",
217
+ "## Links",
218
+ f"- plan.md: {payload.get('reports', {}).get('plan_md', '')}",
219
+ f"- review.md: {payload.get('reports', {}).get('review_md', '')}",
220
+ f"- verify.md: {payload.get('reports', {}).get('verify_md', '')}",
221
+ ])
222
+ lines.append("")
223
+ return "\n".join(lines)
224
+
225
+
226
+ def main() -> None:
227
+ parser = argparse.ArgumentParser(description="把当前会话执行结果回写到 OpenSpec change 目录。")
228
+ parser.add_argument("--workspace", help="工作空间路径,默认读取当前目录")
229
+ args = parser.parse_args()
230
+
231
+ workspace = workspace_root(Path(args.workspace).expanduser() if args.workspace else None)
232
+ config = load_workspace_config(workspace)
233
+ if workflow_source(config) != "openspec":
234
+ raise SystemExit("当前 workspace.yml 未启用 OpenSpec 模式,无需执行 writeback-openspec。")
235
+
236
+ session_meta = current_session_meta(config)
237
+ plan = read_json(data_artifact_path(config, "plan.json", session_meta), {})
238
+ review = read_json(data_artifact_path(config, "review.json", session_meta), {})
239
+ verify = read_json(data_artifact_path(config, "verify.json", session_meta), {})
240
+ status = read_json(data_artifact_path(config, "status.json", session_meta), {})
241
+ todo_text = read_text(todo_path(config))
242
+ plan_bridge_context = plan.get("bridge_context", {})
243
+ if not isinstance(plan_bridge_context, dict) or not plan_bridge_context:
244
+ plan_bridge_context = read_json(openspec_bridge_context_path(config), {})
245
+ if not isinstance(plan_bridge_context, dict):
246
+ plan_bridge_context = {}
247
+ archive_ready = bool(
248
+ review.get("result") == "passed"
249
+ and verify.get("result") == "通过"
250
+ and str(status.get("phase", "")).strip() == "done"
251
+ )
252
+ archive_blockers: list[str] = []
253
+ if review.get("result") != "passed":
254
+ archive_blockers.append("review 未通过")
255
+ if verify.get("result") != "通过":
256
+ archive_blockers.append("verify 未通过")
257
+ if str(status.get("phase", "")).strip() != "done":
258
+ archive_blockers.append(f"当前状态不是 done:{status.get('phase', '')}")
259
+
260
+ payload = {
261
+ "change_name": str(config.get("openspec", {}).get("change_name", "")),
262
+ "change_dir": str(openspec_change_dir(config)),
263
+ "session_id": session_meta["session_id"],
264
+ "updated_at": now_iso(),
265
+ "status": {
266
+ "phase": status.get("phase", ""),
267
+ "progress": status.get("progress", 0),
268
+ "current_task": status.get("current_task", ""),
269
+ "duration": format_duration(float(status.get("duration_seconds", 0) or 0)),
270
+ },
271
+ "archive_ready": archive_ready,
272
+ "archive_blockers": archive_blockers,
273
+ "plan": {
274
+ "requirement_summary": plan.get("requirement_summary", ""),
275
+ "confidence": plan.get("confidence", ""),
276
+ "target_codebases": plan.get("target_codebases", []),
277
+ "impacted_files": plan.get("impacted_files", []),
278
+ "acceptance_criteria": plan.get("acceptance_criteria", []),
279
+ },
280
+ "bridge_context": plan_bridge_context,
281
+ "review": {
282
+ "result": review.get("result", "missing"),
283
+ "findings": review.get("findings", []),
284
+ },
285
+ "verify": {
286
+ "result": verify.get("result", "missing"),
287
+ "sections": verify.get("sections", []),
288
+ },
289
+ "acceptance_result": summarize_acceptance(plan, verify),
290
+ "task_mapping": task_mapping(plan, verify, todo_text),
291
+ "spec_impacts": infer_spec_impacts(plan, plan_bridge_context),
292
+ "residual_risks": residual_risks(plan, review, verify),
293
+ "manual_decisions": [],
294
+ "review_summary": summarize_review(review),
295
+ "verify_summary": summarize_verify(verify),
296
+ "reports": {
297
+ "plan_md": str(report_artifact_path(config, "plan.md", session_meta)),
298
+ "review_md": str(report_artifact_path(config, "review.md", session_meta)),
299
+ "verify_md": str(report_artifact_path(config, "verify.md", session_meta)),
300
+ },
301
+ }
302
+
303
+ output_dir = openspec_writeback_dir(config)
304
+ write_managed_json(config, output_dir / "execution-summary.json", payload)
305
+ write_managed_text(config, output_dir / "execution-summary.md", build_markdown(payload))
306
+ print(f"writeback_dir={output_dir}")
307
+ print(f"change_dir={openspec_change_dir(config)}")
308
+
309
+
310
+ if __name__ == "__main__":
311
+ main()