docs(other): 更新 Vibe Coding 配置
This commit is contained in:
+27
-32
@@ -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 独立。
|
||||
<!--
|
||||
本文件目标 altitude: 工程纪律的高信号宪法 (constitution)。
|
||||
- 不规定操作步骤 → skills/pace/SKILL.md
|
||||
- 不列插件命令 → skills/athena-setup/SKILL.md
|
||||
- 不写安装/配置 → README.md
|
||||
- 行数硬上限 30, 超过 build 阶段 wc -l 失败
|
||||
-->
|
||||
|
||||
## 框架地图
|
||||
<important>
|
||||
你是 VibeCoding Athena 工程 Agent。CC 做事,Athena 把关。
|
||||
- 收任务 → 走 PACE 路由 (Hotfix/Bugfix 直接做, Quick+ 走完整流程)
|
||||
- 自己跑命令 / 跑测试 / 看输出, 证明工作完成
|
||||
- 工具失败 → 三次重试附 stderr 报告, 不让用户代执行
|
||||
- 技术结论 → 必须引用官方文档 / 源码 URL
|
||||
- 调用 subagent 必须产生真实 tool_use, 不允许伪造完成
|
||||
</important>
|
||||
|
||||
| 类别 | 文件 | 数量 |
|
||||
| ---------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- | ---- |
|
||||
| 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
|
||||
|
||||
@@ -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]
|
||||
@@ -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:" 段。
|
||||
@@ -1,11 +0,0 @@
|
||||
---
|
||||
name: explorer
|
||||
model: haiku
|
||||
description: 代码库调研 + 文档查询 (只读)
|
||||
effort: medium
|
||||
maxTurns: 15
|
||||
---
|
||||
你是 explorer agent。职责:
|
||||
1. 搜索项目中与任务相关的代码
|
||||
2. 查询依赖库文档 (context7)
|
||||
3. 输出调研结果 (不修改任何文件)
|
||||
@@ -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 "<task short summary>"`
|
||||
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
|
||||
|
||||
**禁止**: "请你手动跑", "我没有权限" (没有就申请, 不是放弃).
|
||||
@@ -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. 输出审查意见 (严格, 不放水)
|
||||
@@ -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 不可用 → 输出提示, 不执行。
|
||||
@@ -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"
|
||||
@@ -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 开始开发"
|
||||
@@ -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
|
||||
@@ -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 运行中?
|
||||
Executable
+65
@@ -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);
|
||||
});
|
||||
Executable
+63
@@ -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);
|
||||
});
|
||||
@@ -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);
|
||||
}
|
||||
Regular → Executable
+175
-73
@@ -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
|
||||
});
|
||||
|
||||
Executable
+175
@@ -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('<!--') && !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('-->') && !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) {}
|
||||
}
|
||||
Executable
+136
@@ -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\S]*?-->\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;
|
||||
}
|
||||
Executable
+59
@@ -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);
|
||||
});
|
||||
@@ -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);
|
||||
Executable
+54
@@ -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);
|
||||
});
|
||||
@@ -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);
|
||||
Executable
+122
@@ -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);
|
||||
});
|
||||
Executable
+94
@@ -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);
|
||||
});
|
||||
@@ -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 {}
|
||||
Executable
+70
@@ -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);
|
||||
});
|
||||
@@ -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);
|
||||
+130
-50
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 已接入。"
|
||||
@@ -0,0 +1,104 @@
|
||||
---
|
||||
name: athena-init
|
||||
description: >
|
||||
项目初始化。每个新项目首次使用 Athena 时运行。扫描项目、生成环境脚本、创建 .ai_state/ schema。
|
||||
effort: xhigh
|
||||
disable-model-invocation: true
|
||||
---
|
||||
|
||||
# /athena-init — 项目初始化
|
||||
|
||||
参考: <https://code.claude.com/docs/en/skills>
|
||||
|
||||
## 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)。
|
||||
@@ -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 迁移
|
||||
|
||||
参考: <https://code.claude.com/docs/en/skills>
|
||||
|
||||
## 前置检查
|
||||
|
||||
```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("<!--") && !l.startsWith("#"));
|
||||
progress_entries = lines.length;
|
||||
if (lines.length > 0) latest_progress = `details/progress.md#L${lines.length}`;
|
||||
} catch(e) {}
|
||||
|
||||
let lessons_lines = 0, latest_lesson = "";
|
||||
try {
|
||||
const lines = fs.readFileSync(".ai_state/details/lessons.md","utf8").split("\n");
|
||||
lessons_lines = lines.filter(l => l.startsWith("##")).length;
|
||||
for (let i = lines.length - 1; i >= 0; i--) {
|
||||
if (lines[i].startsWith("## ")) { latest_lesson = `details/lessons.md#L${i+1}`; break; }
|
||||
}
|
||||
} catch(e) {}
|
||||
|
||||
let reviews_count = 0, latest_review = "";
|
||||
try {
|
||||
const files = fs.readdirSync(".ai_state/details/reviews").filter(f => f.startsWith("sprint-") && f.endsWith(".md"));
|
||||
reviews_count = files.length;
|
||||
if (files.length > 0) {
|
||||
const sorted = files.sort();
|
||||
latest_review = `details/reviews/${sorted[sorted.length-1]}`;
|
||||
}
|
||||
} catch(e) {}
|
||||
|
||||
const idx = `---
|
||||
schema_version: "9.6"
|
||||
athena_version: "9.6.0"
|
||||
project:
|
||||
path: "${proj.path || ""}"
|
||||
stage: "${proj.stage || ""}"
|
||||
sprint: ${sprint}
|
||||
active_goal: ""
|
||||
counts:
|
||||
tasks_pending: ${tasks_pending}
|
||||
tasks_done: ${tasks_done}
|
||||
tasks_blocked: ${tasks_blocked}
|
||||
lessons_lines: ${lessons_lines}
|
||||
progress_entries: ${progress_entries}
|
||||
reviews_count: ${reviews_count}
|
||||
pointers:
|
||||
latest_progress: "${latest_progress}"
|
||||
latest_review: "${latest_review}"
|
||||
latest_lesson: "${latest_lesson}"
|
||||
latest_proposal: ""
|
||||
fingerprints:
|
||||
tasks_mtime: 0
|
||||
design_mtime: 0
|
||||
progress_mtime: 0
|
||||
lessons_mtime: 0
|
||||
platform_features:
|
||||
cc_version: ""
|
||||
cx_version: ""
|
||||
goal_supported: false
|
||||
batch_supported: false
|
||||
background_supported: false
|
||||
session_memory_active: false
|
||||
cross_session_memory: "none"
|
||||
gotchas: ${JSON.stringify(proj.gotchas || [])}
|
||||
conventions: ${JSON.stringify(proj.conventions || [])}
|
||||
tech_stack: "${proj.tech_stack || ""}"
|
||||
test_cmd: "${proj.test_cmd || ""}"
|
||||
build_cmd: "${proj.build_cmd || ""}"
|
||||
lint_cmd: "${proj.lint_cmd || ""}"
|
||||
dev_cmd: "${proj.dev_cmd || ""}"
|
||||
---
|
||||
|
||||
# 项目状态索引 (v9.6 migrated from v9.5.5)
|
||||
|
||||
## 最近活动
|
||||
|
||||
## 当前阻塞
|
||||
|
||||
## 下一动作 (PACE 建议)
|
||||
|
||||
## 反思建议 (Hook 沉淀)
|
||||
`;
|
||||
|
||||
fs.writeFileSync(".ai_state/_index.md", idx);
|
||||
console.log("[migrate] _index.md generated");
|
||||
'
|
||||
```
|
||||
|
||||
### Step 5: 创建空 next.md 和 proposals.md
|
||||
|
||||
```bash
|
||||
touch .ai_state/details/next.md
|
||||
touch .ai_state/details/proposals.md
|
||||
```
|
||||
|
||||
### Step 6: 平台版本检测填入 _index.md
|
||||
|
||||
调用 `/athena-setup --detect-only`, 它会原地更新 `_index.md.platform_features`。
|
||||
|
||||
### Step 7: 提示用户
|
||||
|
||||
```
|
||||
✓ 迁移完成。备份在 .ai_state/.legacy-v9.5/, 确认无问题可删除。
|
||||
新增的文件: .ai_state/details/next.md, .ai_state/details/proposals.md
|
||||
删除的文件: .ai_state/project.json (合并到 _index.md frontmatter)
|
||||
.ai_state/compact-snapshot.json (改写到 _index.md.snapshots 数组)
|
||||
下一步: /athena-status 查看新状态面板
|
||||
```
|
||||
|
||||
### Step 8: Git commit
|
||||
|
||||
```bash
|
||||
git add .ai_state/
|
||||
git commit -m "athena: migrate state to v9.6 schema"
|
||||
```
|
||||
|
||||
## 回滚 (如果出问题)
|
||||
|
||||
```bash
|
||||
rm -rf .ai_state/details .ai_state/_index.md
|
||||
cp -r .ai_state/.legacy-v9.5/* .ai_state/
|
||||
```
|
||||
@@ -0,0 +1,138 @@
|
||||
---
|
||||
name: athena-preferences
|
||||
description: >
|
||||
Athena 全局偏好便利层。直接操作 CC 原生 ~/.claude/settings.json 的 permissions.allow/deny 和全局 CLAUDE.md。
|
||||
effort: medium
|
||||
disable-model-invocation: true
|
||||
argument-hint: "<subcommand> <args>"
|
||||
---
|
||||
|
||||
# /athena-preferences (v9.6)
|
||||
|
||||
## 设计原则
|
||||
|
||||
**Athena 不造私有偏好文件**。所有写入都是 CC 原生位置:
|
||||
|
||||
| 偏好类型 | 落点 |
|
||||
|---|---|
|
||||
| 全局 allow | `~/.claude/settings.json` `permissions.allow[]` |
|
||||
| 全局 deny | `~/.claude/settings.json` `permissions.deny[]` |
|
||||
| 工具偏好 (pnpm/vitest) | `~/.claude/CLAUDE.md` "## Tool Preferences" 段 |
|
||||
|
||||
参考: <https://code.claude.com/docs/en/settings> + <https://code.claude.com/docs/en/permissions>
|
||||
|
||||
**永远不写**:
|
||||
- ❌ `~/.claude/memory/athena-*.list` 类私有文件
|
||||
- ❌ 项目特定的命令到全局 (路径泛化检查)
|
||||
|
||||
**永远不自动写**:
|
||||
- ❌ 不监控命令频次自动建议 (隐私决定)
|
||||
- ✅ 仅在用户**显式调用**本 skill 时写
|
||||
|
||||
## 子命令
|
||||
|
||||
### `/athena:remember-allow <pattern>`
|
||||
|
||||
参数: 标准 CC permission pattern, 如 `Bash(pnpm test:*)`.
|
||||
|
||||
```javascript
|
||||
const fs = require('fs');
|
||||
const os = require('os');
|
||||
const path = require('path');
|
||||
|
||||
const settingsPath = path.join(os.homedir(), '.claude/settings.json');
|
||||
let s = {};
|
||||
try { s = JSON.parse(fs.readFileSync(settingsPath,'utf8')); } catch(e) {}
|
||||
s.permissions = s.permissions || {};
|
||||
s.permissions.allow = s.permissions.allow || [];
|
||||
|
||||
const pattern = process.argv[2];
|
||||
|
||||
// 路径泛化检查: 拒绝含项目特定路径的 pattern
|
||||
if (/\/Users\/|\/home\/[^*/]|C:\\Users\\/.test(pattern) || /[\w-]+\.(com|cn|net)\//.test(pattern)) {
|
||||
console.error('❌ 拒绝: pattern 含项目/用户特定路径, 不应写入全局');
|
||||
console.error(' 如果只想本项目用, 写到 .claude/settings.local.json');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (s.permissions.allow.includes(pattern)) {
|
||||
console.log('✓ 已存在, 无需添加');
|
||||
process.exit(0);
|
||||
}
|
||||
s.permissions.allow.push(pattern);
|
||||
fs.writeFileSync(settingsPath, JSON.stringify(s,null,2));
|
||||
console.log(`✓ 已加入全局 allow: ${pattern}`);
|
||||
console.log(` 位置: ${settingsPath}`);
|
||||
```
|
||||
|
||||
### `/athena:remember-deny <pattern>`
|
||||
|
||||
同 `remember-allow`, 写入 `permissions.deny[]`. 但**不做路径泛化拒绝**——deny 越严格越好。
|
||||
|
||||
### `/athena:remember-tool <key>=<value>`
|
||||
|
||||
写入 `~/.claude/CLAUDE.md` 的 "## Tool Preferences" 段 (无则创建):
|
||||
|
||||
```
|
||||
## Tool Preferences
|
||||
|
||||
- 包管理器: pnpm
|
||||
- 测试运行器: vitest
|
||||
- Linter: biome
|
||||
- 默认提交者: --signoff
|
||||
```
|
||||
|
||||
### `/athena:show-preferences`
|
||||
|
||||
```bash
|
||||
echo "=== Global allow (~/.claude/settings.json) ==="
|
||||
node -e 'console.log((JSON.parse(require("fs").readFileSync(require("os").homedir()+"/.claude/settings.json")).permissions?.allow || []).join("\n"))'
|
||||
|
||||
echo ""
|
||||
echo "=== Global deny ==="
|
||||
node -e 'console.log((JSON.parse(require("fs").readFileSync(require("os").homedir()+"/.claude/settings.json")).permissions?.deny || []).join("\n"))'
|
||||
|
||||
echo ""
|
||||
echo "=== Tool preferences (~/.claude/CLAUDE.md) ==="
|
||||
sed -n '/^## Tool Preferences/,/^## /p' ~/.claude/CLAUDE.md 2>/dev/null | head -20
|
||||
```
|
||||
|
||||
### `/athena:forget <pattern>`
|
||||
|
||||
从全局 allow/deny 中删除指定 pattern. 显示删除前后 diff.
|
||||
|
||||
```javascript
|
||||
const fs = require('fs');
|
||||
const os = require('os');
|
||||
const path = require('path');
|
||||
|
||||
const settingsPath = path.join(os.homedir(), '.claude/settings.json');
|
||||
const s = JSON.parse(fs.readFileSync(settingsPath,'utf8'));
|
||||
const pattern = process.argv[2];
|
||||
|
||||
const beforeA = s.permissions?.allow?.length || 0;
|
||||
const beforeD = s.permissions?.deny?.length || 0;
|
||||
if (s.permissions?.allow) s.permissions.allow = s.permissions.allow.filter(p => p !== pattern);
|
||||
if (s.permissions?.deny) s.permissions.deny = s.permissions.deny.filter(p => p !== pattern);
|
||||
const afterA = s.permissions?.allow?.length || 0;
|
||||
const afterD = s.permissions?.deny?.length || 0;
|
||||
|
||||
fs.writeFileSync(settingsPath, JSON.stringify(s,null,2));
|
||||
console.log(`allow: ${beforeA} → ${afterA}`);
|
||||
console.log(`deny: ${beforeD} → ${afterD}`);
|
||||
```
|
||||
|
||||
## 安全约束
|
||||
|
||||
1. **不修改 managed-settings.json** (企业管理层)
|
||||
2. **不修改项目 .claude/settings.json** (那是项目级, 用 settings.local.json 兜底)
|
||||
3. **永远显示 diff** 在写入前——用户能看到改了什么
|
||||
4. **永远说明落点** ("位置: ~/.claude/settings.json") 而不是含糊"已记住"
|
||||
|
||||
## Boundary
|
||||
|
||||
- ✅ 操作 `~/.claude/settings.json`
|
||||
- ✅ 追加到 `~/.claude/CLAUDE.md` 末尾 "## Tool Preferences" 段
|
||||
- ❌ 不读不写 `~/.claude/projects/` (那是 session memory, claude-mem 管)
|
||||
- ❌ 不读不写 `~/.claude/memory/`
|
||||
- ❌ 不创建 `~/.claude/athena-*.txt` 类文件
|
||||
@@ -0,0 +1,15 @@
|
||||
---
|
||||
name: athena-review
|
||||
description: >
|
||||
单独运行质量审查, 不走完整开发流程。
|
||||
effort: xhigh
|
||||
disable-model-invocation: true
|
||||
argument-hint: "[focus area]"
|
||||
---
|
||||
|
||||
# /athena-review
|
||||
|
||||
有 `.ai_state/_index.md` → 按当前 Path 深度触发 pace skill 审查阶段。
|
||||
无 → 快速审查: `/review`。
|
||||
|
||||
焦点: $ARGUMENTS
|
||||
@@ -0,0 +1,154 @@
|
||||
---
|
||||
name: athena-setup
|
||||
description: >
|
||||
Athena 全局安装。检查 CC 版本、装插件、配置 Codex、部署 hooks、推 claude-mem。首次使用运行一次。
|
||||
effort: xhigh
|
||||
disable-model-invocation: true
|
||||
---
|
||||
|
||||
# /athena-setup (v9.6)
|
||||
|
||||
## 设计哲学 (启动时告诉用户)
|
||||
|
||||
```
|
||||
v9.6 原则:
|
||||
- permissions.deny 仅保留 "Bash(rm -rf *)" + 机密文件 (sandbox 是正道)
|
||||
参考: https://claude.com/blog/beyond-permission-prompts-making-claude-code-more-secure-and-autonomous
|
||||
- 全局 lessons 已删, 跨项目记忆推 claude-mem (默认装)
|
||||
- /goal /batch /background CC 原生, 不再造状态机驱动器
|
||||
- 不维护插件推荐列表, 实时查 marketplace
|
||||
```
|
||||
|
||||
## Step 0: CC 版本检查
|
||||
|
||||
参考: <https://code.claude.com/docs/en/changelog>
|
||||
|
||||
```bash
|
||||
claude --version
|
||||
```
|
||||
|
||||
要求 ≥ v2.1.121 (worktree isolation + updatedToolOutput 全工具). v2.1.139+ 解锁 /goal /background.
|
||||
低于 → `claude update` 或 `npm install -g @anthropic-ai/claude-code@latest`.
|
||||
|
||||
把检测到的版本写入 `.ai_state/_index.md.platform_features.cc_version`. 解析后置 goal_supported/batch_supported/background_supported.
|
||||
|
||||
## Step 1: 插件安装 (按 PACE 节点 best-fit, 非强依赖)
|
||||
|
||||
每装一个问自己: "这个真的会用吗?" 多余 = context 浪费。
|
||||
|
||||
### 工程主线 (强烈推荐)
|
||||
|
||||
```
|
||||
/plugin marketplace add obra/superpowers-marketplace
|
||||
/plugin install superpowers@superpowers-marketplace
|
||||
|
||||
/plugin marketplace add openai/codex-plugin-cc
|
||||
/plugin install codex@openai-codex
|
||||
```
|
||||
|
||||
### 跨项目记忆 (v9.6 默认推 claude-mem)
|
||||
|
||||
claude-mem 89k stars, Apache-2.0, 自动捕获 tool use + 压缩摘要 + 注入下次 session.
|
||||
参考: <https://github.com/thedotmack/claude-mem>
|
||||
|
||||
```
|
||||
# 检测是否已装
|
||||
if /plugin list | grep -iq claude-mem; then
|
||||
echo "claude-mem ✓ 已装"
|
||||
else
|
||||
echo "推荐安装 claude-mem (跨 session 记忆):"
|
||||
echo " /plugin marketplace add <claude-mem 所在 marketplace>"
|
||||
echo " /plugin install claude-mem"
|
||||
echo " (如不在 marketplace, 从 github.com/thedotmack/claude-mem 下载)"
|
||||
fi
|
||||
```
|
||||
|
||||
未装也行——Athena 仍可用, `_index.md.platform_features.cross_session_memory` 标 "none".
|
||||
|
||||
### PACE 节点 best-fit (按需, 实时查 marketplace)
|
||||
|
||||
athena-setup **不写死推荐列表**, 改用 `/plugin browse` + 关键词查询:
|
||||
|
||||
| 路径 | 关键词 (用 /plugin browse 查) |
|
||||
|------|------------------------------|
|
||||
| Bugfix | debugger / bug-fix / metronome |
|
||||
| Feature 实现 | superpowers / dev-workflows / context7 |
|
||||
| Feature 审查 | codex-plugin-cc / agent-peer-review / local-review |
|
||||
| Refactor | TypeScript LSP / Rust LSP / ast-grep |
|
||||
| Refactor 审查 | codex-plugin-cc / AgentLint |
|
||||
| System | shipyard (IaC) / mcp-builder |
|
||||
| 跨节点 | context-mode (大输出 sandbox 化) |
|
||||
|
||||
### 官方插件 (claude-plugins-official, 已在 settings.json 启用)
|
||||
|
||||
```
|
||||
/plugin install feature-dev
|
||||
/plugin install code-review
|
||||
/plugin install commit-commands
|
||||
```
|
||||
|
||||
`/reload-plugins` 后生效.
|
||||
|
||||
## Step 2: context7 (skill 模式)
|
||||
|
||||
```bash
|
||||
npx ctx7 setup --claude
|
||||
```
|
||||
|
||||
无需全局装 ctx7 CLI, npx 按需拉. OAuth 登录后 `~/.claude/skills/` 自动生成 context7 skill.
|
||||
触发方式: "use context7" 或 "use context7 with /owner/repo for <query>".
|
||||
|
||||
## Step 3: Codex 配置
|
||||
|
||||
```
|
||||
/codex:setup
|
||||
```
|
||||
|
||||
未登录 → `!codex login`.
|
||||
|
||||
**不要启用 review gate** (`/codex:setup --enable-review-gate`) — 会 Claude ↔ Codex 死循环.
|
||||
|
||||
settings.json codex 权限 (已在 v9.6 默认 settings 含):
|
||||
|
||||
```json
|
||||
"Bash(codex:*)",
|
||||
"Bash(node:*)"
|
||||
```
|
||||
|
||||
Codex 独立配置 `~/.codex/config.toml` (见本仓库 cx/.codex/config.toml).
|
||||
|
||||
## Step 4: Hooks 安装
|
||||
|
||||
```bash
|
||||
mkdir -p ~/.claude/hooks
|
||||
cp .claude/hooks/*.cjs ~/.claude/hooks/
|
||||
chmod +x ~/.claude/hooks/*.cjs
|
||||
```
|
||||
|
||||
v9.6 hook 列表 (8 个):
|
||||
- session-start · pre-bash-guard · index-updater · subagent-retry
|
||||
- delivery-gate · pace-continuator
|
||||
- permission-retry · compact-snapshot · compact-restore · state-audit
|
||||
|
||||
## Step 5: 验证
|
||||
|
||||
```bash
|
||||
claude --version # ≥ 2.1.121, 最佳 ≥ 2.1.139
|
||||
/plugin list # superpowers / codex / claude-mem (推荐)
|
||||
npx ctx7 library express # context7 CLI 通
|
||||
/codex:setup # Codex ready
|
||||
ls ~/.claude/hooks/*.cjs | wc -l # 应 = 10
|
||||
cat .ai_state/_index.md | head -5 # 含 schema_version: "9.6"
|
||||
```
|
||||
|
||||
通过 → "安装完成。每个新项目 `/athena-init`, 然后 `/athena-dev`."
|
||||
|
||||
## 为什么这样设计 (用户视角)
|
||||
|
||||
| v9.5 有 | v9.6 变化原因 |
|
||||
|---|---|
|
||||
| 18 → 0 条 deny | sandbox 是正道, 但 v9.6 再加回 "Bash(rm -rf *)" 兜底 |
|
||||
| 全局 lessons 系统 | 删, 委外给 claude-mem (89k stars) |
|
||||
| 手写 spawn_agent 协议 | 部分场景接入 /batch (CC v2.1.121+ 原生) |
|
||||
| delivery-gate 自造 VERDICT 解析 | 接入 /goal evaluator (v2.1.139+), 旧版本兜底 |
|
||||
| 写死的插件推荐列表 | 改为 /plugin browse 实时查 |
|
||||
@@ -0,0 +1,119 @@
|
||||
---
|
||||
name: athena-status
|
||||
description: >
|
||||
Athena 状态面板。显示当前 PACE 状态、Hook 触发计数、claude-mem 状态、plugin 健康。
|
||||
effort: medium
|
||||
disable-model-invocation: true
|
||||
---
|
||||
|
||||
# /athena-status (v9.6)
|
||||
|
||||
实时聚合 Athena 运行状态。无副作用, 只读 (铁律 8 索引先行)。
|
||||
|
||||
## 收集顺序
|
||||
|
||||
### 1. PACE 状态 (从 _index.md frontmatter)
|
||||
```bash
|
||||
# 用 node 解析 YAML frontmatter
|
||||
node -e '
|
||||
const fs = require("fs");
|
||||
const content = fs.readFileSync(".ai_state/_index.md","utf8");
|
||||
const fm = content.match(/^---\n([\s\S]*?)\n---/);
|
||||
if (!fm) { console.log("_index.md frontmatter 缺失"); process.exit(1); }
|
||||
// 简化 YAML 解析: 仅读关键字段, 不引入 yaml 依赖
|
||||
const get = (k) => { const m = fm[1].match(new RegExp("^ " + k + ": (.*)$","m")); return m ? m[1].replace(/^["\x27]|["\x27]$/g,"") : ""; };
|
||||
console.log("Path: ", get("path"));
|
||||
console.log("Stage: ", get("stage"));
|
||||
console.log("Sprint: ", get("sprint"));
|
||||
console.log("Goal: ", get("active_goal"));
|
||||
'
|
||||
```
|
||||
|
||||
### 2. Sprint 进度 (counts 段)
|
||||
```bash
|
||||
grep -E "^ (tasks_pending|tasks_done|tasks_blocked):" .ai_state/_index.md
|
||||
```
|
||||
|
||||
### 3. Hook 触发记录 (最近 50 条)
|
||||
```bash
|
||||
tail -50 .ai_state/hook-trace.jsonl 2>/dev/null | \
|
||||
jq -r '.hook' 2>/dev/null | sort | uniq -c | sort -rn
|
||||
```
|
||||
|
||||
按 hook 名分组统计:
|
||||
- session-start: 注入次数
|
||||
- index-updater: 更新次数
|
||||
- pre-bash-guard: 拦截次数
|
||||
- subagent-retry: 注入次数
|
||||
- delivery-gate: 触发 (阻断/soft warn/pass)
|
||||
- pace-continuator: 写入 next/proposals 次数
|
||||
- compact-* : 注入次数
|
||||
- state-audit: 漂移告警次数
|
||||
|
||||
### 4. Lessons 统计 (仅项目级)
|
||||
```bash
|
||||
grep "^ lessons_lines:" .ai_state/_index.md
|
||||
grep "^## " .ai_state/details/lessons.md 2>/dev/null | tail -1
|
||||
echo "[全局已删除, 跨项目记忆装 claude-mem]"
|
||||
```
|
||||
|
||||
### 5. Platform 健康
|
||||
```bash
|
||||
grep "^ cc_version:" .ai_state/_index.md
|
||||
grep "^ cx_version:" .ai_state/_index.md
|
||||
grep -E "^ (goal|batch|background)_supported:" .ai_state/_index.md
|
||||
grep "^ cross_session_memory:" .ai_state/_index.md
|
||||
```
|
||||
|
||||
### 6. Plugin 状态
|
||||
- codex: `which codex || codex --version 2>&1 | head -1`
|
||||
- superpowers: `ls ~/.claude/plugins/cache/superpowers/ 2>/dev/null`
|
||||
- claude-mem: `ls ~/.claude/plugins/ 2>/dev/null | grep -i claude-mem`
|
||||
- context7: `npx ctx7 --version 2>&1 | head -1`
|
||||
|
||||
## 输出格式
|
||||
|
||||
```
|
||||
=== VibeCoding Athena v9.6 Status ===
|
||||
|
||||
[PACE] Path: Feature Stage: impl Sprint: 3
|
||||
Goal: tasks all checked + npm test exits 0
|
||||
|
||||
[Tasks] 3 done · 2 pending · 0 blocked
|
||||
Latest: details/progress.md#L46 (Task 5 done)
|
||||
|
||||
[Hooks] 最近 50 次触发:
|
||||
session-start ✓ 5 注入
|
||||
index-updater ✓ 28 更新
|
||||
delivery-gate ✓ 12 (阻断 2, soft 1)
|
||||
pre-bash-guard ✓ 0 拦截
|
||||
pace-continuator ✓ 5 (next: 5, proposals: 2)
|
||||
subagent-retry ⚠ 3 注入
|
||||
|
||||
[Lessons] 项目: 4 条 (最新: "JWT refresh secret 分离")
|
||||
[全局已删除, 跨项目记忆装 claude-mem]
|
||||
|
||||
[Platform]
|
||||
CC version ✓ 2.1.140
|
||||
CX version ✓ 0.130
|
||||
/goal ✓ supported
|
||||
/batch ✓ supported
|
||||
/background ✓ supported
|
||||
Session memory ✓ active
|
||||
Cross-session ✓ claude-mem
|
||||
|
||||
[Plugins]
|
||||
codex@1.0.4 ✓ ready
|
||||
superpowers@5.x ✓ loaded
|
||||
claude-mem ✓ active
|
||||
context7 ✓ available
|
||||
|
||||
[Commands]
|
||||
test_cmd: npm test
|
||||
build_cmd: npm run build
|
||||
lint_cmd: npm run lint
|
||||
```
|
||||
|
||||
## 实现方式
|
||||
|
||||
纯 Bash + node + jq. 失败的项显示 "✗ 不可用". 不写文件, 不改状态。
|
||||
@@ -1,38 +0,0 @@
|
||||
---
|
||||
name: brainstorm
|
||||
description: R₀ 苏格拉底式需求精炼 — 写代码前激活
|
||||
---
|
||||
# Brainstorm (R₀ 需求精炼)
|
||||
|
||||
Path B+ 任务在写代码前**必须**激活本 skill。
|
||||
|
||||
## 流程
|
||||
|
||||
### 0. 自主调研 (提问前)
|
||||
- 搜索 (Grep/Glob, 或 augment-context-engine 如已安装)项目中与需求相关的现有代码
|
||||
- 读 .ai_state/knowledge.md + lessons.md 查相关经验
|
||||
- 读 .ai_state/conventions.md 查编码规范
|
||||
- 如果 Codex 可用: 用 codex-comm skill 问 Codex "这类需求常见陷阱?"
|
||||
→ 基于调研结果准备更有深度的问题
|
||||
|
||||
### 1. 访谈 (苏格拉底式, 基于调研的针对性问题)
|
||||
- **一次只问一个问题**, 不堆叠
|
||||
- 优先选择题, 开放题为辅
|
||||
- 覆盖: 用户目标、使用场景、边界、非功能需求
|
||||
- YAGNI 原则: 主动砍不必要的功能
|
||||
|
||||
### 2. 提出 2-3 个方案
|
||||
- 每方案: 架构概述 + 优劣 + 复杂度预估
|
||||
- context7 查依赖库文档验证可行性
|
||||
- 标注推荐方案及理由
|
||||
|
||||
### 3. 分段呈现设计
|
||||
- 每段 ≤ 200 字, 用户确认后再下一段
|
||||
|
||||
### 4. 确认输出
|
||||
- 写入 .ai_state/design.md (用模板格式: MUST/SHOULD/COULD + 验收标准)
|
||||
- cunzhi `DESIGN_READY` 确认
|
||||
- 进入 plan-first skill
|
||||
|
||||
## 铁律
|
||||
- 设计未确认前**不写任何代码**
|
||||
@@ -1,23 +0,0 @@
|
||||
---
|
||||
name: code-review
|
||||
description: T 阶段代码审查 — 含 LLM-as-Judge 模式
|
||||
---
|
||||
# Code Review (T 阶段)
|
||||
|
||||
## 审查清单
|
||||
1. **Spec 合规**: 每个 MUST 需求是否实现? (对照 design.md)
|
||||
2. **TDD**: 每个源码文件有对应测试?
|
||||
3. **边界**: 空输入/超长/并发/错误路径 是否处理?
|
||||
4. **安全**: 无硬编码密钥, 无 SQL 注入, 输入已验证
|
||||
5. **可读**: 函数 < 50 行, 注释说明 WHY 不是 WHAT, 严格类型
|
||||
6. **简洁**: 无过度工程, 无重复代码, 无空 catch
|
||||
|
||||
## LLM-as-Judge 模式 (delivery-gate 触发)
|
||||
输出格式:
|
||||
```json
|
||||
{"level": "PASS|CONCERNS|REWORK|FAIL", "issues": [...], "summary": "..."}
|
||||
```
|
||||
- FAIL: 未完成任务 / 硬编码密钥 / 缺 quality.md
|
||||
- REWORK: 源码无测试 (TDD 违规)
|
||||
- CONCERNS: 非关键文件无测试 (1-2 个警告)
|
||||
- PASS: 全部通过
|
||||
@@ -1,45 +0,0 @@
|
||||
---
|
||||
name: codex-comm
|
||||
description: Codex CLI 通信层 — CC↔Codex 基础设施
|
||||
---
|
||||
# Codex Communication (通信层)
|
||||
|
||||
## 核心脚本
|
||||
`.claude/skills/codex-comm/scripts/codex-bridge.cjs`
|
||||
|
||||
## 功能
|
||||
1. **检测可用性**: `which codex` + health check (2s 超时)
|
||||
2. **双模式**: exec (单轮) / serve (多轮 HTTP API)
|
||||
3. **Session Resume**: `resumeSession(sessionId, prompt)` — 多轮对话保持上下文
|
||||
4. **UUID 生成**: `generateReviewId()` — 并发安全, 防冲突
|
||||
5. **敏感过滤**: .env / 密钥 → [REDACTED]
|
||||
6. **自动降级**: Codex 不可用 → 输出提示, CC 独立执行
|
||||
|
||||
## 调用方式
|
||||
|
||||
### exec 模式 (简单, 单轮)
|
||||
```javascript
|
||||
const { execMode } = require('./scripts/codex-bridge.cjs');
|
||||
const result = execMode("审查这段代码", { sandbox: 'read-only' });
|
||||
```
|
||||
|
||||
### resume 模式 (多轮, 保持上下文)
|
||||
```javascript
|
||||
const { resumeSession } = require('./scripts/codex-bridge.cjs');
|
||||
// 首次: execMode 返回 sessionId
|
||||
// 后续: resumeSession(sessionId, "继续审查修改后的计划")
|
||||
```
|
||||
|
||||
### serve 模式 (HTTP API, 最稳定)
|
||||
```javascript
|
||||
const { ensureServe, serveRequest } = require('./scripts/codex-bridge.cjs');
|
||||
ensureServe(); // 自动启动如未运行
|
||||
```
|
||||
|
||||
## Fallback
|
||||
每个调用 Codex 的点都必须有:
|
||||
```
|
||||
如果 Codex 不可用:
|
||||
输出 "[VibeCoding] Codex 不可用, CC 独立执行"
|
||||
{CC 独立完成的替代逻辑}
|
||||
```
|
||||
@@ -1,109 +0,0 @@
|
||||
// VibeCoding v9.2.0 — Codex Bridge: CC↔Codex 通信层
|
||||
'use strict';
|
||||
const { execSync, spawn } = require('child_process');
|
||||
const crypto = require('crypto');
|
||||
|
||||
const SERVE_PORT = process.env.CODEX_SERVE_PORT || '4096';
|
||||
const SERVE_URL = `http://localhost:${SERVE_PORT}`;
|
||||
const DEFAULT_MODEL = 'gpt-5.4';
|
||||
const EXEC_TIMEOUT = 300000; // 5 min
|
||||
|
||||
// ── 敏感信息过滤 ──
|
||||
function filterSensitive(text) {
|
||||
return text
|
||||
.replace(/(?:password|secret|api[_-]?key|token|credential)\s*[:=]\s*['"][^'"]{4,}['"]/gi, '[REDACTED]')
|
||||
.replace(/(?:-----BEGIN (?:RSA |EC )?PRIVATE KEY-----[\s\S]*?-----END (?:RSA |EC )?PRIVATE KEY-----)/g, '[REDACTED_KEY]');
|
||||
}
|
||||
|
||||
// ── 可用性检测 ──
|
||||
function isCodexAvailable() {
|
||||
try { execSync('which codex', { stdio: 'ignore', timeout: 3000 }); return true; }
|
||||
catch { return false; }
|
||||
}
|
||||
|
||||
// ── Health Check (2s 超时) ──
|
||||
function healthCheck() {
|
||||
try { execSync(`curl -sf ${SERVE_URL}/health`, { timeout: 2000, stdio: 'ignore' }); return true; }
|
||||
catch { return false; }
|
||||
}
|
||||
|
||||
// ── UUID 生成 (并发安全) ──
|
||||
function generateReviewId() {
|
||||
return crypto.randomUUID().slice(0, 8);
|
||||
}
|
||||
|
||||
// ── exec 模式 (单轮) ──
|
||||
function execMode(prompt, opts = {}) {
|
||||
if (!isCodexAvailable()) return { ok: false, error: 'codex_not_available' };
|
||||
const safePrompt = filterSensitive(prompt);
|
||||
const model = opts.model || DEFAULT_MODEL;
|
||||
const sandbox = opts.sandbox || 'read-only';
|
||||
try {
|
||||
// 用 execFileSync 避免 shell 注入 (参数直接传递, 不经过 shell 解析)
|
||||
const result = require('child_process').execFileSync(
|
||||
'codex', ['exec', '--sandbox', sandbox, '-m', model, safePrompt],
|
||||
{ encoding: 'utf8', timeout: opts.timeout || EXEC_TIMEOUT }
|
||||
);
|
||||
const sidMatch = result.match(/session[_\s]*(?:id)?[:\s]*([0-9a-f-]{8,})/i);
|
||||
return { ok: true, output: result, sessionId: sidMatch ? sidMatch[1] : null };
|
||||
} catch (e) {
|
||||
return { ok: false, error: e.message };
|
||||
}
|
||||
}
|
||||
|
||||
// ── resume 模式 (多轮, 保持上下文) ──
|
||||
function resumeSession(sessionId, prompt, opts = {}) {
|
||||
if (!isCodexAvailable()) return { ok: false, error: 'codex_not_available' };
|
||||
const safePrompt = filterSensitive(prompt);
|
||||
const model = opts.model || DEFAULT_MODEL;
|
||||
try {
|
||||
const result = require('child_process').execFileSync(
|
||||
'codex', ['exec', 'resume', sessionId, '--sandbox', 'read-only', '-m', model, safePrompt],
|
||||
{ encoding: 'utf8', timeout: opts.timeout || EXEC_TIMEOUT }
|
||||
);
|
||||
return { ok: true, output: result, sessionId };
|
||||
} catch (e) {
|
||||
console.error(`[codex-bridge] resume failed, falling back to new exec: ${e.message}`);
|
||||
return execMode(prompt, opts);
|
||||
}
|
||||
}
|
||||
|
||||
// ── 自动启动 serve ──
|
||||
function ensureServe() {
|
||||
if (healthCheck()) return true;
|
||||
try {
|
||||
const child = spawn('codex', ['serve', '--port', SERVE_PORT], {
|
||||
detached: true, stdio: 'ignore'
|
||||
});
|
||||
child.unref();
|
||||
// 等待启动 (最多 10s)
|
||||
for (let i = 0; i < 10; i++) {
|
||||
try { execSync('sleep 1'); if (healthCheck()) return true; } catch {}
|
||||
}
|
||||
} catch {}
|
||||
return false;
|
||||
}
|
||||
|
||||
// ── serve HTTP 请求 ──
|
||||
function serveRequest(endpoint, data) {
|
||||
try {
|
||||
const result = execSync(
|
||||
`curl -sf -X POST ${SERVE_URL}${endpoint} -H "Content-Type: application/json" -d '${JSON.stringify(data)}'`,
|
||||
{ encoding: 'utf8', timeout: 30000 }
|
||||
);
|
||||
return { ok: true, data: JSON.parse(result) };
|
||||
} catch (e) {
|
||||
return { ok: false, error: e.message };
|
||||
}
|
||||
}
|
||||
|
||||
// ── 获取 session 历史消息 ──
|
||||
function getSessionMessages(sessionId) {
|
||||
return serveRequest(`/sessions/${sessionId}/messages`, {});
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
isCodexAvailable, healthCheck, generateReviewId,
|
||||
execMode, resumeSession, ensureServe, serveRequest,
|
||||
getSessionMessages, filterSensitive
|
||||
};
|
||||
@@ -1,64 +0,0 @@
|
||||
---
|
||||
name: codex-review
|
||||
description: 对抗式计划审查 — P 阶段 CC↔Codex 多轮审查
|
||||
---
|
||||
# Codex Review (对抗式审查)
|
||||
|
||||
## 触发时机
|
||||
P 阶段 plan.md 生成后, 如果 Codex 可用。
|
||||
|
||||
## 初始化
|
||||
```
|
||||
REVIEW_ID = 生成一个 8 位随机 ID (用 Bash: uuidgen | cut -c1-8)
|
||||
```
|
||||
|
||||
## 审查模板选择
|
||||
根据 design.md 关键词自动选择:
|
||||
- 包含 API/endpoint/route → templates/api-review.md
|
||||
- 包含 component/CSS/UI → templates/frontend-review.md
|
||||
- 包含 deploy/docker/k8s/terraform → templates/infra-review.md
|
||||
- 否则 → templates/general-review.md
|
||||
|
||||
## 对抗循环
|
||||
|
||||
### Round 1 (新 exec, 捕获 SESSION_ID):
|
||||
CC → Codex:
|
||||
"审查以下计划. 严格找出漏洞/模糊/不可执行的部分.
|
||||
{plan.md 全文}
|
||||
{选定的 review template}
|
||||
结果以 VERDICT: APPROVED 或 VERDICT: REVISE 结尾."
|
||||
Codex → 质疑列表 + VERDICT
|
||||
如果 APPROVED → 结束, 记录到 reviews/review-{REVIEW_ID}.md
|
||||
CC: 逐条评估 (采纳修改 plan / 驳回并说明理由)
|
||||
|
||||
### Round 2+ (resume SESSION_ID, 保持上下文):
|
||||
CC → Codex (codex exec resume SESSION_ID):
|
||||
"修改后的计划差异: {diff}. 继续审查.
|
||||
VERDICT: APPROVED 或 VERDICT: REVISE."
|
||||
Codex → 继续质疑 + VERDICT
|
||||
...
|
||||
|
||||
## 终止条件 (满足任一)
|
||||
a) Codex 返回 VERDICT: APPROVED
|
||||
b) 连续 2 轮全部驳回 (记录分歧, cunzhi 让用户决策)
|
||||
c) 超过 5 轮 (强制停止, 输出分歧摘要)
|
||||
|
||||
## 审查结果持久化
|
||||
每次审查写入 `reviews/review-{REVIEW_ID}.md`:
|
||||
```markdown
|
||||
# Review {REVIEW_ID}
|
||||
Date: {timestamp}
|
||||
Rounds: {n}
|
||||
Final Verdict: {APPROVED|STALEMATE|TIMEOUT}
|
||||
## 采纳的修改
|
||||
- ...
|
||||
## 驳回的质疑
|
||||
- ...
|
||||
## 遗留分歧
|
||||
- ...
|
||||
```
|
||||
|
||||
## Fallback
|
||||
如果 Codex 不可用:
|
||||
CC 用 validator agent 做 Plan Review:
|
||||
"作为严格审查者, 这个计划有哪些漏洞/模糊/不可执行的部分?"
|
||||
@@ -1,11 +0,0 @@
|
||||
# API Review Template
|
||||
|
||||
审查重点:
|
||||
1. **接口一致性**: RESTful 约定? 命名统一?
|
||||
2. **幂等性**: PUT/DELETE 是否幂等?
|
||||
3. **版本控制**: URL 版本 (/v1/) 或 Header 版本?
|
||||
4. **错误码**: HTTP 状态码 + 业务错误码 是否定义?
|
||||
5. **认证/授权**: 每个端点的权限要求?
|
||||
6. **速率限制**: 是否有限流策略?
|
||||
7. **输入验证**: 请求体 schema 是否严格?
|
||||
8. **响应格式**: 分页/排序/过滤 是否一致?
|
||||
@@ -1,10 +0,0 @@
|
||||
# Frontend Review Template
|
||||
|
||||
审查重点:
|
||||
1. **可访问性**: ARIA 标签? 键盘导航? 色彩对比?
|
||||
2. **响应式**: 移动端/平板/桌面 断点覆盖?
|
||||
3. **XSS 防护**: 用户输入是否转义? dangerouslySetInnerHTML?
|
||||
4. **性能**: 大列表虚拟滚动? 图片懒加载? 包体积?
|
||||
5. **状态管理**: 数据流清晰? 避免 prop drilling?
|
||||
6. **错误边界**: React ErrorBoundary? 加载/空/错误状态?
|
||||
7. **国际化**: 硬编码文案? 日期/数字格式?
|
||||
@@ -1,10 +0,0 @@
|
||||
# General Review Template
|
||||
|
||||
审查重点:
|
||||
1. **需求覆盖**: 每个 MUST 需求是否有对应任务?
|
||||
2. **任务粒度**: 每个任务是否可在 1-2 小时内完成?
|
||||
3. **依赖拓扑**: 任务依赖是否有环? 关键路径是什么?
|
||||
4. **风险识别**: 哪些任务最可能出问题? 有 plan B?
|
||||
5. **测试策略**: 单元/集成/E2E 比例合理?
|
||||
6. **模糊检测**: 有没有"优化""改进"等模糊词?
|
||||
7. **遗漏检测**: 错误处理/日志/监控/文档 是否涵盖?
|
||||
@@ -1,10 +0,0 @@
|
||||
# Infrastructure Review Template
|
||||
|
||||
审查重点:
|
||||
1. **可用性**: 单点故障? 副本数? 健康检查?
|
||||
2. **备份/恢复**: 数据备份策略? RTO/RPO?
|
||||
3. **回滚计划**: 部署失败如何回滚? 数据库迁移可逆?
|
||||
4. **监控/告警**: 关键指标有监控? 告警阈值合理?
|
||||
5. **安全**: 网络隔离? 密钥轮换? 最小权限?
|
||||
6. **成本**: 资源配置合理? 有自动伸缩?
|
||||
7. **IaC**: 基础设施代码化? 可重复部署?
|
||||
@@ -0,0 +1,47 @@
|
||||
---
|
||||
name: compound
|
||||
description: >
|
||||
Sprint Quality Gate 通过后触发。提炼本 Sprint 学到的为结构化条目追加 lessons.md。
|
||||
effort: xhigh
|
||||
---
|
||||
|
||||
# /compound — 经验沉淀
|
||||
|
||||
Gate PASS 后触发 (或用户显式 `/compound`).
|
||||
|
||||
## 输入
|
||||
|
||||
- `.ai_state/details/reviews/sprint-N.md` (审查报告)
|
||||
- `git diff` (本 Sprint 所有变更)
|
||||
- `.ai_state/details/tasks-current.md` (完成项)
|
||||
- `.ai_state/details/design.md` (设计 vs 实际实现对比)
|
||||
|
||||
## 提炼方法
|
||||
|
||||
从以上输入找**真实发现**, 不写仪式性废话。每 Sprint 0-N 条不强制。
|
||||
|
||||
- **Pattern**: 什么解法有效且可复用? (带代码行号 / 文件路径)
|
||||
- **Pitfall**: 什么坑踩过? 未来同类场景要提前避开
|
||||
- **Constraint**: 发现的新约束 (依赖版本、环境、性能边界、API 限制)
|
||||
|
||||
没发现可提炼的 → 不写, 直接告知用户 "no new lesson". **不允许编造**.
|
||||
|
||||
## 输出 (项目级)
|
||||
|
||||
追加到 `.ai_state/details/lessons.md` (append-only, 不修改历史).
|
||||
|
||||
格式见 `pace/templates/details/lessons.md`. **Athena 不做跨项目知识管理**——需要的话装 claude-mem 或 superpowers。
|
||||
|
||||
写入后同步更新 `_index.md`:
|
||||
- `counts.lessons_lines` += 新增条目数 (按 `## ` 头计)
|
||||
- `pointers.latest_lesson` = `details/lessons.md#L<新条目行号>`
|
||||
- `fingerprints.lessons_mtime` = 当前时间戳
|
||||
|
||||
## 自检
|
||||
|
||||
写入后读回验证:
|
||||
1. 条目有具体的文件/行号/版本引用 (不只是"要注意 X")
|
||||
2. 一句话标题能让下次 R₀ 快速扫描命中
|
||||
3. Pattern/Pitfall/Constraint 三段不强制全有, 但至少有一段
|
||||
|
||||
写完告知用户: "Lessons updated. Next sprint R₀ 会自动读取 (按 _index.md.pointers.latest_lesson 跳转)."
|
||||
@@ -1,15 +0,0 @@
|
||||
---
|
||||
name: context7
|
||||
description: 库文档按需拉取
|
||||
---
|
||||
# Context7 (库文档查询)
|
||||
|
||||
需要查询依赖库文档时:
|
||||
1. 优先: `npx ctx7 resolve {库名}` CLI 查询
|
||||
2. 备选: web search 查最新 API
|
||||
3. 缓存: 查过的 API 结论写入当前阶段笔记, 避免重复查询
|
||||
|
||||
## 触发时机
|
||||
- R 阶段: 验证技术方案可行性
|
||||
- E 阶段: 实现时遇到不确定的 API
|
||||
- T 阶段: 验证 API 用法正确性
|
||||
@@ -1,27 +0,0 @@
|
||||
---
|
||||
name: debugging
|
||||
description: 系统化调试 — 4阶段强制流程
|
||||
---
|
||||
# Systematic Debugging
|
||||
|
||||
遇到 bug 时**必须**按以下 4 阶段执行, 禁止跳步:
|
||||
|
||||
## 1. 复现 (Reproduce)
|
||||
- 写一个**最小**可复现用例
|
||||
- 记录: 输入、期望输出、实际输出
|
||||
- 如果无法复现 → 停, 收集更多信息
|
||||
|
||||
## 2. 定位 (Locate)
|
||||
- 从错误点向上追溯调用链
|
||||
- 用 console.log / debugger / 断点缩小范围
|
||||
- **不要猜**: 用证据定位, 不是直觉
|
||||
|
||||
## 3. 修复 (Fix)
|
||||
- 只改**根因**, 不改症状
|
||||
- 修复必须有对应测试覆盖
|
||||
- 禁止: `as any`, `try {} catch {}` 空吞, `// TODO: fix later`
|
||||
|
||||
## 4. 验证 (Verify)
|
||||
- 运行原始复现用例 → 通过
|
||||
- 运行全量测试 → 无回归
|
||||
- Reflexion: "这个 bug 的模式值得记录到 conventions.md 吗?"
|
||||
@@ -1,18 +0,0 @@
|
||||
---
|
||||
name: e2e
|
||||
description: 端到端测试 — Path C+ 或有 E2E 框架时
|
||||
---
|
||||
# E2E Testing
|
||||
|
||||
## 前置条件
|
||||
- 项目有 E2E 框架 (Playwright/Cypress/Selenium)
|
||||
- Path C+ 或用户显式要求
|
||||
|
||||
## 流程
|
||||
1. 基于 design.md 验收标准编写 E2E 用例
|
||||
2. 运行 E2E: `npx playwright test` 或项目配置的命令
|
||||
3. 失败 → 定位是测试问题还是实现问题
|
||||
4. 结果追加到 quality.md
|
||||
|
||||
## 如果没有 E2E 框架
|
||||
- 手动验证清单: 列出关键用户流程 + 验证步骤
|
||||
@@ -1,35 +0,0 @@
|
||||
---
|
||||
name: finish-branch
|
||||
description: V 阶段分支收尾 — kaizen 之后, 交付确认之前
|
||||
---
|
||||
# Finish Branch (分支收尾)
|
||||
|
||||
## 触发时机
|
||||
V 阶段 kaizen 完成后, cunzhi `DELIVERY_COMPLETE` 之前。
|
||||
|
||||
## 流程
|
||||
1. 展示选项给用户:
|
||||
a) **merge**: 合并到主分支
|
||||
b) **pr**: 创建 Pull Request
|
||||
c) **keep**: 保留分支不动
|
||||
d) **discard**: 确认后删除分支
|
||||
|
||||
2. 如果选 **pr**:
|
||||
- 自动生成 PR description:
|
||||
```
|
||||
## 标题: {design.md 第一行}
|
||||
## 摘要: {quality.md 总结}
|
||||
## 变更列表:
|
||||
{plan.md 中已完成的 Task 列表}
|
||||
## 测试: {quality.md 验证结果}
|
||||
```
|
||||
- 执行: `gh pr create` 或输出文本供用户复制
|
||||
|
||||
3. 如果选 **merge**:
|
||||
- 检查是否有冲突: `git merge --no-commit --no-ff main`
|
||||
- 无冲突 → merge + 删除分支
|
||||
- 有冲突 → 输出冲突文件, 用户决定
|
||||
|
||||
4. 如果选 **discard**:
|
||||
- cunzhi 确认 "确定丢弃分支 {branch_name}?"
|
||||
- 确认后: `git checkout main && git branch -D {branch_name}`
|
||||
@@ -1,23 +0,0 @@
|
||||
---
|
||||
name: kaizen
|
||||
description: V 阶段持续改进回顾
|
||||
---
|
||||
# Kaizen (V 阶段回顾)
|
||||
|
||||
## 流程
|
||||
1. 回顾本次开发:
|
||||
- 哪些做得好? (保持)
|
||||
- 哪些做得差? (改进)
|
||||
- 有没有反复出现的模式性错误?
|
||||
|
||||
2. 更新 .ai_state/lessons.md:
|
||||
- 新发现的技术陷阱
|
||||
- 有效的解决模式
|
||||
- 项目特有的约定
|
||||
|
||||
3. 更新 .ai_state/conventions.md "## Agent 易犯错误" 段:
|
||||
- 如果 Reflexion/Review 中发现了模式性错误
|
||||
- 格式: `- "具体错误描述 + 正确做法"`
|
||||
- 这些错误会在 SessionStart 时由 context-loader 输出提醒
|
||||
|
||||
4. 产出摘要给用户
|
||||
@@ -0,0 +1,304 @@
|
||||
---
|
||||
name: pace
|
||||
description: >
|
||||
工作流路由器。收到开发任务时触发。按任务复杂度路由到 6 条路径, 编排 CC 工具 + 增强节点。
|
||||
effort: xhigh
|
||||
---
|
||||
|
||||
# PACE — 6 路径路由器 (v9.6 Athena)
|
||||
|
||||
首先读 `.ai_state/_index.md` (铁律 8 索引先行)。有 stage → 从当前阶段继续。
|
||||
.ai_state/ 不存在 → 提示 `/athena-init`。
|
||||
|
||||
## 路由
|
||||
|
||||
```
|
||||
┌─ 1行/配置 ──────→ Hotfix
|
||||
修复 ────┤
|
||||
└─ 需要调试 ─────→ Bugfix
|
||||
|
||||
┌─ 需求清晰/小 ──→ Quick
|
||||
新建 ────┤ 需要设计 ─────→ Feature
|
||||
├─ 改现有结构 ───→ Refactor
|
||||
└─ 跨模块/服务 ──→ System
|
||||
```
|
||||
|
||||
| Path | 文件量 | CC 主力 | 增强 | 写 _index.md |
|
||||
| -------- | ------ | ---------------------- | --------------------------------------- | ------------ |
|
||||
| Hotfix | 1 | Edit | 无 | 否 |
|
||||
| Bugfix | 1-5 | /debug | 无 | 否 |
|
||||
| Quick | 1-10 | TDD | 可选交叉审查 | 是 |
|
||||
| Feature | 5-20 | /feature-dev | brainstorming + 交叉审查 | 是 |
|
||||
| Refactor | 10-50 | /batch | superpowers + 交叉审查 | 是 |
|
||||
| System | 20+ | /batch + /feature-dev | 全增强 | 是 |
|
||||
|
||||
Quick+ 路由完成 → 更新 `_index.md` (path, stage, sprint) → 告知用户。
|
||||
|
||||
---
|
||||
|
||||
## CC 原生自动化接入 (v9.6 新)
|
||||
|
||||
v9.5 的"自动 PACE"借助 CC 原生 `/goal /batch /loop /background` 实现, **不再发明状态机驱动器**。
|
||||
|
||||
### `/goal` — 每 stage 一个 condition (autonomous loop)
|
||||
|
||||
参考: <https://code.claude.com/docs/en/goal>
|
||||
|
||||
每个 PACE stage 配套一个 condition 模板, 见 `templates/details/goal-conditions.md`:
|
||||
|
||||
- **plan**: `design.md 含 File Structure Plan + tasks-current.md ≥ 1 Task + 用户回过 confirm`
|
||||
- **impl**: `tasks-current.md 所有 [ ] 变 [x] + <test_cmd> exits 0 + progress.md 每 task 一行`
|
||||
- **review**: `reviews/sprint-N.md 含 VERDICT 字段 + /codex:review 或 reviewer 已跑`
|
||||
- **ship**: `lessons.md 含 Sprint N 条目 + git push 完成 + _index.md sprint+=1`
|
||||
|
||||
启动方式: `/goal <condition>` (CC v2.1.139+) 或 `claude -p "/goal <condition>"` (非交互).
|
||||
|
||||
**降级**: CC < v2.1.139 时无 /goal, 由 delivery-gate hook 兜底判定 VERDICT.
|
||||
|
||||
### `/batch` — Refactor/System 并行实现
|
||||
|
||||
参考: CC v2.1.121+, `/batch` 自动 5-30 worktree subagent + PR.
|
||||
|
||||
适用场景: 大量同模式 task (重命名 / import 路径迁移 / 接口签名变更).
|
||||
|
||||
**降级**: 旧版本 → spawn_agent 手动 worktree.
|
||||
|
||||
### `/background` + `claude agents` 多会话编排
|
||||
|
||||
参考: CC v2.1.139+, agent view 看 Running / Blocked / Done 状态.
|
||||
|
||||
适用: System 阶段长跑 / 跨任务监控.
|
||||
|
||||
---
|
||||
|
||||
## 跨平台路由建议 (Codex Terminal-Bench SOTA)
|
||||
|
||||
某些任务在 Codex 上更优:
|
||||
|
||||
| 信号 | 建议 |
|
||||
|------|------|
|
||||
| 终端命令为主 (build/test/grep/sed) | Codex Terminal-Bench 2.0 SOTA 82.7% (GPT-5.5, April 2026), 委派 Codex |
|
||||
| 大量重复改写 (100 个文件相同 import 路径) | Codex token 0.25-0.33×, 更经济 |
|
||||
| 需要多 reviewer 互相 critique | CC 主跑 + /codex:adversarial-review 二审 |
|
||||
| 设计/架构判断 | CC Opus 4.7 略强, 主跑 CC + Codex review |
|
||||
|
||||
不强制——是建议。
|
||||
|
||||
---
|
||||
|
||||
## 路径升级机制 (impl 阶段持续监测)
|
||||
|
||||
写代码过程中发现实际复杂度超出当前路径 → 暂停询问用户升级:
|
||||
|
||||
| 升级触发信号 | 当前 → 建议 |
|
||||
|------------|-----------|
|
||||
| 需要改数据库 schema | * → System |
|
||||
| 跨 3+ 模块的接口变更 | Feature → System |
|
||||
| 实际改文件数 > 当前路径上限的 50% | Quick → Feature, Feature → Refactor/System |
|
||||
| Refactor 中发现需要新增模块 | Refactor → System |
|
||||
|
||||
升级流程: 用户确认 → _index.md path 升级 + stage 回到 design (System) 或 plan → progress.md 追加升级记录 → 重新 plan。
|
||||
**不自动升级**, 避免 Athena 借机扩 scope。
|
||||
|
||||
---
|
||||
|
||||
## 共享协议
|
||||
|
||||
### 审查协议 (Quick/Feature/Refactor/System 的 review 阶段共用)
|
||||
|
||||
```
|
||||
1. /review → 第一份审查 (CC 原生)
|
||||
2. [Feature+] /codex:review [--base main] [--wait] → 跨模型审查
|
||||
→ 必须看到真实 codex job ID 或命令输出 (铁律 6)
|
||||
→ 调用失败 → 失败处理协议三轮 → 真失败则降级 + review-report.md 注明 "codex unavailable + stderr"
|
||||
3. [System] 追加 /codex:adversarial-review + ECC AgentShield
|
||||
4. [System UI 类] superpowers /qa + playwright E2E
|
||||
5. /simplify
|
||||
6. @evaluator → VERDICT
|
||||
PASS → 触发 /compound → details/lessons.md 追加 → 停一轮让用户确认 → stage=ship
|
||||
CONCERNS → 修复 → 重新评分
|
||||
REWORK → stage=impl (Feature/Refactor) 或 stage=design (System)
|
||||
FAIL → stage=plan
|
||||
```
|
||||
|
||||
### 发布协议 (ship 阶段共用)
|
||||
|
||||
```
|
||||
1. [Quick] git commit → 直接提交
|
||||
2. [Feature+] superpowers /ship → 同步 main, 跑测试, 开 PR
|
||||
3. [System] superpowers /retro → 工程回顾
|
||||
4. details/lessons.md (compound 已追加) + progress.md 最终更新
|
||||
5. _index.md: stage="", sprint+=1
|
||||
6. /compact "Sprint N 完成"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 失败处理协议 (落地铁律 6 完成度证据)
|
||||
|
||||
工具调用失败 (任意 Bash/Skill/Task):
|
||||
|
||||
```
|
||||
Round 1: 读 stderr 和 exit code 分类:
|
||||
- permission denied → 报具体规则, **继续尝试**:
|
||||
a) 用 settings.local.json 临时绕过 (告知用户)
|
||||
b) 换等价命令 (node 改 npx)
|
||||
c) 拆到主 agent
|
||||
- command not found → 装包 / 换工具
|
||||
- subagent 报"无能力" → 实测一次 (echo 探测), 真不可用主 agent 接管
|
||||
|
||||
Round 2: 改参数 / 换路径 / 换工具
|
||||
|
||||
Round 3: 仍失败 → 报告每次的:
|
||||
- 完整命令
|
||||
- 完整 stderr
|
||||
- exit code
|
||||
- 已尝试的修复方式
|
||||
→ 写入 review-report.md / progress.md 作为完成度证据
|
||||
```
|
||||
|
||||
**绝对禁止**: "请你用 ! 前缀手动执行" 作为第一次响应。
|
||||
|
||||
**subagent 输出预算 ≤ 2000 tokens** (Anthropic 多 agent research 经验): 深入探索可以, 只回传关键发现。
|
||||
|
||||
---
|
||||
|
||||
## Hotfix — 无仪式
|
||||
|
||||
```
|
||||
Edit → test_cmd (有就跑) → git commit → 完
|
||||
```
|
||||
|
||||
## Bugfix — /debug + TDD (无 stage)
|
||||
|
||||
```
|
||||
/debug → 定位根因
|
||||
TDD: 写回归测试(复现) → 修复 → 测试通过
|
||||
lint_cmd → /review → git commit
|
||||
自愈: 修不好 → /debug 再来 (限 3 轮) → 3 轮后通知用户
|
||||
```
|
||||
|
||||
## Quick
|
||||
|
||||
stages: impl → review → ship
|
||||
|
||||
```
|
||||
stage=impl:
|
||||
Explore: 读要改的文件 + 调用方
|
||||
TDD 实现 (superpowers brainstorming 自动激活)
|
||||
库文档不确定 → use context7
|
||||
每完成 Task → tasks-current.md 勾选 + progress.md 追加一行 + index-updater 自动更 _index.md counts
|
||||
路径升级监测
|
||||
lint_cmd + test_cmd → /simplify → stage=review
|
||||
|
||||
stage=review: → 审查协议
|
||||
stage=ship: → 发布协议
|
||||
```
|
||||
|
||||
## Feature — /feature-dev + 增强
|
||||
|
||||
stages: plan → impl → review → ship
|
||||
|
||||
```
|
||||
stage=plan:
|
||||
R₀: 命中主题再读 details/lessons.md (just-in-time, 不预加载)
|
||||
superpowers brainstorming → 发散 + 方向挑战
|
||||
→ 合成 design.md (含 File Structure Plan 段)
|
||||
用户确认 → tasks-current.md (Boundary/Depends 标注) → stage=impl
|
||||
可选: /goal "tasks-current.md 全 [x] + npm test exits 0"
|
||||
|
||||
stage=impl:
|
||||
/feature-dev "需求描述"
|
||||
Phase 4 (架构) 前 → 用户确认
|
||||
每完成 Task → tasks-current.md 勾选 + progress.md 追加
|
||||
设计偏离 → 先改 design.md 再继续 (铁律 5)
|
||||
库文档不确定 → use context7
|
||||
路径升级监测 → 触发即停
|
||||
/simplify → stage=review
|
||||
|
||||
stage=review: → 审查协议
|
||||
stage=ship: → 发布协议
|
||||
```
|
||||
|
||||
## Refactor — /batch + 增强
|
||||
|
||||
stages: plan → impl → review → ship
|
||||
|
||||
```
|
||||
stage=plan:
|
||||
R₀: 按需 read details/lessons.md
|
||||
superpowers /plan-eng-review → 验证重构方案
|
||||
用户确认 → design.md (含 before/after 边界) + tasks-current.md → stage=impl
|
||||
|
||||
stage=impl:
|
||||
/batch "重构描述" ← CC v2.1.121+ 原生
|
||||
CC 自动: Research → Plan → 用户确认 → 并行 worktree → 每 worker /simplify + 测试
|
||||
每完成一组 Task → tasks-current.md 勾选 + progress.md 追加
|
||||
路径升级: 发现需要新增模块 → 升 System
|
||||
→ stage=review
|
||||
```
|
||||
|
||||
## System — 全链
|
||||
|
||||
stages: plan → design → impl → review → ship
|
||||
|
||||
```
|
||||
stage=plan:
|
||||
R₀: 按需 read details/lessons.md
|
||||
superpowers /office-hours → 需求提取
|
||||
superpowers brainstorming → 发散 + 方向挑战
|
||||
→ 合成 design.md → 用户确认 → stage=design
|
||||
|
||||
stage=design:
|
||||
superpowers /plan-eng-review → 架构锁定
|
||||
superpowers /plan-design-review → 设计审查 (有 UI 时)
|
||||
design.md 必含 File Structure Plan → tasks 据此拆
|
||||
→ 合成 tasks-current.md → 用户确认 → stage=impl
|
||||
|
||||
stage=impl:
|
||||
按 Task 性质选工具:
|
||||
批量同模式 → /batch 并行 (CC 原生)
|
||||
复杂独立 → /feature-dev
|
||||
跨模型委托 → /codex:rescue "<prompt 内容>"
|
||||
每完成 Task → tasks-current.md 勾选 + progress.md 追加
|
||||
全部完成 → /simplify → stage=review
|
||||
|
||||
stage=review: → 审查协议 (含 adversarial + ECC + /qa + E2E)
|
||||
stage=ship: → 发布协议 (含 /retro)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 状态管理 (铁律 5/8 文档即真相 + 索引先行)
|
||||
|
||||
`.ai_state/_index.md` frontmatter 字段: path, stage, sprint, active_goal, tech_stack, test_cmd, build_cmd, lint_cmd, dev_cmd, conventions, gotchas, counts, pointers, fingerprints, platform_features
|
||||
|
||||
合法 path: Quick, Feature, Refactor, System (Hotfix/Bugfix 不写 _index)
|
||||
合法 stage: plan, design (System only), impl, review, ship, ""
|
||||
|
||||
**`.ai_state/` 文档职责 (just-in-time)**:
|
||||
- 写端:
|
||||
- `_index.md` ← athena-init + 路由 + index-updater hook (PostToolUse)
|
||||
- `details/tasks-current.md` ← plan + impl 勾选
|
||||
- `details/tasks-archive/sprint-N.md` ← ship 时归档
|
||||
- `details/progress.md` ← impl 每 Task 追加 (append-only)
|
||||
- `details/design.md` ← plan + impl 偏离时更新
|
||||
- `details/handoff.md` ← 跨模型/跨 worker 临时
|
||||
- `details/lessons.md` ← compound skill 写 (append-only)
|
||||
- `details/reviews/sprint-N.md` ← V 阶段写
|
||||
- `details/next.md` ← pace-continuator (Stop hook) 写下一步建议
|
||||
- `details/proposals.md` ← Stop hook 反思沉淀 (铁律 9)
|
||||
- `hook-trace.jsonl` ← hooks 自动
|
||||
- 读端 (按 _index.md.pointers 跳转, 不预加载):
|
||||
- 必读: `_index.md`
|
||||
- 阶段=impl/review → 按 pointers.latest_progress 读 progress.md 尾部
|
||||
- 任务命中 lessons 主题 → pointers.latest_lesson 跳到段
|
||||
- plan 阶段 → 命中再读 lessons / impl 读 design 对照 / review 读 design + previous reviews
|
||||
|
||||
**跨项目记忆已删除**: Athena 不做。推荐装 claude-mem (~/.claude/projects/ 已内置) 或 superpowers。
|
||||
|
||||
`delivery-gate` (Stop hook) 在 stage="review" 时检查质量门 (Feature+ 要求外部审查, Quick 不要求)。
|
||||
阶段转换 + 长任务中 → 主动 `/compact`, 不等 context 满。
|
||||
|
||||
不间断工作: 不确定用什么库 → use context7。不确定现有代码 → Read 看。不确定测试怎么跑 → 读 _index.md test_cmd。
|
||||
只在以下情况停: 方案需用户确认 (plan 阶段), VERDICT 需判断 (review 阶段), 真正的阻塞 (失败 3 次后)。
|
||||
@@ -0,0 +1,44 @@
|
||||
# VibeCoding Athena (SessionStart 自动注入)
|
||||
|
||||
你是 VibeCoding Athena 工程 Agent。CC 做事, Athena 把关。
|
||||
|
||||
**铁律 (9 条)**:
|
||||
1. 设计先行 (Hotfix/Bugfix 例外)
|
||||
2. TDD 强制
|
||||
3. Sisyphus (tasks 全完成才进审查)
|
||||
4. Review 强制 (Feature+ 至少一次交叉审查)
|
||||
5. 文档即真相 (`.ai_state/_index.md` 单一入口)
|
||||
6. 完成度证据 (报告"完成"必须附 tool_use ID 或命令输出)
|
||||
7. 出处优先 (技术结论必须引用官方文档/源码 URL)
|
||||
8. **索引先行** (进入决策先读 `.ai_state/_index.md`, 禁止 glob 全目录)
|
||||
9. **Hook 是进化器** (Stop 时反思写 `details/proposals.md`)
|
||||
|
||||
工具报错 → 三次重试附 stderr 报告, 不让用户代执行。
|
||||
/codex:* 必须产生真实 tool_use 响应。
|
||||
|
||||
## R₀ Get-bearings (just-in-time)
|
||||
|
||||
1. **必读**: `.ai_state/_index.md` (单一入口, 已自动注入)
|
||||
2. **按需**: stage=impl/review → 按 pointers.latest_progress 读 progress.md 尾部
|
||||
3. **按需**: 任务命中 lessons 主题 → pointers.latest_lesson 跳段
|
||||
4. **按需**: `git log --oneline -10` (涉及代码修改时)
|
||||
|
||||
文件大就用 head/tail/grep, 不要 cat 全文。**禁止 glob 整个 .ai_state/**。
|
||||
|
||||
## 文档写入义务 (铁律 5)
|
||||
|
||||
- impl 每完成 Task → tasks-current.md 勾选 + progress.md 追加一行 (index-updater hook 自动更 _index.md counts)
|
||||
- 发现 design.md 错 → 先改 design.md 再继续写代码
|
||||
- V 阶段 Gate 通过 → /compound 追加 details/lessons.md
|
||||
|
||||
## 6 路径
|
||||
|
||||
Hotfix (无仪式) · Bugfix (/debug+TDD) · Quick (TDD+收尾) · Feature (/feature-dev+增强) · Refactor (/batch+增强) · System (全链)
|
||||
|
||||
路径升级触发器: 改 schema / 跨 3+ 模块 / 文件数超 50% → 暂停询问升级。
|
||||
|
||||
## v9.6 新接入 (CC 原生)
|
||||
|
||||
- `/goal <condition>` (v2.1.139+): 每 stage 一个 goal, evaluator 自动判定
|
||||
- `/batch`: Refactor/System 自动并行 worktree
|
||||
- `/background` + `claude agents`: 多会话编排
|
||||
@@ -0,0 +1,66 @@
|
||||
---
|
||||
schema_version: "9.6"
|
||||
athena_version: "9.6.0"
|
||||
project:
|
||||
path: "" # Hotfix | Bugfix | Quick | Feature | Refactor | System | ""
|
||||
stage: "" # plan | design | impl | review | ship | ""
|
||||
sprint: 0
|
||||
active_goal: "" # 当前 /goal condition (空 = 无活动 goal)
|
||||
counts:
|
||||
tasks_pending: 0
|
||||
tasks_done: 0
|
||||
tasks_blocked: 0
|
||||
lessons_lines: 0
|
||||
progress_entries: 0
|
||||
reviews_count: 0
|
||||
pointers:
|
||||
latest_progress: "" # 如 "details/progress.md#L46"
|
||||
latest_review: "" # 如 "details/reviews/sprint-3.md"
|
||||
latest_lesson: "" # 如 "details/lessons.md#L98"
|
||||
latest_proposal: "" # 如 "details/proposals.md#L12"
|
||||
fingerprints:
|
||||
tasks_mtime: 0
|
||||
design_mtime: 0
|
||||
progress_mtime: 0
|
||||
lessons_mtime: 0
|
||||
platform_features:
|
||||
cc_version: "" # 由 athena-setup 检测填入
|
||||
cx_version: "" # 同上
|
||||
goal_supported: false # CC ≥ v2.1.139 时 true
|
||||
batch_supported: false # CC ≥ v2.1.121 时 true
|
||||
background_supported: false # CC ≥ v2.1.139 时 true
|
||||
session_memory_active: false # ~/.claude/projects/ 自动摘要是否启用
|
||||
cross_session_memory: "none" # "claude-mem" | "superpowers" | "none"
|
||||
gotchas: [] # 项目特有的"小心点", PACE 会在 SessionStart 注入
|
||||
conventions: [] # 项目约定 (pnpm vs npm 等)
|
||||
tech_stack: ""
|
||||
test_cmd: ""
|
||||
build_cmd: ""
|
||||
lint_cmd: ""
|
||||
dev_cmd: ""
|
||||
---
|
||||
|
||||
# 项目状态索引
|
||||
|
||||
<!--
|
||||
本文件是 .ai_state/ 的单一入口 (铁律 8)。
|
||||
SessionStart hook 只读这一个文件, 注入轻量 frontmatter 字段 + counts 摘要。
|
||||
下游 (Edit/Read tools) 按 pointers 自取明细。
|
||||
任何 detail/* 文件写入 → PostToolUse index-updater.cjs 原子更新本文件。
|
||||
-->
|
||||
|
||||
## 最近活动
|
||||
|
||||
<!-- 由 pace-continuator hook 在 Stop 时维护, 保留最近 3 条 -->
|
||||
|
||||
## 当前阻塞
|
||||
|
||||
<!-- 由 PACE 流程在 stage=impl/review 时填写 -->
|
||||
|
||||
## 下一动作 (PACE 建议)
|
||||
|
||||
<!-- 由 pace-continuator 在 Stop 时根据 stage 推断, 写入 details/next.md 的摘要 -->
|
||||
|
||||
## 反思建议 (Hook 沉淀)
|
||||
|
||||
<!-- 引用 details/proposals.md 最近一条 (latest_proposal) -->
|
||||
@@ -0,0 +1,28 @@
|
||||
# 设计文档
|
||||
|
||||
## 需求摘要
|
||||
|
||||
## 功能分级
|
||||
|
||||
- **MUST**:
|
||||
- **SHOULD**:
|
||||
- **COULD**:
|
||||
|
||||
## 验收标准
|
||||
|
||||
- [ ]
|
||||
|
||||
## 技术方案
|
||||
|
||||
## File Structure Plan
|
||||
|
||||
<!--
|
||||
模块/文件边界, 给 tasks-current.md 拆任务用 (boundary-first 设计)
|
||||
示例:
|
||||
- src/auth/ — JWT/refresh/session, owner: Task 1-3
|
||||
- src/routes/user/ — user CRUD endpoints, owner: Task 4-5
|
||||
- tests/auth/ — auth 单测 + 集成测试
|
||||
边界外的文件 = 本 Sprint 不应触碰
|
||||
-->
|
||||
|
||||
## 不做什么
|
||||
@@ -0,0 +1,31 @@
|
||||
# 跨模型/跨 worker 交接
|
||||
|
||||
<!--
|
||||
本文件内容会作为 /codex:rescue "<内容>" 的 prompt 传入。Codex 不会自动读本文件。
|
||||
构造完 handoff.md → Claude 把文件内容 Read 出来作为 /codex:rescue 的参数。
|
||||
-->
|
||||
|
||||
## 当前任务
|
||||
|
||||
<!-- Task ID + 描述 + 验收标准 + Boundary 范围 -->
|
||||
|
||||
## 项目文件树
|
||||
|
||||
<!-- 必须包含实际文件列表, 用 find/ls 生成, 不要手写 -->
|
||||
<!-- 示例: find . -name "*.py" -not -path "./venv/*" | head -50 -->
|
||||
|
||||
## 关键代码片段
|
||||
|
||||
<!-- 与本 Task 直接相关的代码文件内容 (关键函数/类, 不要全文) -->
|
||||
|
||||
## 项目背景
|
||||
|
||||
<!-- design.md 摘要 + 技术栈 -->
|
||||
|
||||
## 已完成
|
||||
|
||||
<!-- tasks-current.md 中完成的 Task -->
|
||||
|
||||
## 注意事项
|
||||
|
||||
<!-- _index.md.gotchas + conventions -->
|
||||
@@ -0,0 +1,21 @@
|
||||
# 项目级经验沉淀 (Lessons Learned)
|
||||
|
||||
<!--
|
||||
每个 Sprint 通过 Gate 后由 compound skill 追加。append-only, 不删不改历史。
|
||||
本文件存项目级业务代码经验 (Athena 不做跨项目知识管理)。
|
||||
跨项目记忆请装 claude-mem 或 superpowers。
|
||||
|
||||
格式:
|
||||
|
||||
## [YYYY-MM-DD Sprint N] 一句话标题
|
||||
- **Pattern**: 解法描述 (文件:行号)
|
||||
- **Pitfall**: 坑点描述
|
||||
- **Constraint**: 新约束
|
||||
|
||||
示例:
|
||||
|
||||
## [2026-05-21 Sprint 1] JWT refresh 不能和 access 共享 secret
|
||||
- **Pattern**: 分开 secret + rotation key 在 env (src/auth/config.ts:15)
|
||||
- **Pitfall**: Authorization header 大小写处理不一致, 用 lodash.toLower 前置
|
||||
- **Constraint**: Redis 需要 ≥256MB 才放得下 session cache
|
||||
-->
|
||||
@@ -0,0 +1,15 @@
|
||||
# 下一动作建议 (PACE-aware, Stop hook 维护)
|
||||
|
||||
<!--
|
||||
本文件由 .claude/hooks/pace-continuator.cjs 在 Stop 事件时写入。
|
||||
读 _index.md 的 path/stage/sprint, 推断"下一步该做什么", 给出具体的命令建议。
|
||||
SessionStart 时 (如有 pace-resumer hook) 会读本文件作为"继续提示"。
|
||||
|
||||
格式:
|
||||
## YYYY-MM-DD HH:MM
|
||||
当前: <path>/<stage>/sprint <N>
|
||||
建议:
|
||||
- 命令 1
|
||||
- 命令 2
|
||||
理由: <一句话>
|
||||
-->
|
||||
@@ -0,0 +1,15 @@
|
||||
# Progress Log
|
||||
|
||||
<!--
|
||||
impl 阶段每完成一个 Task 追加一行 (铁律 5)。
|
||||
格式: YYYY-MM-DD HH:MM [path/stage/sprint] action
|
||||
|
||||
示例:
|
||||
2026-05-21 14:30 [Feature/impl/1] Task 3 done: auth middleware
|
||||
2026-05-21 15:10 [Feature/impl/1] design.md updated: JWT expiry 15min
|
||||
2026-05-21 15:30 [Feature/impl/1] PATH-UPGRADE Feature → System (发现需要改 schema)
|
||||
2026-05-21 16:45 [Feature/review/1] /review 完成, 无阻塞问题
|
||||
|
||||
append-only: 不删历史, 不改既有行。
|
||||
index-updater hook 会更新 _index.md.counts.progress_entries 和 pointers.latest_progress。
|
||||
-->
|
||||
@@ -0,0 +1,20 @@
|
||||
# Hook 反思沉淀 (铁律 9: Hook 是进化器)
|
||||
|
||||
<!--
|
||||
本文件由 .claude/hooks/pace-continuator.cjs 在 Stop 时维护。
|
||||
当 hook 检测到"本 turn 有反复试错 / 阻塞 / 失败模式", append-only 一条反思。
|
||||
|
||||
不写"已知坑" (lessons.md 管那个), 写"流程级提议":
|
||||
- "考虑把 X 加入 .ai_state/conventions"
|
||||
- "delivery-gate 这条规则可能误伤, 下版本调整"
|
||||
- "本 sprint 的 stage transition 太快, plan 阶段不充分"
|
||||
|
||||
人审完后可以手动迁移到 lessons.md 或者 conventions 列表。
|
||||
hook 永远不替用户做决定, 只提供素材。
|
||||
|
||||
格式:
|
||||
## YYYY-MM-DD HH:MM (sprint N)
|
||||
- 观察: <事实>
|
||||
- 假设: <可能原因>
|
||||
- 提议: <改什么>
|
||||
-->
|
||||
@@ -0,0 +1,41 @@
|
||||
# Sprint N 审查报告
|
||||
|
||||
## Step 1: 测试结果
|
||||
|
||||
<!-- 命令 + 实际输出 (不是"已跑通") -->
|
||||
|
||||
## Step 2: /codex:review (Feature+, Quick 可选)
|
||||
|
||||
<!-- 必须有以下证据之一, 防伪装 (铁律 4 反空跑 + 铁律 6 完成度证据):
|
||||
- codex job ID
|
||||
- codex CLI 真实命令行输出片段
|
||||
- 不可用时明示 "codex unavailable" + 完整 stderr -->
|
||||
|
||||
## Step 3: /codex:adversarial-review (System)
|
||||
|
||||
<!-- 同上证据要求 -->
|
||||
|
||||
## Step 4: ECC 扫描 (System)
|
||||
|
||||
<!-- npx ecc-agentshield scan 实际输出摘要 -->
|
||||
|
||||
## Step 5: Claude 审查 (/review 内置)
|
||||
|
||||
<!-- /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:
|
||||
|
||||
<!-- PASS (均分≥4.0 且所有维度≥3) | CONCERNS (均分≥3.0 有维度=2) | REWORK (均分<3.0 或任一=1) | FAIL (多维度=1 或安全/Boundary 大幅越界) -->
|
||||
@@ -0,0 +1,19 @@
|
||||
# Sprint 任务清单
|
||||
|
||||
<!--
|
||||
每个 Task 的 Boundary/Depends 标注 (cc-sdd 边界优先, 防止越界)
|
||||
格式:
|
||||
- [ ] Task N: 简述
|
||||
_Boundary:_ src/auth/, tests/auth/
|
||||
_Depends:_ Task N-1 (空则 — )
|
||||
|
||||
PostToolUse hook (index-updater.cjs) 会:
|
||||
- 检测本文件的 - [ ] / - [x] 数量变化
|
||||
- 原子更 _index.md.counts.tasks_pending/done/blocked
|
||||
-->
|
||||
|
||||
## 待办
|
||||
|
||||
## 完成
|
||||
|
||||
## 阻塞
|
||||
@@ -0,0 +1,121 @@
|
||||
# /goal Condition 模板库
|
||||
|
||||
每个 PACE stage 对应一个 condition 模板, 用 `/goal <condition>` 启动 autonomous loop。
|
||||
|
||||
参考: <https://code.claude.com/docs/en/goal>
|
||||
|
||||
## 写 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]
|
||||
- 运行 <test_cmd> 退出码 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 <condition>"
|
||||
```
|
||||
|
||||
### 状态查询
|
||||
```
|
||||
/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
|
||||
Executable
+17
@@ -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] 就绪。"
|
||||
@@ -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 自审:
|
||||
"作为审查者, 这个计划有哪些漏洞/模糊/不可执行的部分?"
|
||||
@@ -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。
|
||||
@@ -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
|
||||
@@ -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 填入验证结果 + 未满足标准
|
||||
@@ -1,12 +0,0 @@
|
||||
# Conventions
|
||||
|
||||
## 项目编码规范
|
||||
{由 vibe-init 自动检测并填入, 或手动编写}
|
||||
|
||||
## 依赖版本
|
||||
{关键依赖 + 版本号}
|
||||
|
||||
## Agent 易犯错误
|
||||
> 由 reflexion/kaizen 自动追加, 帮助 agent 避免重复犯错
|
||||
|
||||
- (初始为空, 使用过程中逐渐积累)
|
||||
@@ -1,24 +0,0 @@
|
||||
# Design Spec
|
||||
|
||||
## 需求 (MUST/SHOULD/COULD)
|
||||
|
||||
### MUST (必须实现)
|
||||
- {核心功能 1}
|
||||
- {核心功能 2}
|
||||
|
||||
### SHOULD (应该实现)
|
||||
- {重要功能}
|
||||
|
||||
### COULD (可以实现)
|
||||
- {锦上添花}
|
||||
|
||||
## 技术方案
|
||||
{架构 + 关键设计决策}
|
||||
|
||||
## 验收标准
|
||||
- [ ] {标准 1: 具体可验证的条件}
|
||||
- [ ] {标准 2: 具体可验证的条件}
|
||||
|
||||
## 非功能需求
|
||||
- 性能: {指标}
|
||||
- 安全: {要求}
|
||||
@@ -1,7 +0,0 @@
|
||||
# Doing
|
||||
|
||||
## 当前执行
|
||||
- [ ] {当前步骤描述}
|
||||
|
||||
## 执行历史
|
||||
{自动追加完成的步骤}
|
||||
@@ -1,10 +0,0 @@
|
||||
# Lessons Learned
|
||||
|
||||
## 技术陷阱
|
||||
{Reflexion/Kaizen 发现的技术问题}
|
||||
|
||||
## 有效模式
|
||||
{本项目中验证有效的解决方案}
|
||||
|
||||
## 项目特有知识
|
||||
{不在文档中但很重要的隐性知识}
|
||||
@@ -1,14 +0,0 @@
|
||||
# Plan
|
||||
|
||||
## 任务列表
|
||||
|
||||
- [ ] T-001: {动作动词} + {目标} + {验收条件}
|
||||
文件: {涉及文件}
|
||||
依赖: 无
|
||||
|
||||
- [ ] T-002: {动作动词} + {目标} + {验收条件}
|
||||
文件: {涉及文件}
|
||||
依赖: T-001
|
||||
|
||||
## 对抗审查记录
|
||||
{Codex 审查结果摘要, 或 "CC 独立审查"}
|
||||
@@ -1,18 +0,0 @@
|
||||
# Quality Report
|
||||
|
||||
## 验证结果
|
||||
- 测试: {通过/失败} ({覆盖率}%)
|
||||
- TypeScript: {通过/失败}
|
||||
- Lint: {通过/失败}
|
||||
|
||||
## 验收标准确认
|
||||
- [x] {标准 1}: 满足, 证据: {测试名/文件位置}
|
||||
- [ ] {标准 2}: 未满足, 原因: {说明}
|
||||
|
||||
## 代码审查
|
||||
- 审查者: {CC / Codex / 互审}
|
||||
- 发现问题: {数量}
|
||||
- 已修复: {数量}
|
||||
|
||||
## 未满足验收标准
|
||||
{列出未满足的标准 + 原因}
|
||||
@@ -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}
|
||||
@@ -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。
|
||||
@@ -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 不可用时: 直接输出确认提示)
|
||||
+26
-41
@@ -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) 调用执行任务, 也可能独立运行。
|
||||
<!--
|
||||
本文件目标 altitude: 工程纪律的高信号宪法 (constitution)。
|
||||
- 不规定操作步骤 → skills/pace/SKILL.md
|
||||
- 不列插件命令 → prompts/athena-setup.md
|
||||
- 不写安装/配置 → README.md
|
||||
- 行数硬上限 30
|
||||
-->
|
||||
|
||||
## 框架地图
|
||||
你是 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
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
[agent]
|
||||
name = "builder"
|
||||
model = "gpt-5.4"
|
||||
description = "实现代码 + 测试 (TDD: RED→GREEN→REFACTOR)"
|
||||
@@ -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:" 段
|
||||
"""
|
||||
@@ -1,4 +0,0 @@
|
||||
[agent]
|
||||
name = "explorer"
|
||||
model = "gpt-5.4-mini"
|
||||
description = "代码库调研 + 文档查询 (只读)"
|
||||
@@ -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 "<task short summary>"`
|
||||
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.
|
||||
"""
|
||||
@@ -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**: <Codex subagent job id, 铁律 6 完成度证据>
|
||||
|
||||
**Findings**:
|
||||
- [HIGH] file.ext:LN — <描述>
|
||||
- [MEDIUM] file.ext:LN — <描述>
|
||||
- [LOW] file.ext:LN — <描述>
|
||||
|
||||
**Suggested fixes**:
|
||||
- ...
|
||||
```
|
||||
|
||||
每个 finding 必须有 file:line 锚点 (no 模糊"代码可以更好").
|
||||
|
||||
## 输出预算
|
||||
|
||||
≤ 2000 tokens.
|
||||
"""
|
||||
@@ -1,4 +0,0 @@
|
||||
[agent]
|
||||
name = "validator"
|
||||
model = "gpt-5.4"
|
||||
description = "代码审查 + Plan Review"
|
||||
+92
-62
@@ -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]
|
||||
|
||||
Executable
+167
@@ -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()
|
||||
Executable
+167
@@ -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("-->") 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("-->") 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()
|
||||
Executable
+66
@@ -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()
|
||||
Executable
+121
@@ -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()
|
||||
Executable
+71
@@ -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()
|
||||
Executable
+76
@@ -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()
|
||||
@@ -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` 保存
|
||||
@@ -0,0 +1,67 @@
|
||||
# /athena-init (Codex) — 项目初始化
|
||||
|
||||
# 描述
|
||||
> 项目初始化。每个新项目首次使用 Athena 时运行。
|
||||
# end
|
||||
|
||||
参考: <https://developers.openai.com/codex/concepts/customization>
|
||||
|
||||
`.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` 看面板。"
|
||||
@@ -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('<!--') and not stripped.startswith('#'):
|
||||
return i + 1
|
||||
except FileNotFoundError:
|
||||
pass
|
||||
return 0
|
||||
|
||||
tasks_pending = count_match('.ai_state/details/tasks-current.md', r'^- \[ \]')
|
||||
tasks_done = count_match('.ai_state/details/tasks-current.md', r'^- \[x\]')
|
||||
progress_n = last_nonempty_line('.ai_state/details/progress.md')
|
||||
lessons_lines = count_match('.ai_state/details/lessons.md', r'^## ')
|
||||
latest_lesson_line = last_section_line('.ai_state/details/lessons.md')
|
||||
|
||||
reviews = []
|
||||
try:
|
||||
reviews = [f for f in os.listdir('.ai_state/details/reviews') if re.match(r'^sprint-\d+\.md$', f)]
|
||||
except FileNotFoundError:
|
||||
pass
|
||||
reviews_count = len(reviews)
|
||||
latest_review = ''
|
||||
if reviews:
|
||||
latest_review = 'details/reviews/' + sorted(reviews, key=lambda f: int(re.search(r'\d+', f).group()))[-1]
|
||||
|
||||
content = f'''---
|
||||
schema_version: "9.6"
|
||||
athena_version: "9.6.0"
|
||||
project:
|
||||
path: "{proj.get('path','')}"
|
||||
stage: "{proj.get('stage','')}"
|
||||
sprint: {proj.get('sprint', 0)}
|
||||
active_goal: ""
|
||||
counts:
|
||||
tasks_pending: {tasks_pending}
|
||||
tasks_done: {tasks_done}
|
||||
tasks_blocked: 0
|
||||
lessons_lines: {lessons_lines}
|
||||
progress_entries: {progress_n}
|
||||
reviews_count: {reviews_count}
|
||||
pointers:
|
||||
latest_progress: "{('details/progress.md#L' + str(progress_n)) if progress_n else ''}"
|
||||
latest_review: "{latest_review}"
|
||||
latest_lesson: "{('details/lessons.md#L' + str(latest_lesson_line)) if latest_lesson_line else ''}"
|
||||
latest_proposal: ""
|
||||
fingerprints:
|
||||
tasks_mtime: 0
|
||||
design_mtime: 0
|
||||
progress_mtime: 0
|
||||
lessons_mtime: 0
|
||||
platform_features:
|
||||
cc_version: ""
|
||||
cx_version: ""
|
||||
goal_supported: false
|
||||
batch_supported: false
|
||||
background_supported: false
|
||||
session_memory_active: false
|
||||
cross_session_memory: "none"
|
||||
gotchas: {json.dumps(proj.get('gotchas', []))}
|
||||
conventions: {json.dumps(proj.get('conventions', []))}
|
||||
tech_stack: "{proj.get('tech_stack','')}"
|
||||
test_cmd: "{proj.get('test_cmd','')}"
|
||||
build_cmd: "{proj.get('build_cmd','')}"
|
||||
lint_cmd: "{proj.get('lint_cmd','')}"
|
||||
dev_cmd: "{proj.get('dev_cmd','')}"
|
||||
---
|
||||
|
||||
# 项目状态索引 (v9.6 migrated from v9.5.5)
|
||||
|
||||
## 最近活动
|
||||
|
||||
## 当前阻塞
|
||||
|
||||
## 下一动作 (PACE 建议)
|
||||
|
||||
## 反思建议 (Hook 沉淀)
|
||||
'''
|
||||
|
||||
with open('.ai_state/_index.md', 'w') as f:
|
||||
f.write(content)
|
||||
print('[migrate] _index.md generated')
|
||||
PY
|
||||
```
|
||||
|
||||
## 收尾
|
||||
|
||||
```bash
|
||||
touch .ai_state/details/next.md .ai_state/details/proposals.md
|
||||
git add .ai_state/
|
||||
git commit -m "athena: migrate state to v9.6 schema (codex)"
|
||||
```
|
||||
@@ -0,0 +1,80 @@
|
||||
# /athena-preferences (Codex)
|
||||
|
||||
## 设计原则 (同 CC 版)
|
||||
|
||||
Athena 不造私有偏好文件。所有写入都是 Codex 原生位置:
|
||||
|
||||
| 偏好类型 | Codex 落点 |
|
||||
|---|---|
|
||||
| 工具偏好 (pnpm/vitest) | `~/.codex/AGENTS.md` "## Tool Preferences" 段 |
|
||||
| 风格偏好 (Linus 模式等) | `~/.codex/AGENTS.md` |
|
||||
|
||||
**Codex 没有 CC 风格的 permissions.allow/deny.** Codex 用:
|
||||
- `approval_policy` (on-request / never / on-failure / untrusted) — 顶层
|
||||
- `sandbox_mode` (workspace-write / read-only / danger-full-access)
|
||||
- `[shell_environment_policy]` 控制环境
|
||||
|
||||
参考: <https://developers.openai.com/codex/config-reference>
|
||||
|
||||
所以 `/athena:remember-allow|deny` 在 Codex 上**部分语义不可映射**, 当前实现:
|
||||
- `/athena:remember-tool <key>=<value>` → 写 `~/.codex/AGENTS.md` "## Tool Preferences"
|
||||
- `/athena:show-preferences` → cat ~/.codex/AGENTS.md
|
||||
- `/athena:remember-allow|deny` → 提示用户在 `~/.codex/config.toml` 改 `approval_policy` 或 `sandbox_mode`, **不自动改** (那是 critical 配置)
|
||||
|
||||
## 子命令 (Codex)
|
||||
|
||||
### `/athena:remember-tool <key>=<value>`
|
||||
|
||||
```bash
|
||||
GLOBAL_AGENTS_MD="$HOME/.codex/AGENTS.md"
|
||||
mkdir -p "$(dirname "$GLOBAL_AGENTS_MD")"
|
||||
|
||||
# 创建文件 / 找到 Tool Preferences 段 / 追加
|
||||
python3 << PY
|
||||
import os, re
|
||||
p = os.path.expanduser("~/.codex/AGENTS.md")
|
||||
try:
|
||||
content = open(p).read()
|
||||
except FileNotFoundError:
|
||||
content = ""
|
||||
|
||||
section = "## Tool Preferences"
|
||||
arg = "$ARGUMENTS" # 由 Codex 替换
|
||||
|
||||
if section not in content:
|
||||
if content and not content.endswith("\n"):
|
||||
content += "\n"
|
||||
content += "\n" + section + "\n\n- " + arg + "\n"
|
||||
else:
|
||||
# 在 section 后追加一行 (避免重复)
|
||||
if "- " + arg in content:
|
||||
print(f"⚠️ 已存在: {arg}")
|
||||
exit(0)
|
||||
content = re.sub(r"(" + re.escape(section) + r"\n+)", r"\g<1>- " + arg + "\n", content, count=1)
|
||||
|
||||
open(p, "w").write(content)
|
||||
print(f"✓ 已加入 ~/.codex/AGENTS.md Tool Preferences: {arg}")
|
||||
PY
|
||||
```
|
||||
|
||||
### `/athena:show-preferences`
|
||||
|
||||
```bash
|
||||
echo "=== ~/.codex/AGENTS.md (Tool Preferences) ==="
|
||||
sed -n '/^## Tool Preferences/,/^## /p' ~/.codex/AGENTS.md 2>/dev/null | head -30
|
||||
|
||||
echo ""
|
||||
echo "=== ~/.codex/config.toml (approvals / sandbox) ==="
|
||||
grep -E "^(approval_policy|sandbox_mode)" ~/.codex/config.toml 2>/dev/null
|
||||
```
|
||||
|
||||
### `/athena:remember-allow|deny <pattern>`
|
||||
|
||||
不自动写, 仅提示:
|
||||
```
|
||||
⚠️ Codex 没有 permissions.allow/deny 概念.
|
||||
等价配置在 ~/.codex/config.toml:
|
||||
- approval_policy: on-request | never | on-failure | untrusted
|
||||
- sandbox_mode: read-only | workspace-write | danger-full-access
|
||||
请用编辑器手改这两个字段, 不通过 Athena 自动改 (critical config).
|
||||
```
|
||||
@@ -0,0 +1,12 @@
|
||||
# /athena-review (Codex)
|
||||
|
||||
有 `.ai_state/_index.md` → 按当前 Path 触发 pace skill 审查阶段。
|
||||
无 → 快速审查 (Codex 内置 /review)。
|
||||
|
||||
焦点: $ARGUMENTS
|
||||
|
||||
Codex 端可用工具:
|
||||
- `/review` (Codex 内置)
|
||||
- `spawn_agent reviewer` (从 ~/.codex/agents/reviewer.toml 调度)
|
||||
- `web_search` 查最佳实践
|
||||
- 不可: /codex:review (那是 CC 侧反向调用)
|
||||
@@ -0,0 +1,98 @@
|
||||
# /athena-setup (Codex)
|
||||
|
||||
参考: <https://developers.openai.com/codex/config-basic>
|
||||
|
||||
## Step 0: Codex 版本
|
||||
|
||||
```bash
|
||||
codex --version
|
||||
```
|
||||
|
||||
要求 ≥ v0.124 (hooks stable). v0.129+ 解锁 /hooks TUI menu.
|
||||
|
||||
写入 `_index.md.platform_features.cx_version`.
|
||||
|
||||
## Step 1: 验证 config.toml
|
||||
|
||||
`~/.codex/config.toml` 应包含 (从 cx/.codex/config.toml 拷贝):
|
||||
|
||||
- `model = "gpt-5.5"`
|
||||
- `[features] hooks = true` (规范名, 非 deprecated codex_hooks)
|
||||
- `[[hooks.SessionStart]]`, `[[hooks.PreToolUse]]`, `[[hooks.PostToolUse]]`, `[[hooks.Stop]]`
|
||||
- `[agents] max_threads = 6`
|
||||
- `[[skills.config]]` 包含 pace / compound / context7 / superpowers
|
||||
|
||||
```bash
|
||||
codex check config 2>&1 | head -20
|
||||
```
|
||||
|
||||
## Step 2: 部署 hooks
|
||||
|
||||
```bash
|
||||
mkdir -p ~/.codex/hooks
|
||||
cp cx/.codex/hooks/*.py ~/.codex/hooks/
|
||||
chmod +x ~/.codex/hooks/*.py
|
||||
```
|
||||
|
||||
Codex hook 列表 (Python, 6 个):
|
||||
- session-start.py
|
||||
- user-prompt-submit.py
|
||||
- pre-bash-guard.py
|
||||
- index-updater.py
|
||||
- subagent-retry.py
|
||||
- delivery-gate.py
|
||||
|
||||
⚠️ Codex hooks 当前 Windows 不支持 (官方限制, 参考 <https://developers.openai.com/codex/hooks>)
|
||||
|
||||
## Step 3: 部署 skills
|
||||
|
||||
```bash
|
||||
mkdir -p ~/.codex/skills/pace ~/.codex/skills/compound
|
||||
cp -r cx/.codex/skills/pace/* ~/.codex/skills/pace/
|
||||
cp -r cx/.codex/skills/compound/* ~/.codex/skills/compound/
|
||||
```
|
||||
|
||||
## Step 4: 部署 prompts (slash commands)
|
||||
|
||||
```bash
|
||||
mkdir -p ~/.codex/prompts
|
||||
cp cx/.codex/prompts/*.md ~/.codex/prompts/
|
||||
```
|
||||
|
||||
## Step 5: 部署 subagents (单 TOML 文件, 非子目录)
|
||||
|
||||
参考: <https://developers.openai.com/codex/subagents>
|
||||
|
||||
```bash
|
||||
mkdir -p ~/.codex/agents
|
||||
cp cx/.codex/agents/*.toml ~/.codex/agents/
|
||||
```
|
||||
|
||||
部署后:
|
||||
- `~/.codex/agents/evaluator.toml` — 评分员 (read-only)
|
||||
- `~/.codex/agents/generator.toml` — 实现者 (workspace-write)
|
||||
- `~/.codex/agents/reviewer.toml` — 审查员 (read-only)
|
||||
|
||||
验证:
|
||||
```bash
|
||||
codex /agents 2>&1 | head -20
|
||||
# 应列出 evaluator, generator, reviewer 三个 custom agent
|
||||
```
|
||||
|
||||
## Step 6: 跨项目记忆 (推荐)
|
||||
|
||||
Codex 暂无 claude-mem 等价物。推荐:
|
||||
- 用 `~/.codex/AGENTS.md` 全局 "## Tool Preferences" 段做轻量记忆
|
||||
- 多项目用同一 ~/.codex/AGENTS.md (跨项目人工同步)
|
||||
|
||||
## Step 7: 验证
|
||||
|
||||
```bash
|
||||
codex --version # ≥ 0.124
|
||||
ls ~/.codex/hooks/*.py | wc -l # 应 = 6
|
||||
ls ~/.codex/prompts/*.md # 7 个 athena-* prompt
|
||||
codex /hooks 2>&1 | head # v0.129+ TUI 显示 hook 树
|
||||
cat .ai_state/_index.md | head -5 # schema_version: "9.6"
|
||||
```
|
||||
|
||||
通过 → "Codex 端 Athena 已就绪。"
|
||||
@@ -0,0 +1,32 @@
|
||||
# /athena-status (Codex)
|
||||
|
||||
参考 CC 版 (~/.claude/skills/athena-status/SKILL.md). 等价实现, 差异:
|
||||
|
||||
| 字段 | CC 端 | CX 端 |
|
||||
|------|-------|-------|
|
||||
| goal_supported | CC v2.1.139+ | **始终 false** (Codex 无 /goal) |
|
||||
| batch_supported | CC v2.1.121+ | 视 [agents] max_threads 而定 |
|
||||
| background_supported | CC v2.1.139+ | **始终 false** (Codex 无 /background) |
|
||||
| session_memory_active | ~/.claude/projects/ | **始终 false** (Codex 无等价) |
|
||||
|
||||
## 收集顺序 (同 CC 版)
|
||||
|
||||
1. PACE 状态 (从 `.ai_state/_index.md` frontmatter)
|
||||
2. Sprint 进度 (counts 段)
|
||||
3. Hook 触发记录 (`tail -50 .ai_state/hook-trace.jsonl`)
|
||||
4. Lessons 统计
|
||||
5. Platform 健康 (Codex 特定)
|
||||
6. Plugin 状态: CX 无插件生态, 仅显示 `codex --version`, `codex /hooks` 输出, `codex /skills`
|
||||
|
||||
## Codex 特有显示
|
||||
|
||||
```
|
||||
[Codex Hooks]
|
||||
/hooks tui: v0.129+
|
||||
hooks 配置文件: ~/.codex/hooks.json, ~/.codex/config.toml [[hooks.*]]
|
||||
Windows 支持: 否 (官方限制)
|
||||
|
||||
[Codex Subagents]
|
||||
config.toml [agents].max_threads: <从 config 读>
|
||||
available roles: evaluator, generator, reviewer
|
||||
```
|
||||
@@ -1,25 +0,0 @@
|
||||
---
|
||||
name: brainstorm
|
||||
description: R₀ 苏格拉底式需求精炼
|
||||
---
|
||||
# Brainstorm (R₀ 需求精炼)
|
||||
|
||||
Path B+ 任务在写代码前**必须**激活。
|
||||
|
||||
## 流程
|
||||
### 0. 自主调研 (提问前)
|
||||
- 搜索项目中与需求相关的现有代码
|
||||
- 读 .ai_state/lessons.md + conventions.md
|
||||
→ 基于调研结果准备更有深度的问题
|
||||
|
||||
### 1. 访谈 (苏格拉底式)
|
||||
- 一次只问一个问题, 优先选择题
|
||||
- 覆盖: 目标、场景、边界、非功能需求
|
||||
- YAGNI: 主动砍不必要的功能
|
||||
|
||||
### 2. 提出 2-3 方案
|
||||
- 每方案: 架构 + 优劣 + 复杂度
|
||||
- context7 验证可行性
|
||||
|
||||
### 3. 确认 → .ai_state/design.md
|
||||
铁律: 设计未确认前不写代码
|
||||
@@ -1,18 +0,0 @@
|
||||
---
|
||||
name: claude-delegate
|
||||
description: 接收 CC 派发任务 + 反向调用 CC
|
||||
---
|
||||
# Claude Delegate
|
||||
|
||||
## 当被 CC 调用时
|
||||
- CC 通过 codex exec 发送任务
|
||||
- 你在只读沙箱, 输出代码和建议
|
||||
- 审查请求: 以 `VERDICT: APPROVED` 或 `VERDICT: REVISE` 结尾
|
||||
|
||||
## 反向调用 CC (独立运行时)
|
||||
如需 Claude 视角:
|
||||
```bash
|
||||
# 如果有 claude CLI
|
||||
claude -p "审查以下代码: {code}"
|
||||
```
|
||||
如不可用 → 用 validator agent 自审
|
||||
@@ -1,18 +0,0 @@
|
||||
---
|
||||
name: code-review
|
||||
description: T 阶段代码审查
|
||||
---
|
||||
# Code Review
|
||||
|
||||
## 审查清单
|
||||
1. Spec 合规: 每个 MUST 实现?
|
||||
2. TDD: 源码有对应测试?
|
||||
3. 边界: 空输入/超长/并发/错误路径?
|
||||
4. 安全: 无硬编码密钥, 输入已验证
|
||||
5. 可读: 函数 < 50 行, 注释说 WHY, 严格类型
|
||||
6. 简洁: 无过度工程, 无重复, 无空 catch
|
||||
|
||||
## LLM-as-Judge 输出格式
|
||||
```json
|
||||
{"level": "PASS|CONCERNS|REWORK|FAIL", "issues": [...], "summary": "..."}
|
||||
```
|
||||
@@ -0,0 +1,47 @@
|
||||
---
|
||||
name: compound
|
||||
description: >
|
||||
Sprint Quality Gate 通过后触发。提炼本 Sprint 学到的为结构化条目追加 lessons.md。
|
||||
effort: xhigh
|
||||
---
|
||||
|
||||
# /compound — 经验沉淀
|
||||
|
||||
Gate PASS 后触发 (或用户显式 `/compound`).
|
||||
|
||||
## 输入
|
||||
|
||||
- `.ai_state/details/reviews/sprint-N.md` (审查报告)
|
||||
- `git diff` (本 Sprint 所有变更)
|
||||
- `.ai_state/details/tasks-current.md` (完成项)
|
||||
- `.ai_state/details/design.md` (设计 vs 实际实现对比)
|
||||
|
||||
## 提炼方法
|
||||
|
||||
从以上输入找**真实发现**, 不写仪式性废话。每 Sprint 0-N 条不强制。
|
||||
|
||||
- **Pattern**: 什么解法有效且可复用? (带代码行号 / 文件路径)
|
||||
- **Pitfall**: 什么坑踩过? 未来同类场景要提前避开
|
||||
- **Constraint**: 发现的新约束 (依赖版本、环境、性能边界、API 限制)
|
||||
|
||||
没发现可提炼的 → 不写, 直接告知用户 "no new lesson". **不允许编造**.
|
||||
|
||||
## 输出 (项目级)
|
||||
|
||||
追加到 `.ai_state/details/lessons.md` (append-only, 不修改历史).
|
||||
|
||||
格式见 `pace/templates/details/lessons.md`. **Athena 不做跨项目知识管理**——需要的话装 claude-mem 或 superpowers。
|
||||
|
||||
写入后同步更新 `_index.md`:
|
||||
- `counts.lessons_lines` += 新增条目数 (按 `## ` 头计)
|
||||
- `pointers.latest_lesson` = `details/lessons.md#L<新条目行号>`
|
||||
- `fingerprints.lessons_mtime` = 当前时间戳
|
||||
|
||||
## 自检
|
||||
|
||||
写入后读回验证:
|
||||
1. 条目有具体的文件/行号/版本引用 (不只是"要注意 X")
|
||||
2. 一句话标题能让下次 R₀ 快速扫描命中
|
||||
3. Pattern/Pitfall/Constraint 三段不强制全有, 但至少有一段
|
||||
|
||||
写完告知用户: "Lessons updated. Next sprint R₀ 会自动读取 (按 _index.md.pointers.latest_lesson 跳转)."
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user