From 7fec02ff77e80814713b2d189c59fdb8954fd7de Mon Sep 17 00:00:00 2001 From: xlsea Date: Sat, 23 May 2026 10:25:03 +0800 Subject: [PATCH] =?UTF-8?q?docs(other):=20=E6=9B=B4=E6=96=B0=20Vibe=20Codi?= =?UTF-8?q?ng=20=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .claude/CLAUDE.md | 59 ++-- .claude/agents/builder.md | 13 - .claude/agents/evaluator.md | 54 ++++ .claude/agents/explorer.md | 11 - .claude/agents/generator.md | 61 ++++ .claude/agents/validator.md | 12 - .claude/commands/vibe-codex.md | 15 - .claude/commands/vibe-dev.md | 14 - .claude/commands/vibe-init.md | 11 - .claude/commands/vibe-resume.md | 10 - .claude/commands/vibe-status.md | 11 - .claude/hooks/compact-restore.cjs | 65 ++++ .claude/hooks/compact-snapshot.cjs | 63 ++++ .claude/hooks/context-loader.cjs | 37 --- .claude/hooks/delivery-gate.cjs | 248 +++++++++----- .claude/hooks/index-updater.cjs | 175 ++++++++++ .claude/hooks/pace-continuator.cjs | 136 ++++++++ .claude/hooks/permission-retry.cjs | 59 ++++ .claude/hooks/post-edit.cjs | 19 -- .claude/hooks/pre-bash-guard.cjs | 54 ++++ .claude/hooks/pre-bash.cjs | 26 -- .claude/hooks/session-start.cjs | 122 +++++++ .claude/hooks/state-audit.cjs | 94 ++++++ .claude/hooks/stop-failure.cjs | 18 -- .claude/hooks/subagent-retry.cjs | 70 ++++ .claude/hooks/tdd-check.cjs | 21 -- .claude/settings.json | 180 ++++++++--- .claude/skills/athena-dev/SKILL.md | 30 ++ .claude/skills/athena-init/SKILL.md | 104 ++++++ .claude/skills/athena-migrate/SKILL.md | 203 ++++++++++++ .claude/skills/athena-preferences/SKILL.md | 138 ++++++++ .claude/skills/athena-review/SKILL.md | 15 + .claude/skills/athena-setup/SKILL.md | 154 +++++++++ .claude/skills/athena-status/SKILL.md | 119 +++++++ .claude/skills/brainstorm/SKILL.md | 38 --- .claude/skills/code-review/SKILL.md | 23 -- .claude/skills/codex-comm/SKILL.md | 45 --- .../codex-comm/scripts/codex-bridge.cjs | 109 ------- .claude/skills/codex-review/SKILL.md | 64 ---- .../codex-review/templates/api-review.md | 11 - .../codex-review/templates/frontend-review.md | 10 - .../codex-review/templates/general-review.md | 10 - .../codex-review/templates/infra-review.md | 10 - .claude/skills/compound/SKILL.md | 47 +++ .claude/skills/context7/SKILL.md | 15 - .claude/skills/debugging/SKILL.md | 27 -- .claude/skills/e2e/SKILL.md | 18 -- .claude/skills/finish-branch/SKILL.md | 35 -- .claude/skills/kaizen/SKILL.md | 23 -- .claude/skills/pace/SKILL.md | 304 ++++++++++++++++++ .claude/skills/pace/context-essentials.md | 44 +++ .claude/skills/pace/templates/_index.md | 66 ++++ .../skills/pace/templates/details/design.md | 28 ++ .../skills/pace/templates/details/handoff.md | 31 ++ .../skills/pace/templates/details/lessons.md | 21 ++ .claude/skills/pace/templates/details/next.md | 15 + .../skills/pace/templates/details/progress.md | 15 + .../pace/templates/details/proposals.md | 20 ++ .../pace/templates/details/review-report.md | 41 +++ .../pace/templates/details/tasks-current.md | 19 ++ .../skills/pace/templates/goal-conditions.md | 121 +++++++ .claude/skills/pace/templates/init.sh | 17 + .claude/skills/plan-first/SKILL.md | 26 -- .claude/skills/reflexion/SKILL.md | 27 -- .claude/skills/security-review/SKILL.md | 16 - .claude/skills/verification/SKILL.md | 20 -- .claude/templates/ai-state/conventions.md | 12 - .claude/templates/ai-state/design.md | 24 -- .claude/templates/ai-state/doing.md | 7 - .claude/templates/ai-state/lessons.md | 10 - .claude/templates/ai-state/plan.md | 14 - .claude/templates/ai-state/quality.md | 18 -- .claude/templates/ai-state/session.md | 13 - .claude/workflows/pace.md | 13 - .claude/workflows/riper-7.md | 71 ---- .codex/AGENTS.md | 67 ++-- .codex/agents/builder.toml | 4 - .codex/agents/evaluator.toml | 47 +++ .codex/agents/explorer.toml | 4 - .codex/agents/generator.toml | 62 ++++ .codex/agents/reviewer.toml | 62 ++++ .codex/agents/validator.toml | 4 - .codex/config.toml | 154 +++++---- .codex/hooks/delivery-gate.py | 167 ++++++++++ .codex/hooks/index-updater.py | 167 ++++++++++ .codex/hooks/pre-bash-guard.py | 66 ++++ .codex/hooks/session-start.py | 121 +++++++ .codex/hooks/subagent-retry.py | 71 ++++ .codex/hooks/user-prompt-submit.py | 76 +++++ .codex/prompts/athena-dev.md | 24 ++ .codex/prompts/athena-init.md | 67 ++++ .codex/prompts/athena-migrate.md | 155 +++++++++ .codex/prompts/athena-preferences.md | 80 +++++ .codex/prompts/athena-review.md | 12 + .codex/prompts/athena-setup.md | 98 ++++++ .codex/prompts/athena-status.md | 32 ++ .codex/skills/brainstorm/SKILL.md | 25 -- .codex/skills/claude-delegate/SKILL.md | 18 -- .codex/skills/code-review/SKILL.md | 18 -- .codex/skills/compound/SKILL.md | 47 +++ .codex/skills/context7/SKILL.md | 9 - .codex/skills/debugging/SKILL.md | 12 - .codex/skills/e2e/SKILL.md | 9 - .codex/skills/finish-branch/SKILL.md | 14 - .codex/skills/kaizen/SKILL.md | 11 - .codex/skills/pace/SKILL.md | 97 ++++++ .codex/skills/pace/context-essentials.md | 32 ++ .codex/skills/pace/templates/_index.md | 66 ++++ .../skills/pace/templates/details/design.md | 28 ++ .../skills/pace/templates/details/handoff.md | 31 ++ .../skills/pace/templates/details/lessons.md | 21 ++ .codex/skills/pace/templates/details/next.md | 15 + .../skills/pace/templates/details/progress.md | 15 + .../pace/templates/details/proposals.md | 20 ++ .../pace/templates/details/review-report.md | 41 +++ .../pace/templates/details/tasks-current.md | 19 ++ .../skills/pace/templates/goal-conditions.md | 121 +++++++ .codex/skills/pace/templates/init.sh | 17 + .codex/skills/plan-first/SKILL.md | 21 -- .codex/skills/reflexion/SKILL.md | 19 -- .codex/skills/security-review/SKILL.md | 8 - .codex/skills/verification/SKILL.md | 16 - .codex/templates/ai-state/conventions.md | 9 - .codex/templates/ai-state/design.md | 19 -- .codex/templates/ai-state/doing.md | 7 - .codex/templates/ai-state/lessons.md | 7 - .codex/templates/ai-state/plan.md | 7 - .codex/templates/ai-state/quality.md | 13 - .codex/templates/ai-state/session.md | 10 - .codex/workflows/pace.md | 13 - .codex/workflows/riper-7.md | 57 ---- .gitignore | 1 + 132 files changed, 4833 insertions(+), 1519 deletions(-) delete mode 100644 .claude/agents/builder.md create mode 100644 .claude/agents/evaluator.md delete mode 100644 .claude/agents/explorer.md create mode 100644 .claude/agents/generator.md delete mode 100644 .claude/agents/validator.md delete mode 100644 .claude/commands/vibe-codex.md delete mode 100644 .claude/commands/vibe-dev.md delete mode 100644 .claude/commands/vibe-init.md delete mode 100644 .claude/commands/vibe-resume.md delete mode 100644 .claude/commands/vibe-status.md create mode 100755 .claude/hooks/compact-restore.cjs create mode 100755 .claude/hooks/compact-snapshot.cjs delete mode 100644 .claude/hooks/context-loader.cjs mode change 100644 => 100755 .claude/hooks/delivery-gate.cjs create mode 100755 .claude/hooks/index-updater.cjs create mode 100755 .claude/hooks/pace-continuator.cjs create mode 100755 .claude/hooks/permission-retry.cjs delete mode 100644 .claude/hooks/post-edit.cjs create mode 100755 .claude/hooks/pre-bash-guard.cjs delete mode 100644 .claude/hooks/pre-bash.cjs create mode 100755 .claude/hooks/session-start.cjs create mode 100755 .claude/hooks/state-audit.cjs delete mode 100644 .claude/hooks/stop-failure.cjs create mode 100755 .claude/hooks/subagent-retry.cjs delete mode 100644 .claude/hooks/tdd-check.cjs create mode 100644 .claude/skills/athena-dev/SKILL.md create mode 100644 .claude/skills/athena-init/SKILL.md create mode 100644 .claude/skills/athena-migrate/SKILL.md create mode 100644 .claude/skills/athena-preferences/SKILL.md create mode 100644 .claude/skills/athena-review/SKILL.md create mode 100644 .claude/skills/athena-setup/SKILL.md create mode 100644 .claude/skills/athena-status/SKILL.md delete mode 100644 .claude/skills/brainstorm/SKILL.md delete mode 100644 .claude/skills/code-review/SKILL.md delete mode 100644 .claude/skills/codex-comm/SKILL.md delete mode 100644 .claude/skills/codex-comm/scripts/codex-bridge.cjs delete mode 100644 .claude/skills/codex-review/SKILL.md delete mode 100644 .claude/skills/codex-review/templates/api-review.md delete mode 100644 .claude/skills/codex-review/templates/frontend-review.md delete mode 100644 .claude/skills/codex-review/templates/general-review.md delete mode 100644 .claude/skills/codex-review/templates/infra-review.md create mode 100644 .claude/skills/compound/SKILL.md delete mode 100644 .claude/skills/context7/SKILL.md delete mode 100644 .claude/skills/debugging/SKILL.md delete mode 100644 .claude/skills/e2e/SKILL.md delete mode 100644 .claude/skills/finish-branch/SKILL.md delete mode 100644 .claude/skills/kaizen/SKILL.md create mode 100644 .claude/skills/pace/SKILL.md create mode 100644 .claude/skills/pace/context-essentials.md create mode 100644 .claude/skills/pace/templates/_index.md create mode 100644 .claude/skills/pace/templates/details/design.md create mode 100644 .claude/skills/pace/templates/details/handoff.md create mode 100644 .claude/skills/pace/templates/details/lessons.md create mode 100644 .claude/skills/pace/templates/details/next.md create mode 100644 .claude/skills/pace/templates/details/progress.md create mode 100644 .claude/skills/pace/templates/details/proposals.md create mode 100644 .claude/skills/pace/templates/details/review-report.md create mode 100644 .claude/skills/pace/templates/details/tasks-current.md create mode 100644 .claude/skills/pace/templates/goal-conditions.md create mode 100755 .claude/skills/pace/templates/init.sh delete mode 100644 .claude/skills/plan-first/SKILL.md delete mode 100644 .claude/skills/reflexion/SKILL.md delete mode 100644 .claude/skills/security-review/SKILL.md delete mode 100644 .claude/skills/verification/SKILL.md delete mode 100644 .claude/templates/ai-state/conventions.md delete mode 100644 .claude/templates/ai-state/design.md delete mode 100644 .claude/templates/ai-state/doing.md delete mode 100644 .claude/templates/ai-state/lessons.md delete mode 100644 .claude/templates/ai-state/plan.md delete mode 100644 .claude/templates/ai-state/quality.md delete mode 100644 .claude/templates/ai-state/session.md delete mode 100644 .claude/workflows/pace.md delete mode 100644 .claude/workflows/riper-7.md delete mode 100644 .codex/agents/builder.toml create mode 100644 .codex/agents/evaluator.toml delete mode 100644 .codex/agents/explorer.toml create mode 100644 .codex/agents/generator.toml create mode 100644 .codex/agents/reviewer.toml delete mode 100644 .codex/agents/validator.toml create mode 100755 .codex/hooks/delivery-gate.py create mode 100755 .codex/hooks/index-updater.py create mode 100755 .codex/hooks/pre-bash-guard.py create mode 100755 .codex/hooks/session-start.py create mode 100755 .codex/hooks/subagent-retry.py create mode 100755 .codex/hooks/user-prompt-submit.py create mode 100644 .codex/prompts/athena-dev.md create mode 100644 .codex/prompts/athena-init.md create mode 100644 .codex/prompts/athena-migrate.md create mode 100644 .codex/prompts/athena-preferences.md create mode 100644 .codex/prompts/athena-review.md create mode 100644 .codex/prompts/athena-setup.md create mode 100644 .codex/prompts/athena-status.md delete mode 100644 .codex/skills/brainstorm/SKILL.md delete mode 100644 .codex/skills/claude-delegate/SKILL.md delete mode 100644 .codex/skills/code-review/SKILL.md create mode 100644 .codex/skills/compound/SKILL.md delete mode 100644 .codex/skills/context7/SKILL.md delete mode 100644 .codex/skills/debugging/SKILL.md delete mode 100644 .codex/skills/e2e/SKILL.md delete mode 100644 .codex/skills/finish-branch/SKILL.md delete mode 100644 .codex/skills/kaizen/SKILL.md create mode 100644 .codex/skills/pace/SKILL.md create mode 100644 .codex/skills/pace/context-essentials.md create mode 100644 .codex/skills/pace/templates/_index.md create mode 100644 .codex/skills/pace/templates/details/design.md create mode 100644 .codex/skills/pace/templates/details/handoff.md create mode 100644 .codex/skills/pace/templates/details/lessons.md create mode 100644 .codex/skills/pace/templates/details/next.md create mode 100644 .codex/skills/pace/templates/details/progress.md create mode 100644 .codex/skills/pace/templates/details/proposals.md create mode 100644 .codex/skills/pace/templates/details/review-report.md create mode 100644 .codex/skills/pace/templates/details/tasks-current.md create mode 100644 .codex/skills/pace/templates/goal-conditions.md create mode 100644 .codex/skills/pace/templates/init.sh delete mode 100644 .codex/skills/plan-first/SKILL.md delete mode 100644 .codex/skills/reflexion/SKILL.md delete mode 100644 .codex/skills/security-review/SKILL.md delete mode 100644 .codex/skills/verification/SKILL.md delete mode 100644 .codex/templates/ai-state/conventions.md delete mode 100644 .codex/templates/ai-state/design.md delete mode 100644 .codex/templates/ai-state/doing.md delete mode 100644 .codex/templates/ai-state/lessons.md delete mode 100644 .codex/templates/ai-state/plan.md delete mode 100644 .codex/templates/ai-state/quality.md delete mode 100644 .codex/templates/ai-state/session.md delete mode 100644 .codex/workflows/pace.md delete mode 100644 .codex/workflows/riper-7.md diff --git a/.claude/CLAUDE.md b/.claude/CLAUDE.md index 7327f2c7..d1eb50be 100644 --- a/.claude/CLAUDE.md +++ b/.claude/CLAUDE.md @@ -1,37 +1,32 @@ -# VibeCoding Kernel v9.2.0 +# VibeCoding Athena v9.6 — PACE Router & State Harness -你是一个INTJ性格的工程化 AI 编码助手。用 P.A.C.E. 路由复杂度, 用 RIPER-7 编排阶段, 用 Skills 执行细节。 -**双 Agent 模式**: CC 编排全流程, P/E/T 阶段可调用 Codex CLI 协作。Codex 不可用时 100% 降级为 CC 独立。 + -## 框架地图 + +你是 VibeCoding Athena 工程 Agent。CC 做事,Athena 把关。 +- 收任务 → 走 PACE 路由 (Hotfix/Bugfix 直接做, Quick+ 走完整流程) +- 自己跑命令 / 跑测试 / 看输出, 证明工作完成 +- 工具失败 → 三次重试附 stderr 报告, 不让用户代执行 +- 技术结论 → 必须引用官方文档 / 源码 URL +- 调用 subagent 必须产生真实 tool_use, 不允许伪造完成 + -| 类别 | 文件 | 数量 | -| ---------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- | ---- | -| Workflows | pace.md, riper-7.md | 2 | -| Skills | brainstorm, plan-first, code-review, verification, debugging, kaizen, security-review, context7, e2e, codex-comm, codex-review, reflexion, finish-branch | 13 | -| Review Templates | api-review, frontend-review, infra-review, general-review | 4 | -| Hooks | context-loader, delivery-gate, pre-bash, post-edit, stop-failure, tdd-check | 6 | -| Commands | vibe-dev, vibe-status, vibe-resume, vibe-init, vibe-codex | 5 | -| Agents | builder, validator, explorer | 3 | -| State Templates | session, doing, design, plan, conventions, quality, lessons | 7 | +## 铁律 (9 条) -## 启动流程 +1. **设计先行** — 未确认不写代码 (Hotfix/Bugfix 例外) +2. **TDD 强制** — 先测试后实现 +3. **Sisyphus** — tasks 全完成才进审查 +4. **Review 强制** — Feature+ 至少一次交叉审查 +5. **文档即真相** — 阶段转换前 .ai_state/ 必须同步, 单一入口 `.ai_state/_index.md` +6. **完成度证据** — 报告"完成"必须附 tool_use ID 或命令输出片段 +7. **出处优先** — API 形态 / 配置字段 / 协议格式必须引用官方文档或源码 URL +8. **索引先行** — 进入决策先读 `.ai_state/_index.md`, 禁止 glob 全目录扫描 +9. **Hook 是进化器** — 在 Stop 时反思并写 `.ai_state/details/proposals.md` -1. SessionStart → context-loader.cjs → 检测 .ai_state/ → 恢复或初始化 -2. 读 conventions.md (含 "Agent 易犯错误" 段) → 输出提醒 -3. 评估任务 → P.A.C.E. 路由 → RIPER-7 执行 - -## 关键规则 - -- 设计未确认前不写代码 (R₀/R/D 阶段) -- TDD: 先写测试再写实现 (E 阶段) -- Sisyphus: plan.md 所有 [ ] 完成才能停 (E 阶段) -- Reflexion: 每个 Task 完成后自我反思再 Micro-review (E 阶段) -- 4 级 Quality Gate: PASS / CONCERNS / REWORK / FAIL (交付前) -- 如安装了 superpowers, 其 skills 细节优先于同名 VibeCoding skills, 但 PACE/RIPER-7 仍主控 - -## MCP 工具 (按需) - -- cunzhi (寸止): 人工确认检查点 -- sequential-thinking: 复杂推理 -- context7 CLI: 库文档查询 (`npx ctx7 resolve {库名}`) +设计原则: SRP · OCP · LSP · ISP · DIP · DRY · KISS · 第一性原理 · 先 WHY 后 HOW diff --git a/.claude/agents/builder.md b/.claude/agents/builder.md deleted file mode 100644 index d0396606..00000000 --- a/.claude/agents/builder.md +++ /dev/null @@ -1,13 +0,0 @@ ---- -name: builder -model: sonnet -description: 实现代码 + 编写测试 (TDD) -isolation: worktree -effort: high -maxTurns: 30 ---- -你是 builder agent。职责: -1. 读取分配的 Task 文本 -2. TDD: 先写测试 (RED) → 实现代码 (GREEN) → 重构 (REFACTOR) -3. 遵循 conventions.md 编码规范 -4. 完成后更新 plan.md: [ ] → [x] diff --git a/.claude/agents/evaluator.md b/.claude/agents/evaluator.md new file mode 100644 index 00000000..88ea9129 --- /dev/null +++ b/.claude/agents/evaluator.md @@ -0,0 +1,54 @@ +--- +name: evaluator +description: > + 代码审查评分员. PACE review 阶段调用, 读 _index.md 和 review 报告输出 VERDICT. +tools: Read, Glob, Grep, Bash +model: opus +--- + +# Evaluator Subagent (v9.6) + +## 输入 + +- 当前 `.ai_state/_index.md` (path/stage/sprint/active_goal) +- `.ai_state/details/reviews/sprint-N.md` (本 sprint 审查报告) +- `.ai_state/details/design.md` (设计 vs 实际) +- `git diff` 本 sprint 提交 + +## 评分维度 (1-5 分) + +``` +Functionality (25%) // 是否实现了 design.md 中所有 MUST 项 +Spec Compliance (25%) // 是否符合 design.md 的约束 (技术栈/接口/不做什么) +Boundary (15%) // 是否越出 File Structure Plan 边界 +Craft (15%) // 代码质量 (命名/SRP/无 dead code) +Robustness (20%) // 边界处理/错误恢复/测试覆盖 +``` + +均分 = F×0.25 + SC×0.25 + B×0.15 + C×0.15 + R×0.20 + +## VERDICT 规则 + +| 条件 | 判定 | +|---|---| +| 均分 ≥ 4.0 且**所有维度 ≥ 3** | PASS | +| 均分 ≥ 3.0 但某维度 = 2 | CONCERNS | +| 均分 < 3.0 或任一维度 = 1 | REWORK | +| 多个维度 = 1 或 Boundary 大幅越界 / 安全严重问题 | FAIL | + +## 输出要求 + +- **每个维度评分必须附代码行号级证据** (file.ext:LN, 不是模糊"看起来不错") +- **不允许 Halo Effect**: 一个维度的分数不影响其他维度的独立评分顺序 +- **不阻止 PASS 也不强制 CONCERNS**, 根据证据决定 + +## 协议约束 + +- 只读 (Read/Glob/Grep), 禁止 Edit/Write +- 输出预算 ≤ 2000 tokens (Anthropic 多 agent 经验: subagent 必须紧凑) +- 不读 .ai_state/.legacy-v9.5/ (那是迁移备份) +- 不要 glob 整个 .ai_state/, 按 _index.md.pointers 跳转 + +## 输出格式 + +直接覆盖写回 `.ai_state/details/reviews/sprint-N.md` 的 "## Step 6: @evaluator 评分" 段和 "## VERDICT:" 段。 diff --git a/.claude/agents/explorer.md b/.claude/agents/explorer.md deleted file mode 100644 index 0fab9fb1..00000000 --- a/.claude/agents/explorer.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -name: explorer -model: haiku -description: 代码库调研 + 文档查询 (只读) -effort: medium -maxTurns: 15 ---- -你是 explorer agent。职责: -1. 搜索项目中与任务相关的代码 -2. 查询依赖库文档 (context7) -3. 输出调研结果 (不修改任何文件) diff --git a/.claude/agents/generator.md b/.claude/agents/generator.md new file mode 100644 index 00000000..84d14f86 --- /dev/null +++ b/.claude/agents/generator.md @@ -0,0 +1,61 @@ +--- +name: generator +description: > + 代码实现者 (隔离 worktree). 接 task spec, 实现 + TDD, 输出可被 reviewer 验证的 commit. +tools: Read, Write, Edit, MultiEdit, Bash, Glob, Grep +model: opus +isolation: worktree +--- + +# Generator Subagent (v9.6) + +## 输入 + +- 单个或一组相关 task (来自 `tasks-current.md`) +- 项目 design.md (设计) +- `_index.md` (tech_stack, test_cmd, lint_cmd, conventions, gotchas) + +## 工作流 + +1. **Get-bearings**: + - 读 `.ai_state/_index.md` + - 读 `.ai_state/details/design.md` 对应模块段 + - 读 `.ai_state/details/tasks-current.md` 当前 task 的 Boundary/Depends +2. **TDD 强制** (铁律 2): + - 先写测试 (单测或集成测试, 视 task 性质) + - 跑测试确认 RED + - 实现 + - 跑测试确认 GREEN +3. **Boundary 守则**: + - 仅修改 task 的 Boundary 段标注的文件路径 + - 越界 → 停, 报告主 agent (可能需要升路径) +4. **执行 lint_cmd**: + - 失败 → 修 +5. **更新文档**: + - `tasks-current.md` 勾选完成项 ([ ] → [x]) + - `progress.md` 追加一行 (YYYY-MM-DD HH:MM [path/stage/sprint] Task N done: ...) + - design.md 不动 (除非发现 design 错; 那种情况报告主 agent 不要自己改) +6. **Commit**: + - `git add -p` 仅添加 Boundary 内文件 + - `git commit -m ""` +7. **报告**: + - 输出 commit hash + 测试通过的命令输出片段 (铁律 6) + - 失败 → 完整 stderr (不允许 "I cannot") + +## 隔离 + +- `isolation: worktree` (CC v2.1.121+): 自动创建 git worktree, 不污染主分支 +- worktree.baseRef = "head" (settings.json 已配): 从当前 HEAD 创建 + +## 输出预算 + +≤ 2000 tokens (Anthropic 多 agent 经验). 详细日志写文件不写回主 agent. + +## 失败协议 + +工具失败 → 三轮重试 (铁律 7): +1. 读 stderr, 尝试明显修复 +2. 换参数/换工具 +3. 仍失败 → 完整 stderr + exit code + 已尝试方案, 报告主 agent + +**禁止**: "请你手动跑", "我没有权限" (没有就申请, 不是放弃). diff --git a/.claude/agents/validator.md b/.claude/agents/validator.md deleted file mode 100644 index ea7b8427..00000000 --- a/.claude/agents/validator.md +++ /dev/null @@ -1,12 +0,0 @@ ---- -name: validator -model: sonnet -description: 代码审查 + Plan Review -isolation: worktree -effort: high -maxTurns: 20 ---- -你是 validator agent。职责: -1. Plan Review: 审查 plan.md, 找漏洞/模糊/不可执行 -2. Code Review: 对标 design.md spec + conventions.md -3. 输出审查意见 (严格, 不放水) diff --git a/.claude/commands/vibe-codex.md b/.claude/commands/vibe-codex.md deleted file mode 100644 index 663a48a6..00000000 --- a/.claude/commands/vibe-codex.md +++ /dev/null @@ -1,15 +0,0 @@ ---- -name: vibe-codex -description: 快捷调用 Codex ---- -# /vibe-codex $INSTRUCTION - -快捷方式: 直接调用 codex-comm skill 发送指令给 Codex。 - -1. 检测 Codex 可用性 -2. 构建上下文 (git diff + 相关文件) -3. 过滤敏感信息 -4. execMode(instruction, { sandbox: 'read-only' }) -5. 展示 Codex 回复 - -如果 Codex 不可用 → 输出提示, 不执行。 diff --git a/.claude/commands/vibe-dev.md b/.claude/commands/vibe-dev.md deleted file mode 100644 index 1cbde883..00000000 --- a/.claude/commands/vibe-dev.md +++ /dev/null @@ -1,14 +0,0 @@ ---- -name: vibe-dev -description: 启动开发工作流 ---- -# /vibe-dev $TASK - -1. 初始化 .ai_state/ (如不存在, 从模板复制) -2. P.A.C.E. 路由 → 确定路径 -3. 写入 .ai_state/session.md: 任务描述 + 路径 + 时间戳 -4. 执行 RIPER-7 对应阶段 - -**注意**: 如果已有进行中的任务 (.ai_state/doing.md 有 [ ]), 先询问用户是否要切换任务。 - -等效于在会话中说 "开始开发: $TASK" diff --git a/.claude/commands/vibe-init.md b/.claude/commands/vibe-init.md deleted file mode 100644 index bd7ba20b..00000000 --- a/.claude/commands/vibe-init.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -name: vibe-init -description: 首次设置 ---- -# /vibe-init - -1. 创建 .ai_state/ 目录 (从 .claude/templates/ai-state/ 复制) -2. 创建 .gitignore (如不存在) -3. 检测项目技术栈 → 写入 conventions.md -4. 检测 Codex 可用性 → 输出状态 -5. 输出: "VibeCoding 已初始化, 用 /vibe-dev 开始开发" diff --git a/.claude/commands/vibe-resume.md b/.claude/commands/vibe-resume.md deleted file mode 100644 index e5744da2..00000000 --- a/.claude/commands/vibe-resume.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -name: vibe-resume -description: 从断点恢复 ---- -# /vibe-resume - -1. 读 .ai_state/session.md 确定断点 -2. 读 .ai_state/doing.md 确定当前步骤 -3. 从断点继续 RIPER-7 流程 -4. 如果是 E 阶段 → 从未完成的 Task 继续 Sisyphus diff --git a/.claude/commands/vibe-status.md b/.claude/commands/vibe-status.md deleted file mode 100644 index bc432e17..00000000 --- a/.claude/commands/vibe-status.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -name: vibe-status -description: 显示当前会话状态 ---- -# /vibe-status - -读取并展示: -1. .ai_state/session.md → 当前任务 + 路径 + 阶段 -2. .ai_state/doing.md → 当前执行中的步骤 -3. .ai_state/plan.md → 进度 (已完成/总数) -4. Codex 状态: 可用? serve 运行中? diff --git a/.claude/hooks/compact-restore.cjs b/.claude/hooks/compact-restore.cjs new file mode 100755 index 00000000..5f691062 --- /dev/null +++ b/.claude/hooks/compact-restore.cjs @@ -0,0 +1,65 @@ +#!/usr/bin/env node +'use strict'; +// VibeCoding Athena PostCompact hook v9.6 +// 协议: hookSpecificOutput.{hookEventName: "PostCompact", additionalContext} +// 参考: https://code.claude.com/docs/en/hooks#postcompact +// +// 设计: +// 读 compact-snapshot.md, 注入 _index.md frontmatter 摘要 +// 提示 model: 详细内容按 _index.md.pointers lazy load, 不要 glob 全目录 (铁律 8) + +const fs = require('fs'); +const path = require('path'); + +const PROJECT_DIR = process.env.CLAUDE_PROJECT_DIR || process.cwd(); +const STATE_DIR = path.join(PROJECT_DIR, '.ai_state'); +const SNAPSHOT_PATH = path.join(STATE_DIR, 'compact-snapshot.md'); + +let input = ''; +process.stdin.setEncoding('utf8'); +process.stdin.on('data', d => input += d); +process.stdin.on('end', () => { + if (!fs.existsSync(SNAPSHOT_PATH)) { + process.exit(0); return; + } + + try { + const snapshot = fs.readFileSync(SNAPSHOT_PATH, 'utf8'); + const fmMatch = snapshot.match(/---\n([\s\S]*?)\n---/); + if (!fmMatch) { process.exit(0); return; } + + const additionalContext = `[Athena PostCompact 恢复] + +上次 compact 前的项目状态 (_index.md frontmatter 快照): + +\`\`\`yaml +${fmMatch[1].slice(0, 1500)} +\`\`\` + +详细内容**按需** lazy load: +- progress 详情 → 按 pointers.latest_progress 读 progress.md 尾部 +- lessons → 命中主题再按 pointers.latest_lesson 跳段 +- 完整 _index.md → \`.ai_state/_index.md\` + +⚠️ 禁止 glob 整个 .ai_state/ (铁律 8 索引先行)`; + + process.stderr.write(`[compact-restore] injected ${additionalContext.length} chars\n`); + process.stdout.write(JSON.stringify({ + hookSpecificOutput: { + hookEventName: 'PostCompact', + additionalContext + } + })); + + const trace = JSON.stringify({ + ts: new Date().toISOString(), + hook: 'compact-restore', + chars: additionalContext.length + }) + '\n'; + fs.appendFileSync(path.join(STATE_DIR, 'hook-trace.jsonl'), trace); + } catch (e) { + process.stderr.write(`[compact-restore] failed: ${e.message}\n`); + } + + process.exit(0); +}); diff --git a/.claude/hooks/compact-snapshot.cjs b/.claude/hooks/compact-snapshot.cjs new file mode 100755 index 00000000..2805434a --- /dev/null +++ b/.claude/hooks/compact-snapshot.cjs @@ -0,0 +1,63 @@ +#!/usr/bin/env node +'use strict'; +// VibeCoding Athena PreCompact hook v9.6 +// 协议: 不阻断, 写文件做副作用 +// 参考: https://code.claude.com/docs/en/hooks#precompact +// +// v9.6 减薄设计 (L2.C): +// 旧版 v9.5 快照整个 .ai_state/{tasks,design,progress,lessons,handoff}.md 多个文件 +// v9.6 仅快照 _index.md frontmatter (≤30 行), context 利用率提升数倍 +// detail/* 内容在 PostCompact 时按需 lazy load (按 pointers 跳转) + +const fs = require('fs'); +const path = require('path'); + +const PROJECT_DIR = process.env.CLAUDE_PROJECT_DIR || process.cwd(); +const STATE_DIR = path.join(PROJECT_DIR, '.ai_state'); +const INDEX_PATH = path.join(STATE_DIR, '_index.md'); +const SNAPSHOT_PATH = path.join(STATE_DIR, 'compact-snapshot.md'); + +let input = ''; +process.stdin.setEncoding('utf8'); +process.stdin.on('data', d => input += d); +process.stdin.on('end', () => { + let event = {}; + try { event = JSON.parse(input); } catch (e) {} + const trigger = event.trigger || 'unknown'; // "manual" | "auto" + + if (!fs.existsSync(INDEX_PATH)) { + process.stderr.write('[compact-snapshot] _index.md not found, skip\n'); + process.exit(0); return; + } + + try { + const content = fs.readFileSync(INDEX_PATH, 'utf8'); + const fmMatch = content.match(/^---\n([\s\S]*?)\n---/); + if (!fmMatch) { + process.stderr.write('[compact-snapshot] _index.md frontmatter missing, skip\n'); + process.exit(0); return; + } + + const ts = new Date().toISOString(); + const snapshot = `# Athena Compact Snapshot (${trigger}) +# Saved at: ${ts} +# 仅快照 _index.md frontmatter (≤30 行). 详细内容按 pointers 在 PostCompact 后 lazy load. + +--- +${fmMatch[1]} +--- +`; + fs.writeFileSync(SNAPSHOT_PATH, snapshot); + process.stderr.write(`[compact-snapshot] saved (${trigger}, ${snapshot.length} chars)\n`); + + const trace = JSON.stringify({ + ts, hook: 'compact-snapshot', trigger, + chars: snapshot.length + }) + '\n'; + fs.appendFileSync(path.join(STATE_DIR, 'hook-trace.jsonl'), trace); + } catch (e) { + process.stderr.write(`[compact-snapshot] failed: ${e.message}\n`); + } + + process.exit(0); +}); diff --git a/.claude/hooks/context-loader.cjs b/.claude/hooks/context-loader.cjs deleted file mode 100644 index 42ec14b4..00000000 --- a/.claude/hooks/context-loader.cjs +++ /dev/null @@ -1,37 +0,0 @@ -// VibeCoding v9.2.0 — SessionStart: 检测状态 + 恢复 + 提醒 -'use strict'; -const fs = require('fs'); -const path = require('path'); -const STATE_DIR = path.join(process.cwd(), '.ai_state'); - -try { - if (fs.existsSync(STATE_DIR)) { - // 恢复: 读取关键状态文件 - const files = ['session.md', 'doing.md', 'plan.md', 'design.md']; - const found = files.filter(f => fs.existsSync(path.join(STATE_DIR, f))); - if (found.length > 0) { - console.log(`[VibeCoding] 恢复会话: ${found.join(', ')} 已加载`); - // 检查未完成任务 - const planFile = path.join(STATE_DIR, 'plan.md'); - if (fs.existsSync(planFile)) { - const plan = fs.readFileSync(planFile, 'utf8'); - const unchecked = (plan.match(/^- \[ \]/gm) || []).length; - if (unchecked > 0) console.log(`[VibeCoding] ⚠ plan.md 有 ${unchecked} 个未完成任务`); - } - } - // Agent 易犯错误提醒 - const convFile = path.join(STATE_DIR, 'conventions.md'); - if (fs.existsSync(convFile)) { - const conv = fs.readFileSync(convFile, 'utf8'); - const errSection = conv.match(/## Agent 易犯错误\n([\s\S]*?)(?=\n##|$)/); - if (errSection && errSection[1].trim()) { - console.log('[VibeCoding] 🔔 Agent 易犯错误提醒:'); - console.log(errSection[1].trim().split('\n').slice(0, 5).join('\n')); - } - } - } else { - console.log('[VibeCoding] 新会话: .ai_state/ 不存在, 首次任务时自动初始化'); - } -} catch (e) { - console.log('[VibeCoding] context-loader 警告: ' + e.message); -} diff --git a/.claude/hooks/delivery-gate.cjs b/.claude/hooks/delivery-gate.cjs old mode 100644 new mode 100755 index 8e8cea29..d3d6b7b3 --- a/.claude/hooks/delivery-gate.cjs +++ b/.claude/hooks/delivery-gate.cjs @@ -1,86 +1,188 @@ -// VibeCoding v9.2.0 — Stop: 4级 Quality Gate +#!/usr/bin/env node 'use strict'; +// VibeCoding Athena Stop hook (delivery-gate) v9.6 +// 协议: +// - block: stdout JSON { decision: "block", reason: "..." } +// - pass: exit 0 无输出 +// 参考: https://code.claude.com/docs/en/hooks#stop +// +// v9.6 设计: +// 1. 只读 _index.md 不读单独文件 (铁律 8) +// 2. 适配 CC v2.1.x 的 Stop hook block-cap=8 (CLAUDE_CODE_STOP_HOOK_BLOCK_CAP) +// hook 自身计数 block 次数, 接近 8 时 warn 而非 block +// 3. 当平台支持 /goal (CC v2.1.139+) 时, 让位给 /goal evaluator, 仅作 fallback +// 4. CONCERNS 时仅 systemMessage 不阻断 +// +// stop_hook_active 防递归: CC 已经在嵌套 Stop hook 时设置该字段为 true + const fs = require('fs'); const path = require('path'); -const { execSync } = require('child_process'); -process.on('uncaughtException', function(e) { - process.stderr.write('[delivery-gate] ERROR: ' + e.message + '\n'); - process.exit(0); // 脚本错误不阻断 -}); +const PROJECT_DIR = process.env.CLAUDE_PROJECT_DIR || process.cwd(); +const STATE_DIR = path.join(PROJECT_DIR, '.ai_state'); +const INDEX_PATH = path.join(STATE_DIR, '_index.md'); +const HOME = process.env.HOME || require('os').homedir(); +const COUNTER_PATH = path.join(HOME, '.claude', 'state', 'athena-block-counter.json'); -if (process.env.VIBECODING_HOOKS_DISABLED === '1') { process.exit(0); } +const BLOCK_CAP_SAFE = 6; // 留 2 次冗余给 CC 的硬 cap 8 -const STATE_DIR = path.join(process.cwd(), '.ai_state'); -const severity = { fail: [], rework: [], concerns: [] }; - -try { - if (!fs.existsSync(STATE_DIR)) { process.exit(0); } // 无状态 = Path A, 放行 - - const planFile = path.join(STATE_DIR, 'plan.md'); - const qualityFile = path.join(STATE_DIR, 'quality.md'); - - // ── FAIL 级: 硬约束 ── - // 未完成任务 - if (fs.existsSync(planFile)) { - const plan = fs.readFileSync(planFile, 'utf8'); - const unchecked = (plan.match(/^- \[ \]/gm) || []).length; - if (unchecked > 0) severity.fail.push(`plan.md 有 ${unchecked} 个未完成任务`); - } - // 缺 quality.md (有 plan 但没 quality = T 阶段未执行) - if (fs.existsSync(planFile) && !fs.existsSync(qualityFile)) { - severity.fail.push('缺少 quality.md — T 阶段验证未执行'); - } - // 硬编码密钥 +function trace(event) { try { - const secrets = execSync( - "git diff --cached -U0 2>/dev/null | grep -iE '(password|secret|api_key|token)\\s*[:=]\\s*.' || true", - { encoding: 'utf8' } - ); - if (secrets.trim()) severity.fail.push('疑似硬编码密钥'); - } catch {} + const line = JSON.stringify({ ts: new Date().toISOString(), hook: 'delivery-gate', ...event }) + '\n'; + fs.appendFileSync(path.join(STATE_DIR, 'hook-trace.jsonl'), line); + } catch (e) {} +} - // ── REWORK 级: 源码无测试 ── +let input = ''; +process.stdin.setEncoding('utf8'); +process.stdin.on('data', d => input += d); +process.stdin.on('end', () => { + let event = {}; + try { event = JSON.parse(input); } catch (e) {} + + // 防递归 (官方字段) + if (event.stop_hook_active) { process.exit(0); return; } + + // 跳过 subagent + if (event.agent_type && event.agent_type !== 'main') { process.exit(0); return; } + + if (!fs.existsSync(INDEX_PATH)) { process.exit(0); return; } + + let fm = ''; try { - const diff = execSync('git diff --cached --name-only 2>/dev/null || git diff --name-only 2>/dev/null', { encoding: 'utf8' }); - const codeExts = /\.(ts|tsx|js|jsx|py|go|rs|java|rb)$/; - const testPattern = /\.test\.|\.spec\.|__test__|__spec__|_test\.|tests\//; - const configPattern = /config|\.env|\.json$|\.yaml$|\.yml$|\.toml$|\.md$|\.txt$/i; - const srcFiles = diff.split('\n').filter(f => - f.match(codeExts) && !f.match(testPattern) && !f.match(configPattern) - ); - const testFiles = diff.split('\n').filter(f => f.match(testPattern)); - if (srcFiles.length > 0 && testFiles.length === 0) { - if (srcFiles.length > 2) { - severity.rework.push(`${srcFiles.length} 个源码文件修改但无对应测试`); - } else { - severity.concerns.push(`${srcFiles.length} 个源码文件修改但无测试 (数量少, 标注)`); + const content = fs.readFileSync(INDEX_PATH, 'utf8'); + const m = content.match(/^---\n([\s\S]*?)\n---/); + if (!m) { process.exit(0); return; } + fm = m[1]; + } catch (e) { process.exit(0); return; } + + const getStr = k => { + const m = fm.match(new RegExp('^ ' + k + ': (.*)$', 'm')); + return m ? m[1].replace(/^["']|["']$/g, '').trim() : ''; + }; + const getNum = k => { + const m = fm.match(new RegExp('^ ' + k + ': (\\d+)$', 'm')); + return m ? parseInt(m[1], 10) : 0; + }; + const getBool = k => { + const m = fm.match(new RegExp('^ ' + k + ': (true|false)$', 'm')); + return m ? m[1] === 'true' : false; + }; + + const pathName = getStr('path'); + const stage = getStr('stage'); + const sprint = getNum('sprint'); + const tasks_pending = getNum('tasks_pending'); + const goal_supported = getBool('goal_supported'); + const active_goal = getStr('active_goal'); + + // 仅在 review 阶段 + 非 Hotfix 时介入 + if (!sprint || !stage || pathName === 'Hotfix' || pathName === 'Bugfix' || stage !== 'review') { + process.exit(0); return; + } + + // 平台支持 /goal 且用户已设置 active_goal → 让位给 /goal evaluator + if (goal_supported && active_goal) { + process.stderr.write('[delivery-gate] defer to /goal evaluator (active goal detected)\n'); + trace({ action: 'defer', reason: 'goal-active', sprint }); + process.exit(0); return; + } + + const needsExtReview = ['Feature', 'Refactor', 'System'].includes(pathName); + const issues = []; + + // 1. tasks 全完成 + if (tasks_pending > 0) { + issues.push(`${tasks_pending} Task 未完成`); + } + + // 2. review report 存在 + const reviewFile = path.join(STATE_DIR, 'details', 'reviews', `sprint-${sprint}.md`); + let rc = ''; + try { + rc = fs.readFileSync(reviewFile, 'utf8'); + } catch (e) { + issues.push('审查报告不存在'); + } + + // 3. Feature+ 外部审查 + if (needsExtReview && rc && + !/\/codex:review|\/codex:adversarial|reviewer|ecc-agentshield|codex unavailable/i.test(rc)) { + issues.push('无外部审查记录'); + } + + // 4. 测试通过记录 + if (rc && !/test|测试|pass|通过|✅/i.test(rc)) { + issues.push('无测试通过记录'); + } + + // 5. VERDICT + let verdict = ''; + if (rc) { + const m = rc.match(/VERDICT:\s*(PASS|CONCERNS|REWORK|FAIL)/i); + if (m) { + verdict = m[1].toUpperCase(); + if (verdict === 'REWORK') issues.push('VERDICT=REWORK'); + else if (verdict === 'FAIL') issues.push('VERDICT=FAIL'); + else if (verdict === 'CONCERNS') { + process.stderr.write('[delivery-gate] CONCERNS: 建议修复后重新评分\n'); } } - } catch {} + } -} catch (e) { + if (issues.length > 0) { + // 检查 block 次数, 接近 cap 时降级 systemMessage + fs.mkdirSync(path.dirname(COUNTER_PATH), { recursive: true }); + let counter = {}; + try { counter = JSON.parse(fs.readFileSync(COUNTER_PATH, 'utf8')); } catch (e) {} + const sessionId = event.session_id || 'unknown'; + counter[sessionId] = (counter[sessionId] || 0) + 1; + try { fs.writeFileSync(COUNTER_PATH, JSON.stringify(counter)); } catch (e) {} + const count = counter[sessionId]; + + process.stderr.write(`[delivery-gate] block #${count}/${BLOCK_CAP_SAFE} ${pathName}/${stage}: ${issues.join(', ')}\n`); + trace({ action: 'block', count, pathName, stage, sprint, issues }); + + if (count >= BLOCK_CAP_SAFE) { + // 接近 cap, 改为 systemMessage 避免触发 CC 的 force-kill + process.stdout.write(JSON.stringify({ + systemMessage: `[Athena] 已连续 block ${count} 次, 建议手动处理: ${issues.join('; ')} (剩余尝试: ${8 - count})` + })); + } else { + process.stdout.write(JSON.stringify({ + decision: 'block', + reason: `[Athena delivery-gate] 阻断:\n${issues.map(i => '• ' + i).join('\n')}\n\n修复后再交付 (block ${count}/${BLOCK_CAP_SAFE}).` + })); + } + process.exit(0); return; + } + + // 通过, 重置计数 + try { + let counter = JSON.parse(fs.readFileSync(COUNTER_PATH, 'utf8')); + delete counter[event.session_id]; + fs.writeFileSync(COUNTER_PATH, JSON.stringify(counter)); + } catch (e) {} + + if (verdict === 'PASS') { + // 检查是否做了 compound + const lessonsText = (() => { + try { return fs.readFileSync(path.join(STATE_DIR, 'details', 'lessons.md'), 'utf8'); } + catch (e) { return ''; } + })(); + const compounded = lessonsText.includes(`Sprint ${sprint}`); + if (!compounded) { + const msg = `Sprint ${sprint} 通过但 details/lessons.md 无条目, 建议 /compound 沉淀经验`; + process.stderr.write(`[delivery-gate] PASS ${pathName}/${stage} · ${msg}\n`); + trace({ action: 'soft-warn', pathName, stage, sprint, reason: 'no compound' }); + process.stdout.write(JSON.stringify({ systemMessage: `[Athena] ${msg}` })); + } else { + process.stderr.write(`[delivery-gate] PASS ${pathName}/${stage} · lessons ✓\n`); + trace({ action: 'pass', pathName, stage, sprint }); + } + } else { + process.stderr.write(`[delivery-gate] 放行 ${pathName}/${stage}\n`); + trace({ action: 'pass', pathName, stage, sprint, verdict }); + } process.exit(0); -} - -// ── 输出结果 ── -// Stop hook 语义: exit 0 = 放行停止, exit 2 = 阻断停止(强制继续) -// stdout 输出会作为 context 注入 Claude -const allIssues = [...severity.fail, ...severity.rework, ...severity.concerns]; -if (severity.fail.length > 0) { - process.stderr.write(`[delivery-gate] ❌ FAIL — 严重问题, 必须修复:\n${severity.fail.map(i => ` ✗ ${i}`).join('\n')}\n`); - if (severity.rework.length) process.stderr.write(` + REWORK: ${severity.rework.join(', ')}\n`); - if (severity.concerns.length) process.stderr.write(` + CONCERNS: ${severity.concerns.join(', ')}\n`); - process.exit(2); // 阻断: 不让停 -} -if (severity.rework.length > 0) { - process.stderr.write(`[delivery-gate] 🔧 REWORK — 需要修复:\n${severity.rework.map(i => ` ✗ ${i}`).join('\n')}\n`); - if (severity.concerns.length) process.stderr.write(` + CONCERNS: ${severity.concerns.join(', ')}\n`); - process.exit(2); // 阻断: 不让停 -} -if (severity.concerns.length > 0) { - // CONCERNS: 放行但输出警告作为 context - process.stdout.write(`[delivery-gate] ⚠ CONCERNS (已放行, 建议修复):\n${severity.concerns.map(i => ` △ ${i}`).join('\n')}\n`); - process.exit(0); // 放行: 但警告已注入 -} -process.exit(0); // PASS +}); diff --git a/.claude/hooks/index-updater.cjs b/.claude/hooks/index-updater.cjs new file mode 100755 index 00000000..3411391e --- /dev/null +++ b/.claude/hooks/index-updater.cjs @@ -0,0 +1,175 @@ +#!/usr/bin/env node +'use strict'; +// VibeCoding Athena PostToolUse index-updater v9.6 ★新增 +// 协议: 无输出即放行 (PostToolUse 不阻断) +// 参考: https://code.claude.com/docs/en/hooks#posttooluse +// +// 设计 (铁律 5 文档即真相 + 铁律 8 索引先行): +// 1. 检测 tool_input.file_path 是否在 .ai_state/details/ +// 2. 若是 → 重新扫描 details/ 计算 counts/fingerprints/pointers +// 3. 原子写 _index.md (写 .tmp 后 rename) +// 4. 避免热路径: 同一秒内对同一文件多次触发只更新一次 (debounce by mtime) + +const fs = require('fs'); +const path = require('path'); + +const PROJECT_DIR = process.env.CLAUDE_PROJECT_DIR || process.cwd(); +const STATE_DIR = path.join(PROJECT_DIR, '.ai_state'); +const INDEX_PATH = path.join(STATE_DIR, '_index.md'); +const DETAILS_DIR = path.join(STATE_DIR, 'details'); + +let input = ''; +process.stdin.setEncoding('utf8'); +process.stdin.on('data', d => input += d); +process.stdin.on('end', () => { + let event = {}; + try { event = JSON.parse(input); } catch (e) { process.exit(0); return; } + + // 检测 file_path 是否在 details/ + const filePath = (event.tool_input && (event.tool_input.file_path || event.tool_input.notebook_path)) || ''; + if (!filePath || !filePath.includes('.ai_state/details/')) { + process.exit(0); return; + } + if (!fs.existsSync(INDEX_PATH)) { process.exit(0); return; } + + try { + updateIndex(); + } catch (e) { + process.stderr.write(`[index-updater] failed: ${e.message}\n`); + } + process.exit(0); +}); + +function readFileSafe(p) { + try { return fs.readFileSync(p, 'utf8'); } catch (e) { return null; } +} + +function mtime(p) { + try { return Math.floor(fs.statSync(p).mtimeMs); } catch (e) { return 0; } +} + +function countMatches(text, regex) { + if (!text) return 0; + return (text.match(regex) || []).length; +} + +function lastSectionLine(text, headPrefix) { + if (!text) return 0; + const lines = text.split('\n'); + for (let i = lines.length - 1; i >= 0; i--) { + if (lines[i].startsWith(headPrefix)) return i + 1; + } + return 0; +} + +function lastNonEmptyLine(text) { + if (!text) return 0; + const lines = text.split('\n'); + for (let i = lines.length - 1; i >= 0; i--) { + if (lines[i].trim() && !lines[i].startsWith('')) return i + 1; + } + return 0; +} + +function updateIndex() { + // 读 _index.md + const content = fs.readFileSync(INDEX_PATH, 'utf8'); + const fmMatch = content.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/); + if (!fmMatch) { + process.stderr.write('[index-updater] _index.md frontmatter parse failed\n'); + return; + } + let fm = fmMatch[1]; + const body = fmMatch[2]; + + // 扫描 details/ + const tasksText = readFileSafe(path.join(DETAILS_DIR, 'tasks-current.md')); + const progressText = readFileSafe(path.join(DETAILS_DIR, 'progress.md')); + const lessonsText = readFileSafe(path.join(DETAILS_DIR, 'lessons.md')); + const designText = readFileSafe(path.join(DETAILS_DIR, 'design.md')); + const proposalsText = readFileSafe(path.join(DETAILS_DIR, 'proposals.md')); + + // counts + const tasks_pending = countMatches(tasksText, /^- \[ \]/gm); + const tasks_done = countMatches(tasksText, /^- \[x\]/gm); + const tasks_blocked = countMatches(tasksText, /^- \[!\]/gm); + const lessons_lines = countMatches(lessonsText, /^## /gm); + + // progress entries: 非空非注释非标题行 + let progress_entries = 0; + if (progressText) { + progress_entries = progressText.split('\n') + .filter(l => l.trim() && !l.startsWith('') && !l.startsWith('#')) + .length; + } + + // reviews_count + let reviews_count = 0; + let latest_review = ''; + try { + const files = fs.readdirSync(path.join(DETAILS_DIR, 'reviews')) + .filter(f => f.match(/^sprint-\d+\.md$/)); + reviews_count = files.length; + if (files.length > 0) { + const sorted = files.sort((a, b) => { + const na = parseInt(a.match(/\d+/)[0], 10); + const nb = parseInt(b.match(/\d+/)[0], 10); + return na - nb; + }); + latest_review = `details/reviews/${sorted[sorted.length - 1]}`; + } + } catch (e) {} + + // pointers + const latest_progress = progressText ? `details/progress.md#L${lastNonEmptyLine(progressText)}` : ''; + const latest_lesson = lessonsText ? `details/lessons.md#L${lastSectionLine(lessonsText, '## ')}` : ''; + const latest_proposal = proposalsText ? `details/proposals.md#L${lastSectionLine(proposalsText, '## ')}` : ''; + + // fingerprints + const tasks_mtime = mtime(path.join(DETAILS_DIR, 'tasks-current.md')); + const design_mtime = mtime(path.join(DETAILS_DIR, 'design.md')); + const progress_mtime = mtime(path.join(DETAILS_DIR, 'progress.md')); + const lessons_mtime = mtime(path.join(DETAILS_DIR, 'lessons.md')); + + // 替换 frontmatter 字段 (保留其他字段不动) + function replaceField(yaml, key, value, isNumeric) { + const re = new RegExp(`(^ ${key}: ).*$`, 'm'); + const val = isNumeric ? value : `"${value}"`; + if (re.test(yaml)) return yaml.replace(re, `$1${val}`); + return yaml; // 不存在不创建, 等用户/init 加 + } + + fm = replaceField(fm, 'tasks_pending', tasks_pending, true); + fm = replaceField(fm, 'tasks_done', tasks_done, true); + fm = replaceField(fm, 'tasks_blocked', tasks_blocked, true); + fm = replaceField(fm, 'lessons_lines', lessons_lines, true); + fm = replaceField(fm, 'progress_entries', progress_entries, true); + fm = replaceField(fm, 'reviews_count', reviews_count, true); + fm = replaceField(fm, 'latest_progress', latest_progress, false); + fm = replaceField(fm, 'latest_review', latest_review, false); + fm = replaceField(fm, 'latest_lesson', latest_lesson, false); + fm = replaceField(fm, 'latest_proposal', latest_proposal, false); + fm = replaceField(fm, 'tasks_mtime', tasks_mtime, true); + fm = replaceField(fm, 'design_mtime', design_mtime, true); + fm = replaceField(fm, 'progress_mtime', progress_mtime, true); + fm = replaceField(fm, 'lessons_mtime', lessons_mtime, true); + + const newContent = `---\n${fm}\n---\n${body}`; + + // 原子写入 + const tmpPath = INDEX_PATH + '.tmp'; + fs.writeFileSync(tmpPath, newContent); + fs.renameSync(tmpPath, INDEX_PATH); + + process.stderr.write(`[index-updater] _index.md updated (pending=${tasks_pending} done=${tasks_done})\n`); + + // hook-trace + try { + const trace = JSON.stringify({ + ts: new Date().toISOString(), + hook: 'index-updater', + tasks_pending, tasks_done, lessons_lines, reviews_count + }) + '\n'; + fs.appendFileSync(path.join(STATE_DIR, 'hook-trace.jsonl'), trace); + } catch (e) {} +} diff --git a/.claude/hooks/pace-continuator.cjs b/.claude/hooks/pace-continuator.cjs new file mode 100755 index 00000000..16419c2b --- /dev/null +++ b/.claude/hooks/pace-continuator.cjs @@ -0,0 +1,136 @@ +#!/usr/bin/env node +'use strict'; +// VibeCoding Athena Stop hook (pace-continuator) v9.6 ★新增 +// 协议: 不阻断 (放在 delivery-gate 之后串联运行) +// - 写入 details/next.md (PACE-aware 下一动作建议) +// - 在检测到"反复试错/失败模式"时, append details/proposals.md (铁律 9) +// 参考: https://code.claude.com/docs/en/hooks#stop +// +// 不发明 "自动 PACE", 只是写"下一步可以做的"备忘, 等用户自己读或下次 SessionStart 注入 + +const fs = require('fs'); +const path = require('path'); + +const PROJECT_DIR = process.env.CLAUDE_PROJECT_DIR || process.cwd(); +const STATE_DIR = path.join(PROJECT_DIR, '.ai_state'); +const INDEX_PATH = path.join(STATE_DIR, '_index.md'); +const NEXT_PATH = path.join(STATE_DIR, 'details', 'next.md'); +const PROPOSALS_PATH = path.join(STATE_DIR, 'details', 'proposals.md'); +const HOOK_TRACE = path.join(STATE_DIR, 'hook-trace.jsonl'); + +let input = ''; +process.stdin.setEncoding('utf8'); +process.stdin.on('data', d => input += d); +process.stdin.on('end', () => { + let event = {}; + try { event = JSON.parse(input); } catch (e) {} + if (event.stop_hook_active) { process.exit(0); return; } + if (event.agent_type && event.agent_type !== 'main') { process.exit(0); return; } + if (!fs.existsSync(INDEX_PATH)) { process.exit(0); return; } + + let fm = ''; + try { + const content = fs.readFileSync(INDEX_PATH, 'utf8'); + const m = content.match(/^---\n([\s\S]*?)\n---/); + if (!m) { process.exit(0); return; } + fm = m[1]; + } catch (e) { process.exit(0); return; } + + const getStr = k => { + const m = fm.match(new RegExp('^ ' + k + ': (.*)$', 'm')); + return m ? m[1].replace(/^["']|["']$/g, '').trim() : ''; + }; + const getNum = k => { + const m = fm.match(new RegExp('^ ' + k + ': (\\d+)$', 'm')); + return m ? parseInt(m[1], 10) : 0; + }; + + const pathName = getStr('path'); + const stage = getStr('stage'); + const sprint = getNum('sprint'); + const tasks_pending = getNum('tasks_pending'); + const tasks_done = getNum('tasks_done'); + + if (!pathName || !stage || pathName === 'Hotfix' || pathName === 'Bugfix') { + process.exit(0); return; + } + + // 推断下一动作 + const suggestions = nextActionSuggestions(pathName, stage, tasks_pending, tasks_done); + + if (suggestions.length === 0) { process.exit(0); return; } + + // 写 next.md (覆盖, 仅保留最新一条 + 历史移到顶部) + const ts = new Date().toISOString().replace('T', ' ').slice(0, 16); + const entry = `## ${ts}\n当前: ${pathName}/${stage}/sprint ${sprint}\n建议:\n${suggestions.map(s => '- ' + s).join('\n')}\n`; + + let existing = ''; + try { existing = fs.readFileSync(NEXT_PATH, 'utf8'); } catch (e) {} + // 去掉模板注释, 在顶部 append + existing = existing.replace(/\s*/g, '').trim(); + const newContent = `# 下一动作建议\n\n${entry}\n${existing ? '\n---\n\n' + existing : ''}`.trim() + '\n'; + fs.writeFileSync(NEXT_PATH, newContent); + + // 检测反复试错: 读 hook-trace 最近 20 条, 看 subagent-retry / delivery-gate block 是否高频 + try { + const trace = fs.readFileSync(HOOK_TRACE, 'utf8').split('\n').filter(l => l).slice(-20); + const retryCount = trace.filter(l => l.includes('"subagent-retry"')).length; + const blockCount = trace.filter(l => l.includes('"delivery-gate"') && l.includes('"block"')).length; + if (retryCount >= 3 || blockCount >= 2) { + // append proposals.md (铁律 9 Hook 反思) + const proposalEntry = `\n## ${ts} (sprint ${sprint})\n- 观察: 最近 20 hook 事件含 subagent-retry × ${retryCount}, delivery-gate block × ${blockCount}\n- 假设: ${pathName}/${stage} 阶段流程可能不顺畅, 可能是 design 不足 / task 过大 / 验收标准模糊\n- 提议: 复盘当前 sprint 设计粒度, 考虑拆 task 或回 plan 阶段\n`; + fs.appendFileSync(PROPOSALS_PATH, proposalEntry); + process.stderr.write(`[pace-continuator] proposals.md +1 (retry=${retryCount}, block=${blockCount})\n`); + } + } catch (e) {} + + process.stderr.write(`[pace-continuator] next.md updated (${pathName}/${stage})\n`); + + // hook-trace + try { + const trace = JSON.stringify({ + ts: new Date().toISOString(), + hook: 'pace-continuator', + path: pathName, stage, sprint, + suggestions_count: suggestions.length + }) + '\n'; + fs.appendFileSync(HOOK_TRACE, trace); + } catch (e) {} + + process.exit(0); +}); + +function nextActionSuggestions(path, stage, pending, done) { + const s = []; + if (stage === 'plan') { + s.push('完成 design.md 的 File Structure Plan 段'); + s.push('和用户确认设计后 → 进入 stage=impl'); + s.push('可选: /goal "design.md 含 File Structure Plan + tasks ≥ 1 + 用户确认"'); + } else if (stage === 'design') { + s.push('锁定架构, 完成 details/tasks-current.md (每 Task 含 Boundary/Depends)'); + s.push('用户确认 → 进入 stage=impl'); + } else if (stage === 'impl') { + if (pending > 0) { + s.push(`完成剩余 ${pending} 个 Task (按 Boundary 拆分)`); + if (path === 'Refactor' || path === 'System') { + s.push('可选: /batch "<重构描述>" 并行加速 (CC v2.1.121+ 原生)'); + } + s.push('每完成 Task → tasks-current.md 勾选 + progress.md 追加'); + } else if (done > 0) { + s.push('所有 task 完成, 跑测试和 lint → 切 stage=review'); + s.push('/simplify 清理冗余'); + } + } else if (stage === 'review') { + s.push('运行 /review → 写 reviews/sprint-N.md'); + if (path !== 'Quick') { + s.push('运行 /codex:review (跨模型审查)'); + } + s.push('@evaluator 评分 → VERDICT'); + s.push('PASS → /compound 沉淀 lessons → stage=ship'); + } else if (stage === 'ship') { + s.push('superpowers /ship (Feature+) 或 git commit (Quick)'); + s.push('归档 details/tasks-archive/sprint-N.md'); + s.push('_index.md sprint += 1, stage="" '); + } + return s; +} diff --git a/.claude/hooks/permission-retry.cjs b/.claude/hooks/permission-retry.cjs new file mode 100755 index 00000000..1ac69347 --- /dev/null +++ b/.claude/hooks/permission-retry.cjs @@ -0,0 +1,59 @@ +#!/usr/bin/env node +'use strict'; +// VibeCoding Athena PermissionDenied hook v9.6 +// 协议: stdout JSON { retry: true } (CC v2.1.89+ 官方标准) +// 参考: https://code.claude.com/docs/en/hooks#permissiondenied +// +// 设计: +// 当 auto mode classifier 拒绝某个工具调用 → 提示 model 可以重试 (但只针对一次) +// 并 systemMessage 建议用户加入全局 deny (如果是反复出现的 pattern) + +const fs = require('fs'); +const path = require('path'); + +const PROJECT_DIR = process.env.CLAUDE_PROJECT_DIR || process.cwd(); +const STATE_DIR = path.join(PROJECT_DIR, '.ai_state'); + +let input = ''; +process.stdin.setEncoding('utf8'); +process.stdin.on('data', d => input += d); +process.stdin.on('end', () => { + let event = {}; + try { event = JSON.parse(input); } catch (e) { process.exit(0); return; } + + const tool = event.tool_name || ''; + const cmd = (event.tool_input && event.tool_input.command) || ''; + const reason = event.reason || ''; + + // 软提示: 如果是 Bash + 看起来是项目本地命令 → 提示用户考虑加入项目 allow + let suggestion = ''; + if (tool === 'Bash' && cmd) { + // 抽取命令名 (第一个 token) + const cmdName = cmd.trim().split(/\s+/)[0]; + if (['npm', 'pnpm', 'yarn', 'pytest', 'cargo', 'go', 'make'].includes(cmdName)) { + suggestion = `如果这是项目常用命令, 可: /athena:remember-allow "Bash(${cmdName}:*)" 加入全局 allow`; + } + } + + if (suggestion) { + process.stderr.write(`[permission-retry] suggest: ${suggestion}\n`); + process.stdout.write(JSON.stringify({ + retry: true, + systemMessage: `[Athena] permission denied. ${suggestion}` + })); + } else { + // 默认让 model 知道可以一次性重试 + process.stdout.write(JSON.stringify({ retry: true })); + } + + try { + const trace = JSON.stringify({ + ts: new Date().toISOString(), + hook: 'permission-retry', + tool, cmd_head: cmd.slice(0, 60), reason: reason.slice(0, 100) + }) + '\n'; + fs.appendFileSync(path.join(STATE_DIR, 'hook-trace.jsonl'), trace); + } catch (e) {} + + process.exit(0); +}); diff --git a/.claude/hooks/post-edit.cjs b/.claude/hooks/post-edit.cjs deleted file mode 100644 index 12c0ddcc..00000000 --- a/.claude/hooks/post-edit.cjs +++ /dev/null @@ -1,19 +0,0 @@ -// VibeCoding v9.2.0 — PostToolUse(Write|Edit): 格式化 -'use strict'; -const { execSync } = require('child_process'); -const fs = require('fs'); -const input = JSON.parse(fs.readFileSync('/dev/stdin', 'utf8')); -const file = input?.tool_input?.file_path || input?.tool_input?.path || ''; - -try { - if (file.match(/\.(ts|tsx|js|jsx)$/)) { - // 先检查 prettier 存在 - if (fs.existsSync('node_modules/.bin/prettier')) { - execSync(`npx prettier --write "${file}" 2>/dev/null`, { timeout: 5000 }); - } - } else if (file.match(/\.py$/)) { - try { execSync('which black', { stdio: 'ignore', timeout: 2000 }); } catch { process.exit(0); } - execSync(`python3 -m black "${file}" --quiet 2>/dev/null`, { timeout: 5000 }); - } -} catch {} -process.exit(0); diff --git a/.claude/hooks/pre-bash-guard.cjs b/.claude/hooks/pre-bash-guard.cjs new file mode 100755 index 00000000..5fd325e9 --- /dev/null +++ b/.claude/hooks/pre-bash-guard.cjs @@ -0,0 +1,54 @@ +#!/usr/bin/env node +'use strict'; +// VibeCoding Athena PreToolUse Bash guard v9.6 +// 协议: hookSpecificOutput.{hookEventName: "PreToolUse", permissionDecision: "deny", permissionDecisionReason} +// 参考: https://code.claude.com/docs/en/hooks#pretooluse +// +// v9.6 设计: +// 配合 settings.json 的 if 字段做精细化拦截 (官方 if syntax: https://code.claude.com/docs/en/hooks#common-fields) +// settings.json 已用 if: "Bash(rm *)" / "Bash(curl *|wget *)" / "Bash(mkfs*|dd *)" 预过滤 +// hook 内部做二次确认 + 提供具体的拒绝原因 +// +// permissions.deny 已含 "Bash(rm -rf *)" 灾难规则, 这里是双重保险 + +let input = ''; +process.stdin.setEncoding('utf8'); +process.stdin.on('data', d => input += d); +process.stdin.on('end', () => { + let cmd = ''; + try { + const d = JSON.parse(input); + cmd = (d.tool_input && d.tool_input.command) || ''; + } catch (e) { process.exit(0); return; } + + if (!cmd) { process.exit(0); return; } + + const deny = (reason) => { + process.stderr.write(`[bash-guard] deny: ${reason} (${cmd.slice(0, 60)})\n`); + process.stdout.write(JSON.stringify({ + hookSpecificOutput: { + hookEventName: 'PreToolUse', + permissionDecision: 'deny', + permissionDecisionReason: `Athena bash-guard 阻断: ${reason}` + } + })); + process.exit(0); + }; + + // 灾难级 (3 条): + // 1. rm -rf 系统/用户根 + if (/rm\s+-[rRf]+\s+(\/\s|\/\*|~\/?\s|~\s|\/$|~$)/.test(cmd) || + /rm\s+-[rRf]+\s+\/\s*[;&|]/.test(cmd)) { + return deny('禁止删除系统/用户根目录'); + } + // 2. 管道远程脚本 + if (/(curl|wget)\s+[^|]*\|\s*(bash|sh|zsh|fish|ksh)/.test(cmd)) { + return deny('禁止管道执行远程脚本 (curl|wget ... | sh)'); + } + // 3. 格式化磁盘 / 写设备 + if (/\bmkfs\.|dd\s+if=.*of=\/dev\/sd|>\s*\/dev\/sd[a-z]/.test(cmd)) { + return deny('禁止格式化磁盘/写设备'); + } + + process.exit(0); +}); diff --git a/.claude/hooks/pre-bash.cjs b/.claude/hooks/pre-bash.cjs deleted file mode 100644 index 6a5b5a64..00000000 --- a/.claude/hooks/pre-bash.cjs +++ /dev/null @@ -1,26 +0,0 @@ -// VibeCoding v9.2.0 — PreToolUse(Bash): 安全守卫 (JSON decision output) -'use strict'; -const input = JSON.parse(require('fs').readFileSync('/dev/stdin', 'utf8')); -const cmd = input?.tool_input?.command || ''; - -const BLOCKED = [ - /rm\s+-rf\s+[\/~]/, /chmod\s+777/, /curl\s+.*\|\s*(?:bash|sh)/, - /wget\s+.*\|\s*(?:bash|sh)/, />\s*\/etc\//, /mkfs\./, /dd\s+if=/, - /:(){ :\|:& };:/, /fork\s*bomb/i -]; - -for (const pattern of BLOCKED) { - if (pattern.test(cmd)) { - // 使用 JSON decision output (官方推荐方式) - const output = JSON.stringify({ - hookSpecificOutput: { - hookEventName: "PreToolUse", - permissionDecision: "deny", - permissionDecisionReason: `[pre-bash] 危险命令被阻断: ${cmd.slice(0, 80)}` - } - }); - process.stdout.write(output); - process.exit(0); - } -} -process.exit(0); diff --git a/.claude/hooks/session-start.cjs b/.claude/hooks/session-start.cjs new file mode 100755 index 00000000..4774b920 --- /dev/null +++ b/.claude/hooks/session-start.cjs @@ -0,0 +1,122 @@ +#!/usr/bin/env node +'use strict'; +// VibeCoding Athena SessionStart hook v9.6 +// 协议: hookSpecificOutput.{hookEventName: "SessionStart", additionalContext} +// 参考: https://code.claude.com/docs/en/hooks#sessionstart +// +// 设计 (铁律 8 索引先行): +// 1. 唯一 IO: read .ai_state/_index.md (frontmatter) +// 2. 注入 context-essentials.md (静态文本) +// 3. 注入 _index.md 关键字段摘要 (path/stage/sprint/counts/active_goal) +// 4. effort=max 时跳过 PACE 状态注入 (高 effort 模型自己探索) +// 5. 不预加载 tasks/design/progress 内容 + +const fs = require('fs'); +const path = require('path'); + +const PROJECT_DIR = process.env.CLAUDE_PROJECT_DIR || process.cwd(); +const STATE_DIR = path.join(PROJECT_DIR, '.ai_state'); +const INDEX_PATH = path.join(STATE_DIR, '_index.md'); + +const PROJECT_ESSENTIALS = path.join(PROJECT_DIR, '.claude', 'skills', 'pace', 'context-essentials.md'); +const HOME = process.env.HOME || require('os').homedir(); +const GLOBAL_ESSENTIALS = path.join(HOME, '.claude', 'skills', 'pace', 'context-essentials.md'); + +let input = ''; +process.stdin.setEncoding('utf8'); +process.stdin.on('data', d => input += d); +process.stdin.on('end', () => { + let event = {}; + try { event = JSON.parse(input); } catch (e) {} + const source = event.source || 'unknown'; + + // CC v2.1.133+ 注入 effort.level + const effort = (event.effort && event.effort.level) || process.env.CLAUDE_EFFORT || ''; + const isMax = effort === 'max'; + + const lines = []; + + // 1. 注入 context-essentials.md (优先项目级, 回落全局) + let essentialsText = ''; + for (const p of [PROJECT_ESSENTIALS, GLOBAL_ESSENTIALS]) { + try { + const t = fs.readFileSync(p, 'utf8').trim(); + if (t) { essentialsText = t; break; } + } catch (e) {} + } + if (essentialsText) { + lines.push(essentialsText); + } else { + lines.push('[VibeCoding Athena v9.6] TDD · Review · Sisyphus · 索引先行 · 完成度证据 · 出处优先\n[Get-bearings] read .ai_state/_index.md 然后按 pointers 跳转明细'); + } + + // 2. effort=max 时不注入 PACE 状态摘要 (高 effort 自己探索) + if (!isMax && fs.existsSync(INDEX_PATH)) { + try { + const content = fs.readFileSync(INDEX_PATH, 'utf8'); + const fmMatch = content.match(/^---\n([\s\S]*?)\n---/); + if (fmMatch) { + const fm = fmMatch[1]; + const getStr = k => { + const m = fm.match(new RegExp('^ ' + k + ': (.*)$', 'm')); + return m ? m[1].replace(/^["']|["']$/g, '').trim() : ''; + }; + const getNum = k => { + const m = fm.match(new RegExp('^ ' + k + ': (\\d+)$', 'm')); + return m ? parseInt(m[1], 10) : 0; + }; + const path_ = getStr('path'); + const stage = getStr('stage'); + const sprint = getNum('sprint'); + const goal = getStr('active_goal'); + const pending = getNum('tasks_pending'); + const done = getNum('tasks_done'); + const blocked = getNum('tasks_blocked'); + + if (path_ && stage) { + lines.push(`\n[PACE] Path:${path_} Stage:${stage} Sprint:${sprint}`); + } + if (goal) { + lines.push(`[/goal active] ${goal.slice(0, 120)}${goal.length > 120 ? '...' : ''}`); + } + if (pending + done + blocked > 0) { + lines.push(`[任务] ${done} 完成 ${pending} 待办 ${blocked} 阻塞 (详见 details/tasks-current.md)`); + } + + // gotchas (按 YAML array 简单解析: 单行 [..] 或多行 - 形式) + const gotchasMatch = fm.match(/^gotchas:\s*(.+)$/m); + if (gotchasMatch) { + const g = gotchasMatch[1].trim(); + if (g.startsWith('[') && !g.match(/^\[\s*\]$/)) { + lines.push(`[Gotchas] ${g.slice(1, -1).trim()}`); + } + } + } + } catch (e) { + process.stderr.write(`[session-start] _index.md parse failed: ${e.message}\n`); + } + } + + if (lines.length === 0) { process.exit(0); return; } + + const additionalContext = lines.join('\n'); + process.stderr.write(`[session-start/${source}${isMax ? '/max' : ''}] injected ${additionalContext.length} chars\n`); + + // hook-trace + try { + const trace = JSON.stringify({ + ts: new Date().toISOString(), + hook: 'session-start', + source, effort, chars: additionalContext.length + }) + '\n'; + fs.appendFileSync(path.join(STATE_DIR, 'hook-trace.jsonl'), trace); + } catch (e) {} + + process.stdout.write(JSON.stringify({ + hookSpecificOutput: { + hookEventName: 'SessionStart', + additionalContext + } + })); + process.exit(0); +}); diff --git a/.claude/hooks/state-audit.cjs b/.claude/hooks/state-audit.cjs new file mode 100755 index 00000000..45c9f7ac --- /dev/null +++ b/.claude/hooks/state-audit.cjs @@ -0,0 +1,94 @@ +#!/usr/bin/env node +'use strict'; +// VibeCoding Athena ConfigChange hook v9.6 ★新增 (L2.G) +// 协议: 不阻断, additionalContext 注入 warning +// 参考: https://code.claude.com/docs/en/hooks#configchange +// +// 设计: +// ConfigChange 事件触发时 (settings.json/CLAUDE.md/MCP 等变更): +// 1. 重新读 _index.md, 验证 schema_version 仍是 "9.6" +// 2. 比对 fingerprints 段, 检测 details/* 是否被外部修改 (mtime 异常跳变) +// 3. 异常时注入 systemMessage 提醒 (而非 block, ConfigChange 不该 block) +// +// 用途场景: 用户用编辑器手改了 _index.md 导致 schema 损坏 / git checkout 切换分支后状态错位 + +const fs = require('fs'); +const path = require('path'); + +const PROJECT_DIR = process.env.CLAUDE_PROJECT_DIR || process.cwd(); +const STATE_DIR = path.join(PROJECT_DIR, '.ai_state'); +const INDEX_PATH = path.join(STATE_DIR, '_index.md'); + +let input = ''; +process.stdin.setEncoding('utf8'); +process.stdin.on('data', d => input += d); +process.stdin.on('end', () => { + let event = {}; + try { event = JSON.parse(input); } catch (e) {} + const configSource = event.matcher || 'unknown'; + + if (!fs.existsSync(INDEX_PATH)) { process.exit(0); return; } + + const warnings = []; + + try { + const content = fs.readFileSync(INDEX_PATH, 'utf8'); + const fmMatch = content.match(/^---\n([\s\S]*?)\n---/); + + if (!fmMatch) { + warnings.push('_index.md frontmatter 已损坏, 无法解析'); + } else { + const fm = fmMatch[1]; + + // 1. schema_version 检查 + const schemaMatch = fm.match(/^schema_version: "([^"]+)"$/m); + if (!schemaMatch || schemaMatch[1] !== '9.6') { + warnings.push(`schema_version 异常: ${schemaMatch ? schemaMatch[1] : 'missing'} (期望 "9.6", 可能需要 /athena-migrate)`); + } + + // 2. fingerprints vs 实际 mtime 比对 + const getFp = k => { + const m = fm.match(new RegExp('^ ' + k + ': (\\d+)$', 'm')); + return m ? parseInt(m[1], 10) : 0; + }; + const fpTasks = getFp('tasks_mtime'); + const fpDesign = getFp('design_mtime'); + const fpProgress = getFp('progress_mtime'); + const fpLessons = getFp('lessons_mtime'); + + const checkFile = (name, fp) => { + try { + const actual = Math.floor(fs.statSync(path.join(STATE_DIR, 'details', name)).mtimeMs); + // 允许 fingerprint 落后于 actual (PostToolUse 还没跑), 但反过来不正常 + if (fp > actual + 1000) { + warnings.push(`${name} fingerprint 比实际 mtime 新 (${fp - actual}ms), 可能被外部修改`); + } + } catch (e) {} + }; + checkFile('tasks-current.md', fpTasks); + checkFile('design.md', fpDesign); + checkFile('progress.md', fpProgress); + checkFile('lessons.md', fpLessons); + } + } catch (e) { + warnings.push(`审计失败: ${e.message}`); + } + + if (warnings.length === 0) { process.exit(0); return; } + + const msg = `[Athena state-audit] ConfigChange (${configSource}) 检测到 ${warnings.length} 个潜在问题:\n${warnings.map(w => '• ' + w).join('\n')}`; + process.stderr.write(msg + '\n'); + process.stdout.write(JSON.stringify({ systemMessage: msg })); + + try { + const trace = JSON.stringify({ + ts: new Date().toISOString(), + hook: 'state-audit', + source: configSource, + warnings_count: warnings.length + }) + '\n'; + fs.appendFileSync(path.join(STATE_DIR, 'hook-trace.jsonl'), trace); + } catch (e) {} + + process.exit(0); +}); diff --git a/.claude/hooks/stop-failure.cjs b/.claude/hooks/stop-failure.cjs deleted file mode 100644 index adfb994c..00000000 --- a/.claude/hooks/stop-failure.cjs +++ /dev/null @@ -1,18 +0,0 @@ -// VibeCoding v9.2.0 — StopFailure: API错误时保存状态 -'use strict'; -const fs = require('fs'); -const path = require('path'); -const STATE_DIR = path.join(process.cwd(), '.ai_state'); - -try { - if (fs.existsSync(STATE_DIR)) { - const sessionFile = path.join(STATE_DIR, 'session.md'); - if (fs.existsSync(sessionFile)) { - let content = fs.readFileSync(sessionFile, 'utf8'); - const ts = new Date().toISOString(); - content += `\n\n## 异常中断\n${ts} — API错误导致会话中断。下次 SessionStart 自动恢复。\n`; - fs.writeFileSync(sessionFile, content); - } - console.log('[VibeCoding] API错误,状态已保存。下次启动自动恢复。'); - } -} catch {} diff --git a/.claude/hooks/subagent-retry.cjs b/.claude/hooks/subagent-retry.cjs new file mode 100755 index 00000000..7d459a19 --- /dev/null +++ b/.claude/hooks/subagent-retry.cjs @@ -0,0 +1,70 @@ +#!/usr/bin/env node +'use strict'; +// VibeCoding Athena PostToolUse subagent-retry v9.6 +// 协议: 不阻断 (additionalContext 注入 hint) +// 参考: https://code.claude.com/docs/en/hooks#posttooluse +// +// 设计: +// Task tool 调用 subagent. 若返回的 tool_response 里含"无法"/"无能力"等放弃语 +// → 注入 systemMessage 提示 main agent 重试或亲自接管 +// 防止 subagent 偷懒 (v9.5 实测问题) + +const fs = require('fs'); +const path = require('path'); + +const PROJECT_DIR = process.env.CLAUDE_PROJECT_DIR || process.cwd(); +const STATE_DIR = path.join(PROJECT_DIR, '.ai_state'); + +let input = ''; +process.stdin.setEncoding('utf8'); +process.stdin.on('data', d => input += d); +process.stdin.on('end', () => { + let event = {}; + try { event = JSON.parse(input); } catch (e) { process.exit(0); return; } + + if (event.tool_name !== 'Task') { process.exit(0); return; } + + const response = event.tool_response; + if (!response || typeof response !== 'object') { process.exit(0); return; } + + // 提取 subagent 的完整响应内容 + let text = ''; + if (typeof response.content === 'string') text = response.content; + else if (Array.isArray(response.content)) text = response.content.map(c => c.text || '').join('\n'); + else text = JSON.stringify(response); + + // 放弃信号词 + const giveupPatterns = [ + /我(无法|不能|没法|无能力|不具备能力)/, + /I (cannot|can't|am not able to|don't have access)/i, + /no (capability|access|permission) to/i, + /unable to (read|write|execute|access)/i, + /tool not available/i, + /(请|你)(自己|手动|帮我)?(运行|执行|跑)/, + /please run (this|the command) (yourself|manually)/i + ]; + + const found = giveupPatterns.find(p => p.test(text)); + if (!found) { process.exit(0); return; } + + process.stderr.write(`[subagent-retry] giveup detected: ${found}\n`); + + const subagentType = (event.tool_input && event.tool_input.subagent_type) || 'subagent'; + + process.stdout.write(JSON.stringify({ + systemMessage: `[Athena subagent-retry] ${subagentType} 返回了放弃信号. 协议: 1) 再尝试一次, 用不同参数或拆分任务 2) 仍失败则主 agent 接管, 不要让用户代执行 (铁律 6 完成度证据)` + })); + + // hook-trace + try { + const trace = JSON.stringify({ + ts: new Date().toISOString(), + hook: 'subagent-retry', + subagent_type: subagentType, + pattern: String(found) + }) + '\n'; + fs.appendFileSync(path.join(STATE_DIR, 'hook-trace.jsonl'), trace); + } catch (e) {} + + process.exit(0); +}); diff --git a/.claude/hooks/tdd-check.cjs b/.claude/hooks/tdd-check.cjs deleted file mode 100644 index da67cc35..00000000 --- a/.claude/hooks/tdd-check.cjs +++ /dev/null @@ -1,21 +0,0 @@ -// VibeCoding v9.2.0 — Stop: TDD 检查 (git diff 对比) -'use strict'; -const { execSync } = require('child_process'); - -try { - const diff = execSync('git diff --name-only 2>/dev/null', { encoding: 'utf8' }); - const codeExts = /\.(ts|tsx|js|jsx|py|go|rs)$/; - const testPattern = /\.test\.|\.spec\.|__test__|_test\./; - const configPattern = /config|\.json$|\.yaml$|\.yml$|\.md$|\.lock$/i; - - const srcFiles = diff.split('\n').filter(f => - f.match(codeExts) && !f.match(testPattern) && !f.match(configPattern) - ); - const testFiles = diff.split('\n').filter(f => f.match(testPattern)); - - if (srcFiles.length > 0 && testFiles.length === 0) { - process.stderr.write(`[tdd-check] ⚠ ${srcFiles.length} 个源码修改但无测试。TDD: 先写测试!\n`); - // 不阻断, 只提醒 (阻断在 delivery-gate) - } -} catch {} -process.exit(0); diff --git a/.claude/settings.json b/.claude/settings.json index caefb55c..f31e2369 100644 --- a/.claude/settings.json +++ b/.claude/settings.json @@ -1,55 +1,65 @@ { - "environmentVariables": { - "VIBECODING_VERSION": "9.2.0", - "LANGUAGE": "zh-Hans", - "LC_ALL": "zh-Hans", + "$schema": "https://json.schemastore.org/claude-code-settings.json", + "model": "opusplan", + "effortLevel": "xhigh", + "env": { + "VIBECODING_ATHENA_VERSION": "9.6.0", + "ANTHROPIC_DEFAULT_OPUS_MODEL": "claude-opus-4-7", + "ANTHROPIC_DEFAULT_SONNET_MODEL": "claude-sonnet-4-6", + "CLAUDE_CODE_SUBAGENT_MODEL": "claude-sonnet-4-6", "CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC": "1", - "API_TIMEOUT_MS": "3000000", - "CLAUDE_CODE_SUBAGENT_MODEL": "claude-sonnet-4-6" + "DISABLE_NON_ESSENTIAL_MODEL_CALLS": "1", + "CLAUDE_CODE_DISABLE_FEEDBACK_SURVEY": "1", + "CLAUDE_CODE_SUBPROCESS_ENV_SCRUB": "1", + "NEXT_TELEMETRY_DISABLED": "1", + "NUXT_TELEMETRY_DISABLED": "1", + "GATSBY_TELEMETRY_DISABLED": "1", + "ASTRO_TELEMETRY_DISABLED": "1", + "ANGULAR_CLI_ANALYTICS": "false", + "API_TIMEOUT_MS": "30000" }, "permissions": { "allow": [ - "Bash(npm run *)", - "Bash(npx *)", - "Bash(node *)", - "Bash(git *)", - "Bash(cat *)", - "Bash(ls *)", - "Bash(find *)", - "Bash(grep *)", - "Bash(head *)", - "Bash(tail *)", - "Bash(wc *)", - "Bash(mkdir *)", - "Bash(cp *)", - "Bash(mv *)", - "Bash(touch *)", - "Bash(codex *)", - "Bash(ast-grep *)", - "Read(*)", - "Write(*)", - "Edit(*)", - "MultiEdit(*)", - "Glob(*)", - "Grep(*)" + "Bash(npm test:*)", + "Bash(npm run:*)", + "Bash(npx:*)", + "Bash(node:*)", + "Bash(python:*)", + "Bash(python3:*)", + "Bash(pytest:*)", + "Bash(uv run:*)", + "Bash(cargo:*)", + "Bash(go:*)", + "Bash(claude *)", + "Bash(git status)", + "Bash(git diff:*)", + "Bash(git log:*)", + "Bash(git add:*)", + "Bash(git commit:*)", + "Bash(git checkout:*)", + "Bash(git branch:*)", + "Bash(git worktree:*)", + "Read(.ai_state/**)", + "Write(.ai_state/**)" ], "deny": [ - "Bash(rm -rf /)", - "Bash(rm -rf /*)", - "Bash(rm -rf ~)", - "Bash(chmod 777 *)", - "Bash(curl * | bash)", - "Read(./secrets/**)", - "Read(./.env.production)" + "Bash(rm -rf *)", + "Read(./.env)", + "Read(./.env.*)", + "Read(./secrets/**)" ] }, + "worktree": { + "baseRef": "head" + }, "hooks": { "SessionStart": [ { + "matcher": "startup|resume|clear|compact", "hooks": [ { "type": "command", - "command": "node .claude/hooks/context-loader.cjs" + "command": "test -f \"${CLAUDE_PROJECT_DIR}/.claude/hooks/session-start.cjs\" && node \"${CLAUDE_PROJECT_DIR}/.claude/hooks/session-start.cjs\" || true" } ] } @@ -60,18 +70,38 @@ "hooks": [ { "type": "command", - "command": "node .claude/hooks/pre-bash.cjs" + "if": "Bash(rm *)", + "command": "test -f \"${CLAUDE_PROJECT_DIR}/.claude/hooks/pre-bash-guard.cjs\" && node \"${CLAUDE_PROJECT_DIR}/.claude/hooks/pre-bash-guard.cjs\" || true" + }, + { + "type": "command", + "if": "Bash(curl *|wget *)", + "command": "test -f \"${CLAUDE_PROJECT_DIR}/.claude/hooks/pre-bash-guard.cjs\" && node \"${CLAUDE_PROJECT_DIR}/.claude/hooks/pre-bash-guard.cjs\" || true" + }, + { + "type": "command", + "if": "Bash(mkfs*|dd *)", + "command": "test -f \"${CLAUDE_PROJECT_DIR}/.claude/hooks/pre-bash-guard.cjs\" && node \"${CLAUDE_PROJECT_DIR}/.claude/hooks/pre-bash-guard.cjs\" || true" } ] } ], "PostToolUse": [ { - "matcher": "Write|Edit|MultiEdit", + "matcher": "Edit|Write|MultiEdit", "hooks": [ { "type": "command", - "command": "node .claude/hooks/post-edit.cjs" + "command": "test -f \"${CLAUDE_PROJECT_DIR}/.claude/hooks/index-updater.cjs\" && node \"${CLAUDE_PROJECT_DIR}/.claude/hooks/index-updater.cjs\" || true" + } + ] + }, + { + "matcher": "Task", + "hooks": [ + { + "type": "command", + "command": "test -f \"${CLAUDE_PROJECT_DIR}/.claude/hooks/subagent-retry.cjs\" && node \"${CLAUDE_PROJECT_DIR}/.claude/hooks/subagent-retry.cjs\" || true" } ] } @@ -81,28 +111,78 @@ "hooks": [ { "type": "command", - "command": "node .claude/hooks/delivery-gate.cjs" - } - ] - }, - { - "hooks": [ + "command": "test -f \"${CLAUDE_PROJECT_DIR}/.claude/hooks/delivery-gate.cjs\" && node \"${CLAUDE_PROJECT_DIR}/.claude/hooks/delivery-gate.cjs\" || true" + }, { "type": "command", - "command": "node .claude/hooks/tdd-check.cjs" + "command": "test -f \"${CLAUDE_PROJECT_DIR}/.claude/hooks/pace-continuator.cjs\" && node \"${CLAUDE_PROJECT_DIR}/.claude/hooks/pace-continuator.cjs\" || true" } ] } ], - "StopFailure": [ + "PermissionDenied": [ { "hooks": [ { "type": "command", - "command": "node .claude/hooks/stop-failure.cjs" + "command": "test -f \"${CLAUDE_PROJECT_DIR}/.claude/hooks/permission-retry.cjs\" && node \"${CLAUDE_PROJECT_DIR}/.claude/hooks/permission-retry.cjs\" || true" + } + ] + } + ], + "PreCompact": [ + { + "hooks": [ + { + "type": "command", + "command": "test -f \"${CLAUDE_PROJECT_DIR}/.claude/hooks/compact-snapshot.cjs\" && node \"${CLAUDE_PROJECT_DIR}/.claude/hooks/compact-snapshot.cjs\" || true" + } + ] + } + ], + "PostCompact": [ + { + "hooks": [ + { + "type": "command", + "command": "test -f \"${CLAUDE_PROJECT_DIR}/.claude/hooks/compact-restore.cjs\" && node \"${CLAUDE_PROJECT_DIR}/.claude/hooks/compact-restore.cjs\" || true" + } + ] + } + ], + "ConfigChange": [ + { + "hooks": [ + { + "type": "command", + "command": "test -f \"${CLAUDE_PROJECT_DIR}/.claude/hooks/state-audit.cjs\" && node \"${CLAUDE_PROJECT_DIR}/.claude/hooks/state-audit.cjs\" || true" } ] } ] + }, + "extraKnownMarketplaces": { + "superpowers-marketplace": { + "source": { + "source": "github", + "repo": "obra/superpowers-marketplace" + } + }, + "openai-codex": { + "source": { + "source": "github", + "repo": "openai/codex-plugin-cc" + } + } + }, + "enabledPlugins": { + "superpowers@superpowers-marketplace": true, + "codex@openai-codex": true, + "feature-dev@claude-plugins-official": true, + "code-review@claude-plugins-official": true, + "commit-commands@claude-plugins-official": true, + "playwright-skill@playwright-skill": true, + "security-guidance@claude-plugins-official": true, + "frontend-design@claude-plugins-official": true } -} \ No newline at end of file +} diff --git a/.claude/skills/athena-dev/SKILL.md b/.claude/skills/athena-dev/SKILL.md new file mode 100644 index 00000000..26932f1e --- /dev/null +++ b/.claude/skills/athena-dev/SKILL.md @@ -0,0 +1,30 @@ +--- +name: athena-dev +description: > + Athena 主入口。从需求到交付的完整工程化开发流程。 +effort: xhigh +disable-model-invocation: true +argument-hint: "<需求描述>" +--- + +# /athena-dev + +`.ai_state/` 不存在 → 提示 `/athena-init` 并停。 +`.ai_state/_index.md.schema_version` != "9.6" → 提示 `/athena-migrate` 并停。 + +## Get-bearings (R₀, just-in-time, 铁律 8) + +1. **必读**: `.ai_state/_index.md` (单一入口) +2. **按需**: stage=impl/review → 按 pointers.latest_progress 读 progress.md 尾部 +3. **按需**: 任务命中 lessons 主题 → pointers.latest_lesson 跳段 +4. **按需**: `git log --oneline -10` +5. **按需**: impl/review 阶段 → `bash .ai_state/init.sh` + +文件大就用 head/tail/grep。**禁止 glob 整个 .ai_state/** (铁律 8)。 + +## Dispatch + +有进行中的 stage → 从当前阶段继续 (触发 pace skill)。 +新需求 → 触发 pace skill 路由, 需求: $ARGUMENTS。 + +首次使用简短说明: "PACE 帮你路由——小事直接做, 大事加设计和审查。CC 原生 /goal /batch /background 已接入。" diff --git a/.claude/skills/athena-init/SKILL.md b/.claude/skills/athena-init/SKILL.md new file mode 100644 index 00000000..1376e7c4 --- /dev/null +++ b/.claude/skills/athena-init/SKILL.md @@ -0,0 +1,104 @@ +--- +name: athena-init +description: > + 项目初始化。每个新项目首次使用 Athena 时运行。扫描项目、生成环境脚本、创建 .ai_state/ schema。 +effort: xhigh +disable-model-invocation: true +--- + +# /athena-init — 项目初始化 + +参考: + +## 1. CC 原生初始化 + +无 CLAUDE.md → 运行 `/init`, 生成后追加: "本项目使用 VibeCoding Athena, 开发用 `/athena-dev`" +已有 CLAUDE.md → 跳过。 + +## 2. 项目扫描 + +```bash +ls package.json pyproject.toml Cargo.toml go.mod build.gradle pom.xml 2>/dev/null +cat package.json 2>/dev/null | grep -A5 '"scripts"' +ls jest.config* vitest.config* pytest.ini .eslintrc* ruff.toml biome.json 2>/dev/null +``` + +## 3. 创建 .ai_state/ 目录树 (v9.6 schema) + +```bash +mkdir -p .ai_state/details/reviews +mkdir -p .ai_state/details/tasks-archive +``` + +## 4. 拷贝模板到 .ai_state/ + +从 `~/.claude/skills/pace/templates/` 复制 (必须全部, 缺一不可): + +- `_index.md` → `.ai_state/_index.md` (单一入口, 铁律 8) +- `details/tasks-current.md` +- `details/progress.md` +- `details/design.md` +- `details/handoff.md` +- `details/lessons.md` +- `details/review-report.md` → `.ai_state/details/reviews/template.md` +- `details/next.md` +- `details/proposals.md` +- `init.sh` → `.ai_state/init.sh` + +## 5. 填充 _index.md frontmatter + +把扫描结果写入: +- `tech_stack` (检测到的: node/python/rust/go) +- `test_cmd` (从 package.json scripts.test 或 pytest 等) +- `build_cmd` +- `lint_cmd` +- `dev_cmd` + +填充 `platform_features` (用 `claude --version` 和 `codex --version 2>&1`): +- `cc_version` +- `cx_version` +- `goal_supported`: 解析 cc_version, ≥ "2.1.139" 即 true +- `batch_supported`: ≥ "2.1.121" +- `background_supported`: ≥ "2.1.139" +- `session_memory_active`: 检测 `~/.claude/projects/` 是否含 session-memory 子目录 +- `cross_session_memory`: 检测 `~/.claude/plugins/` 含 claude-mem 则 "claude-mem", 含 superpowers 则 "superpowers", 否则 "none" + +## 6. 生成 init.sh + +根据扫描结果定制 `.ai_state/init.sh`: +- 依赖安装命令 (npm install / pip install / cargo build) +- dev server 启动 (如有) +- 基线测试命令 +- `chmod +x .ai_state/init.sh` + +## 7. .gitignore + +```bash +grep -q '.ai_state' .gitignore 2>/dev/null || echo '.ai_state/' >> .gitignore +``` + +## 8. 跨项目记忆推荐 (替代删除的全局 lessons) + +```bash +# 检测 claude-mem 或 superpowers, 缺则软提示 +if ! ls ~/.claude/plugins/ 2>/dev/null | grep -iE 'claude-mem|superpowers' > /dev/null; then + echo "💡 推荐装 claude-mem (89k stars) 做跨项目记忆" + echo " /plugin install claude-mem (待加入官方 marketplace)" + echo " 或: /athena-setup 走完整推荐流" +fi +``` + +## 9. Git commit + +```bash +git add .ai_state/ .gitignore +git commit -m "athena: project initialized (v9.6 schema)" +``` + +## 10. 验证 + +运行 `bash .ai_state/init.sh` 确认脚本正常。 +告知用户: "Athena 已就绪。用 `/athena-dev <需求>` 开始开发, 或 `/athena-status` 看面板。" + +已有 .ai_state/ 但 schema_version != "9.6" → 提示运行 `/athena-migrate` 升级。 +已有 .ai_state/ 且 schema_version == "9.6" → 提示用户是否重新初始化 (会重置 _index.md)。 diff --git a/.claude/skills/athena-migrate/SKILL.md b/.claude/skills/athena-migrate/SKILL.md new file mode 100644 index 00000000..721108e3 --- /dev/null +++ b/.claude/skills/athena-migrate/SKILL.md @@ -0,0 +1,203 @@ +--- +name: athena-migrate +description: > + 把 v9.5.5 项目的 .ai_state/ 升级到 v9.6 schema (INDEX + details/ 重构)。 +effort: xhigh +disable-model-invocation: true +--- + +# /athena-migrate — v9.5.5 → v9.6 迁移 + +参考: + +## 前置检查 + +```bash +test -d .ai_state || { echo "[migrate] 无 .ai_state, 请先 /athena-init"; exit 1; } +test -f .ai_state/_index.md && grep -q 'schema_version: "9.6"' .ai_state/_index.md && { + echo "[migrate] 已是 v9.6 schema, 无需迁移"; exit 0; +} +``` + +## 迁移算法 + +### Step 1: 备份 + +```bash +mkdir -p .ai_state/.legacy-v9.5 +cp -r .ai_state/{tasks.md,design.md,progress.md,lessons.md,handoff.md,project.json,reviews,hook-trace.jsonl,compact-snapshot.json} .ai_state/.legacy-v9.5/ 2>/dev/null || true +echo "[migrate] 旧文件已备份到 .ai_state/.legacy-v9.5/" +``` + +### Step 2: 创建 details/ 目录 + +```bash +mkdir -p .ai_state/details/reviews +mkdir -p .ai_state/details/tasks-archive +``` + +### Step 3: 迁移文件 + +| v9.5.5 路径 | v9.6 路径 | 备注 | +|---|---|---| +| `.ai_state/tasks.md` | `.ai_state/details/tasks-current.md` | 改名 | +| `.ai_state/design.md` | `.ai_state/details/design.md` | 平移 | +| `.ai_state/progress.md` | `.ai_state/details/progress.md` | 平移 | +| `.ai_state/lessons.md` | `.ai_state/details/lessons.md` | 平移 | +| `.ai_state/handoff.md` | `.ai_state/details/handoff.md` | 平移 | +| `.ai_state/reviews/sprint-N.md` | `.ai_state/details/reviews/sprint-N.md` | 平移 | +| `.ai_state/project.json` | (merge 到 _index.md frontmatter) | 转 markdown frontmatter | +| `.ai_state/hook-trace.jsonl` | `.ai_state/hook-trace.jsonl` | 保持原位置 | +| `.ai_state/compact-snapshot.json` | (删, v9.6 改写到 _index.md.snapshots) | 删除 | + +```bash +# 平移 +[ -f .ai_state/tasks.md ] && mv .ai_state/tasks.md .ai_state/details/tasks-current.md +for f in design.md progress.md lessons.md handoff.md; do + [ -f .ai_state/$f ] && mv .ai_state/$f .ai_state/details/$f +done +[ -d .ai_state/reviews ] && mv .ai_state/reviews .ai_state/details/reviews +[ -f .ai_state/compact-snapshot.json ] && rm .ai_state/compact-snapshot.json +``` + +### Step 4: 生成 _index.md + +从 `.ai_state/project.json` 读 path/stage/sprint/tech_stack/test_cmd/build_cmd/lint_cmd/dev_cmd/conventions/gotchas, 写入新 _index.md frontmatter。 + +```bash +# 用 node 脚本生成 _index.md (避免手写 yaml escape 出错) +node -e ' +const fs = require("fs"); +const path = require("path"); + +let proj = {}; +try { proj = JSON.parse(fs.readFileSync(".ai_state/.legacy-v9.5/project.json","utf8")); } catch(e) {} + +const sprint = proj.sprint || 0; + +// 计算 counts +let tasks_pending = 0, tasks_done = 0, tasks_blocked = 0; +try { + const t = fs.readFileSync(".ai_state/details/tasks-current.md","utf8"); + tasks_pending = (t.match(/^- \[ \]/gm) || []).length; + tasks_done = (t.match(/^- \[x\]/gm) || []).length; +} catch(e) {} + +let progress_entries = 0, latest_progress = ""; +try { + const lines = fs.readFileSync(".ai_state/details/progress.md","utf8").split("\n").filter(l => l.trim().length > 0 && !l.startsWith(" + +## 最近活动 + + + +## 当前阻塞 + + + +## 下一动作 (PACE 建议) + + + +## 反思建议 (Hook 沉淀) + + diff --git a/.claude/skills/pace/templates/details/design.md b/.claude/skills/pace/templates/details/design.md new file mode 100644 index 00000000..3b45ac81 --- /dev/null +++ b/.claude/skills/pace/templates/details/design.md @@ -0,0 +1,28 @@ +# 设计文档 + +## 需求摘要 + +## 功能分级 + +- **MUST**: +- **SHOULD**: +- **COULD**: + +## 验收标准 + +- [ ] + +## 技术方案 + +## File Structure Plan + + + +## 不做什么 diff --git a/.claude/skills/pace/templates/details/handoff.md b/.claude/skills/pace/templates/details/handoff.md new file mode 100644 index 00000000..bea31b9d --- /dev/null +++ b/.claude/skills/pace/templates/details/handoff.md @@ -0,0 +1,31 @@ +# 跨模型/跨 worker 交接 + + + +## 当前任务 + + + +## 项目文件树 + + + + +## 关键代码片段 + + + +## 项目背景 + + + +## 已完成 + + + +## 注意事项 + + diff --git a/.claude/skills/pace/templates/details/lessons.md b/.claude/skills/pace/templates/details/lessons.md new file mode 100644 index 00000000..8d473029 --- /dev/null +++ b/.claude/skills/pace/templates/details/lessons.md @@ -0,0 +1,21 @@ +# 项目级经验沉淀 (Lessons Learned) + + diff --git a/.claude/skills/pace/templates/details/next.md b/.claude/skills/pace/templates/details/next.md new file mode 100644 index 00000000..86579c8d --- /dev/null +++ b/.claude/skills/pace/templates/details/next.md @@ -0,0 +1,15 @@ +# 下一动作建议 (PACE-aware, Stop hook 维护) + + diff --git a/.claude/skills/pace/templates/details/progress.md b/.claude/skills/pace/templates/details/progress.md new file mode 100644 index 00000000..1c82015c --- /dev/null +++ b/.claude/skills/pace/templates/details/progress.md @@ -0,0 +1,15 @@ +# Progress Log + + diff --git a/.claude/skills/pace/templates/details/proposals.md b/.claude/skills/pace/templates/details/proposals.md new file mode 100644 index 00000000..1a85b244 --- /dev/null +++ b/.claude/skills/pace/templates/details/proposals.md @@ -0,0 +1,20 @@ +# Hook 反思沉淀 (铁律 9: Hook 是进化器) + + diff --git a/.claude/skills/pace/templates/details/review-report.md b/.claude/skills/pace/templates/details/review-report.md new file mode 100644 index 00000000..a879ae39 --- /dev/null +++ b/.claude/skills/pace/templates/details/review-report.md @@ -0,0 +1,41 @@ +# Sprint N 审查报告 + +## Step 1: 测试结果 + + + +## Step 2: /codex:review (Feature+, Quick 可选) + + + +## Step 3: /codex:adversarial-review (System) + + + +## Step 4: ECC 扫描 (System) + + + +## Step 5: Claude 审查 (/review 内置) + + + +## Step 6: @evaluator 评分 + +| 维度 | 分数 | 证据 | +|------|------|------| +| Functionality | /5 | (代码行号) | +| Spec Compliance | /5 | (代码行号 vs design.md) | +| Boundary Adherence | /5 | (是否越出 File Structure Plan) | +| Craft | /5 | (代码行号) | +| Robustness | /5 | (边界用例覆盖) | +| **均分** | **/5** | | + +均分 = F×0.25 + SC×0.25 + B×0.15 + C×0.15 + R×0.20 + +## VERDICT: + + diff --git a/.claude/skills/pace/templates/details/tasks-current.md b/.claude/skills/pace/templates/details/tasks-current.md new file mode 100644 index 00000000..9371576d --- /dev/null +++ b/.claude/skills/pace/templates/details/tasks-current.md @@ -0,0 +1,19 @@ +# Sprint 任务清单 + + + +## 待办 + +## 完成 + +## 阻塞 diff --git a/.claude/skills/pace/templates/goal-conditions.md b/.claude/skills/pace/templates/goal-conditions.md new file mode 100644 index 00000000..1d0be3e5 --- /dev/null +++ b/.claude/skills/pace/templates/goal-conditions.md @@ -0,0 +1,121 @@ +# /goal Condition 模板库 + +每个 PACE stage 对应一个 condition 模板, 用 `/goal ` 启动 autonomous loop。 + +参考: + +## 写 condition 的四件套 (官方推荐) + +``` +End Goal + Verification + Constraints + Turn/Time Limit +``` + +- **End Goal**: 一个可测的终态 (test 通过 / 文件存在 / git log 含 commit) +- **Verification**: 如何证明 (npm test exits 0 / rg "X" 0 行) +- **Constraints**: 边界 (不动 X 文件 / 不引入新依赖) +- **Turn/Time Limit**: stop after 20 turns / stop after 60 minutes + +--- + +## Feature/Refactor/System stage=plan + +``` +ALL of: + - .ai_state/details/design.md 含 "## File Structure Plan" 段且段下至少 3 个 owner 行 + - .ai_state/details/tasks-current.md 至少 1 个 - [ ] Task + - 我已经 ask 用户确认且收到了 "yes" 或 "go" 回复 +Constraints: + - 不修改 design.md 之外的源码文件 + - 不超过 20 turns +``` + +## Quick/Feature stage=impl + +``` +ALL of: + - .ai_state/details/tasks-current.md 中所有 - [ ] 都变成 - [x] + - 运行 退出码 0 (从 _index.md.test_cmd 读) + - .ai_state/details/progress.md 在本 turn 序列至少新增 N 行 (N = 原待办 task 数) + - 没有任何对 design.md 的修改 +Constraints: + - 不动 .ai_state/_index.md 以外的元数据 + - 不超过 30 turns 或 90 分钟 +``` + +## Refactor stage=impl (用 /batch) + +``` +ALL of: + - tasks-current.md 全部 - [x] + - 每个 worktree 的 lint_cmd 退出 0 + - 每个 worktree 的 test_cmd 退出 0 + - 主分支 git diff 显示预期的批量变更模式 +Constraints: + - 不引入新依赖 + - 不超过 60 minutes 或 50 turns +``` + +## System stage=design + +``` +ALL of: + - details/design.md 含 "## File Structure Plan" 且段下覆盖所有新增/修改的模块路径 + - details/design.md 含 "## 验收标准" 段且至少 5 条 - [ ] (会在 impl 阶段勾选) + - details/tasks-current.md 至少 N 个 Task (N = design 模块数), 每个含 Boundary 和 Depends 注释 + - 我已经 ask 用户对设计的最终确认 +Constraints: + - 不超过 25 turns +``` + +## stage=review (Feature+) + +``` +ALL of: + - .ai_state/details/reviews/sprint-{sprint}.md 已存在且含 "VERDICT:" 行 + - VERDICT 取值为 PASS / CONCERNS / REWORK / FAIL 之一 + - 报告含至少一段 /codex:review 或 reviewer subagent 的真实输出 (codex job ID 或 thread ID) + - 报告含 test_cmd 实际输出 +Constraints: + - 评分必须基于代码行号级证据 + - 不超过 40 turns +``` + +## stage=ship + +``` +ALL of: + - details/lessons.md 末尾含 "## [{date} Sprint {sprint}]" 标题 + - git log --oneline -1 显示本 sprint 的提交 + - _index.md.project.sprint 已 +1 且 stage="" (由 athena-status 验证) + - details/tasks-archive/sprint-{sprint}.md 存在 (归档) +Constraints: + - 不超过 10 turns +``` + +--- + +## 启动方式 + +### 交互式 +``` +/goal <粘贴上面对应 stage 的 condition> +``` + +### 非交互式 (overnight 跑) +```bash +claude -p "/goal " +``` + +### 状态查询 +``` +/goal # 不带参数, 看 turns/tokens/最近 evaluator reason +/goal clear # 中止 +``` + +## 注意事项 (官方文档) + +- evaluator 只读 transcript, 不跑命令——condition 必须是"transcript 能证明的事" +- 一个 session 只能有一个 active goal, 设置新 goal 替换旧的 +- condition ≤ 4000 字符 +- v2.1.139+ 才有, 旧版本 delivery-gate hook 兜底 +- "Vague in, vague out"——含糊的 condition = 含糊的 loop diff --git a/.claude/skills/pace/templates/init.sh b/.claude/skills/pace/templates/init.sh new file mode 100755 index 00000000..edde9940 --- /dev/null +++ b/.claude/skills/pace/templates/init.sh @@ -0,0 +1,17 @@ +#!/bin/bash +# Athena init.sh — 启动开发环境 + 基线测试 +# 由 /athena-init 根据项目类型生成具体命令 +set -e + +echo "[Athena] 启动开发环境..." +# 示例 (根据项目类型): +# npm install +# pip install -e . +# cargo build + +echo "[Athena] 运行基线测试..." +# npm test +# pytest +# cargo test + +echo "[Athena] 就绪。" diff --git a/.claude/skills/plan-first/SKILL.md b/.claude/skills/plan-first/SKILL.md deleted file mode 100644 index 583f8571..00000000 --- a/.claude/skills/plan-first/SKILL.md +++ /dev/null @@ -1,26 +0,0 @@ ---- -name: plan-first -description: P 阶段计划生成 — design.md 确认后激活 ---- -# Plan-First (P 阶段) - -## 流程 -1. 读 .ai_state/design.md 全文 -2. 分解为可执行任务, 每个任务: - ``` - - [ ] T-001: {动作动词} + {目标} + {验收条件} - 文件: {涉及文件} - 依赖: {前置任务} - ``` -3. 任务排序: 依赖拓扑 → 风险高的先做 -4. 写入 .ai_state/plan.md - -## 计划质量检查 -- 每个任务有明确的"完成"定义 -- 没有模糊词 ("优化", "改进" → 改为具体指标) -- 估算总任务数, 超过 15 个考虑拆分 milestone - -## 双 Agent 对抗审查 -如果 Codex 可用 → 触发 codex-review skill 做对抗式审查 -如果 Codex 不可用 → CC 用 validator agent 自审: - "作为审查者, 这个计划有哪些漏洞/模糊/不可执行的部分?" diff --git a/.claude/skills/reflexion/SKILL.md b/.claude/skills/reflexion/SKILL.md deleted file mode 100644 index 0263f9f6..00000000 --- a/.claude/skills/reflexion/SKILL.md +++ /dev/null @@ -1,27 +0,0 @@ ---- -name: reflexion -description: E 阶段自我反思 — 每个 Task Micro-review 之前 ---- -# Reflexion (自我反思) - -## 触发时机 -E 阶段每个 Task 实现完成后, Micro-review 之前。 - -## 反思清单 -逐条自问: -1. 我有没有走捷径? (如: 用 `as any` 绕过类型检查, 空 catch 吞错误) -2. 我有没有忽略边界条件? (空输入/超长/并发/零值/null) -3. 我有没有硬编码本应配置的值? (URL/端口/阈值/路径) -4. 我有没有过度工程? (加了需求没要求的功能/抽象层) -5. 我有没有偏离 design.md 的方案? (擅自"优化"设计) -6. 这段代码 6 个月后我还能理解吗? (命名/结构/注释) - -## 处理 -- 发现问题 → **立即修复**, 不留 TODO -- 值得记录的模式 → 追加到 .ai_state/lessons.md -- 模式性错误 → 追加到 conventions.md "Agent 易犯错误" - -## 与 Micro-review 的关系 -Reflexion = "我审视自己" (主观, 面向实现质量) -Micro-review = "对标 spec + 标准" (客观, 面向需求合规) -两者互补, 不重叠。先 Reflexion 再 Micro-review。 diff --git a/.claude/skills/security-review/SKILL.md b/.claude/skills/security-review/SKILL.md deleted file mode 100644 index e705b743..00000000 --- a/.claude/skills/security-review/SKILL.md +++ /dev/null @@ -1,16 +0,0 @@ ---- -name: security-review -description: 安全审查 — Path C+ 或显式触发 ---- -# Security Review - -## 检查项 -1. **认证/授权**: 每个端点是否有权限检查? -2. **输入验证**: 所有外部输入是否已验证和消毒? -3. **密钥管理**: 无硬编码, 用环境变量或密钥管理服务 -4. **依赖**: `npm audit` / `pip audit` 无高危漏洞 -5. **日志**: 不记录敏感信息 (密码/token/PII) -6. **CORS/CSP**: 如有 Web 接口, 是否配置正确? - -## 产出 -- 安全审查结果追加到 quality.md diff --git a/.claude/skills/verification/SKILL.md b/.claude/skills/verification/SKILL.md deleted file mode 100644 index 617d03ae..00000000 --- a/.claude/skills/verification/SKILL.md +++ /dev/null @@ -1,20 +0,0 @@ ---- -name: verification -description: T 阶段验证 — 测试 + 覆盖率 + 验收标准 ---- -# Verification (T 阶段) - -## 流程 -1. 运行测试: `npm test` 或项目配置的测试命令 -2. 覆盖率: 如有配置, 检查覆盖率阈值 -3. TypeScript: `npx tsc --noEmit` (如有 tsconfig.json) -4. Lint: `npx eslint . --max-warnings 0` (如有 eslint 配置, Path C+ 强制) - -## 验收标准逐条确认 (v9.2.0 新增) -5. 读 .ai_state/design.md 的 "## 验收标准" -6. 逐条确认: 这个标准是否被满足? 证据是什么 (测试名/文件位置)? -7. 未满足的标准 → 标注到 .ai_state/quality.md "## 未满足验收标准" -8. 如果有 E2E 框架 → 运行 E2E 确认用户流程 - -## 产出 -- .ai_state/quality.md 填入验证结果 + 未满足标准 diff --git a/.claude/templates/ai-state/conventions.md b/.claude/templates/ai-state/conventions.md deleted file mode 100644 index b0d0584e..00000000 --- a/.claude/templates/ai-state/conventions.md +++ /dev/null @@ -1,12 +0,0 @@ -# Conventions - -## 项目编码规范 -{由 vibe-init 自动检测并填入, 或手动编写} - -## 依赖版本 -{关键依赖 + 版本号} - -## Agent 易犯错误 -> 由 reflexion/kaizen 自动追加, 帮助 agent 避免重复犯错 - -- (初始为空, 使用过程中逐渐积累) diff --git a/.claude/templates/ai-state/design.md b/.claude/templates/ai-state/design.md deleted file mode 100644 index 8e171068..00000000 --- a/.claude/templates/ai-state/design.md +++ /dev/null @@ -1,24 +0,0 @@ -# Design Spec - -## 需求 (MUST/SHOULD/COULD) - -### MUST (必须实现) -- {核心功能 1} -- {核心功能 2} - -### SHOULD (应该实现) -- {重要功能} - -### COULD (可以实现) -- {锦上添花} - -## 技术方案 -{架构 + 关键设计决策} - -## 验收标准 -- [ ] {标准 1: 具体可验证的条件} -- [ ] {标准 2: 具体可验证的条件} - -## 非功能需求 -- 性能: {指标} -- 安全: {要求} diff --git a/.claude/templates/ai-state/doing.md b/.claude/templates/ai-state/doing.md deleted file mode 100644 index 2505a0f2..00000000 --- a/.claude/templates/ai-state/doing.md +++ /dev/null @@ -1,7 +0,0 @@ -# Doing - -## 当前执行 -- [ ] {当前步骤描述} - -## 执行历史 -{自动追加完成的步骤} diff --git a/.claude/templates/ai-state/lessons.md b/.claude/templates/ai-state/lessons.md deleted file mode 100644 index 00e6fd79..00000000 --- a/.claude/templates/ai-state/lessons.md +++ /dev/null @@ -1,10 +0,0 @@ -# Lessons Learned - -## 技术陷阱 -{Reflexion/Kaizen 发现的技术问题} - -## 有效模式 -{本项目中验证有效的解决方案} - -## 项目特有知识 -{不在文档中但很重要的隐性知识} diff --git a/.claude/templates/ai-state/plan.md b/.claude/templates/ai-state/plan.md deleted file mode 100644 index a262a771..00000000 --- a/.claude/templates/ai-state/plan.md +++ /dev/null @@ -1,14 +0,0 @@ -# Plan - -## 任务列表 - -- [ ] T-001: {动作动词} + {目标} + {验收条件} - 文件: {涉及文件} - 依赖: 无 - -- [ ] T-002: {动作动词} + {目标} + {验收条件} - 文件: {涉及文件} - 依赖: T-001 - -## 对抗审查记录 -{Codex 审查结果摘要, 或 "CC 独立审查"} diff --git a/.claude/templates/ai-state/quality.md b/.claude/templates/ai-state/quality.md deleted file mode 100644 index cac67a46..00000000 --- a/.claude/templates/ai-state/quality.md +++ /dev/null @@ -1,18 +0,0 @@ -# Quality Report - -## 验证结果 -- 测试: {通过/失败} ({覆盖率}%) -- TypeScript: {通过/失败} -- Lint: {通过/失败} - -## 验收标准确认 -- [x] {标准 1}: 满足, 证据: {测试名/文件位置} -- [ ] {标准 2}: 未满足, 原因: {说明} - -## 代码审查 -- 审查者: {CC / Codex / 互审} -- 发现问题: {数量} -- 已修复: {数量} - -## 未满足验收标准 -{列出未满足的标准 + 原因} diff --git a/.claude/templates/ai-state/session.md b/.claude/templates/ai-state/session.md deleted file mode 100644 index 2646e4ae..00000000 --- a/.claude/templates/ai-state/session.md +++ /dev/null @@ -1,13 +0,0 @@ -# Session - -## 任务 -{任务描述} - -## 路径 -P.A.C.E. Path: {A|B|C|D} - -## 当前阶段 -RIPER-7: {R₀|R|D|P|E|T|V} - -## 时间戳 -开始: {ISO timestamp} diff --git a/.claude/workflows/pace.md b/.claude/workflows/pace.md deleted file mode 100644 index 781e2be0..00000000 --- a/.claude/workflows/pace.md +++ /dev/null @@ -1,13 +0,0 @@ -# P.A.C.E. 复杂度路由 v9.2.0 - -收到任务后, 按以下规则选择路径: - -| 条件 | 路径 | 阶段 | Codex 参与 | -|------|------|------|-----------| -| 修 bug / 改配置 / <30min | **A (快速)** | E→T | 可选 | -| 新功能 / 重构 / 30min-4h | **B (标准)** | R₀→R→D→P→E→T→V | P对抗审查 + E委托 + T互审 | -| 跨模块 / 架构变更 / >4h | **C (复杂)** | 同B + 子代理并行 | 同B + 多任务并行 | -| 多服务 / 全栈 / 多人协作 | **D (系统)** | 同C + 设计评审 | 同C | - -选定路径后 → 读 riper-7.md 执行对应阶段。 -Path A 跳过 R₀/R/D/P, 直接 E→T。 diff --git a/.claude/workflows/riper-7.md b/.claude/workflows/riper-7.md deleted file mode 100644 index 3a9cfee4..00000000 --- a/.claude/workflows/riper-7.md +++ /dev/null @@ -1,71 +0,0 @@ -# RIPER-7 阶段编排 v9.2.0 - -## 每阶段进入前 (通用) -1. 这一步要解决的核心问题是什么? -2. 项目里有没有类似实现? → 搜索 (Grep/Glob, 或 augment-context-engine 如已安装) -3. 用到的库 API 确定吗? → context7 skill 查文档 -4. .ai_state/lessons.md + conventions.md 有相关经验吗? - ---- - -## R₀ 需求精炼 (Path B+) -**技能**: brainstorm skill (含步骤0自主调研) -**产出**: .ai_state/design.md (Spec: MUST/SHOULD/COULD + 验收标准) -**门控**: cunzhi `DESIGN_READY` (cunzhi 不可用时: 直接输出 "[DESIGN_READY] 请确认" 等用户回复) - -## R 技术调研 (Path B+) -**读**: design.md -**动手**: 搜索相关代码 + 查库版本和 API -**产出**: design.md 追加技术方案 -**要回答**: 有没有可复用的现有代码? 需要新依赖吗? - -## D 方案定稿 (Path B+) -**读**: design.md -**检查**: 接口最小化? 错误处理完整? 有没有更简单的方案? -**产出**: design.md 最终版 (含验收标准) -**门控**: cunzhi `DESIGN_FINALIZED` (cunzhi 不可用时: 直接输出确认提示) - -## P 计划 (Path B+) -**技能**: plan-first skill → 生成 plan.md (T-001/T-002... 格式) -**双 Agent**: 如果 Codex 可用 → codex-review skill 对抗审查 - - CC 提交 plan.md → Codex 质疑 → CC 评估采纳/驳回 → 循环 - - 终止: VERDICT: APPROVED / 连续2轮全驳 / 超5轮 - - 审查结果持久化到 reviews/review-{uuid}.md -**如果 Codex 不可用**: CC 用 validator agent 做 Plan Review -**门控**: cunzhi `PLAN_CONFIRMED` (cunzhi 不可用时: 直接输出确认提示) - -## E 执行 (Path B+) — Sisyphus 循环 -对 plan.md 每个 Task: - 1. 读 Task 文本 + 相关文件 + conventions.md - 2. **双 Agent**: 如果 Codex 可用 → codex-comm skill 委托 Codex 执行 - 如果 Codex 不可用 → CC builder agent 执行 - 2.5. **Reflexion** (自我反思 → reflexion skill): - "这个实现有没有: 走捷径/忽略边界/硬编码/过度工程/偏离 spec?" - 发现问题 → 立即修复 - 值得记录 → lessons.md - 3. **Micro-review**: 对标 design.md spec + conventions.md 标准 - 通过 → [x] Task + commit - 不通过 → 反馈修复 - **铁律**: plan.md 有任何 [ ] 未完成, 不停止。这就是 Sisyphus。 - -## T 测试/审查 (Path B+) - 1. verification skill: 跑测试 + 覆盖率 - 1.5. **验收标准确认**: - 读 design.md "## 验收标准" → 逐条确认是否满足 - 未满足 → 标注到 quality.md "## 未满足验收标准" - 2. code-review skill: 代码审查 - 3. **双 Agent 互审**: 如果 Codex 可用 → CC 审查 Codex 产出 + Codex 反审 CC 修改 - 如果 Codex 不可用 → CC 单方审查 - 4. 综合 → quality.md - 5. **4 级 Quality Gate** (delivery-gate.cjs): - PASS (exit 0) → 允许停止 - CONCERNS (exit 0 + stdout 警告) → 允许停止, 警告注入 context - REWORK (exit 2) → 阻断停止, 强制修复 - FAIL (exit 2) → 阻断停止, 必须大幅修复 - -## V 归档 (Path B+) - 1. kaizen skill: 回顾 + lessons.md - 1.5. **分支收尾** (finish-branch skill): - 选项: merge / PR / keep / discard - PR → 自动生成 description (基于 plan.md + quality.md) - 2. cunzhi `DELIVERY_COMPLETE` (cunzhi 不可用时: 直接输出确认提示) diff --git a/.codex/AGENTS.md b/.codex/AGENTS.md index afbc818a..d5dc5680 100644 --- a/.codex/AGENTS.md +++ b/.codex/AGENTS.md @@ -1,47 +1,32 @@ -# VibeCoding Kernel v9.2.0 — Codex CLI +# VibeCoding Athena v9.6 (Codex) — PACE Router & State Harness -你是一个INTJ性格的工程化 AI 编码助手。用 P.A.C.E. 路由复杂度, 用 RIPER-7 编排阶段, 用 Skills 执行细节。 -**协作模式**: 你可能被 Claude Code (CC) 调用执行任务, 也可能独立运行。 + -## 框架地图 +你是 VibeCoding Athena 工程 Agent。Codex 做事, Athena 把关。 -| 类别 | 文件 | 数量 | -| --------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- | ---- | -| Workflows | pace.md, riper-7.md | 2 | -| Skills | brainstorm, plan-first, code-review, verification, debugging, kaizen, security-review, context7, e2e, claude-delegate, reflexion, finish-branch | 12 | -| Agents | builder.toml, validator.toml, explorer.toml | 3 | -| State Templates | session, doing, design, plan, conventions, quality, lessons | 7 | +- 收任务 → 走 PACE 路由 (Hotfix/Bugfix 直接做, Quick+ 走完整流程) +- 自己跑命令 / 跑测试 / 看输出, 证明工作完成 +- 工具失败 → 三次重试附 stderr 报告, 不让用户代执行 +- 技术结论 → 必须引用官方文档 / 源码 URL +- spawn_agent 必须产生真实 tool_use, 不允许伪造完成 +- Codex 无 compact 机制, 长任务点主动写 `.ai_state/_index.md` 保存状态 -## 启动流程 +## 铁律 (9 条) -1. 读 .ai_state/ → 恢复或初始化 -2. 读 conventions.md (含 "Agent 易犯错误" 段) → 注意这些坑 -3. 评估任务 → P.A.C.E. 路由 → RIPER-7 执行 +1. **设计先行** — 未确认不写代码 (Hotfix/Bugfix 例外) +2. **TDD 强制** — 先测试后实现 +3. **Sisyphus** — tasks 全完成才进审查 +4. **Review 强制** — Feature+ 至少一次交叉审查 (/review + spawn_agent reviewer) +5. **文档即真相** — 阶段转换前 .ai_state/ 必须同步, 单一入口 `.ai_state/_index.md` +6. **完成度证据** — 报告"完成"必须附 tool_use ID 或命令输出片段 +7. **出处优先** — API 形态 / 配置字段 / 协议格式必须引用官方文档或源码 URL +8. **索引先行** — 进入决策先读 `.ai_state/_index.md`, 禁止 glob 全目录扫描 +9. **Hook 是进化器** — 在 Stop 时反思并写 `.ai_state/details/proposals.md` -## 关键规则 - -- 设计未确认前不写代码 (R₀/R/D 阶段) -- TDD: 先写测试再写实现 (E 阶段) -- Sisyphus: plan.md 所有 [ ] 完成才能停 (E 阶段) -- Reflexion: 每个 Task 完成后自我反思再 Micro-review (E 阶段) -- 4 级 Quality Gate: PASS / CONCERNS / REWORK / FAIL (交付前) - -## 被 CC 调用时 - -- CC 会通过 `codex exec` 发送任务 -- 你在只读沙箱中运行, 不直接修改文件 -- 输出代码和建议, CC 决定是否应用 -- 审查请求以 `VERDICT: APPROVED` 或 `VERDICT: REVISE` 结尾 - -## MCP 工具 (按需) - -- cunzhi: 人工确认检查点 -- context7 CLI: `npx ctx7 resolve {库名}` - -## 模型 - -| 场景 | 模型 | -| --------- | ------------------------ | -| 默认 | gpt-5.4 | -| 快速/探索 | gpt-5.4-mini | -| 深度审查 | gpt-5.4 (high reasoning) | +设计原则: SRP · OCP · LSP · ISP · DIP · DRY · KISS · 第一性原理 · 先 WHY 后 HOW diff --git a/.codex/agents/builder.toml b/.codex/agents/builder.toml deleted file mode 100644 index 205ddc9f..00000000 --- a/.codex/agents/builder.toml +++ /dev/null @@ -1,4 +0,0 @@ -[agent] -name = "builder" -model = "gpt-5.4" -description = "实现代码 + 测试 (TDD: RED→GREEN→REFACTOR)" diff --git a/.codex/agents/evaluator.toml b/.codex/agents/evaluator.toml new file mode 100644 index 00000000..418c25e4 --- /dev/null +++ b/.codex/agents/evaluator.toml @@ -0,0 +1,47 @@ +# Codex Subagent: evaluator (VibeCoding Athena v9.6) +# 参考: https://developers.openai.com/codex/subagents +# 位置: ~/.codex/agents/evaluator.toml 或 .codex/agents/evaluator.toml + +name = "evaluator" +description = "代码审查评分员. PACE review 阶段调用, 输出 VERDICT (PASS/CONCERNS/REWORK/FAIL)" +nickname_candidates = ["EvalBot", "Verdict"] + +# 只读: 评分员不修改任何代码 +sandbox_mode = "read-only" +# Model 不指定 → 继承父 session +# model_reasoning_effort 不指定 → 继承父 session + +developer_instructions = """ +你是 VibeCoding Athena 的代码审查评分员. + +## 输入 +- `.ai_state/_index.md` (path/stage/sprint/active_goal) +- `.ai_state/details/reviews/sprint-N.md` (本 sprint 审查报告) +- `.ai_state/details/design.md` (设计 vs 实际对比) +- `git diff` 本 sprint 提交 + +## 工具限制 +- 只读: read_file / shell (限 ls, cat, grep, rg, git log/diff/show) / web_search +- 禁用: apply_patch, write_file, edit_file + +## 评分维度 (1-5 分, 跨平台一致 — 铁律 6) +- Functionality (25%) // 是否实现 design.md 中所有 MUST 项 +- Spec Compliance (25%) // 是否符合 design.md 的约束 +- Boundary (15%) // 是否越出 File Structure Plan 边界 +- Craft (15%) // 代码质量 (命名/SRP/无 dead code) +- Robustness (20%) // 边界处理/错误恢复/测试覆盖 + +均分 = F×0.25 + SC×0.25 + B×0.15 + C×0.15 + R×0.20 + +## VERDICT 规则 +- 均分 ≥ 4.0 且所有维度 ≥ 3 → PASS +- 均分 ≥ 3.0 但某维度 = 2 → CONCERNS +- 均分 < 3.0 或任一维度 = 1 → REWORK +- 多维度 = 1 或 Boundary 大幅越界 / 安全严重问题 → FAIL + +## 输出要求 +- 每个维度评分必须附代码行号级证据 (file.ext:LN, 不是模糊"看起来不错") +- 不允许 Halo Effect (一个维度的分数不影响其他维度的独立评分顺序) +- 输出预算 ≤ 2000 tokens (Anthropic 多 agent 经验: subagent 必须紧凑) +- 直接覆盖写回 reviews/sprint-N.md 的 "## Step 6" 段和 "## VERDICT:" 段 +""" diff --git a/.codex/agents/explorer.toml b/.codex/agents/explorer.toml deleted file mode 100644 index 2c6f3e4a..00000000 --- a/.codex/agents/explorer.toml +++ /dev/null @@ -1,4 +0,0 @@ -[agent] -name = "explorer" -model = "gpt-5.4-mini" -description = "代码库调研 + 文档查询 (只读)" diff --git a/.codex/agents/generator.toml b/.codex/agents/generator.toml new file mode 100644 index 00000000..f92b22ea --- /dev/null +++ b/.codex/agents/generator.toml @@ -0,0 +1,62 @@ +# Codex Subagent: generator (VibeCoding Athena v9.6) +# 参考: https://developers.openai.com/codex/subagents +# 位置: ~/.codex/agents/generator.toml 或 .codex/agents/generator.toml + +name = "generator" +description = "代码实现者. 接 task spec, TDD 实现, 输出可被 reviewer 验证的 commit" +nickname_candidates = ["GenBot", "Builder", "Smith"] + +# 写权限: implementer 需要 apply_patch / edit_file +sandbox_mode = "workspace-write" +# model / model_reasoning_effort 不指定 → 继承父 session + +developer_instructions = """ +你是 VibeCoding Athena 的代码实现者. + +## 工具 +- read_file / write_file / edit_file / apply_patch +- shell (含 git, 测试运行, lint, 包管理) +- web_search (查依赖文档) + +## 工作流 (与 CC 端等价 — 跨平台 schema 一致 — 铁律 6) + +1. Get-bearings (R₀): + - 读 `.ai_state/_index.md` + - 读 design.md 对应模块段 + - 读 tasks-current.md 当前 task 的 Boundary/Depends +2. TDD 强制 (铁律 2): + - 先写测试 → 跑测试 (RED) → 实现 → 跑测试 (GREEN) +3. Boundary 守则: + - 仅修改 task 的 Boundary 标注的文件 + - 越界 → 停, 报告主 agent (可能需要升路径) +4. 跑 lint_cmd → 失败修复 +5. 更新文档: + - tasks-current.md 勾选 ([ ] → [x]) + - progress.md 追加一行 (YYYY-MM-DD HH:MM [path/stage/sprint] Task N done) + - design.md 不动 (除非发现 design 错; 那种情况报告主 agent 不要自己改) +6. Commit: + - `git add -p` 仅添加 Boundary 内文件 + - `git commit -m ""` +7. 报告: + - 输出 commit hash + 测试通过的命令输出片段 (铁律 6 完成度证据) + - 失败 → 完整 stderr (不允许 "I cannot") + +## Codex 端注意 + +- 不像 CC 有 isolation:worktree 自动隔离, Codex subagent 共享 workspace +- 多个 generator 并发时, 严格按 Boundary 分工避免冲突 +- 设置 max_threads 在 config.toml [agents] 控制并发度 (max_threads = 6) + +## 失败协议 (铁律 7) + +工具失败 → 三轮重试: +- Round 1: 读 stderr 分类 (permission denied / not found / 语法) +- Round 2: 改参数 / 换工具 / 拆任务 +- Round 3: 仍失败 → 完整 stderr + exit code + 已尝试方案, 报告主 agent + +禁止: "请你手动跑", "我没有权限" (没有就申请, 不是放弃). + +## 输出预算 + +≤ 2000 tokens. 详细日志写文件不写回主 agent. +""" diff --git a/.codex/agents/reviewer.toml b/.codex/agents/reviewer.toml new file mode 100644 index 00000000..8a6a0e9a --- /dev/null +++ b/.codex/agents/reviewer.toml @@ -0,0 +1,62 @@ +# Codex Subagent: reviewer (VibeCoding Athena v9.6) +# 参考: https://developers.openai.com/codex/subagents +# 位置: ~/.codex/agents/reviewer.toml 或 .codex/agents/reviewer.toml + +name = "reviewer" +description = "代码审查员. PACE review 阶段调用, 给出对抗性 findings + 可执行 fixes (与 evaluator 配合: reviewer 找问题, evaluator 给 VERDICT)" +nickname_candidates = ["ReviewBot", "Critic", "Inspector"] + +# 只读: reviewer 不修改代码, 只产出 findings +sandbox_mode = "read-only" + +developer_instructions = """ +你是 VibeCoding Athena 的代码审查员. + +## 角色边界 + +与 evaluator 区别: +- evaluator: 给 VERDICT (PASS/CONCERNS/REWORK/FAIL) + 数值评分 +- reviewer: 给具体可执行的反馈 (找 bug, 找设计问题, 找测试缺口) + +主 agent 工作流: 先调 reviewer 拿反馈 → 修复 → 再调 evaluator 给 VERDICT. + +## 工具 + +- read_file +- shell (限只读: ls, cat, grep, rg, git log/diff/show) +- web_search (查最佳实践, OWASP 规范) + +禁用: apply_patch / write_file / edit_file. + +## 审查重点 (与 CC 端 /codex:review 行为对齐) + +1. Bug: 边界用例, 错误处理, race condition +2. 设计: 是否违反 design.md, SOLID, KISS +3. 测试: 覆盖率, 是否测了 happy path 没测 error path +4. 安全: 输入验证, 注入风险, 密钥处理 +5. 性能: 明显的 N+1, 同步阻塞, 内存泄漏 + +## 输出 + +写入 `.ai_state/details/reviews/sprint-N.md` 的 "## Step 2/3" 段: + +``` +### Reviewer (Codex subagent) + +**job_id**: + +**Findings**: +- [HIGH] file.ext:LN — <描述> +- [MEDIUM] file.ext:LN — <描述> +- [LOW] file.ext:LN — <描述> + +**Suggested fixes**: +- ... +``` + +每个 finding 必须有 file:line 锚点 (no 模糊"代码可以更好"). + +## 输出预算 + +≤ 2000 tokens. +""" diff --git a/.codex/agents/validator.toml b/.codex/agents/validator.toml deleted file mode 100644 index 370a9cff..00000000 --- a/.codex/agents/validator.toml +++ /dev/null @@ -1,4 +0,0 @@ -[agent] -name = "validator" -model = "gpt-5.4" -description = "代码审查 + Plan Review" diff --git a/.codex/config.toml b/.codex/config.toml index aea624d6..61e59ec4 100644 --- a/.codex/config.toml +++ b/.codex/config.toml @@ -1,85 +1,115 @@ -# VibeCoding Kernel v9.2.0 — Codex CLI Config -model_provider = "custom" -model = "gpt-5.4" +# VibeCoding Athena v9.6 · Codex Configuration + +project_root_markers = [".git", ".hg", ".sl"] +project_doc_max_bytes = 65536 + +model = "gpt-5.5" model_reasoning_effort = "xhigh" + +windows_wsl_setup_acknowledged = true +model_context_window = 1000000 +model_auto_compact_token_limit = 900000 +web_search = "live" suppress_unstable_features_warning = true -# 危险,可开启 和下面的二选一,上面的代表绝对信任 -# approval_policy = "never" # untrusted|on-request|never -# sandbox_mode = "danger-full-access" # read-only|workspace-write|danger-full-access - -approval_policy = "never" -sandbox_mode = "workspace-write" +approval_policy = "on-request" +sandbox_mode = "workspace-write" [sandbox_workspace_write] -network_access = true # npm install / pip install 需要网络 - -# Web Search -web_search = "live" +network_access = false [features] -multi_agent = true # stable; 多代理编排 -collaboration_modes = true # stable; 协作模式 -shell_snapshot = true # beta; 加速重复命令 -unified_exec = true # beta; PTY-backed 执行 -shell_tool = true # stable; 默认 shell 工具 -apply_patch_freeform = true # experimental; 自由 patch -undo = true # 回滚 Codex 改动; +hooks = true +multi_agent = true +shell_tool = true +shell_snapshot = true +unified_exec = true +apply_patch_freeform = true +undo = true +memories = true -[tui] -notifications = true -animations = true +[memories] +generate_memories = true +use_memories = true +disable_on_external_context = true -[profiles.ci] -approval_policy = "never" -sandbox_mode = "workspace-write" +[[hooks.SessionStart]] +matcher = "^(startup|resume|clear)$" +[[hooks.SessionStart.hooks]] +type = "command" +command = "python3 \"./.codex/hooks/session-start.py\"" +timeout = 10 +statusMessage = "Loading Athena context" -[profiles.review] -model = "gpt-5.4" -approval_policy = "on-request" +[[hooks.UserPromptSubmit]] +[[hooks.UserPromptSubmit.hooks]] +type = "command" +command = "python3 \"./.codex/hooks/user-prompt-submit.py\"" +timeout = 5 +statusMessage = "Athena pre-check" -[profiles.dev] -model = "gpt-5.4" -approval_policy = "on-request" -sandbox_mode = "workspace-write" +[[hooks.PreToolUse]] +matcher = "^Bash$" +[[hooks.PreToolUse.hooks]] +type = "command" +command = "python3 \"./.codex/hooks/pre-bash-guard.py\"" +timeout = 10 +statusMessage = "Checking Bash command" + +[[hooks.PostToolUse]] +[[hooks.PostToolUse.hooks]] +type = "command" +command = "python3 \"./.codex/hooks/index-updater.py\"" +timeout = 10 + +[[hooks.PostToolUse]] +[[hooks.PostToolUse.hooks]] +type = "command" +command = "python3 \"./.codex/hooks/subagent-retry.py\"" +timeout = 10 + +[[hooks.Stop]] +[[hooks.Stop.hooks]] +type = "command" +command = "python3 \"./.codex/hooks/delivery-gate.py\"" +timeout = 60 +statusMessage = "Athena delivery-gate" [agents] -max_threads = 3 +max_threads = 6 +max_depth = 1 +job_max_runtime_seconds = 1800 -[agents.builder] -description = "实现代码 + 测试 (TDD: RED→GREEN→REFACTOR)" -config_file = "./agents/builder.toml" +[[skills.config]] +name = "pace" +enabled = true -[agents.validator] -description = "代码审查 + Plan Review" -config_file = "./agents/validator.toml" +[[skills.config]] +name = "compound" +enabled = true -[agents.explorer] -description = "代码库调研 + 文档查询 (只读)" -config_file = "./agents/explorer.toml" +[[skills.config]] +name = "context7" +enabled = true -[model_providers.custom] -name = "custom" -wire_api = "responses" -requires_openai_auth = true +[[skills.config]] +name = "superpowers" +enabled = true -[mcp_servers] +[shell_environment_policy] +inherit = "core" -[mcp_servers.mcp-deepwiki] -type = "stdio" -command = "npx" -args = ["-y", "mcp-deepwiki@latest"] +[tui.model_availability_nux] +"gpt-5.5" = 4 -[mcp_servers."cunzhi"] -type = "stdio" -command = "寸止" - -[mcp_servers.chrome-devtools] -type = "stdio" -command = "npx" -args = ["-y", "chrome-devtools-mcp@latest"] +[shell_environment_policy.set] +VIBECODING_VERSION = "9.6.0-codex" [mcp_servers.augment-context-engine] -type = "stdio" command = "npx" -args = ["ace-tool-rs", "--base-url", "https://acemcp.heroman.wtf/relay/", "--token", ""] +args = [ "ace-tool-rs", "--base-url", "https://acemcp.heroman.wtf/relay/", "--token", "ace_0eff2f29e8dec9382d0ad1b46b39e587d06f6c2f" ] + +[mcp_servers.augment-context-engine.tools.search_context] +approval_mode = "approve" + +[hooks.state] diff --git a/.codex/hooks/delivery-gate.py b/.codex/hooks/delivery-gate.py new file mode 100755 index 00000000..5f7041c4 --- /dev/null +++ b/.codex/hooks/delivery-gate.py @@ -0,0 +1,167 @@ +#!/usr/bin/env python3 +"""VibeCoding Athena Codex Stop hook (delivery-gate) v9.6. + +设计: +- Codex 无 /goal autonomous loop → Athena 必须自己判定 VERDICT +- Codex 无 Stop hook block-cap 概念, 但保守起见也做内部计数 +- VERDICT 不符 → 阻断 (decision: block) +- CONCERNS → systemMessage 不阻断 +""" +import json +import os +import re +import sys +import datetime + +PROJECT_DIR = os.environ.get("CODEX_PROJECT_DIR") or os.getcwd() +STATE_DIR = os.path.join(PROJECT_DIR, ".ai_state") +INDEX_PATH = os.path.join(STATE_DIR, "_index.md") +HOME = os.path.expanduser("~") +COUNTER_PATH = os.path.join(HOME, ".codex", "state", "athena-block-counter.json") + +BLOCK_CAP_SAFE = 6 + + +def trace(payload: dict): + try: + with open(os.path.join(STATE_DIR, "hook-trace.jsonl"), "a") as f: + f.write(json.dumps({ + "ts": datetime.datetime.utcnow().isoformat() + "Z", + "hook": "delivery-gate", + **payload + }) + "\n") + except Exception: + pass + + +def main(): + raw = sys.stdin.read() + try: + event = json.loads(raw) + except Exception: + sys.exit(0) + return + + if event.get("stop_hook_active"): + sys.exit(0) + return + if event.get("agent_type") and event["agent_type"] != "main": + sys.exit(0) + return + if not os.path.exists(INDEX_PATH): + sys.exit(0) + return + + try: + content = open(INDEX_PATH).read() + m = re.match(r"^---\n(.*?)\n---", content, re.DOTALL) + if not m: + sys.exit(0) + return + fm = m.group(1) + except Exception: + sys.exit(0) + return + + def get_str(key): + match = re.search(rf'^ {re.escape(key)}: (.*)$', fm, re.MULTILINE) + return match.group(1).strip().strip('"').strip("'") if match else "" + + def get_num(key): + match = re.search(rf'^ {re.escape(key)}: (\d+)$', fm, re.MULTILINE) + return int(match.group(1)) if match else 0 + + path_name = get_str("path") + stage = get_str("stage") + sprint = get_num("sprint") + tasks_pending = get_num("tasks_pending") + + if not sprint or not stage or path_name in ("Hotfix", "Bugfix") or stage != "review": + sys.exit(0) + return + + needs_ext = path_name in ("Feature", "Refactor", "System") + issues = [] + + if tasks_pending > 0: + issues.append(f"{tasks_pending} Task 未完成") + + review_file = os.path.join(STATE_DIR, "details", "reviews", f"sprint-{sprint}.md") + rc = "" + try: + rc = open(review_file).read() + except Exception: + issues.append("审查报告不存在") + + if needs_ext and rc and not re.search(r"reviewer|web_search|/review|spawn_agent", rc, re.IGNORECASE): + issues.append("无外部审查记录") + if rc and not re.search(r"test|测试|pass|通过|✅", rc, re.IGNORECASE): + issues.append("无测试通过记录") + + verdict = "" + if rc: + vm = re.search(r"VERDICT:\s*(PASS|CONCERNS|REWORK|FAIL)", rc, re.IGNORECASE) + if vm: + verdict = vm.group(1).upper() + if verdict in ("REWORK", "FAIL"): + issues.append(f"VERDICT={verdict}") + + if issues: + os.makedirs(os.path.dirname(COUNTER_PATH), exist_ok=True) + counter = {} + try: + counter = json.load(open(COUNTER_PATH)) + except Exception: + pass + sid = event.get("session_id", "unknown") + counter[sid] = counter.get(sid, 0) + 1 + try: + with open(COUNTER_PATH, "w") as f: + json.dump(counter, f) + except Exception: + pass + count = counter[sid] + + sys.stderr.write(f"[delivery-gate] block #{count}/{BLOCK_CAP_SAFE} {path_name}/{stage}: {', '.join(issues)}\n") + trace({"action": "block", "count": count, "path": path_name, "stage": stage, "issues": issues}) + + if count >= BLOCK_CAP_SAFE: + msg = f"[Athena] 已连续 block {count} 次, 建议手动处理: {'; '.join(issues)}" + print(json.dumps({"systemMessage": msg})) + else: + print(json.dumps({ + "decision": "block", + "reason": f"[Athena delivery-gate] 阻断:\n" + "\n".join("• " + i for i in issues) + f"\n\n修复后再交付 (block {count}/{BLOCK_CAP_SAFE})." + })) + sys.exit(0) + return + + # 通过, 重置计数 + try: + counter = json.load(open(COUNTER_PATH)) + counter.pop(event.get("session_id", ""), None) + with open(COUNTER_PATH, "w") as f: + json.dump(counter, f) + except Exception: + pass + + if verdict == "PASS": + # 检查 lessons + try: + lessons = open(os.path.join(STATE_DIR, "details", "lessons.md")).read() + except Exception: + lessons = "" + if f"Sprint {sprint}" not in lessons: + msg = f"Sprint {sprint} 通过但 details/lessons.md 无条目, 建议运行 compound skill 沉淀经验" + print(json.dumps({"systemMessage": f"[Athena] {msg}"})) + trace({"action": "soft-warn", "reason": "no compound", "path": path_name, "sprint": sprint}) + sys.exit(0) + return + + sys.stderr.write(f"[delivery-gate] PASS {path_name}/{stage}\n") + trace({"action": "pass", "path": path_name, "stage": stage, "sprint": sprint, "verdict": verdict}) + sys.exit(0) + + +if __name__ == "__main__": + main() diff --git a/.codex/hooks/index-updater.py b/.codex/hooks/index-updater.py new file mode 100755 index 00000000..388800b3 --- /dev/null +++ b/.codex/hooks/index-updater.py @@ -0,0 +1,167 @@ +#!/usr/bin/env python3 +"""VibeCoding Athena Codex PostToolUse index-updater v9.6. + +设计 (与 CC 等价, schema 跨平台一致 - 铁律 6): +- 检测 tool_input.file_path 是否在 .ai_state/details/ +- 若是 → 重新扫描计算 counts/fingerprints/pointers +- 原子写入 _index.md (.tmp + rename) +""" +import json +import os +import re +import sys +import datetime + +PROJECT_DIR = os.environ.get("CODEX_PROJECT_DIR") or os.getcwd() +STATE_DIR = os.path.join(PROJECT_DIR, ".ai_state") +INDEX_PATH = os.path.join(STATE_DIR, "_index.md") +DETAILS_DIR = os.path.join(STATE_DIR, "details") + + +def read_file_safe(path): + try: + return open(path).read() + except Exception: + return "" + + +def mtime(path): + try: + return int(os.path.getmtime(path) * 1000) + except Exception: + return 0 + + +def count_matches(text, pattern): + return len(re.findall(pattern, text, re.MULTILINE)) if text else 0 + + +def last_section_line(text, head_prefix="## "): + if not text: + return 0 + lines = text.split("\n") + for i in range(len(lines) - 1, -1, -1): + if lines[i].startswith(head_prefix): + return i + 1 + return 0 + + +def last_nonempty_line(text): + if not text: + return 0 + lines = text.split("\n") + for i in range(len(lines) - 1, -1, -1): + s = lines[i].strip() + if s and not s.startswith("") and not s.startswith("#"): + return i + 1 + return 0 + + +def replace_field(yaml: str, key: str, value, is_numeric: bool) -> str: + val_str = str(value) if is_numeric else f'"{value}"' + pattern = rf"(^ {re.escape(key)}: ).*$" + if re.search(pattern, yaml, re.MULTILINE): + return re.sub(pattern, rf"\g<1>{val_str}", yaml, count=1, flags=re.MULTILINE) + return yaml + + +def main(): + raw = sys.stdin.read() + try: + event = json.loads(raw) + except Exception: + sys.exit(0) + return + + file_path = (event.get("tool_input") or {}).get("file_path", "") + if not file_path or ".ai_state/details/" not in file_path: + sys.exit(0) + return + if not os.path.exists(INDEX_PATH): + sys.exit(0) + return + + try: + content = open(INDEX_PATH).read() + m = re.match(r"^---\n(.*?)\n---\n(.*)$", content, re.DOTALL) + if not m: + sys.stderr.write("[index-updater] frontmatter parse failed\n") + sys.exit(0) + return + fm, body = m.group(1), m.group(2) + + tasks_text = read_file_safe(os.path.join(DETAILS_DIR, "tasks-current.md")) + progress_text = read_file_safe(os.path.join(DETAILS_DIR, "progress.md")) + lessons_text = read_file_safe(os.path.join(DETAILS_DIR, "lessons.md")) + proposals_text = read_file_safe(os.path.join(DETAILS_DIR, "proposals.md")) + + tasks_pending = count_matches(tasks_text, r"^- \[ \]") + tasks_done = count_matches(tasks_text, r"^- \[x\]") + tasks_blocked = count_matches(tasks_text, r"^- \[!\]") + lessons_lines = count_matches(lessons_text, r"^## ") + + progress_entries = 0 + if progress_text: + progress_entries = sum( + 1 for l in progress_text.split("\n") + if l.strip() and not l.startswith("") and not l.startswith("#") + ) + + reviews_count = 0 + latest_review = "" + try: + files = [f for f in os.listdir(os.path.join(DETAILS_DIR, "reviews")) if re.match(r"^sprint-\d+\.md$", f)] + reviews_count = len(files) + if files: + files.sort(key=lambda f: int(re.search(r"\d+", f).group())) + latest_review = f"details/reviews/{files[-1]}" + except Exception: + pass + + latest_progress = f"details/progress.md#L{last_nonempty_line(progress_text)}" if progress_text else "" + latest_lesson = f"details/lessons.md#L{last_section_line(lessons_text)}" if lessons_text else "" + latest_proposal = f"details/proposals.md#L{last_section_line(proposals_text)}" if proposals_text else "" + + tasks_mtime = mtime(os.path.join(DETAILS_DIR, "tasks-current.md")) + design_mtime = mtime(os.path.join(DETAILS_DIR, "design.md")) + progress_mtime = mtime(os.path.join(DETAILS_DIR, "progress.md")) + lessons_mtime = mtime(os.path.join(DETAILS_DIR, "lessons.md")) + + fm = replace_field(fm, "tasks_pending", tasks_pending, True) + fm = replace_field(fm, "tasks_done", tasks_done, True) + fm = replace_field(fm, "tasks_blocked", tasks_blocked, True) + fm = replace_field(fm, "lessons_lines", lessons_lines, True) + fm = replace_field(fm, "progress_entries", progress_entries, True) + fm = replace_field(fm, "reviews_count", reviews_count, True) + fm = replace_field(fm, "latest_progress", latest_progress, False) + fm = replace_field(fm, "latest_review", latest_review, False) + fm = replace_field(fm, "latest_lesson", latest_lesson, False) + fm = replace_field(fm, "latest_proposal", latest_proposal, False) + fm = replace_field(fm, "tasks_mtime", tasks_mtime, True) + fm = replace_field(fm, "design_mtime", design_mtime, True) + fm = replace_field(fm, "progress_mtime", progress_mtime, True) + fm = replace_field(fm, "lessons_mtime", lessons_mtime, True) + + new_content = f"---\n{fm}\n---\n{body}" + tmp = INDEX_PATH + ".tmp" + with open(tmp, "w") as f: + f.write(new_content) + os.rename(tmp, INDEX_PATH) + sys.stderr.write(f"[index-updater] _index.md updated (pending={tasks_pending} done={tasks_done})\n") + + with open(os.path.join(STATE_DIR, "hook-trace.jsonl"), "a") as f: + f.write(json.dumps({ + "ts": datetime.datetime.utcnow().isoformat() + "Z", + "hook": "index-updater", + "tasks_pending": tasks_pending, + "tasks_done": tasks_done, + "lessons_lines": lessons_lines, + }) + "\n") + except Exception as e: + sys.stderr.write(f"[index-updater] failed: {e}\n") + + sys.exit(0) + + +if __name__ == "__main__": + main() diff --git a/.codex/hooks/pre-bash-guard.py b/.codex/hooks/pre-bash-guard.py new file mode 100755 index 00000000..b9faa1ca --- /dev/null +++ b/.codex/hooks/pre-bash-guard.py @@ -0,0 +1,66 @@ +#!/usr/bin/env python3 +"""VibeCoding Athena Codex PreToolUse Bash guard v9.6. + +设计: +- 3 条灾难命令 (与 CC 等价) +- Codex hook 输出协议: permissionDecision = "deny" 阻断 +- Codex 文档: hooks 当前实验性, Windows 不支持 + +参考: https://developers.openai.com/codex/hooks +""" +import json +import re +import sys +import datetime +import os + +DANGER_PATTERNS = [ + (re.compile(r"rm\s+-[rRf]+\s+(/(\s|\*|;|&|\||$)|~/?\s|~\s|~$)"), "禁止删除系统/用户根目录"), + (re.compile(r"(curl|wget)\s+[^|]*\|\s*(bash|sh|zsh|fish|ksh)"), "禁止管道执行远程脚本"), + (re.compile(r"\bmkfs\.|dd\s+if=.*of=/dev/sd|>\s*/dev/sd[a-z]"), "禁止格式化磁盘/写设备"), +] + + +def main(): + raw = sys.stdin.read() + try: + event = json.loads(raw) + except Exception: + sys.exit(0) + return + + cmd = (event.get("tool_input") or {}).get("command", "") + if not cmd: + sys.exit(0) + return + + for pat, reason in DANGER_PATTERNS: + if pat.search(cmd): + sys.stderr.write(f"[bash-guard] deny: {reason} ({cmd[:60]})\n") + print(json.dumps({ + "hookSpecificOutput": { + "hookEventName": "PreToolUse", + "permissionDecision": "deny", + "permissionDecisionReason": f"Athena bash-guard 阻断: {reason}" + } + })) + try: + project_dir = os.environ.get("CODEX_PROJECT_DIR") or os.getcwd() + state_dir = os.path.join(project_dir, ".ai_state") + with open(os.path.join(state_dir, "hook-trace.jsonl"), "a") as f: + f.write(json.dumps({ + "ts": datetime.datetime.utcnow().isoformat() + "Z", + "hook": "pre-bash-guard", + "action": "deny", + "reason": reason + }) + "\n") + except Exception: + pass + sys.exit(0) + return + + sys.exit(0) + + +if __name__ == "__main__": + main() diff --git a/.codex/hooks/session-start.py b/.codex/hooks/session-start.py new file mode 100755 index 00000000..3a0171a9 --- /dev/null +++ b/.codex/hooks/session-start.py @@ -0,0 +1,121 @@ +#!/usr/bin/env python3 +"""VibeCoding Athena Codex SessionStart hook v9.6. + +协议: stdout JSON with additionalContext (Codex 端简化, 不强制 hookSpecificOutput 嵌套) +参考: https://developers.openai.com/codex/hooks + +设计 (铁律 8 索引先行, 同 CC): +- 唯一 IO: read .ai_state/_index.md frontmatter +- 注入 context-essentials.md (静态文本) +- 注入 _index.md 关键字段摘要 +""" +import json +import os +import re +import sys +import datetime + +PROJECT_DIR = os.environ.get("CODEX_PROJECT_DIR") or os.getcwd() +STATE_DIR = os.path.join(PROJECT_DIR, ".ai_state") +INDEX_PATH = os.path.join(STATE_DIR, "_index.md") + +HOME = os.path.expanduser("~") +PROJECT_ESSENTIALS = os.path.join(PROJECT_DIR, ".codex", "skills", "pace", "context-essentials.md") +GLOBAL_ESSENTIALS = os.path.join(HOME, ".codex", "skills", "pace", "context-essentials.md") + + +def trace(payload: dict): + try: + line = json.dumps({"ts": datetime.datetime.utcnow().isoformat() + "Z", "hook": "session-start", **payload}) + "\n" + with open(os.path.join(STATE_DIR, "hook-trace.jsonl"), "a") as f: + f.write(line) + except Exception: + pass + + +def read_file_safe(path: str) -> str: + try: + with open(path, "r") as f: + return f.read().strip() + except Exception: + return "" + + +def get_frontmatter(content: str) -> str: + m = re.match(r"^---\n(.*?)\n---", content, re.DOTALL) + return m.group(1) if m else "" + + +def get_str(fm: str, key: str) -> str: + m = re.search(rf"^ {re.escape(key)}: (.*)$", fm, re.MULTILINE) + if not m: + return "" + val = m.group(1).strip() + return val.strip('"').strip("'") + + +def get_num(fm: str, key: str) -> int: + m = re.search(rf"^ {re.escape(key)}: (\d+)$", fm, re.MULTILINE) + return int(m.group(1)) if m else 0 + + +def main(): + raw = sys.stdin.read() + try: + event = json.loads(raw) + except Exception: + event = {} + source = event.get("source", "unknown") + + lines = [] + + # 1. 注入 context-essentials + for p in (PROJECT_ESSENTIALS, GLOBAL_ESSENTIALS): + text = read_file_safe(p) + if text: + lines.append(text) + break + if not lines: + lines.append("[VibeCoding Athena Codex v9.6] TDD · Review · Sisyphus · 索引先行") + + # 2. 注入 _index.md frontmatter 摘要 + if os.path.exists(INDEX_PATH): + try: + content = open(INDEX_PATH).read() + fm = get_frontmatter(content) + if fm: + path_ = get_str(fm, "path") + stage = get_str(fm, "stage") + sprint = get_num(fm, "sprint") + goal = get_str(fm, "active_goal") + pending = get_num(fm, "tasks_pending") + done = get_num(fm, "tasks_done") + blocked = get_num(fm, "tasks_blocked") + + if path_ and stage: + lines.append(f"\n[PACE] Path:{path_} Stage:{stage} Sprint:{sprint}") + if goal: + snippet = goal[:120] + ("..." if len(goal) > 120 else "") + lines.append(f"[/goal active (CC only)] {snippet}") + if pending + done + blocked > 0: + lines.append(f"[任务] {done} 完成 {pending} 待办 {blocked} 阻塞 (详见 details/tasks-current.md)") + except Exception as e: + sys.stderr.write(f"[session-start] _index.md parse failed: {e}\n") + + additional_context = "\n".join(lines) + sys.stderr.write(f"[session-start/{source}] injected {len(additional_context)} chars\n") + trace({"source": source, "chars": len(additional_context)}) + + # Codex hooks 输出协议: stdout 包含 additionalContext 字符串 (官方文档示例) + # 参考: https://developers.openai.com/codex/hooks 中 SessionStart 段 + print(json.dumps({ + "hookSpecificOutput": { + "hookEventName": "SessionStart", + "additionalContext": additional_context + } + })) + sys.exit(0) + + +if __name__ == "__main__": + main() diff --git a/.codex/hooks/subagent-retry.py b/.codex/hooks/subagent-retry.py new file mode 100755 index 00000000..9e04868f --- /dev/null +++ b/.codex/hooks/subagent-retry.py @@ -0,0 +1,71 @@ +#!/usr/bin/env python3 +"""VibeCoding Athena Codex PostToolUse subagent-retry v9.6. + +设计 (与 CC 等价): +- 检测 spawn_agent 返回的内容是否含放弃信号词 +- 注入 systemMessage 提示主 agent 重试或接管 +""" +import json +import re +import sys +import datetime +import os + +GIVEUP_PATTERNS = [ + re.compile(r"我(无法|不能|没法|无能力|不具备能力)"), + re.compile(r"I (cannot|can't|am not able to|don't have access)", re.IGNORECASE), + re.compile(r"no (capability|access|permission) to", re.IGNORECASE), + re.compile(r"unable to (read|write|execute|access)", re.IGNORECASE), + re.compile(r"tool not available", re.IGNORECASE), + re.compile(r"(请|你)(自己|手动|帮我)?(运行|执行|跑)"), + re.compile(r"please run (this|the command) (yourself|manually)", re.IGNORECASE), +] + + +def main(): + raw = sys.stdin.read() + try: + event = json.loads(raw) + except Exception: + sys.exit(0) + return + + if event.get("tool_name") not in ("Task", "spawn_agent"): + sys.exit(0) + return + + response = event.get("tool_response") or {} + text = "" + if isinstance(response.get("content"), str): + text = response["content"] + elif isinstance(response.get("content"), list): + text = "\n".join(c.get("text", "") for c in response["content"] if isinstance(c, dict)) + else: + text = json.dumps(response) + + for pat in GIVEUP_PATTERNS: + if pat.search(text): + sys.stderr.write(f"[subagent-retry] giveup detected: {pat.pattern[:50]}\n") + subagent = (event.get("tool_input") or {}).get("subagent_type") or (event.get("tool_input") or {}).get("agent_type") or "subagent" + msg = f"[Athena subagent-retry] {subagent} 返回了放弃信号. 协议: 1) 再尝试一次, 用不同参数或拆分任务 2) 仍失败则主 agent 接管, 不要让用户代执行 (铁律 6 完成度证据)" + print(json.dumps({"systemMessage": msg})) + + try: + project_dir = os.environ.get("CODEX_PROJECT_DIR") or os.getcwd() + state_dir = os.path.join(project_dir, ".ai_state") + with open(os.path.join(state_dir, "hook-trace.jsonl"), "a") as f: + f.write(json.dumps({ + "ts": datetime.datetime.utcnow().isoformat() + "Z", + "hook": "subagent-retry", + "subagent": subagent + }) + "\n") + except Exception: + pass + sys.exit(0) + return + + sys.exit(0) + + +if __name__ == "__main__": + main() diff --git a/.codex/hooks/user-prompt-submit.py b/.codex/hooks/user-prompt-submit.py new file mode 100755 index 00000000..3c82f3e7 --- /dev/null +++ b/.codex/hooks/user-prompt-submit.py @@ -0,0 +1,76 @@ +#!/usr/bin/env python3 +"""VibeCoding Athena Codex UserPromptSubmit hook v9.6. + +仅在用户 prompt 看起来是"开发请求" 时, 提示当前 PACE 状态, 帮助 model 路由. +不强制添加任何内容, 单纯增强 context. + +参考: https://developers.openai.com/codex/hooks +""" +import json +import os +import re +import sys +import datetime + +PROJECT_DIR = os.environ.get("CODEX_PROJECT_DIR") or os.getcwd() +STATE_DIR = os.path.join(PROJECT_DIR, ".ai_state") +INDEX_PATH = os.path.join(STATE_DIR, "_index.md") + +DEV_KEYWORDS = re.compile( + r"(实现|开发|新增|加上|做|搭|跑|写|改|修|fix|implement|build|add|create|refactor|review|test|debug)", + re.IGNORECASE, +) + + +def main(): + raw = sys.stdin.read() + try: + event = json.loads(raw) + except Exception: + sys.exit(0) + return + + prompt = event.get("prompt") or event.get("user_prompt") or "" + if not DEV_KEYWORDS.search(prompt): + sys.exit(0) + return + + if not os.path.exists(INDEX_PATH): + sys.exit(0) + return + + try: + content = open(INDEX_PATH).read() + m = re.match(r"^---\n(.*?)\n---", content, re.DOTALL) + if not m: + sys.exit(0) + return + fm = m.group(1) + path_m = re.search(r'^ path: "([^"]*)"$', fm, re.MULTILINE) + stage_m = re.search(r'^ stage: "([^"]*)"$', fm, re.MULTILINE) + path_val = path_m.group(1) if path_m else "" + stage_val = stage_m.group(1) if stage_m else "" + + if path_val and stage_val: + msg = f"[Athena] 当前 PACE 状态: {path_val}/{stage_val}. 如果是延续任务, 请走 pace skill 既有流程; 如果是新任务, 评估是否升路径." + sys.stderr.write(msg + "\n") + print(json.dumps({"systemMessage": msg})) + + try: + line = json.dumps({ + "ts": datetime.datetime.utcnow().isoformat() + "Z", + "hook": "user-prompt-submit", + "path": path_val, "stage": stage_val + }) + "\n" + with open(os.path.join(STATE_DIR, "hook-trace.jsonl"), "a") as f: + f.write(line) + except Exception: + pass + except Exception as e: + sys.stderr.write(f"[user-prompt-submit] failed: {e}\n") + + sys.exit(0) + + +if __name__ == "__main__": + main() diff --git a/.codex/prompts/athena-dev.md b/.codex/prompts/athena-dev.md new file mode 100644 index 00000000..a3f30c67 --- /dev/null +++ b/.codex/prompts/athena-dev.md @@ -0,0 +1,24 @@ +# /athena-dev (Codex) — 主入口 + +`.ai_state/` 不存在 → 提示 `/athena-init` 并停。 +`schema_version` != "9.6" → 提示 `/athena-migrate` 并停。 + +## Get-bearings (R₀, just-in-time, 铁律 8) + +1. **必读**: `.ai_state/_index.md` +2. **按需**: stage=impl/review → 按 pointers.latest_progress 读 progress.md 尾部 +3. **按需**: 任务命中 lessons 主题 → pointers.latest_lesson 跳段 +4. **按需**: `git log --oneline -10` +5. **按需**: impl/review 阶段 → `bash .ai_state/init.sh` + +文件大就用 head/tail/grep。**禁止 glob 整个 .ai_state/**。 + +## Dispatch + +有进行中的 stage → 从当前阶段继续 (参考 `~/.codex/skills/pace/SKILL.md`)。 +新需求 → 触发 pace skill 路由, 需求: $ARGUMENTS。 + +Codex 端特别注意: +- **无 /goal** → delivery-gate hook 兜底判定 +- **无 /batch** → 多任务用 spawn_agent (config.toml [agents] 配置) +- **无 compact 生命周期** → 长任务点主动写 `.ai_state/_index.md` 保存 diff --git a/.codex/prompts/athena-init.md b/.codex/prompts/athena-init.md new file mode 100644 index 00000000..e15d3a75 --- /dev/null +++ b/.codex/prompts/athena-init.md @@ -0,0 +1,67 @@ +# /athena-init (Codex) — 项目初始化 + +# 描述 +> 项目初始化。每个新项目首次使用 Athena 时运行。 +# end + +参考: + +`.ai_state/_index.md` 已存在 → 提示用户用 `/athena-migrate` 升级或确认覆盖。 + +## 1. 项目扫描 + +```bash +ls package.json pyproject.toml Cargo.toml go.mod build.gradle pom.xml 2>/dev/null +cat package.json 2>/dev/null | grep -A5 '"scripts"' +ls jest.config* vitest.config* pytest.ini .eslintrc* ruff.toml biome.json 2>/dev/null +``` + +## 2. 创建 .ai_state/ (v9.6 schema) + +```bash +mkdir -p .ai_state/details/reviews .ai_state/details/tasks-archive +``` + +## 3. 拷贝模板 + +从 `~/.codex/skills/pace/templates/` 复制: +- `_index.md` → `.ai_state/_index.md` +- 所有 `details/*.md` +- `init.sh` + +## 4. 填充 _index.md frontmatter + +把扫描结果写入: tech_stack, test_cmd, build_cmd, lint_cmd, dev_cmd + +平台特征 (`codex --version` 输出): +- `cx_version` +- `goal_supported`: Codex 当前**没有原生 /goal** → false +- `batch_supported`: Codex subagent (max_threads in [agents]) 部分支持 → 视 max_threads 而定 +- `background_supported`: false (Codex 无 /background) +- `session_memory_active`: false (Codex 无 ~/.claude/projects/ 等价物) +- `cross_session_memory`: "none" + +## 5. 生成 init.sh + +根据扫描结果定制 `.ai_state/init.sh`. `chmod +x`. + +## 6. .gitignore + +```bash +grep -q '.ai_state' .gitignore 2>/dev/null || echo '.ai_state/' >> .gitignore +``` + +## 7. Git commit + +```bash +git add .ai_state/ .gitignore +git commit -m "athena: project initialized (v9.6 schema, Codex)" +``` + +## 8. 验证 + +```bash +bash .ai_state/init.sh +``` + +告知用户: "Athena (Codex 版) 已就绪。用 `/athena-dev <需求>` 开始, 或 `/athena-status` 看面板。" diff --git a/.codex/prompts/athena-migrate.md b/.codex/prompts/athena-migrate.md new file mode 100644 index 00000000..8b55641a --- /dev/null +++ b/.codex/prompts/athena-migrate.md @@ -0,0 +1,155 @@ +# /athena-migrate (Codex) — v9.5 → v9.6 迁移 + +参考 CC 版 `~/.claude/skills/athena-migrate/SKILL.md`. 算法一致 (因为 schema 跨平台相同, 这是铁律 6). + +Codex 端差异: +- 用 python3 替代 node 生成 `_index.md` (Codex 默认有 python3) +- 检测 `cx_version` 写入 `platform_features.cx_version`, **不写** cc_version + +## 备份脚本 + +```bash +mkdir -p .ai_state/.legacy-v9.5 +cp -r .ai_state/{tasks.md,design.md,progress.md,lessons.md,handoff.md,project.json,reviews,hook-trace.jsonl} .ai_state/.legacy-v9.5/ 2>/dev/null || true +``` + +## 创建 details/ + +```bash +mkdir -p .ai_state/details/reviews .ai_state/details/tasks-archive +``` + +## 平移 + +```bash +[ -f .ai_state/tasks.md ] && mv .ai_state/tasks.md .ai_state/details/tasks-current.md +for f in design.md progress.md lessons.md handoff.md; do + [ -f .ai_state/$f ] && mv .ai_state/$f .ai_state/details/$f +done +[ -d .ai_state/reviews ] && mv .ai_state/reviews .ai_state/details/reviews +[ -f .ai_state/compact-snapshot.json ] && rm .ai_state/compact-snapshot.json # Codex 无 compact 也能有的旧文件 +``` + +## 生成 _index.md (python3) + +```bash +python3 << 'PY' +import json, os, re, sys + +proj = {} +try: + with open('.ai_state/.legacy-v9.5/project.json') as f: + proj = json.load(f) +except FileNotFoundError: + pass + +def count_match(path, pattern): + try: + with open(path) as f: + return len(re.findall(pattern, f.read(), re.MULTILINE)) + except FileNotFoundError: + return 0 + +def last_section_line(path, head_prefix='## '): + try: + lines = open(path).read().split('\n') + for i in range(len(lines) - 1, -1, -1): + if lines[i].startswith(head_prefix): + return i + 1 + except FileNotFoundError: + pass + return 0 + +def last_nonempty_line(path): + try: + lines = open(path).read().split('\n') + for i in range(len(lines) - 1, -1, -1): + stripped = lines[i].strip() + if stripped and not stripped.startswith(' + +## 最近活动 + + + +## 当前阻塞 + + + +## 下一动作 (PACE 建议) + + + +## 反思建议 (Hook 沉淀) + + diff --git a/.codex/skills/pace/templates/details/design.md b/.codex/skills/pace/templates/details/design.md new file mode 100644 index 00000000..3b45ac81 --- /dev/null +++ b/.codex/skills/pace/templates/details/design.md @@ -0,0 +1,28 @@ +# 设计文档 + +## 需求摘要 + +## 功能分级 + +- **MUST**: +- **SHOULD**: +- **COULD**: + +## 验收标准 + +- [ ] + +## 技术方案 + +## File Structure Plan + + + +## 不做什么 diff --git a/.codex/skills/pace/templates/details/handoff.md b/.codex/skills/pace/templates/details/handoff.md new file mode 100644 index 00000000..bea31b9d --- /dev/null +++ b/.codex/skills/pace/templates/details/handoff.md @@ -0,0 +1,31 @@ +# 跨模型/跨 worker 交接 + + + +## 当前任务 + + + +## 项目文件树 + + + + +## 关键代码片段 + + + +## 项目背景 + + + +## 已完成 + + + +## 注意事项 + + diff --git a/.codex/skills/pace/templates/details/lessons.md b/.codex/skills/pace/templates/details/lessons.md new file mode 100644 index 00000000..8d473029 --- /dev/null +++ b/.codex/skills/pace/templates/details/lessons.md @@ -0,0 +1,21 @@ +# 项目级经验沉淀 (Lessons Learned) + + diff --git a/.codex/skills/pace/templates/details/next.md b/.codex/skills/pace/templates/details/next.md new file mode 100644 index 00000000..86579c8d --- /dev/null +++ b/.codex/skills/pace/templates/details/next.md @@ -0,0 +1,15 @@ +# 下一动作建议 (PACE-aware, Stop hook 维护) + + diff --git a/.codex/skills/pace/templates/details/progress.md b/.codex/skills/pace/templates/details/progress.md new file mode 100644 index 00000000..1c82015c --- /dev/null +++ b/.codex/skills/pace/templates/details/progress.md @@ -0,0 +1,15 @@ +# Progress Log + + diff --git a/.codex/skills/pace/templates/details/proposals.md b/.codex/skills/pace/templates/details/proposals.md new file mode 100644 index 00000000..1a85b244 --- /dev/null +++ b/.codex/skills/pace/templates/details/proposals.md @@ -0,0 +1,20 @@ +# Hook 反思沉淀 (铁律 9: Hook 是进化器) + + diff --git a/.codex/skills/pace/templates/details/review-report.md b/.codex/skills/pace/templates/details/review-report.md new file mode 100644 index 00000000..a879ae39 --- /dev/null +++ b/.codex/skills/pace/templates/details/review-report.md @@ -0,0 +1,41 @@ +# Sprint N 审查报告 + +## Step 1: 测试结果 + + + +## Step 2: /codex:review (Feature+, Quick 可选) + + + +## Step 3: /codex:adversarial-review (System) + + + +## Step 4: ECC 扫描 (System) + + + +## Step 5: Claude 审查 (/review 内置) + + + +## Step 6: @evaluator 评分 + +| 维度 | 分数 | 证据 | +|------|------|------| +| Functionality | /5 | (代码行号) | +| Spec Compliance | /5 | (代码行号 vs design.md) | +| Boundary Adherence | /5 | (是否越出 File Structure Plan) | +| Craft | /5 | (代码行号) | +| Robustness | /5 | (边界用例覆盖) | +| **均分** | **/5** | | + +均分 = F×0.25 + SC×0.25 + B×0.15 + C×0.15 + R×0.20 + +## VERDICT: + + diff --git a/.codex/skills/pace/templates/details/tasks-current.md b/.codex/skills/pace/templates/details/tasks-current.md new file mode 100644 index 00000000..9371576d --- /dev/null +++ b/.codex/skills/pace/templates/details/tasks-current.md @@ -0,0 +1,19 @@ +# Sprint 任务清单 + + + +## 待办 + +## 完成 + +## 阻塞 diff --git a/.codex/skills/pace/templates/goal-conditions.md b/.codex/skills/pace/templates/goal-conditions.md new file mode 100644 index 00000000..1d0be3e5 --- /dev/null +++ b/.codex/skills/pace/templates/goal-conditions.md @@ -0,0 +1,121 @@ +# /goal Condition 模板库 + +每个 PACE stage 对应一个 condition 模板, 用 `/goal ` 启动 autonomous loop。 + +参考: + +## 写 condition 的四件套 (官方推荐) + +``` +End Goal + Verification + Constraints + Turn/Time Limit +``` + +- **End Goal**: 一个可测的终态 (test 通过 / 文件存在 / git log 含 commit) +- **Verification**: 如何证明 (npm test exits 0 / rg "X" 0 行) +- **Constraints**: 边界 (不动 X 文件 / 不引入新依赖) +- **Turn/Time Limit**: stop after 20 turns / stop after 60 minutes + +--- + +## Feature/Refactor/System stage=plan + +``` +ALL of: + - .ai_state/details/design.md 含 "## File Structure Plan" 段且段下至少 3 个 owner 行 + - .ai_state/details/tasks-current.md 至少 1 个 - [ ] Task + - 我已经 ask 用户确认且收到了 "yes" 或 "go" 回复 +Constraints: + - 不修改 design.md 之外的源码文件 + - 不超过 20 turns +``` + +## Quick/Feature stage=impl + +``` +ALL of: + - .ai_state/details/tasks-current.md 中所有 - [ ] 都变成 - [x] + - 运行 退出码 0 (从 _index.md.test_cmd 读) + - .ai_state/details/progress.md 在本 turn 序列至少新增 N 行 (N = 原待办 task 数) + - 没有任何对 design.md 的修改 +Constraints: + - 不动 .ai_state/_index.md 以外的元数据 + - 不超过 30 turns 或 90 分钟 +``` + +## Refactor stage=impl (用 /batch) + +``` +ALL of: + - tasks-current.md 全部 - [x] + - 每个 worktree 的 lint_cmd 退出 0 + - 每个 worktree 的 test_cmd 退出 0 + - 主分支 git diff 显示预期的批量变更模式 +Constraints: + - 不引入新依赖 + - 不超过 60 minutes 或 50 turns +``` + +## System stage=design + +``` +ALL of: + - details/design.md 含 "## File Structure Plan" 且段下覆盖所有新增/修改的模块路径 + - details/design.md 含 "## 验收标准" 段且至少 5 条 - [ ] (会在 impl 阶段勾选) + - details/tasks-current.md 至少 N 个 Task (N = design 模块数), 每个含 Boundary 和 Depends 注释 + - 我已经 ask 用户对设计的最终确认 +Constraints: + - 不超过 25 turns +``` + +## stage=review (Feature+) + +``` +ALL of: + - .ai_state/details/reviews/sprint-{sprint}.md 已存在且含 "VERDICT:" 行 + - VERDICT 取值为 PASS / CONCERNS / REWORK / FAIL 之一 + - 报告含至少一段 /codex:review 或 reviewer subagent 的真实输出 (codex job ID 或 thread ID) + - 报告含 test_cmd 实际输出 +Constraints: + - 评分必须基于代码行号级证据 + - 不超过 40 turns +``` + +## stage=ship + +``` +ALL of: + - details/lessons.md 末尾含 "## [{date} Sprint {sprint}]" 标题 + - git log --oneline -1 显示本 sprint 的提交 + - _index.md.project.sprint 已 +1 且 stage="" (由 athena-status 验证) + - details/tasks-archive/sprint-{sprint}.md 存在 (归档) +Constraints: + - 不超过 10 turns +``` + +--- + +## 启动方式 + +### 交互式 +``` +/goal <粘贴上面对应 stage 的 condition> +``` + +### 非交互式 (overnight 跑) +```bash +claude -p "/goal " +``` + +### 状态查询 +``` +/goal # 不带参数, 看 turns/tokens/最近 evaluator reason +/goal clear # 中止 +``` + +## 注意事项 (官方文档) + +- evaluator 只读 transcript, 不跑命令——condition 必须是"transcript 能证明的事" +- 一个 session 只能有一个 active goal, 设置新 goal 替换旧的 +- condition ≤ 4000 字符 +- v2.1.139+ 才有, 旧版本 delivery-gate hook 兜底 +- "Vague in, vague out"——含糊的 condition = 含糊的 loop diff --git a/.codex/skills/pace/templates/init.sh b/.codex/skills/pace/templates/init.sh new file mode 100644 index 00000000..edde9940 --- /dev/null +++ b/.codex/skills/pace/templates/init.sh @@ -0,0 +1,17 @@ +#!/bin/bash +# Athena init.sh — 启动开发环境 + 基线测试 +# 由 /athena-init 根据项目类型生成具体命令 +set -e + +echo "[Athena] 启动开发环境..." +# 示例 (根据项目类型): +# npm install +# pip install -e . +# cargo build + +echo "[Athena] 运行基线测试..." +# npm test +# pytest +# cargo test + +echo "[Athena] 就绪。" diff --git a/.codex/skills/plan-first/SKILL.md b/.codex/skills/plan-first/SKILL.md deleted file mode 100644 index ebbe41d4..00000000 --- a/.codex/skills/plan-first/SKILL.md +++ /dev/null @@ -1,21 +0,0 @@ ---- -name: plan-first -description: P 阶段计划生成 ---- -# Plan-First (P 阶段) - -## 流程 -1. 读 design.md -2. 分解为可执行任务: - ``` - - [ ] T-001: {动作} + {目标} + {验收条件} - 文件: {涉及文件} - 依赖: {前置任务} - ``` -3. 排序: 依赖拓扑 → 风险高先做 -4. 写入 plan.md - -## 质量检查 -- 每个任务有"完成"定义 -- 无模糊词 -- 超过 15 个任务考虑拆分 diff --git a/.codex/skills/reflexion/SKILL.md b/.codex/skills/reflexion/SKILL.md deleted file mode 100644 index fe720bc8..00000000 --- a/.codex/skills/reflexion/SKILL.md +++ /dev/null @@ -1,19 +0,0 @@ ---- -name: reflexion -description: E 阶段自我反思 ---- -# Reflexion (自我反思) - -E 阶段每个 Task 实现完成后, Micro-review 之前。 - -## 反思清单 -1. 走捷径了吗? (as any, 空 catch) -2. 忽略边界了吗? (空/超长/并发/null) -3. 硬编码了吗? (URL/端口/阈值) -4. 过度工程了吗? (需求没要求的功能) -5. 偏离 spec 了吗? -6. 6 个月后能理解吗? - -发现问题 → 立即修复 -值得记录 → lessons.md -模式性错误 → conventions.md "Agent 易犯错误" diff --git a/.codex/skills/security-review/SKILL.md b/.codex/skills/security-review/SKILL.md deleted file mode 100644 index 54eb2b66..00000000 --- a/.codex/skills/security-review/SKILL.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -name: security-review -description: 安全审查 ---- -# Security Review - -检查: 认证/授权, 输入验证, 密钥管理, 依赖漏洞, 日志安全, CORS/CSP -产出: 追加到 quality.md diff --git a/.codex/skills/verification/SKILL.md b/.codex/skills/verification/SKILL.md deleted file mode 100644 index a50ea524..00000000 --- a/.codex/skills/verification/SKILL.md +++ /dev/null @@ -1,16 +0,0 @@ ---- -name: verification -description: T 阶段验证 ---- -# Verification - -## 流程 -1. 运行测试 + 覆盖率 -2. TypeScript 类型检查 (如有) -3. Lint (如有, Path C+ 强制) - -## 验收标准逐条确认 -4. 读 design.md "## 验收标准" -5. 逐条确认: 满足? 证据? -6. 未满足 → quality.md "## 未满足验收标准" -7. E2E (如有框架) diff --git a/.codex/templates/ai-state/conventions.md b/.codex/templates/ai-state/conventions.md deleted file mode 100644 index e69c3b42..00000000 --- a/.codex/templates/ai-state/conventions.md +++ /dev/null @@ -1,9 +0,0 @@ -# Conventions - -## 项目编码规范 -{自动检测或手动填写} - -## Agent 易犯错误 -> 由 reflexion/kaizen 自动追加 - -- (初始为空) diff --git a/.codex/templates/ai-state/design.md b/.codex/templates/ai-state/design.md deleted file mode 100644 index 172d82e3..00000000 --- a/.codex/templates/ai-state/design.md +++ /dev/null @@ -1,19 +0,0 @@ -# Design Spec - -## 需求 (MUST/SHOULD/COULD) - -### MUST -- {核心功能} - -### SHOULD -- {重要功能} - -### COULD -- {锦上添花} - -## 技术方案 -{架构} - -## 验收标准 -- [ ] {标准 1} -- [ ] {标准 2} diff --git a/.codex/templates/ai-state/doing.md b/.codex/templates/ai-state/doing.md deleted file mode 100644 index 4023e936..00000000 --- a/.codex/templates/ai-state/doing.md +++ /dev/null @@ -1,7 +0,0 @@ -# Doing - -## 当前执行 -- [ ] {当前步骤} - -## 历史 -{自动追加} diff --git a/.codex/templates/ai-state/lessons.md b/.codex/templates/ai-state/lessons.md deleted file mode 100644 index daee1aef..00000000 --- a/.codex/templates/ai-state/lessons.md +++ /dev/null @@ -1,7 +0,0 @@ -# Lessons Learned - -## 技术陷阱 -{发现的问题} - -## 有效模式 -{有效的解决方案} diff --git a/.codex/templates/ai-state/plan.md b/.codex/templates/ai-state/plan.md deleted file mode 100644 index 3af1b56d..00000000 --- a/.codex/templates/ai-state/plan.md +++ /dev/null @@ -1,7 +0,0 @@ -# Plan - -## 任务列表 - -- [ ] T-001: {动作} + {目标} + {验收条件} - 文件: {文件} - 依赖: 无 diff --git a/.codex/templates/ai-state/quality.md b/.codex/templates/ai-state/quality.md deleted file mode 100644 index a220ae27..00000000 --- a/.codex/templates/ai-state/quality.md +++ /dev/null @@ -1,13 +0,0 @@ -# Quality Report - -## 验证结果 -- 测试: {通过/失败} -- 类型检查: {通过/失败} - -## 验收标准确认 -- [x] {标准}: 满足 -- [ ] {标准}: 未满足 - -## 代码审查 -- 发现: {数量} -- 已修复: {数量} diff --git a/.codex/templates/ai-state/session.md b/.codex/templates/ai-state/session.md deleted file mode 100644 index 2aa357f6..00000000 --- a/.codex/templates/ai-state/session.md +++ /dev/null @@ -1,10 +0,0 @@ -# Session - -## 任务 -{任务描述} - -## 路径 -P.A.C.E. Path: {A|B|C|D} - -## 当前阶段 -RIPER-7: {R₀|R|D|P|E|T|V} diff --git a/.codex/workflows/pace.md b/.codex/workflows/pace.md deleted file mode 100644 index fe65c1da..00000000 --- a/.codex/workflows/pace.md +++ /dev/null @@ -1,13 +0,0 @@ -# P.A.C.E. 复杂度路由 v9.2.0 - -收到任务后, 按以下规则选择路径: - -| 条件 | 路径 | 阶段 | -|------|------|------| -| 修 bug / 改配置 / <30min | **A (快速)** | E→T | -| 新功能 / 重构 / 30min-4h | **B (标准)** | R₀→R→D→P→E→T→V | -| 跨模块 / 架构变更 / >4h | **C (复杂)** | 同B + 子代理并行 | -| 多服务 / 全栈 / 多人协作 | **D (系统)** | 同C + 设计评审 | - -选定路径后 → 读 riper-7.md 执行对应阶段。 -Path A 跳过 R₀/R/D/P, 直接 E→T。 diff --git a/.codex/workflows/riper-7.md b/.codex/workflows/riper-7.md deleted file mode 100644 index 0c327162..00000000 --- a/.codex/workflows/riper-7.md +++ /dev/null @@ -1,57 +0,0 @@ -# RIPER-7 阶段编排 v9.2.0 - -## 每阶段进入前 (通用) -1. 这一步要解决的核心问题是什么? -2. 项目里有没有类似实现? → 搜索相关代码 -3. 用到的库 API 确定吗? → context7 skill 查文档 -4. .ai_state/lessons.md + conventions.md 有相关经验吗? - ---- - -## R₀ 需求精炼 (Path B+) -**技能**: brainstorm skill (含步骤0自主调研) -**产出**: .ai_state/design.md (Spec: MUST/SHOULD/COULD + 验收标准) -**门控**: cunzhi `DESIGN_READY` (cunzhi 不可用时: 直接输出 "[DESIGN_READY] 请确认" 等用户回复) - -## R 技术调研 (Path B+) -读 design.md → 搜索相关代码 + 查库文档 -产出: design.md 追加技术方案 - -## D 方案定稿 (Path B+) -读 design.md → 检查接口最小化? 错误处理完整? 更简单的方案? -产出: design.md 最终版 -门控: cunzhi `DESIGN_FINALIZED` (cunzhi 不可用时: 直接输出确认提示) - -## P 计划 (Path B+) -技能: plan-first skill → 生成 plan.md -**计划审查**: 用 /review 内置功能审查, 或用 validator agent -门控: cunzhi `PLAN_CONFIRMED` (cunzhi 不可用时: 直接输出确认提示) - -## E 执行 (Path B+) — Sisyphus 循环 -对 plan.md 每个 Task: - 1. 读 Task + 相关文件 + conventions.md - 2. TDD: 先写测试 → 实现 → 重构 - 2.5. **Reflexion** (自我反思 → reflexion skill): - "这个实现有没有: 走捷径/忽略边界/硬编码/过度工程/偏离 spec?" - 发现问题 → 立即修复 - 值得记录 → lessons.md - 3. **Micro-review**: 对标 design.md spec + conventions.md - 通过 → [x] Task + commit - 不通过 → 修复 - **铁律**: plan.md 有任何 [ ] 未完成, 不停止。 - -## T 测试/审查 (Path B+) - 1. verification skill: 跑测试 + 覆盖率 - 1.5. **验收标准确认**: - 读 design.md "## 验收标准" → 逐条确认是否满足 - 未满足 → 标注到 quality.md - 2. code-review skill: 代码审查 - 3. 用 /review 内置审查功能 做代码审查 - 4. 综合 → quality.md - 5. Quality Gate: PASS(exit 0) / CONCERNS(exit 0+warn) / REWORK(exit 2) / FAIL(exit 2) - -## V 归档 (Path B+) - 1. kaizen skill: 回顾 + lessons.md - 1.5. **分支收尾** (finish-branch skill): - 选项: PR description / keep / discard (Codex 不直接 merge) - 2. cunzhi `DELIVERY_COMPLETE` (cunzhi 不可用时: 直接输出确认提示) diff --git a/.gitignore b/.gitignore index 4669c78b..9de6d97b 100644 --- a/.gitignore +++ b/.gitignore @@ -37,3 +37,4 @@ src-tauri/target # VibeCoding state .ai_state/ +AGENTS.md