@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.
- package/CHANGELOG.md +94 -0
- package/LICENSE +21 -0
- package/README.md +315 -2
- package/RELEASE_NOTES.md +124 -0
- package/atoms/registry.json +758 -0
- package/bin/xflow.js +7261 -0
- package/docs/adoption/README.md +128 -0
- package/docs/adoption/openflow-release-hardening.md +61 -0
- package/docs/benchmark-openflow-vs-gstack-superpowers-openspec-2026-05-25.md +81 -0
- package/docs/compatibility.md +52 -0
- package/docs/competitive-benchmark.md +108 -0
- package/docs/corps-operator-guide.md +75 -0
- package/docs/demo-proof.md +126 -0
- package/docs/examples-gallery.md +115 -0
- package/docs/fixtures/tracker-item.json +7 -0
- package/docs/goal-vs-codex.md +129 -0
- package/docs/gstack-comparison.md +88 -0
- package/docs/install-upgrade.md +133 -0
- package/docs/integrations.md +49 -0
- package/docs/launch-demo.md +123 -0
- package/docs/launch-dossier.md +180 -0
- package/docs/methodology.md +149 -0
- package/docs/npm-publish-handoff.md +96 -0
- package/docs/openspec-migration.md +106 -0
- package/docs/public-benchmark.md +68 -0
- package/docs/public-release.md +145 -0
- package/docs/quality-assessment.md +139 -0
- package/docs/quickstart.md +156 -0
- package/docs/reviewer-guide.md +43 -0
- package/docs/spec-kit-benchmark.md +73 -0
- package/docs/super-assistant-comparison.md +106 -0
- package/docs/superpowers-comparison.md +82 -0
- package/docs/team-adoption.md +123 -0
- package/docs/tooling-matrix.md +91 -0
- package/docs/walkthrough.md +76 -0
- package/docs/workflow-manual.md +1130 -0
- package/package.json +83 -4
- package/prompts/pencil-draft.md +46 -0
- package/prompts/pencil-refine.md +46 -0
- package/prompts/phase-artifact.md +24 -0
- package/prompts/phase-execution.md +32 -0
- package/schemas/competitor-reconstruction-contract.schema.json +91 -0
- package/schemas/llm-design-review-aggregate.schema.json +83 -0
- package/schemas/visual-benchmark.schema.json +978 -0
- package/schemas/workflow.schema.json +100 -0
- package/scripts/run-tests.mjs +23 -0
- package/scripts/takein_digest.py +139 -0
- package/specs/README.md +118 -0
- package/specs/architecture.md +36 -0
- package/specs/product.md +33 -0
- package/specs/workflow.md +349 -0
- package/src/agent_team/atoms/codex-app-bridge.js +8 -0
- package/src/agent_team/atoms/design/competitor-reconstruction-review.js +44 -0
- package/src/agent_team/atoms/design/contract-freeze.js +8 -0
- package/src/agent_team/atoms/design/generation-contract.js +442 -0
- package/src/agent_team/atoms/design/reconstruction-pack.js +246 -0
- package/src/agent_team/atoms/design/reference-surface-lock.js +49 -0
- package/src/agent_team/atoms/patch-challenge.js +19 -0
- package/src/agent_team/atoms/pencil/accept.js +9 -0
- package/src/agent_team/atoms/pencil/draft.js +365 -0
- package/src/agent_team/atoms/pencil/refine.js +229 -0
- package/src/agent_team/atoms/phase-run-dispatch.js +8 -0
- package/src/agent_team/atoms/team-run.js +380 -0
- package/src/agent_team/atoms/visual/aesthetic-review.js +231 -0
- package/src/agent_team/atoms/visual/benchmark-repair-plan.js +291 -0
- package/src/agent_team/atoms/visual/benchmark.js +1652 -0
- package/src/agent_team/atoms/visual/design-selection.js +23 -0
- package/src/agent_team/atoms/visual/direction-synthesis.js +124 -0
- package/src/agent_team/atoms/visual/dom-rect-export.js +129 -0
- package/src/agent_team/atoms/visual/image-diff.js +186 -0
- package/src/agent_team/atoms/visual/layout-competition.js +8 -0
- package/src/agent_team/atoms/visual/page-evidence-capture.js +806 -0
- package/src/agent_team/atoms/visual/playwright-runtime.js +8 -0
- package/src/agent_team/atoms/visual/review-aggregate.js +87 -0
- package/src/agent_team/atoms/visual/review.js +77 -0
- package/src/agent_team/atoms/visual/visual-report.js +197 -0
- package/src/agent_team/atoms/visual/visual-token-export.js +417 -0
- package/src/agent_team/common.js +311 -0
- package/src/core/atoms/agent-invoke.js +684 -0
- package/src/core/atoms/event/ingest.js +30 -0
- package/src/core/atoms/experience/digest.js +55 -0
- package/src/core/atoms/gate/artifact-verify.js +83 -0
- package/src/core/atoms/gate/competitor-contract-validate.js +119 -0
- package/src/core/atoms/gate/evidence-collect.js +64 -0
- package/src/core/atoms/gate/payload-from-phase-run.js +78 -0
- package/src/core/atoms/gate/visual-benchmark-validate.js +183 -0
- package/src/core/atoms/gate/workbench.js +79 -0
- package/src/core/atoms/intake/classify.js +15 -0
- package/src/core/atoms/intake/design-skill-select.js +16 -0
- package/src/core/atoms/intake/preview-or-refine.js +19 -0
- package/src/core/atoms/intake/recommend.js +20 -0
- package/src/core/atoms/intake/research-same-category.js +19 -0
- package/src/core/atoms/intervention/create.js +30 -0
- package/src/core/atoms/memory/manage.js +27 -0
- package/src/core/atoms/memory/mount.js +13 -0
- package/src/core/atoms/mission/create.js +19 -0
- package/src/core/atoms/mission/list.js +14 -0
- package/src/core/atoms/mission/show.js +26 -0
- package/src/core/atoms/notify/checkpoint.js +46 -0
- package/src/core/atoms/phase-run/complete.js +42 -0
- package/src/core/atoms/phase-run/fail.js +40 -0
- package/src/core/atoms/phase-run/persist.js +39 -0
- package/src/core/atoms/phase-run/start.js +30 -0
- package/src/core/atoms/plugin/run.js +76 -0
- package/src/core/atoms/policy/gate.js +90 -0
- package/src/core/atoms/policy/resolve.js +11 -0
- package/src/core/atoms/storage/sync.js +57 -0
- package/src/core/atoms/workflow/advance.js +25 -0
- package/src/core/atoms/workflow/guard.js +127 -0
- package/src/core/atoms/workflow/state-load.js +37 -0
- package/src/core/bindings.js +37 -0
- package/src/core/change-artifacts.js +134 -0
- package/src/core/competitive-score.js +265 -0
- package/src/core/concurrency-policy.js +35 -0
- package/src/core/execution-log.js +108 -0
- package/src/core/intake-recommendation.js +134 -0
- package/src/core/memory-state.js +62 -0
- package/src/core/mission-state.js +780 -0
- package/src/core/pending-gates.js +66 -0
- package/src/core/phase-cost.js +116 -0
- package/src/core/phase-run-read.js +27 -0
- package/src/core/policy-overlay.js +180 -0
- package/src/core/state-store.js +201 -0
- package/src/core/workflow-executor.js +1014 -0
- package/src/core/workflow-integrity.js +111 -0
- package/src/core/workflow-loader.js +163 -0
- package/src/core/workflow-rerun.js +84 -0
- package/src/core/workflow-state.js +119 -0
- package/src/server.js +1307 -0
- package/workflows/corps.yaml +481 -0
- package/workflows/yolo.yaml +241 -0
- package/xflow/README.md +76 -0
- package/xflow/aha/SKILL.md +82 -0
- package/xflow/aha/scripts/append_aha.py +50 -0
- package/xflow/archive/scripts/archive_change.py +338 -0
- package/xflow/archive/templates/merge-design.md +54 -0
- package/xflow/archive/templates/merge-workflow.md +26 -0
- package/xflow/atoms/__init__.py +6 -0
- package/xflow/atoms/a1_issue_create_or_patch.py +70 -0
- package/xflow/atoms/a2_branch_create_and_link.py +42 -0
- package/xflow/atoms/a3_repo_doctor.py +11 -0
- package/xflow/atoms/a4_project_set_status.py +11 -0
- package/xflow/atoms/a5_archive_commit_push_close.py +11 -0
- package/xflow/atoms/a6_pr_create.py +72 -0
- package/xflow/atoms/b1_change_scaffold.py +11 -0
- package/xflow/atoms/b2_status_read.py +16 -0
- package/xflow/atoms/b2b_status_write.py +20 -0
- package/xflow/atoms/b3_status_transition.py +27 -0
- package/xflow/atoms/b4_handoff_scaffold_or_refresh.py +11 -0
- package/xflow/atoms/b5_aha_append.py +11 -0
- package/xflow/atoms/b6_takein_digest.py +11 -0
- package/xflow/atoms/b6b_lookback_digest.py +11 -0
- package/xflow/atoms/b7_change_mission_bind.py +43 -0
- package/xflow/atoms/c1_mem.py +54 -0
- package/xflow/atoms/e6_gate_local_precheck.py +23 -0
- package/xflow/atoms/h1_design_lite_gate.py +74 -0
- package/xflow/atoms/i6a_tdd_run.py +107 -0
- package/xflow/atoms/i6b_tdd_proof_validate.py +184 -0
- package/xflow/atoms/i6c_tdd_quality_review.py +372 -0
- package/xflow/atoms/j1_tests_run.py +129 -0
- package/xflow/atoms/j4a_spec_consistency_structural.py +105 -0
- package/xflow/atoms/j4b_spec_delta_review.py +316 -0
- package/xflow/atoms/j4c_openspec_migration_map.py +121 -0
- package/xflow/atoms/j4d_spec_clarify_gate.py +373 -0
- package/xflow/atoms/j6_cr_review.py +443 -0
- package/xflow/atoms/k1_artifacts_complete_check.py +39 -0
- package/xflow/atoms/k2_merge_snippets_apply.py +11 -0
- package/xflow/atoms/k3_handoff_refresh.py +14 -0
- package/xflow/atoms/k4_mem_lesson_persist.py +65 -0
- package/xflow/atoms/k5_archive_publish.py +21 -0
- package/xflow/atoms/k6_aha_merge.py +42 -0
- package/xflow/atoms/tdd_lib.py +956 -0
- package/xflow/corps/SKILL.md +109 -0
- package/xflow/exec/scripts/set_issue_in_progress.py +159 -0
- package/xflow/goal/SKILL.md +150 -0
- package/xflow/handoff/SKILL.md +95 -0
- package/xflow/handoff/scripts/scaffold_handoff.py +304 -0
- package/xflow/lookback/SKILL.md +66 -0
- package/xflow/lookback/scripts/lookback_digest.py +46 -0
- package/xflow/openissue/scripts/open_issue_flow.py +438 -0
- package/xflow/plan/SKILL.md +47 -0
- package/xflow/ralph/SKILL.md +123 -0
- package/xflow/scripts/check_installed_xflow_skill_sync.sh +44 -0
- package/xflow/scripts/common.py +483 -0
- package/xflow/scripts/doctor.py +278 -0
- package/xflow/scripts/sync_installed_xflow_skill.sh +134 -0
- package/xflow/takein/SKILL.md +81 -0
- package/xflow/takein/scripts/takein_digest.py +88 -0
- package/xflow/yolo/SKILL.md +75 -0
- package/xflow/yolo/scripts/scaffold_specs_change.py +673 -0
- package/xflow/yolo/scripts/yolo_gate.py +312 -0
- package/xflow/yolo/templates/DESIGN.md +179 -0
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
import argparse
|
|
3
|
+
import json
|
|
4
|
+
import re
|
|
5
|
+
import subprocess
|
|
6
|
+
from datetime import date, datetime, timezone
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
CURRENT_START = "<!-- xflow:handoff-current:start -->"
|
|
11
|
+
CURRENT_END = "<!-- xflow:handoff-current:end -->"
|
|
12
|
+
VERIFIED_START = "<!-- xflow:handoff-verified:start -->"
|
|
13
|
+
VERIFIED_END = "<!-- xflow:handoff-verified:end -->"
|
|
14
|
+
|
|
15
|
+
TEMPLATE = """# {title} Handoff
|
|
16
|
+
|
|
17
|
+
Date: {today}
|
|
18
|
+
|
|
19
|
+
## Current State
|
|
20
|
+
|
|
21
|
+
- Fill in the latest high-signal status.
|
|
22
|
+
|
|
23
|
+
## Context Pack
|
|
24
|
+
|
|
25
|
+
### TL;DR
|
|
26
|
+
|
|
27
|
+
- Objective:
|
|
28
|
+
- Current status:
|
|
29
|
+
- Next action:
|
|
30
|
+
- Key risks:
|
|
31
|
+
|
|
32
|
+
### Key Facts
|
|
33
|
+
|
|
34
|
+
- (Anchor to code/paths/commands/numbers/dates)
|
|
35
|
+
|
|
36
|
+
### Decisions
|
|
37
|
+
|
|
38
|
+
- (Decision + brief rationale)
|
|
39
|
+
|
|
40
|
+
### Commands
|
|
41
|
+
|
|
42
|
+
- (High-value commands, include caveats)
|
|
43
|
+
|
|
44
|
+
### Pointers
|
|
45
|
+
|
|
46
|
+
- (Key files/dirs + 1-line description)
|
|
47
|
+
|
|
48
|
+
### Gotchas
|
|
49
|
+
|
|
50
|
+
- (Operational sharp edges)
|
|
51
|
+
|
|
52
|
+
## Project Background
|
|
53
|
+
|
|
54
|
+
- What this project is and why it exists.
|
|
55
|
+
|
|
56
|
+
## Architecture
|
|
57
|
+
|
|
58
|
+
- Core system shape.
|
|
59
|
+
- Non-negotiable invariants.
|
|
60
|
+
|
|
61
|
+
## Current Objective
|
|
62
|
+
|
|
63
|
+
- The specific task or delivery target the next session is inheriting.
|
|
64
|
+
|
|
65
|
+
## Progress
|
|
66
|
+
|
|
67
|
+
- What has been completed and what was verified.
|
|
68
|
+
|
|
69
|
+
## Open Problems
|
|
70
|
+
|
|
71
|
+
- Current blockers, risks, unknowns, and failure modes.
|
|
72
|
+
|
|
73
|
+
## Lessons Learned
|
|
74
|
+
|
|
75
|
+
- Operational gotchas, failed approaches, and patterns worth preserving.
|
|
76
|
+
|
|
77
|
+
## Recommendations
|
|
78
|
+
|
|
79
|
+
1. First recommended next action.
|
|
80
|
+
2. Second recommended next action.
|
|
81
|
+
|
|
82
|
+
## Key Files
|
|
83
|
+
|
|
84
|
+
- `HANDOFF.md`
|
|
85
|
+
"""
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def read_json(path: Path) -> dict:
|
|
89
|
+
if not path.exists():
|
|
90
|
+
return {}
|
|
91
|
+
try:
|
|
92
|
+
return json.loads(path.read_text(encoding="utf-8"))
|
|
93
|
+
except json.JSONDecodeError:
|
|
94
|
+
return {}
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def git_value(project_root: Path, args: list[str], fallback: str = "unknown") -> str:
|
|
98
|
+
try:
|
|
99
|
+
result = subprocess.run(
|
|
100
|
+
["git", *args],
|
|
101
|
+
cwd=str(project_root),
|
|
102
|
+
text=True,
|
|
103
|
+
capture_output=True,
|
|
104
|
+
check=True,
|
|
105
|
+
)
|
|
106
|
+
except Exception:
|
|
107
|
+
return fallback
|
|
108
|
+
return result.stdout.strip() or fallback
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def format_issue(status: dict) -> str:
|
|
112
|
+
if status.get("issue_url"):
|
|
113
|
+
return str(status["issue_url"])
|
|
114
|
+
repo = status.get("issue_repo") or status.get("target_repo")
|
|
115
|
+
issue_number = status.get("issue_number")
|
|
116
|
+
if repo and issue_number:
|
|
117
|
+
return f"{repo}#{issue_number}"
|
|
118
|
+
return "none"
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def build_current_state_block(project_root: Path, change_id: str | None) -> str:
|
|
122
|
+
status = read_json(project_root / "specs" / "changes" / str(change_id or "") / "status.json")
|
|
123
|
+
branch = git_value(project_root, ["branch", "--show-current"], status.get("branch_name") or "unknown")
|
|
124
|
+
resolved_change_id = status.get("change_id") or change_id or "none"
|
|
125
|
+
updated_at = datetime.now(timezone.utc).replace(microsecond=0).isoformat().replace("+00:00", "Z")
|
|
126
|
+
lines = [
|
|
127
|
+
CURRENT_START,
|
|
128
|
+
f"- Updated: {updated_at}",
|
|
129
|
+
f"- Git branch: `{branch}`",
|
|
130
|
+
f"- Change: `{resolved_change_id}`",
|
|
131
|
+
f"- Title: {status.get('title') or 'none'}",
|
|
132
|
+
f"- Stage / status: `{status.get('current_stage') or 'unknown'}` / `{status.get('status') or 'unknown'}`",
|
|
133
|
+
f"- Verification: `{status.get('verification_status') or 'unknown'}`",
|
|
134
|
+
f"- Archive: `{status.get('archival_status') or 'unknown'}`",
|
|
135
|
+
f"- Issue: {format_issue(status)}",
|
|
136
|
+
f"- Next action: {status.get('next_action') or 'none'}",
|
|
137
|
+
CURRENT_END,
|
|
138
|
+
]
|
|
139
|
+
return "\n".join(lines)
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
def code_span(value: object) -> str:
|
|
143
|
+
text = str(value)
|
|
144
|
+
if "`" not in text:
|
|
145
|
+
return f"`{text}`"
|
|
146
|
+
return f"`` {text} ``"
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
def build_verified_commands_block(project_root: Path, change_id: str | None) -> str:
|
|
150
|
+
status = read_json(project_root / "specs" / "changes" / str(change_id or "") / "status.json")
|
|
151
|
+
proof = read_json(project_root / "specs" / "changes" / str(change_id or "") / "verify_proof.json")
|
|
152
|
+
results = proof.get("results") if isinstance(proof.get("results"), list) else []
|
|
153
|
+
resolved_change_id = status.get("change_id") or change_id or "none"
|
|
154
|
+
updated_at = datetime.now(timezone.utc).replace(microsecond=0).isoformat().replace("+00:00", "Z")
|
|
155
|
+
passed_count = sum(1 for result in results if result.get("passed"))
|
|
156
|
+
total_count = len(results)
|
|
157
|
+
summary = f"{passed_count}/{total_count} passed" if total_count else "no verify_proof results"
|
|
158
|
+
|
|
159
|
+
lines = [
|
|
160
|
+
VERIFIED_START,
|
|
161
|
+
f"- Updated: {updated_at}",
|
|
162
|
+
f"- Change: `{resolved_change_id}`",
|
|
163
|
+
f"- Verification: `{status.get('verification_status') or 'unknown'}`",
|
|
164
|
+
f"- Proof recorded: `{proof.get('recorded_at') or 'unknown'}`",
|
|
165
|
+
f"- Summary: `{summary}`",
|
|
166
|
+
]
|
|
167
|
+
if total_count:
|
|
168
|
+
lines.append("- Commands:")
|
|
169
|
+
for result in results:
|
|
170
|
+
verdict = "PASS" if result.get("passed") else "FAIL"
|
|
171
|
+
command = code_span(result.get("command") or "unknown")
|
|
172
|
+
exit_code = code_span(result.get("exit_code", "unknown"))
|
|
173
|
+
lines.append(f" - {verdict} {command} (exit {exit_code})")
|
|
174
|
+
else:
|
|
175
|
+
lines.append("- Commands: none recorded")
|
|
176
|
+
lines.append(VERIFIED_END)
|
|
177
|
+
return "\n".join(lines)
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
def upsert_current_state(content: str, block: str) -> str:
|
|
181
|
+
managed_pattern = re.compile(
|
|
182
|
+
rf"{re.escape(CURRENT_START)}.*?{re.escape(CURRENT_END)}",
|
|
183
|
+
re.DOTALL,
|
|
184
|
+
)
|
|
185
|
+
if managed_pattern.search(content):
|
|
186
|
+
return managed_pattern.sub(block, content, count=1)
|
|
187
|
+
|
|
188
|
+
heading_pattern = re.compile(r"(^## Current State\s*\n)", re.MULTILINE)
|
|
189
|
+
heading_match = heading_pattern.search(content)
|
|
190
|
+
if heading_match:
|
|
191
|
+
section_start = heading_match.start()
|
|
192
|
+
body_start = heading_match.end()
|
|
193
|
+
next_heading = re.search(r"^## ", content[body_start:], re.MULTILINE)
|
|
194
|
+
section_end = body_start + next_heading.start() if next_heading else len(content)
|
|
195
|
+
return (
|
|
196
|
+
content[:section_start]
|
|
197
|
+
+ "## Current State\n\n"
|
|
198
|
+
+ block
|
|
199
|
+
+ "\n\n"
|
|
200
|
+
+ content[section_end:].lstrip("\n")
|
|
201
|
+
)
|
|
202
|
+
|
|
203
|
+
first_heading = re.match(r"^# .*\n", content)
|
|
204
|
+
if first_heading:
|
|
205
|
+
insert_at = first_heading.end()
|
|
206
|
+
return content[:insert_at] + "\n## Current State\n\n" + block + "\n" + content[insert_at:]
|
|
207
|
+
return "## Current State\n\n" + block + "\n\n" + content
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
def upsert_verified_commands(content: str, block: str) -> str:
|
|
211
|
+
managed_pattern = re.compile(
|
|
212
|
+
rf"{re.escape(VERIFIED_START)}.*?{re.escape(VERIFIED_END)}",
|
|
213
|
+
re.DOTALL,
|
|
214
|
+
)
|
|
215
|
+
if managed_pattern.search(content):
|
|
216
|
+
return managed_pattern.sub(block, content, count=1)
|
|
217
|
+
|
|
218
|
+
heading_pattern = re.compile(r"(^## Latest Verified Commands(?: and Results)?\s*\n)", re.MULTILINE)
|
|
219
|
+
heading_match = heading_pattern.search(content)
|
|
220
|
+
if heading_match:
|
|
221
|
+
section_start = heading_match.start()
|
|
222
|
+
body_start = heading_match.end()
|
|
223
|
+
next_heading = re.search(r"^## ", content[body_start:], re.MULTILINE)
|
|
224
|
+
section_end = body_start + next_heading.start() if next_heading else len(content)
|
|
225
|
+
return (
|
|
226
|
+
content[:section_start]
|
|
227
|
+
+ "## Latest Verified Commands\n\n"
|
|
228
|
+
+ block
|
|
229
|
+
+ "\n\n"
|
|
230
|
+
+ content[section_end:].lstrip("\n")
|
|
231
|
+
)
|
|
232
|
+
|
|
233
|
+
current_section = re.search(r"^## Current State\s*\n", content, re.MULTILINE)
|
|
234
|
+
if current_section:
|
|
235
|
+
body_start = current_section.end()
|
|
236
|
+
next_heading = re.search(r"^## ", content[body_start:], re.MULTILINE)
|
|
237
|
+
insert_at = body_start + next_heading.start() if next_heading else len(content)
|
|
238
|
+
return (
|
|
239
|
+
content[:insert_at].rstrip()
|
|
240
|
+
+ "\n\n## Latest Verified Commands\n\n"
|
|
241
|
+
+ block
|
|
242
|
+
+ "\n\n"
|
|
243
|
+
+ content[insert_at:].lstrip("\n")
|
|
244
|
+
)
|
|
245
|
+
|
|
246
|
+
first_heading = re.match(r"^# .*\n", content)
|
|
247
|
+
if first_heading:
|
|
248
|
+
insert_at = first_heading.end()
|
|
249
|
+
return content[:insert_at] + "\n## Latest Verified Commands\n\n" + block + "\n" + content[insert_at:]
|
|
250
|
+
return "## Latest Verified Commands\n\n" + block + "\n\n" + content
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
def parse_args() -> argparse.Namespace:
|
|
254
|
+
parser = argparse.ArgumentParser(
|
|
255
|
+
description="Create or overwrite the canonical root HANDOFF.md scaffold.",
|
|
256
|
+
)
|
|
257
|
+
parser.add_argument(
|
|
258
|
+
"--project-root",
|
|
259
|
+
default=".",
|
|
260
|
+
help="Project root where HANDOFF.md should be written.",
|
|
261
|
+
)
|
|
262
|
+
parser.add_argument(
|
|
263
|
+
"--title",
|
|
264
|
+
default=None,
|
|
265
|
+
help="Project title for the handoff heading. Defaults to the project root directory name.",
|
|
266
|
+
)
|
|
267
|
+
parser.add_argument(
|
|
268
|
+
"--refresh",
|
|
269
|
+
action="store_true",
|
|
270
|
+
help="Keep an existing HANDOFF.md intact. Create a scaffold only when missing.",
|
|
271
|
+
)
|
|
272
|
+
parser.add_argument(
|
|
273
|
+
"--change-id",
|
|
274
|
+
default=None,
|
|
275
|
+
help="Accepted for atom-run compatibility; not used by this scaffold.",
|
|
276
|
+
)
|
|
277
|
+
return parser.parse_args()
|
|
278
|
+
|
|
279
|
+
|
|
280
|
+
def main() -> int:
|
|
281
|
+
args = parse_args()
|
|
282
|
+
project_root = Path(args.project_root).expanduser().resolve()
|
|
283
|
+
title = args.title or project_root.name
|
|
284
|
+
handoff_path = project_root / "HANDOFF.md"
|
|
285
|
+
project_root.mkdir(parents=True, exist_ok=True)
|
|
286
|
+
if args.refresh and handoff_path.exists():
|
|
287
|
+
if args.change_id:
|
|
288
|
+
content = handoff_path.read_text(encoding="utf-8")
|
|
289
|
+
content = upsert_current_state(content, build_current_state_block(project_root, args.change_id))
|
|
290
|
+
content = upsert_verified_commands(content, build_verified_commands_block(project_root, args.change_id))
|
|
291
|
+
handoff_path.write_text(content, encoding="utf-8")
|
|
292
|
+
print(str(handoff_path))
|
|
293
|
+
return 0
|
|
294
|
+
content = TEMPLATE.format(title=title, today=date.today().isoformat())
|
|
295
|
+
if args.change_id:
|
|
296
|
+
content = upsert_current_state(content, build_current_state_block(project_root, args.change_id))
|
|
297
|
+
content = upsert_verified_commands(content, build_verified_commands_block(project_root, args.change_id))
|
|
298
|
+
handoff_path.write_text(content, encoding="utf-8")
|
|
299
|
+
print(str(handoff_path))
|
|
300
|
+
return 0
|
|
301
|
+
|
|
302
|
+
|
|
303
|
+
if __name__ == "__main__":
|
|
304
|
+
raise SystemExit(main())
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: xflow:lookback
|
|
3
|
+
description: 通过读取项目根目录的 `AHA.md`、`HANDOFF.md` 和核心设计文档,重新加载最相关的持久上下文,用于识别当前工作与既有决策之间是否发生偏移。适用于验证新方向是否仍符合过去的关键认知、设计意图或 handoff 事实时。
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Superflow Lookback
|
|
7
|
+
|
|
8
|
+
Use this skill to check whether current design or implementation has drifted from recorded insight.
|
|
9
|
+
|
|
10
|
+
Primary sources:
|
|
11
|
+
|
|
12
|
+
- project root `AHA.md`
|
|
13
|
+
- project root `HANDOFF.md`
|
|
14
|
+
- project root `DESIGN.md` (if frontend project)
|
|
15
|
+
- core design docs under `docs/plans/`
|
|
16
|
+
|
|
17
|
+
## Goal
|
|
18
|
+
|
|
19
|
+
Answer one question:
|
|
20
|
+
|
|
21
|
+
`Is the current direction still aligned with the strongest prior reasoning?`
|
|
22
|
+
|
|
23
|
+
## Workflow
|
|
24
|
+
|
|
25
|
+
1. Read `AHA.md` if present.
|
|
26
|
+
2. Read `HANDOFF.md`.
|
|
27
|
+
3. Read `DESIGN.md` if present (check design decisions).
|
|
28
|
+
4. Read only the directly relevant design docs in `docs/plans/`.
|
|
29
|
+
5. Compare current design / code / proposal against:
|
|
30
|
+
- active aha constraints
|
|
31
|
+
- frozen design intent
|
|
32
|
+
- design decisions log (if UI)
|
|
33
|
+
- current handoff truth
|
|
34
|
+
6. Produce a concise drift report:
|
|
35
|
+
- aligned
|
|
36
|
+
- partially drifted
|
|
37
|
+
- materially drifted
|
|
38
|
+
7. For any drift, name the exact file or design surface that drifted.
|
|
39
|
+
|
|
40
|
+
## Output requirements
|
|
41
|
+
|
|
42
|
+
Every lookback should include:
|
|
43
|
+
|
|
44
|
+
1. current target
|
|
45
|
+
2. prior anchors consulted
|
|
46
|
+
3. alignment verdict
|
|
47
|
+
4. concrete drift points, if any
|
|
48
|
+
5. recommended correction
|
|
49
|
+
|
|
50
|
+
## Guardrails
|
|
51
|
+
|
|
52
|
+
- Do not read the whole repo by default.
|
|
53
|
+
- Prefer high-signal docs over broad exploration.
|
|
54
|
+
- Treat `AHA.md` as durable reasoning, not optional commentary.
|
|
55
|
+
- If `AHA.md` and `HANDOFF.md` conflict, surface the conflict explicitly.
|
|
56
|
+
|
|
57
|
+
## Script
|
|
58
|
+
|
|
59
|
+
The agent autonomously calls this helper for source listing:
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
python3 ~/.codex/skills/xflow/lookback/scripts/lookback_digest.py
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Agent uses output to prioritize which docs to read.
|
|
66
|
+
User invokes skill, agent handles execution.
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
import argparse
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def parse_args() -> argparse.Namespace:
|
|
7
|
+
parser = argparse.ArgumentParser(
|
|
8
|
+
description="List the canonical lookback sources for a project.",
|
|
9
|
+
)
|
|
10
|
+
parser.add_argument(
|
|
11
|
+
"--project-root",
|
|
12
|
+
default=".",
|
|
13
|
+
help="Project root containing HANDOFF.md, optional AHA.md, and docs/plans.",
|
|
14
|
+
)
|
|
15
|
+
return parser.parse_args()
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def top_design_docs(plans_dir: Path, limit: int = 8) -> list[Path]:
|
|
19
|
+
if not plans_dir.exists():
|
|
20
|
+
return []
|
|
21
|
+
docs = sorted(
|
|
22
|
+
[path for path in plans_dir.glob("*.md") if path.is_file()],
|
|
23
|
+
key=lambda path: path.stat().st_mtime,
|
|
24
|
+
reverse=True,
|
|
25
|
+
)
|
|
26
|
+
return docs[:limit]
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def main() -> int:
|
|
30
|
+
args = parse_args()
|
|
31
|
+
root = Path(args.project_root).expanduser().resolve()
|
|
32
|
+
handoff = root / "HANDOFF.md"
|
|
33
|
+
aha = root / "AHA.md"
|
|
34
|
+
plans = root / "docs" / "plans"
|
|
35
|
+
|
|
36
|
+
print(f"PROJECT_ROOT: {root}")
|
|
37
|
+
print(f"HANDOFF: {'present' if handoff.exists() else 'missing'} {handoff}")
|
|
38
|
+
print(f"AHA: {'present' if aha.exists() else 'missing'} {aha}")
|
|
39
|
+
print("DESIGN_DOCS:")
|
|
40
|
+
for doc in top_design_docs(plans):
|
|
41
|
+
print(f"- {doc}")
|
|
42
|
+
return 0
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
if __name__ == "__main__":
|
|
46
|
+
raise SystemExit(main())
|