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