@surlymochan/evalid 0.0.0-stage → 0.4.1

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 (192) hide show
  1. package/CHANGELOG.md +94 -0
  2. package/LICENSE +21 -0
  3. package/README.md +315 -2
  4. package/RELEASE_NOTES.md +124 -0
  5. package/atoms/registry.json +758 -0
  6. package/bin/xflow.js +7261 -0
  7. package/docs/adoption/README.md +128 -0
  8. package/docs/adoption/openflow-release-hardening.md +61 -0
  9. package/docs/benchmark-openflow-vs-gstack-superpowers-openspec-2026-05-25.md +81 -0
  10. package/docs/compatibility.md +52 -0
  11. package/docs/competitive-benchmark.md +108 -0
  12. package/docs/corps-operator-guide.md +75 -0
  13. package/docs/demo-proof.md +126 -0
  14. package/docs/examples-gallery.md +115 -0
  15. package/docs/fixtures/tracker-item.json +7 -0
  16. package/docs/goal-vs-codex.md +129 -0
  17. package/docs/gstack-comparison.md +88 -0
  18. package/docs/install-upgrade.md +133 -0
  19. package/docs/integrations.md +49 -0
  20. package/docs/launch-demo.md +123 -0
  21. package/docs/launch-dossier.md +180 -0
  22. package/docs/methodology.md +149 -0
  23. package/docs/npm-publish-handoff.md +96 -0
  24. package/docs/openspec-migration.md +106 -0
  25. package/docs/public-benchmark.md +68 -0
  26. package/docs/public-release.md +145 -0
  27. package/docs/quality-assessment.md +139 -0
  28. package/docs/quickstart.md +156 -0
  29. package/docs/reviewer-guide.md +43 -0
  30. package/docs/spec-kit-benchmark.md +73 -0
  31. package/docs/super-assistant-comparison.md +106 -0
  32. package/docs/superpowers-comparison.md +82 -0
  33. package/docs/team-adoption.md +123 -0
  34. package/docs/tooling-matrix.md +91 -0
  35. package/docs/walkthrough.md +76 -0
  36. package/docs/workflow-manual.md +1130 -0
  37. package/package.json +83 -4
  38. package/prompts/pencil-draft.md +46 -0
  39. package/prompts/pencil-refine.md +46 -0
  40. package/prompts/phase-artifact.md +24 -0
  41. package/prompts/phase-execution.md +32 -0
  42. package/schemas/competitor-reconstruction-contract.schema.json +91 -0
  43. package/schemas/llm-design-review-aggregate.schema.json +83 -0
  44. package/schemas/visual-benchmark.schema.json +978 -0
  45. package/schemas/workflow.schema.json +100 -0
  46. package/scripts/run-tests.mjs +23 -0
  47. package/scripts/takein_digest.py +139 -0
  48. package/specs/README.md +118 -0
  49. package/specs/architecture.md +36 -0
  50. package/specs/product.md +33 -0
  51. package/specs/workflow.md +349 -0
  52. package/src/agent_team/atoms/codex-app-bridge.js +8 -0
  53. package/src/agent_team/atoms/design/competitor-reconstruction-review.js +44 -0
  54. package/src/agent_team/atoms/design/contract-freeze.js +8 -0
  55. package/src/agent_team/atoms/design/generation-contract.js +442 -0
  56. package/src/agent_team/atoms/design/reconstruction-pack.js +246 -0
  57. package/src/agent_team/atoms/design/reference-surface-lock.js +49 -0
  58. package/src/agent_team/atoms/patch-challenge.js +19 -0
  59. package/src/agent_team/atoms/pencil/accept.js +9 -0
  60. package/src/agent_team/atoms/pencil/draft.js +365 -0
  61. package/src/agent_team/atoms/pencil/refine.js +229 -0
  62. package/src/agent_team/atoms/phase-run-dispatch.js +8 -0
  63. package/src/agent_team/atoms/team-run.js +380 -0
  64. package/src/agent_team/atoms/visual/aesthetic-review.js +231 -0
  65. package/src/agent_team/atoms/visual/benchmark-repair-plan.js +291 -0
  66. package/src/agent_team/atoms/visual/benchmark.js +1652 -0
  67. package/src/agent_team/atoms/visual/design-selection.js +23 -0
  68. package/src/agent_team/atoms/visual/direction-synthesis.js +124 -0
  69. package/src/agent_team/atoms/visual/dom-rect-export.js +129 -0
  70. package/src/agent_team/atoms/visual/image-diff.js +186 -0
  71. package/src/agent_team/atoms/visual/layout-competition.js +8 -0
  72. package/src/agent_team/atoms/visual/page-evidence-capture.js +806 -0
  73. package/src/agent_team/atoms/visual/playwright-runtime.js +8 -0
  74. package/src/agent_team/atoms/visual/review-aggregate.js +87 -0
  75. package/src/agent_team/atoms/visual/review.js +77 -0
  76. package/src/agent_team/atoms/visual/visual-report.js +197 -0
  77. package/src/agent_team/atoms/visual/visual-token-export.js +417 -0
  78. package/src/agent_team/common.js +311 -0
  79. package/src/core/atoms/agent-invoke.js +684 -0
  80. package/src/core/atoms/event/ingest.js +30 -0
  81. package/src/core/atoms/experience/digest.js +55 -0
  82. package/src/core/atoms/gate/artifact-verify.js +83 -0
  83. package/src/core/atoms/gate/competitor-contract-validate.js +119 -0
  84. package/src/core/atoms/gate/evidence-collect.js +64 -0
  85. package/src/core/atoms/gate/payload-from-phase-run.js +78 -0
  86. package/src/core/atoms/gate/visual-benchmark-validate.js +183 -0
  87. package/src/core/atoms/gate/workbench.js +79 -0
  88. package/src/core/atoms/intake/classify.js +15 -0
  89. package/src/core/atoms/intake/design-skill-select.js +16 -0
  90. package/src/core/atoms/intake/preview-or-refine.js +19 -0
  91. package/src/core/atoms/intake/recommend.js +20 -0
  92. package/src/core/atoms/intake/research-same-category.js +19 -0
  93. package/src/core/atoms/intervention/create.js +30 -0
  94. package/src/core/atoms/memory/manage.js +27 -0
  95. package/src/core/atoms/memory/mount.js +13 -0
  96. package/src/core/atoms/mission/create.js +19 -0
  97. package/src/core/atoms/mission/list.js +14 -0
  98. package/src/core/atoms/mission/show.js +26 -0
  99. package/src/core/atoms/notify/checkpoint.js +46 -0
  100. package/src/core/atoms/phase-run/complete.js +42 -0
  101. package/src/core/atoms/phase-run/fail.js +40 -0
  102. package/src/core/atoms/phase-run/persist.js +39 -0
  103. package/src/core/atoms/phase-run/start.js +30 -0
  104. package/src/core/atoms/plugin/run.js +76 -0
  105. package/src/core/atoms/policy/gate.js +90 -0
  106. package/src/core/atoms/policy/resolve.js +11 -0
  107. package/src/core/atoms/storage/sync.js +57 -0
  108. package/src/core/atoms/workflow/advance.js +25 -0
  109. package/src/core/atoms/workflow/guard.js +127 -0
  110. package/src/core/atoms/workflow/state-load.js +37 -0
  111. package/src/core/bindings.js +37 -0
  112. package/src/core/change-artifacts.js +134 -0
  113. package/src/core/competitive-score.js +265 -0
  114. package/src/core/concurrency-policy.js +35 -0
  115. package/src/core/execution-log.js +108 -0
  116. package/src/core/intake-recommendation.js +134 -0
  117. package/src/core/memory-state.js +62 -0
  118. package/src/core/mission-state.js +780 -0
  119. package/src/core/pending-gates.js +66 -0
  120. package/src/core/phase-cost.js +116 -0
  121. package/src/core/phase-run-read.js +27 -0
  122. package/src/core/policy-overlay.js +180 -0
  123. package/src/core/state-store.js +201 -0
  124. package/src/core/workflow-executor.js +1014 -0
  125. package/src/core/workflow-integrity.js +111 -0
  126. package/src/core/workflow-loader.js +163 -0
  127. package/src/core/workflow-rerun.js +84 -0
  128. package/src/core/workflow-state.js +119 -0
  129. package/src/server.js +1307 -0
  130. package/workflows/corps.yaml +481 -0
  131. package/workflows/yolo.yaml +241 -0
  132. package/xflow/README.md +76 -0
  133. package/xflow/aha/SKILL.md +82 -0
  134. package/xflow/aha/scripts/append_aha.py +50 -0
  135. package/xflow/archive/scripts/archive_change.py +338 -0
  136. package/xflow/archive/templates/merge-design.md +54 -0
  137. package/xflow/archive/templates/merge-workflow.md +26 -0
  138. package/xflow/atoms/__init__.py +6 -0
  139. package/xflow/atoms/a1_issue_create_or_patch.py +70 -0
  140. package/xflow/atoms/a2_branch_create_and_link.py +42 -0
  141. package/xflow/atoms/a3_repo_doctor.py +11 -0
  142. package/xflow/atoms/a4_project_set_status.py +11 -0
  143. package/xflow/atoms/a5_archive_commit_push_close.py +11 -0
  144. package/xflow/atoms/a6_pr_create.py +72 -0
  145. package/xflow/atoms/b1_change_scaffold.py +11 -0
  146. package/xflow/atoms/b2_status_read.py +16 -0
  147. package/xflow/atoms/b2b_status_write.py +20 -0
  148. package/xflow/atoms/b3_status_transition.py +27 -0
  149. package/xflow/atoms/b4_handoff_scaffold_or_refresh.py +11 -0
  150. package/xflow/atoms/b5_aha_append.py +11 -0
  151. package/xflow/atoms/b6_takein_digest.py +11 -0
  152. package/xflow/atoms/b6b_lookback_digest.py +11 -0
  153. package/xflow/atoms/b7_change_mission_bind.py +43 -0
  154. package/xflow/atoms/c1_mem.py +54 -0
  155. package/xflow/atoms/e6_gate_local_precheck.py +23 -0
  156. package/xflow/atoms/h1_design_lite_gate.py +74 -0
  157. package/xflow/atoms/i6a_tdd_run.py +107 -0
  158. package/xflow/atoms/i6b_tdd_proof_validate.py +184 -0
  159. package/xflow/atoms/i6c_tdd_quality_review.py +372 -0
  160. package/xflow/atoms/j1_tests_run.py +129 -0
  161. package/xflow/atoms/j4a_spec_consistency_structural.py +105 -0
  162. package/xflow/atoms/j4b_spec_delta_review.py +316 -0
  163. package/xflow/atoms/j4c_openspec_migration_map.py +121 -0
  164. package/xflow/atoms/j4d_spec_clarify_gate.py +373 -0
  165. package/xflow/atoms/j6_cr_review.py +443 -0
  166. package/xflow/atoms/k1_artifacts_complete_check.py +39 -0
  167. package/xflow/atoms/k2_merge_snippets_apply.py +11 -0
  168. package/xflow/atoms/k3_handoff_refresh.py +14 -0
  169. package/xflow/atoms/k4_mem_lesson_persist.py +65 -0
  170. package/xflow/atoms/k5_archive_publish.py +21 -0
  171. package/xflow/atoms/k6_aha_merge.py +42 -0
  172. package/xflow/atoms/tdd_lib.py +956 -0
  173. package/xflow/corps/SKILL.md +109 -0
  174. package/xflow/exec/scripts/set_issue_in_progress.py +159 -0
  175. package/xflow/goal/SKILL.md +150 -0
  176. package/xflow/handoff/SKILL.md +95 -0
  177. package/xflow/handoff/scripts/scaffold_handoff.py +304 -0
  178. package/xflow/lookback/SKILL.md +66 -0
  179. package/xflow/lookback/scripts/lookback_digest.py +46 -0
  180. package/xflow/openissue/scripts/open_issue_flow.py +438 -0
  181. package/xflow/plan/SKILL.md +47 -0
  182. package/xflow/ralph/SKILL.md +123 -0
  183. package/xflow/scripts/check_installed_xflow_skill_sync.sh +44 -0
  184. package/xflow/scripts/common.py +483 -0
  185. package/xflow/scripts/doctor.py +278 -0
  186. package/xflow/scripts/sync_installed_xflow_skill.sh +134 -0
  187. package/xflow/takein/SKILL.md +81 -0
  188. package/xflow/takein/scripts/takein_digest.py +88 -0
  189. package/xflow/yolo/SKILL.md +75 -0
  190. package/xflow/yolo/scripts/scaffold_specs_change.py +673 -0
  191. package/xflow/yolo/scripts/yolo_gate.py +312 -0
  192. package/xflow/yolo/templates/DESIGN.md +179 -0
@@ -0,0 +1,278 @@
1
+ #!/usr/bin/env python3
2
+ import argparse
3
+ import json
4
+ import subprocess
5
+ import sys
6
+ from pathlib import Path
7
+ from typing import Any
8
+
9
+ sys.path.append(str(Path(__file__).resolve().parent))
10
+
11
+ from common import ( # noqa: E402
12
+ find_git_root,
13
+ get_current_branch,
14
+ get_remote_url,
15
+ gh_json,
16
+ load_status,
17
+ remote_branch_exists,
18
+ resolve_project_context,
19
+ )
20
+
21
+
22
+ def extract_repo_from_remote_url(remote_url: str) -> str | None:
23
+ import re
24
+
25
+ match = re.search(r"github\.com[:/](?P<repo>[^/]+/[^/.]+?)(?:\.git)?$", remote_url.strip(), flags=re.IGNORECASE)
26
+ if not match:
27
+ return None
28
+ return str(match.group("repo"))
29
+
30
+
31
+ def check(name: str, ok: bool, detail: str, *, value: Any | None = None, severity: str = "error") -> dict[str, Any]:
32
+ payload: dict[str, Any] = {
33
+ "name": name,
34
+ "ok": ok,
35
+ "detail": detail,
36
+ "severity": severity,
37
+ }
38
+ if value is not None:
39
+ payload["value"] = value
40
+ return payload
41
+
42
+
43
+ def parse_args() -> argparse.Namespace:
44
+ parser = argparse.ArgumentParser(description="Validate Superflow repo / remote / issue / branch / project / status.json consistency.")
45
+ parser.add_argument("--project-root", default=".", help="Project root containing specs/changes.")
46
+ parser.add_argument("--change-id", required=True, help="Superflow change id.")
47
+ parser.add_argument("--repo", help="Override target repo from status.json.")
48
+ parser.add_argument("--issue-number", type=int, help="Override issue number from status.json.")
49
+ parser.add_argument("--branch-name", help="Override linked branch from status.json.")
50
+ parser.add_argument("--checkout-path", help="Override checkout path from status.json.")
51
+ parser.add_argument("--json", action="store_true", help="Print machine-readable JSON output.")
52
+ return parser.parse_args()
53
+
54
+
55
+ def issue_branch_comment_present(repo: str, issue_number: int, branch_name: str) -> bool:
56
+ marker = "<!-- xflow:branch-link -->"
57
+ try:
58
+ comments = gh_json(["api", f"repos/{repo}/issues/{issue_number}/comments"])
59
+ except subprocess.CalledProcessError:
60
+ return False
61
+ for comment in comments:
62
+ body = comment.get("body") or ""
63
+ if marker in body and f"`{branch_name}`" in body:
64
+ return True
65
+ return False
66
+
67
+
68
+ def run_doctor_checks(
69
+ *,
70
+ project_root: str | Path,
71
+ change_id: str,
72
+ repo: str | None = None,
73
+ issue_number: int | None = None,
74
+ branch_name: str | None = None,
75
+ checkout_path_override: str | Path | None = None,
76
+ ) -> dict[str, Any]:
77
+ status_path, status = load_status(project_root, change_id)
78
+
79
+ repo = repo or status.get("issue_repo") or status.get("target_repo")
80
+ issue_number = issue_number or status.get("issue_number")
81
+ branch_name = branch_name or status.get("branch_name")
82
+ checkout_path_value = str(checkout_path_override) if checkout_path_override else status.get("checkout_path")
83
+ code_repo = status.get("code_repo")
84
+ checkout_path = None
85
+ checks: list[dict[str, Any]] = []
86
+
87
+ checks.append(
88
+ check(
89
+ "status-file",
90
+ status_path.exists(),
91
+ f"Loaded status file {status_path}" if status_path.exists() else f"Missing status file {status_path}",
92
+ value=str(status_path),
93
+ )
94
+ )
95
+ checks.append(check("status-issue-repo", bool(repo), "issue_repo is present" if repo else "issue_repo missing from status.json"))
96
+ checks.append(check("status-issue-number", bool(issue_number), "issue_number is present" if issue_number else "issue_number missing from status.json"))
97
+ checks.append(check("status-branch-name", bool(branch_name), "branch_name is present" if branch_name else "branch_name missing from status.json"))
98
+ checks.append(check("status-code-repo", bool(code_repo), "code_repo is present" if code_repo else "code_repo missing from status.json", value=code_repo, severity="warn"))
99
+ checks.append(
100
+ check(
101
+ "status-checkout-path",
102
+ bool(checkout_path_value),
103
+ "checkout_path is present" if checkout_path_value else "checkout_path missing from status.json",
104
+ value=checkout_path_value,
105
+ )
106
+ )
107
+
108
+ if checkout_path_value:
109
+ try:
110
+ checkout_path = Path(checkout_path_value).expanduser().resolve()
111
+ checks.append(check("checkout-resolve", True, f"Resolved checkout {checkout_path}", value=str(checkout_path)))
112
+ except Exception as exc:
113
+ checks.append(check("checkout-resolve", False, f"Unable to resolve checkout path: {exc}", severity="error"))
114
+ else:
115
+ try:
116
+ checkout_path = find_git_root(project_root)
117
+ checks.append(check("checkout-resolve", True, f"Resolved checkout {checkout_path} from project root", value=str(checkout_path)))
118
+ except subprocess.CalledProcessError as exc:
119
+ checks.append(check("checkout-resolve", False, f"Unable to resolve checkout from project root: {exc}", severity="error"))
120
+
121
+ checkout_remote_url = None
122
+ checkout_repo = None
123
+ if checkout_path:
124
+ try:
125
+ checkout_remote_url = get_remote_url(checkout_path)
126
+ checkout_repo = extract_repo_from_remote_url(checkout_remote_url)
127
+ checks.append(check("checkout-remote", True, "Resolved checkout remote", value={"remote_url": checkout_remote_url, "checkout_repo": checkout_repo}))
128
+ except subprocess.CalledProcessError as exc:
129
+ checks.append(check("checkout-remote", False, f"Unable to resolve checkout remote: {exc}", severity="error"))
130
+
131
+ if code_repo and checkout_repo:
132
+ checks.append(
133
+ check(
134
+ "checkout-code-repo-match",
135
+ code_repo == checkout_repo,
136
+ "checkout remote matches recorded code_repo" if code_repo == checkout_repo else "checkout remote differs from recorded code_repo",
137
+ value={"recorded_code_repo": code_repo, "checkout_repo": checkout_repo},
138
+ )
139
+ )
140
+
141
+ current_branch = None
142
+ if checkout_path:
143
+ try:
144
+ current_branch = get_current_branch(checkout_path)
145
+ checks.append(check("current-branch", True, "Current branch resolved", value=current_branch))
146
+ except subprocess.CalledProcessError as exc:
147
+ checks.append(check("current-branch", False, f"Unable to resolve current branch: {exc}", severity="error"))
148
+
149
+ if branch_name and checkout_path:
150
+ linked_branch_exists = bool(current_branch == branch_name or remote_branch_exists(checkout_path, branch_name))
151
+ checks.append(
152
+ check(
153
+ "linked-branch-local-or-remote",
154
+ linked_branch_exists,
155
+ f"Linked branch {branch_name} exists locally or on origin" if linked_branch_exists else f"Linked branch {branch_name} not found locally or on origin",
156
+ value=branch_name,
157
+ )
158
+ )
159
+ if current_branch is not None:
160
+ checks.append(
161
+ check(
162
+ "linked-branch-current-match",
163
+ current_branch == branch_name,
164
+ f"Current branch matches linked branch {branch_name}" if current_branch == branch_name else f"Current branch {current_branch} differs from linked branch {branch_name}",
165
+ value={"current_branch": current_branch, "linked_branch": branch_name},
166
+ )
167
+ )
168
+ checks.append(
169
+ check(
170
+ "branch-worktree-ready",
171
+ linked_branch_exists and current_branch == branch_name,
172
+ "Linked branch is available and the current checkout matches it"
173
+ if (linked_branch_exists and current_branch == branch_name)
174
+ else f"Linked branch/worktree is not ready: current={current_branch}, linked={branch_name}",
175
+ value={
176
+ "current_branch": current_branch,
177
+ "linked_branch": branch_name,
178
+ "checkout_path": str(checkout_path),
179
+ },
180
+ )
181
+ )
182
+
183
+ issue_data = None
184
+ if repo and issue_number:
185
+ try:
186
+ issue_data = gh_json(
187
+ [
188
+ "issue",
189
+ "view",
190
+ str(issue_number),
191
+ "--repo",
192
+ repo,
193
+ "--json",
194
+ "number,title,url,state,projectItems",
195
+ ]
196
+ )
197
+ checks.append(check("issue-exists", True, f"Issue {repo}#{issue_number} exists", value={"url": issue_data.get("url"), "state": issue_data.get("state")}))
198
+ except subprocess.CalledProcessError as exc:
199
+ checks.append(check("issue-exists", False, f"Unable to resolve issue {repo}#{issue_number}: {exc}", severity="error"))
200
+
201
+ if repo and issue_number and branch_name:
202
+ has_branch_comment = issue_branch_comment_present(repo, int(issue_number), branch_name)
203
+ checks.append(
204
+ check(
205
+ "issue-branch-comment-link",
206
+ has_branch_comment,
207
+ "Found Superflow branch-link comment on issue" if has_branch_comment else "Missing Superflow branch-link comment on issue",
208
+ value=branch_name,
209
+ severity="warn",
210
+ )
211
+ )
212
+
213
+ project_context = None
214
+ if repo and issue_number:
215
+ project_context = resolve_project_context(repo, int(issue_number))
216
+ checks.append(
217
+ check(
218
+ "project-context",
219
+ project_context is not None,
220
+ f"Resolved project context {project_context['owner']}#{project_context['number']}" if project_context else "No unique project context discovered from issue",
221
+ value=project_context,
222
+ severity="warn",
223
+ )
224
+ )
225
+
226
+ recorded_project = status.get("project_status") or status.get("project_done") or status.get("project_context")
227
+ if recorded_project and project_context:
228
+ same_owner = recorded_project.get("owner") == project_context.get("owner")
229
+ same_number = int(recorded_project.get("number")) == int(project_context.get("number"))
230
+ checks.append(
231
+ check(
232
+ "project-record-match",
233
+ same_owner and same_number,
234
+ "Recorded project context matches discovered project context" if (same_owner and same_number) else "Recorded project context differs from discovered project context",
235
+ value={"recorded": recorded_project, "discovered": project_context},
236
+ severity="warn",
237
+ )
238
+ )
239
+
240
+ ok = all(item["ok"] or item["severity"] == "warn" for item in checks)
241
+ hard_failures = [item for item in checks if (not item["ok"] and item["severity"] != "warn")]
242
+ warnings = [item for item in checks if (not item["ok"] and item["severity"] == "warn")]
243
+
244
+ result = {
245
+ "change_id": change_id,
246
+ "ok": ok,
247
+ "hard_failures": len(hard_failures),
248
+ "warnings": len(warnings),
249
+ "checks": checks,
250
+ }
251
+ return result
252
+
253
+
254
+ def main() -> int:
255
+ args = parse_args()
256
+ result = run_doctor_checks(
257
+ project_root=args.project_root,
258
+ change_id=args.change_id,
259
+ repo=args.repo,
260
+ issue_number=args.issue_number,
261
+ branch_name=args.branch_name,
262
+ checkout_path_override=args.checkout_path,
263
+ )
264
+
265
+ if args.json:
266
+ print(json.dumps(result, indent=2))
267
+ else:
268
+ print(f"Superflow doctor: {args.change_id}")
269
+ print(f"Overall: {'OK' if result['ok'] else 'FAIL'}")
270
+ for item in result["checks"]:
271
+ icon = "PASS" if item["ok"] else ("WARN" if item["severity"] == "warn" else "FAIL")
272
+ print(f"- [{icon}] {item['name']}: {item['detail']}")
273
+
274
+ return 0 if result["ok"] else 1
275
+
276
+
277
+ if __name__ == "__main__":
278
+ raise SystemExit(main())
@@ -0,0 +1,134 @@
1
+ #!/bin/sh
2
+ # Sync the repo-owned xflow skill into installed skill dirs without pruning
3
+ # skills that are managed by companion repositories.
4
+
5
+ set -eu
6
+
7
+ SCRIPT_PATH=$0
8
+ case "$SCRIPT_PATH" in
9
+ /*) ;;
10
+ *) SCRIPT_PATH=$(pwd)/$SCRIPT_PATH ;;
11
+ esac
12
+
13
+ SCRIPT_DIR=$(cd "$(dirname "$SCRIPT_PATH")" && pwd)
14
+ PROJECT_ROOT=$(cd "$SCRIPT_DIR/../.." && pwd)
15
+
16
+ config_value() {
17
+ key_path=$1
18
+ node -e '
19
+ const fs = require("fs");
20
+ const path = require("path");
21
+ const root = process.argv[1];
22
+ const keyPath = process.argv[2].split(".");
23
+ const configPath = path.join(root, ".as-xflow", "config.json");
24
+ if (!fs.existsSync(configPath)) process.exit(0);
25
+ const expandHome = (value) => typeof value === "string" && value.startsWith("~/")
26
+ ? path.join(process.env.HOME || "", value.slice(2))
27
+ : value;
28
+ let value = JSON.parse(fs.readFileSync(configPath, "utf8"));
29
+ for (const key of keyPath) value = value && value[key];
30
+ if (Array.isArray(value)) console.log(value.map(expandHome).join("\n"));
31
+ else if (value) console.log(expandHome(value));
32
+ ' "$PROJECT_ROOT" "$key_path"
33
+ }
34
+
35
+ CONFIG_SYNC_SCRIPT=$(config_value skills.sync_script)
36
+ CONFIG_EXTRA_SOURCE_DIRS=$(config_value skills.extra_source_dirs)
37
+ CONFIG_TARGET_DIRS=$(config_value skills.installed_dir)
38
+
39
+ default_target_dirs() {
40
+ if [ -d "$HOME/.codex/skills" ]; then
41
+ printf '%s\n' "$HOME/.codex/skills"
42
+ return 0
43
+ fi
44
+
45
+ printf '%s\n' "$HOME/.agents/skills"
46
+ }
47
+
48
+ SYNC_SCRIPT=${XFLOW_SKILL_SYNC_SCRIPT:-${SKILLHUB_SYNC_SCRIPT:-${CONFIG_SYNC_SCRIPT:-$HOME/Documents/workspace/pro/as-skillhub/skills/skills_sync.sh}}}
49
+ EXTRA_SOURCE_DIRS=${XFLOW_SKILL_SYNC_EXTRA_SOURCE_DIRS:-${SKILL_SYNC_EXTRA_SOURCE_DIRS:-${CONFIG_EXTRA_SOURCE_DIRS:-$HOME/Documents/workspace/pro/as-skillhub/skills}}}
50
+ TARGET_DIRS=${SKILL_SYNC_TARGET_DIRS:-${CONFIG_TARGET_DIRS:-$(default_target_dirs)}}
51
+ DEFAULT_XMEM_SOURCE_DIR=$(cd "$PROJECT_ROOT/../as-xmem" 2>/dev/null && pwd || true)
52
+ XMEM_SOURCE_DIR=${XFLOW_XMEM_SOURCE_DIR:-$DEFAULT_XMEM_SOURCE_DIR}
53
+
54
+ if [ ! -f "$SYNC_SCRIPT" ]; then
55
+ printf 'sync_installed_xflow_skill.sh: missing skills sync script: %s\n' "$SYNC_SCRIPT" >&2
56
+ exit 1
57
+ fi
58
+
59
+ if [ ! -d "$PROJECT_ROOT/xflow" ]; then
60
+ printf 'sync_installed_xflow_skill.sh: missing source xflow dir: %s\n' "$PROJECT_ROOT/xflow" >&2
61
+ exit 1
62
+ fi
63
+
64
+ extra_source_found=$(mktemp)
65
+ printf '%s\n' "$EXTRA_SOURCE_DIRS" | while IFS= read -r source_dir; do
66
+ [ -n "$source_dir" ] || continue
67
+ [ -d "$source_dir" ] || continue
68
+ printf 'yes\n' >"$extra_source_found"
69
+ done
70
+
71
+ if [ ! -s "$extra_source_found" ]; then
72
+ rm -f "$extra_source_found"
73
+ printf 'sync_installed_xflow_skill.sh: no extra source dir exists in:\n%s\n' "$EXTRA_SOURCE_DIRS" >&2
74
+ printf 'Set XFLOW_SKILL_SYNC_EXTRA_SOURCE_DIRS or SKILL_SYNC_EXTRA_SOURCE_DIRS to the as-skillhub skills dir.\n' >&2
75
+ exit 1
76
+ fi
77
+ rm -f "$extra_source_found"
78
+
79
+ SKILLHUB="$PROJECT_ROOT" \
80
+ SKILL_SYNC_EXTRA_SOURCE_DIRS="$EXTRA_SOURCE_DIRS" \
81
+ SKILL_SYNC_TARGET_DIRS="$TARGET_DIRS" \
82
+ sh "$SYNC_SCRIPT" xflow
83
+
84
+ install_xmem_skill_family() {
85
+ [ -n "$XMEM_SOURCE_DIR" ] || return 0
86
+ [ -d "$XMEM_SOURCE_DIR" ] || return 0
87
+ [ -f "$XMEM_SOURCE_DIR/skill.md" ] || return 0
88
+
89
+ printf '%s\n' "$TARGET_DIRS" | while IFS= read -r target_dir; do
90
+ [ -n "$target_dir" ] || continue
91
+ target_xmem=$target_dir/xmem
92
+
93
+ rm -rf "$target_xmem"
94
+ cp -RL "$XMEM_SOURCE_DIR" "$target_xmem"
95
+ rm -f "$target_xmem/.xmem-root-skill.tmp"
96
+ mv "$target_xmem/skill.md" "$target_xmem/.xmem-root-skill.tmp"
97
+ mv "$target_xmem/.xmem-root-skill.tmp" "$target_xmem/SKILL.md"
98
+ rm -f "$target_xmem/.skillhub-source"
99
+ printf '%s\n' "$XMEM_SOURCE_DIR" >"$target_xmem/.xflow-source"
100
+
101
+ find "$target_xmem" -type d -name '__pycache__' -prune -exec rm -rf {} +
102
+ find "$target_xmem" -type f -name '*.pyc' -delete
103
+ printf 'verified xmem skill sync target: %s\n' "$target_xmem"
104
+ done
105
+ }
106
+
107
+ install_xmem_skill_family
108
+
109
+ printf '%s\n' "$TARGET_DIRS" | while IFS= read -r target_dir; do
110
+ [ -n "$target_dir" ] || continue
111
+ target_xflow=$target_dir/xflow
112
+
113
+ if [ ! -d "$target_xflow" ]; then
114
+ printf 'sync_installed_xflow_skill.sh: target xflow dir missing after sync: %s\n' "$target_xflow" >&2
115
+ exit 1
116
+ fi
117
+
118
+ find "$target_xflow" -type d -name '__pycache__' -prune -exec rm -rf {} +
119
+ find "$target_xflow" -type f -name '*.pyc' -delete
120
+
121
+ diff_file=$(mktemp)
122
+ filtered_file=$(mktemp)
123
+ diff -qr "$PROJECT_ROOT/xflow" "$target_xflow" >"$diff_file" 2>&1 || true
124
+ grep -v -E 'Only in .*/xflow: \.skillhub-source$|__pycache__|\.pyc$' "$diff_file" >"$filtered_file" || true
125
+
126
+ if [ -s "$filtered_file" ]; then
127
+ cat "$filtered_file" >&2
128
+ rm -f "$diff_file" "$filtered_file"
129
+ exit 1
130
+ fi
131
+
132
+ rm -f "$diff_file" "$filtered_file"
133
+ printf 'verified xflow skill sync target: %s\n' "$target_xflow"
134
+ done
@@ -0,0 +1,81 @@
1
+ ---
2
+ name: xflow:takein
3
+ description: 通过读取项目根目录的规范 `HANDOFF.md`,提取当前目标、进展、阻塞、经验和下一步动作,并在执行前完成一次轻量级仓库状态摄入,从而在新线程中接手工作。适用于用户要求在新线程继续、接管项目、根据 handoff 衔接、恢复上次会话,或从 `HANDOFF.md` 启动上下文时。
4
+ ---
5
+
6
+ # Superflow Takein
7
+
8
+ This skill is for new-thread takeover.
9
+
10
+ Your primary source of truth is:
11
+
12
+ - project root `.xflow/GOAL.md` (if present)
13
+ - project root `HANDOFF.md`
14
+ - project root `AHA.md`
15
+ - project root `DESIGN.md` (if frontend project)
16
+
17
+ Do not start by reading a large random slice of the repository.
18
+ Start from `HANDOFF.md`, then validate only the minimum additional context needed to begin the task safely.
19
+
20
+ ## Goal
21
+
22
+ Build a correct working context for the new thread fast enough to act, without redoing full-project discovery.
23
+
24
+ ## Required Intake Output
25
+
26
+ Before substantial implementation work, produce an intake summary covering:
27
+
28
+ 1. project background
29
+ 2. architecture and invariants
30
+ 3. current objective
31
+ 4. verified progress
32
+ 5. blockers / risks / open problems
33
+ 6. lessons learned / operational gotchas
34
+ 7. recommended first action in this thread
35
+
36
+ If `HANDOFF.md` is missing or stale, say so explicitly and repair the situation instead of pretending the context is complete.
37
+
38
+ ## Workflow
39
+
40
+ 1. Read project root `.xflow/GOAL.md` if present.
41
+ 2. Read project root `HANDOFF.md`.
42
+ 3. Read project root `AHA.md` if present.
43
+ 4. Read project root `DESIGN.md` if present (frontend projects).
44
+ 5. Extract the current objective, verified state, blockers, recommendations, and any active constraints.
45
+ 6. Run a lightweight repo-state check:
46
+ - `git status -sb`
47
+ - `npm run drift:scan` when the project exposes it; for evalid this is required before substantial implementation
48
+ - inspect directly relevant files only
49
+ 7. Compare current repo reality against `.xflow/GOAL.md`, `HANDOFF.md`, `AHA.md`, and `DESIGN.md`.
50
+ 8. Call out any mismatch between goal, handoff, aha log, design decisions, and repo state.
51
+ 9. State the first concrete action you will take next.
52
+ 10. Then proceed with execution.
53
+
54
+ ## Scope Control
55
+
56
+ Keep intake tight.
57
+
58
+ - do not re-read the entire codebase unless the handoff is clearly insufficient
59
+ - do not expand into open-ended architecture exploration unless blocked
60
+ - do not overwrite `HANDOFF.md` during takein unless the user asks, or the file is clearly dangerously stale
61
+
62
+ ## Guardrails
63
+
64
+ - treat `HANDOFF.md` as canonical until contradicted by repo evidence
65
+ - treat `AHA.md` as canonical for durable discussion insights until contradicted by repo evidence
66
+ - prefer verified repo truth over stale handoff claims
67
+ - distinguish verified facts from assumptions
68
+ - surface drift immediately if handoff and code disagree
69
+ - surface drift immediately if current design or implementation conflicts with a recorded aha
70
+ - always end intake with a recommended first action
71
+
72
+ ## Script
73
+
74
+ The agent autonomously calls this helper to generate intake digest:
75
+
76
+ ```bash
77
+ python3 ~/.codex/skills/xflow/takein/scripts/takein_digest.py --project-root .
78
+ ```
79
+
80
+ Agent uses digest as starting point, then validates repo reality.
81
+ User invokes skill, agent handles execution.
@@ -0,0 +1,88 @@
1
+ #!/usr/bin/env python3
2
+ import argparse
3
+ from pathlib import Path
4
+
5
+
6
+ SECTION_HEADERS = [
7
+ "Current State",
8
+ "Context Pack",
9
+ "Project Background",
10
+ "Architecture",
11
+ "Current Objective",
12
+ "Progress",
13
+ "Open Problems",
14
+ "Lessons Learned",
15
+ "Recommendations",
16
+ "Key Files",
17
+ ]
18
+
19
+
20
+ def parse_args() -> argparse.Namespace:
21
+ parser = argparse.ArgumentParser(
22
+ description="Extract a compact digest from the canonical root HANDOFF.md and AHA.md.",
23
+ )
24
+ parser.add_argument(
25
+ "--project-root",
26
+ default=".",
27
+ help="Project root containing HANDOFF.md and optional AHA.md.",
28
+ )
29
+ return parser.parse_args()
30
+
31
+
32
+ def extract_sections(text: str) -> list[tuple[str, list[str]]]:
33
+ sections: list[tuple[str, list[str]]] = []
34
+ current_name = "Preamble"
35
+ current_lines: list[str] = []
36
+
37
+ for line in text.splitlines():
38
+ if line.startswith("## "):
39
+ sections.append((current_name, current_lines))
40
+ current_name = line[3:].strip()
41
+ current_lines = []
42
+ continue
43
+ current_lines.append(line)
44
+
45
+ sections.append((current_name, current_lines))
46
+ return sections
47
+
48
+
49
+ def first_content_lines(lines: list[str], limit: int = 3) -> list[str]:
50
+ content = [line.strip() for line in lines if line.strip()]
51
+ return content[:limit]
52
+
53
+
54
+ def main() -> int:
55
+ args = parse_args()
56
+ project_root = Path(args.project_root).expanduser().resolve()
57
+ handoff_path = project_root / "HANDOFF.md"
58
+ aha_path = project_root / "AHA.md"
59
+ if not handoff_path.exists():
60
+ raise SystemExit(f"Missing canonical handoff: {handoff_path}")
61
+
62
+ text = handoff_path.read_text(encoding="utf-8")
63
+ sections = dict(extract_sections(text))
64
+
65
+ print(f"HANDOFF: {handoff_path}")
66
+ for name in SECTION_HEADERS:
67
+ lines = sections.get(name)
68
+ if not lines:
69
+ continue
70
+ digest = first_content_lines(lines)
71
+ if not digest:
72
+ continue
73
+ print(f"\n[{name}]")
74
+ for line in digest:
75
+ print(line)
76
+
77
+ if aha_path.exists():
78
+ aha_lines = [line.strip() for line in aha_path.read_text(encoding="utf-8").splitlines() if line.strip()]
79
+ digest = aha_lines[:12]
80
+ if digest:
81
+ print(f"\n[AHA] {aha_path}")
82
+ for line in digest:
83
+ print(line)
84
+ return 0
85
+
86
+
87
+ if __name__ == "__main__":
88
+ raise SystemExit(main())
@@ -0,0 +1,75 @@
1
+ ---
2
+ name: xflow:yolo
3
+ description: "轻量轨交付工作流:从 change-init 到 archive。适用于后端、文档、基础设施和小型 UI 改动。可复用 xflow:plan 产物,或在缺少 plan 时先补一轮通用规划。"
4
+ ---
5
+
6
+ # Superflow Yolo — Track Execution Driver
7
+
8
+ This skill drives the lite workflow. It does not own a separate planning
9
+ track; planning is shared with `xflow:plan`.
10
+
11
+ ## How to run
12
+
13
+ ```bash
14
+ xflow workflow validate workflows/yolo.yaml
15
+ ```
16
+
17
+ Then execute the workflow phases in order.
18
+
19
+ For a full run:
20
+
21
+ ```bash
22
+ xflow workflow run workflows/yolo.yaml --title "<中文标题>" --change-type backend
23
+ ```
24
+
25
+ When `--change-id` is omitted and the current branch cannot provide one
26
+ (e.g. `main`), the run derives the change id from `--title` and prints it;
27
+ pass `--change-id` explicitly to override.
28
+
29
+ ## Plan reuse rule
30
+
31
+ - Read `.xflow/GOAL.md` before execution when present. State whether the current
32
+ change aligns with it, intentionally narrows it, or conflicts with it.
33
+ - Reuse `specs/changes/${CHANGE_ID}/plan.md` when it is current.
34
+ - If the plan is missing or still a scaffold placeholder, create it once before continuing.
35
+ - Planning stays track-neutral; do not decide yolo vs corps here.
36
+
37
+ ## State and context
38
+
39
+ - Project root: `process.env.XFLOW_PROJECT_ROOT || cwd`
40
+ - Project goal: `.xflow/GOAL.md` when present; use it as direction context only
41
+ - Change root: `specs/changes/${CHANGE_ID}/`
42
+ - Status file: `specs/changes/${CHANGE_ID}/status.json`
43
+
44
+ ## Phase playbook
45
+
46
+ 1. `change-init`
47
+ 2. `brainstorm`
48
+ 3. `design-check` if the change is frontend/UI
49
+ 4. `proposal-freeze`
50
+ 5. `proposal-consistency-check`
51
+ 6. `plan`
52
+ 7. `tdd` (red proof — sealed in the same pass as the plan, see below)
53
+ 8. `openissue`
54
+ 9. `set-in-progress`
55
+ 10. `execute`
56
+ 11. `verify-consistency`
57
+ 12. `archive`
58
+
59
+ On the lite track the red proof is sealed immediately after planning: the
60
+ pass that writes the plan also writes the failing test, so `tdd-red` only
61
+ runs and seals it — no second exploration pass, and the issue/branch
62
+ ceremony no longer sits between planning and the red proof.
63
+
64
+ The workflow YAML is the canonical execution source for atom order, gates, and
65
+ artifact checks. This skill only explains the contract.
66
+
67
+ ## Guardrails
68
+
69
+ - Keep the plan reusable and track-neutral
70
+ - Do not introduce a separate tasks artifact
71
+ - Do not re-plan if a current `plan.md` already exists
72
+ - Do not treat `.xflow/GOAL.md` as executable scope; it is alignment context
73
+ that must be reflected in the final verification summary
74
+ - Do not enter `set-in-progress` or `execute` unless doctor confirms the linked branch and checkout are ready
75
+ - Do not expose deleted top-level xflow utility skills as entry points