ccg-workflow 3.0.0 → 3.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,17 +1,31 @@
1
1
  # CCG - Claude + Codex + Gemini Multi-Model Collaboration
2
2
 
3
+ <div align="center">
4
+
5
+ <img src="assets/logo/ccg-logo-cropped.png" alt="CCG Workflow" width="400">
6
+
3
7
  [![GitHub stars](https://img.shields.io/github/stars/fengshao1227/ccg-workflow?style=social)](https://github.com/fengshao1227/ccg-workflow)
4
8
  [![NPM Downloads](https://img.shields.io/npm/dt/ccg-workflow?style=flat-square&color=blue)](https://www.npmjs.com/package/ccg-workflow)
5
9
  [![npm version](https://img.shields.io/npm/v/ccg-workflow.svg)](https://www.npmjs.com/package/ccg-workflow)
6
10
  [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
11
+ [![Claude Code](https://img.shields.io/badge/Claude%20Code-Compatible-green.svg)](https://claude.ai/code)
12
+ [![Tests](https://img.shields.io/badge/Tests-139%20passed-brightgreen.svg)]()
13
+ [![Follow on X](https://img.shields.io/badge/X-@CCG__Workflow-black?logo=x&logoColor=white)](https://x.com/CCG_Workflow)
14
+ ![star](https://atomgit.com/fengshao1227/ccg-workflow/star/badge.svg)
7
15
 
8
16
  [简体中文](./README.zh-CN.md) | English
9
17
 
10
- ## Sponsor
18
+ </div>
19
+
20
+ ## ♥️ Sponsor
11
21
 
12
- [302.AI](https://share.302.ai/oUDqQ6) — Pay-as-you-go enterprise AI resource hub with the latest AI models and APIs.
22
+ [![302.AI](assets/sponsors/302.ai-en.jpg)](https://share.302.ai/oUDqQ6)
13
23
 
14
- [n1n.ai](https://api.n1n.ai/register?channel=c_ivgzug0w) One API Key to access 500+ AI models.
24
+ [302.AI](https://share.302.ai/oUDqQ6) is a pay-as-you-go enterprise AI resource hub that offers the latest and most comprehensive AI models and APIs on the market, along with a variety of ready-to-use online AI applications.
25
+
26
+ ---
27
+
28
+ [**n1n.ai**](https://api.n1n.ai/register?channel=c_ivgzug0w) — Global LLM API Gateway. One API Key to access 500+ top AI models (GPT-5, Claude 4.5, Gemini 3 Pro, and more).
15
29
 
16
30
  ---
17
31
 
@@ -224,10 +238,15 @@ npx ccg-workflow # Select "Uninstall" from menu
224
238
  - **Issues**: [GitHub Issues](https://github.com/fengshao1227/ccg-workflow/issues)
225
239
  - **Community**: [Linux.do](https://linux.do)
226
240
 
241
+
242
+ ## Star History
243
+
244
+ [![Star History Chart](https://api.star-history.com/svg?repos=fengshao1227/ccg-workflow&type=timeline&legend=top-left)](https://www.star-history.com/#fengshao1227/ccg-workflow&type=timeline&legend=top-left)
245
+
227
246
  ## License
228
247
 
229
248
  MIT
230
249
 
231
250
  ---
232
251
 
233
- v3.0.0 | [Issues](https://github.com/fengshao1227/ccg-workflow/issues) | [Contributing](./CONTRIBUTING.md)
252
+ v3.0.2 | [Issues](https://github.com/fengshao1227/ccg-workflow/issues) | [Contributing](./CONTRIBUTING.md)
package/README.zh-CN.md CHANGED
@@ -1,15 +1,32 @@
1
1
  # CCG - Claude + Codex + Gemini 多模型协作
2
+ # CCG - Claude + Codex + Gemini Multi-Model Collaboration
2
3
 
4
+ <div align="center">
5
+
6
+ <img src="assets/logo/ccg-logo-cropped.png" alt="CCG Workflow" width="400">
7
+
8
+ [![GitHub stars](https://img.shields.io/github/stars/fengshao1227/ccg-workflow?style=social)](https://github.com/fengshao1227/ccg-workflow)
9
+ [![NPM Downloads](https://img.shields.io/npm/dt/ccg-workflow?style=flat-square&color=blue)](https://www.npmjs.com/package/ccg-workflow)
3
10
  [![npm version](https://img.shields.io/npm/v/ccg-workflow.svg)](https://www.npmjs.com/package/ccg-workflow)
4
11
  [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
12
+ [![Claude Code](https://img.shields.io/badge/Claude%20Code-Compatible-green.svg)](https://claude.ai/code)
13
+ [![Tests](https://img.shields.io/badge/Tests-139%20passed-brightgreen.svg)]()
14
+ [![Follow on X](https://img.shields.io/badge/X-@CCG__Workflow-black?logo=x&logoColor=white)](https://x.com/CCG_Workflow)
15
+ ![star](https://atomgit.com/fengshao1227/ccg-workflow/star/badge.svg)
16
+
17
+ [简体中文](./README.zh-CN.md) | English
5
18
 
6
- 简体中文 | [English](./README.md)
19
+ </div>
7
20
 
8
- ## 赞助商
21
+ ## ♥️ Sponsor
9
22
 
10
- [302.AI](https://share.302.ai/oUDqQ6) — 按用量付费的企业级 AI 资源平台。
23
+ [![302.AI](assets/sponsors/302.ai-en.jpg)](https://share.302.ai/oUDqQ6)
24
+
25
+ [302.AI](https://share.302.ai/oUDqQ6) is a pay-as-you-go enterprise AI resource hub that offers the latest and most comprehensive AI models and APIs on the market, along with a variety of ready-to-use online AI applications.
26
+
27
+ ---
11
28
 
12
- [n1n.ai](https://api.n1n.ai/register?channel=c_ivgzug0w) — 一个 API Key 连接 500+ AI 模型。
29
+ [**n1n.ai**](https://api.n1n.ai/register?channel=c_ivgzug0w) — Global LLM API Gateway. One API Key to access 500+ top AI models (GPT-5, Claude 4.5, Gemini 3 Pro, and more).
13
30
 
14
31
  ---
15
32
 
@@ -227,4 +244,4 @@ MIT
227
244
 
228
245
  ---
229
246
 
230
- v3.0.0 | [Issues](https://github.com/fengshao1227/ccg-workflow/issues) | [Contributing](./CONTRIBUTING.md)
247
+ v3.0.2 | [Issues](https://github.com/fengshao1227/ccg-workflow/issues) | [Contributing](./CONTRIBUTING.md)
package/dist/cli.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import cac from 'cac';
3
3
  import ansis from 'ansis';
4
- import { z as diagnoseMcpConfig, A as isWindows, B as readClaudeCodeConfig, C as fixWindowsMcpConfig, D as writeClaudeCodeConfig, r as readCcgConfig, b as initI18n, a as i18n, s as showMainMenu, i as init, E as configMcp, F as version } from './shared/ccg-workflow.81OoN8XX.mjs';
4
+ import { z as diagnoseMcpConfig, A as isWindows, B as readClaudeCodeConfig, C as fixWindowsMcpConfig, D as writeClaudeCodeConfig, r as readCcgConfig, b as initI18n, a as i18n, s as showMainMenu, i as init, E as configMcp, F as version } from './shared/ccg-workflow.CGOOq1xo.mjs';
5
5
  import 'inquirer';
6
6
  import 'ora';
7
7
  import 'node:child_process';
package/dist/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- export { c as changeLanguage, x as checkForUpdates, y as compareVersions, d as createDefaultConfig, e as createDefaultRouting, g as getCcgDir, f as getConfigPath, t as getCurrentVersion, v as getLatestVersion, j as getWorkflowById, h as getWorkflowConfigs, a as i18n, i as init, b as initI18n, l as installAceTool, m as installAceToolRs, k as installWorkflows, p as migrateToV1_4_0, q as needsMigration, r as readCcgConfig, s as showMainMenu, o as uninstallAceTool, n as uninstallWorkflows, u as update, w as writeCcgConfig } from './shared/ccg-workflow.81OoN8XX.mjs';
1
+ export { c as changeLanguage, x as checkForUpdates, y as compareVersions, d as createDefaultConfig, e as createDefaultRouting, g as getCcgDir, f as getConfigPath, t as getCurrentVersion, v as getLatestVersion, j as getWorkflowById, h as getWorkflowConfigs, a as i18n, i as init, b as initI18n, l as installAceTool, m as installAceToolRs, k as installWorkflows, p as migrateToV1_4_0, q as needsMigration, r as readCcgConfig, s as showMainMenu, o as uninstallAceTool, n as uninstallWorkflows, u as update, w as writeCcgConfig } from './shared/ccg-workflow.CGOOq1xo.mjs';
2
2
  import 'ansis';
3
3
  import 'inquirer';
4
4
  import 'ora';
@@ -10,7 +10,7 @@ import fs from 'fs-extra';
10
10
  import { parse, stringify } from 'smol-toml';
11
11
  import i18next from 'i18next';
12
12
 
13
- const version = "3.0.0";
13
+ const version = "3.0.2";
14
14
 
15
15
  function cmd(id, order, category, name, nameEn, description, descriptionEn, cmdOverride) {
16
16
  return {
@@ -1028,6 +1028,10 @@ async function installSkillFiles(ctx) {
1028
1028
  overwrite: true,
1029
1029
  errorOnExist: false
1030
1030
  });
1031
+ const securityDir = join(skillsDestDir, "domains", "security");
1032
+ if (await fs.pathExists(securityDir)) {
1033
+ await fs.remove(securityDir);
1034
+ }
1031
1035
  const replacePathsInDir = async (dir) => {
1032
1036
  const entries = await fs.readdir(dir, { withFileTypes: true });
1033
1037
  for (const entry of entries) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ccg-workflow",
3
- "version": "3.0.0",
3
+ "version": "3.0.2",
4
4
  "description": "Claude + Codex + Gemini multi-model collaboration system - smart routing development workflow",
5
5
  "type": "module",
6
6
  "packageManager": "pnpm@10.17.1",
@@ -127,7 +127,9 @@ $ARGUMENTS
127
127
  mkdir -p .ccg/tasks/{task-name}
128
128
  ```
129
129
 
130
- **Step 3**: 写入 `.ccg/tasks/{task-name}/task.json`:
130
+ **Step 3**: 获取当前 git 分支名:`git rev-parse --abbrev-ref HEAD`
131
+
132
+ **Step 4**: 写入 `.ccg/tasks/{task-name}/task.json`:
131
133
 
132
134
  ```json
133
135
  {
@@ -138,11 +140,15 @@ mkdir -p .ccg/tasks/{task-name}
138
140
  "currentPhase": "1",
139
141
  "nextAction": "{策略第一阶段的描述}",
140
142
  "gate": null,
143
+ "branch": "{当前 git 分支}",
144
+ "scope": "{task-name}",
141
145
  "createdAt": "{当前 ISO 日期时间}"
142
146
  }
143
147
  ```
144
148
 
145
- **Step 4**: 如果 `.ccg/spec/` 目录存在,创建 `.ccg/tasks/{task-name}/context.jsonl`,列出相关 spec 文件。
149
+ **Step 5**: 创建 `.ccg/tasks/{task-name}/context.jsonl` 种子文件:
150
+ - 第一行写种子示例:`{"_example": "Fill with {\"file\": \"path\", \"reason\": \"why\"}. Seed rows are skipped."}`
151
+ - 如果 `.ccg/spec/` 存在 → 追加 spec 文件条目
146
152
 
147
153
  **复杂度 S → 跳过任务创建**(保持轻量)。
148
154
 
@@ -93,3 +93,115 @@ TeamCreate 失败(Agent Teams 未启用)→ Claude 自己按计划顺序实
93
93
  - 代码块标明语言
94
94
  - 变更摘要用 git diff 格式
95
95
  - 研究结果用表格对比
96
+
97
+ ## 8. Spec Evolution Protocol — Spec 反馈环
98
+
99
+ > 让 `.ccg/spec/` 从静态文档变为随项目开发自动进化的活知识库。
100
+
101
+ ### 触发条件
102
+
103
+ 任务归档前(status → "archived"),如果以下任一条件成立,**必须执行 Spec Evolution**:
104
+ - 本次开发中发现了可复用的编码模式或约定
105
+ - 外部模型审查提出了有价值的规范建议
106
+ - 修复了一个非显而易见的坑(未来可能再踩)
107
+ - 引入了新的第三方库/API/架构模式
108
+
109
+ ### 执行步骤
110
+
111
+ 1. **提炼经验**:分析 `git diff` + review.md(如有),提取可复用的经验教训
112
+ 2. **分类归属**:判断经验属于哪个 Spec 域:
113
+ - 后端相关 → `.ccg/spec/backend/index.md`
114
+ - 前端相关 → `.ccg/spec/frontend/index.md`
115
+ - 跨模块/通用 → `.ccg/spec/guides/index.md`
116
+ 3. **草拟更新**:以追加方式写出建议新增的 Spec 条目(不覆盖现有内容)
117
+ 4. **展示给用户**:
118
+ ```
119
+ 📝 Spec Evolution — 本次开发经验提炼
120
+
121
+ 建议新增到 .ccg/spec/backend/index.md:
122
+ - [规范条目](来源:{task-name},{日期})
123
+
124
+ 确认写入?[Y/n]
125
+ ```
126
+ 5. **用户确认后写入**(⛔ 不可静默写入 Spec)
127
+ 6. **无值得提炼的经验 → 跳过**(不要强行凑条目)
128
+
129
+ ### 条目质量标准
130
+
131
+ 好的 Spec 条目:
132
+ - ✅ 具体:引用真实文件路径和 API 签名
133
+ - ✅ 说明 Why:不只说"要这样做",还说"因为…"
134
+ - ✅ 可验证:子 Agent 能根据条目判断对错
135
+
136
+ 坏的 Spec 条目:
137
+ - ❌ 空泛:"写好的代码" / "注意安全"
138
+ - ❌ 一次性:只对本次任务有价值,对未来无意义
139
+
140
+ ## 9. Loop Detection & Recovery — 死循环检测
141
+
142
+ > workflow-state Hook 自动追踪每轮的 phase + nextAction。连续 3 轮无变化触发 Break-Loop Protocol。
143
+
144
+ ### 机制
145
+
146
+ - Hook 在每轮用户消息时写入 `.ccg/tasks/{name}/.turns.json`(最近 10 轮滚动缓冲)
147
+ - 检测规则:连续 3 轮 `phase` + `nextAction` 完全相同 → 判定为死循环
148
+ - 触发后在 `<ccg-state>` 面包屑中注入 `⚠️ LOOP DETECTED` 警告
149
+
150
+ ### Break-Loop Protocol(Claude 收到警告后必须执行)
151
+
152
+ 1. **立即停止**当前重复动作
153
+ 2. **根因分析**(5 Why):
154
+ - 是外部依赖阻塞?(网络/API/权限)→ 告知用户
155
+ - 是策略不适配?→ 建议升级策略
156
+ - 是信息不足?→ 向用户提问
157
+ - 是实现路径走死?→ 换方案
158
+ 3. **更新 task.json**:`nextAction` 必须变更为新的动作描述(打破循环)
159
+ 4. **如果连续 2 次触发 Break-Loop**(即 6 轮无进展)→ 强制暂停,输出完整状态摘要请用户介入
160
+
161
+ ## 10. Ralph Loop — 迭代审查协议
162
+
163
+ > 审查不是一次性动作。每轮 spawn 新 Agent(干净上下文),读取磁盘最新状态重新验证,循环自修复。
164
+
165
+ ### 适用场景
166
+
167
+ 策略中标注为 `[Ralph Loop]` 的审查阶段,使用迭代审查代替一次性审查。
168
+
169
+ ### 标准流程
170
+
171
+ ```
172
+ Round N (N=1,2,...,MAX_ROUNDS):
173
+ 1. 双模型并行审查(每次 spawn 新 Agent,干净上下文)
174
+ 2. 质量关卡(verify-security / verify-quality / verify-change)
175
+ 3. 综合审查报告,按 Critical / Warning / Info 分级
176
+ 4. 展示给用户,询问:
177
+ - 有 Critical → "发现 N 个 Critical 问题,是否修复后再审?[Y/n]"
178
+ - 无 Critical → "审查通过,是否需要再审一轮?[y/N]"
179
+ 5. 用户选择继续 →
180
+ a. spawn fix-dev(新 Agent,干净上下文)修复 Critical 问题
181
+ b. 进度追加到 .ccg/tasks/{name}/fix-log.jsonl
182
+ c. 回到 Round N+1
183
+ 6. 用户选择停止 → 退出循环,进入下一阶段
184
+ ```
185
+
186
+ ### 关键规则
187
+
188
+ - **每轮审查必须是新 Agent** — 不复用上一轮的 Agent 上下文,避免"上下文污染越修越烂"
189
+ - **fix-dev 也是新 Agent** — 从磁盘读取最新代码状态,只修分配的问题
190
+ - **最多 3 轮**(MAX_ROUNDS=3)— 超过 3 轮说明问题根深,应该回退到规划阶段
191
+ - **用户始终有决定权** — 每轮结束后由用户决定是否继续,不自动循环
192
+ - **fix-log.jsonl 追踪进度** — 每轮结果追加一行 JSON,格式:
193
+ ```jsonl
194
+ {"round": 1, "critical": 2, "warning": 5, "fixed": ["file1:issue", "file2:issue"], "ts": "ISO"}
195
+ {"round": 2, "critical": 0, "warning": 3, "fixed": ["file3:issue"], "ts": "ISO"}
196
+ ```
197
+
198
+ ### context.jsonl 角色标注
199
+
200
+ 策展 context.jsonl 时,按角色标注 `roles` 字段:
201
+ ```jsonl
202
+ {"file": ".ccg/spec/backend/index.md", "reason": "后端规范", "roles": ["implement", "review"]}
203
+ {"file": ".ccg/tasks/{name}/plan.md", "reason": "实施计划", "roles": ["implement"]}
204
+ {"file": ".ccg/tasks/{name}/research/lib-comparison.md", "reason": "库选型", "roles": ["research", "implement"]}
205
+ ```
206
+
207
+ SubAgent-context Hook 自动按角色过滤:无 `roles` 字段 = 注入所有角色。
@@ -144,7 +144,20 @@ Gate: 用户已确认修复方向 ✓
144
144
  📍 Next: /ccg commit 提交修复
145
145
  ```
146
146
 
147
- **Task 更新**:`status → "completed"`, `nextAction → "可用 /ccg:commit 提交修复"`
147
+ #### Spec Evolution(归档前必须执行)
148
+
149
+ 参考 `phase-guide.md § 8 Spec Evolution Protocol` 执行:
150
+ 1. 分析本次调试的根因和修复方案,提炼可复用的调试经验和防御性编码约定
151
+ 2. 如有值得记录的经验(特别是非显而易见的坑)→ 草拟 Spec 条目,展示给用户确认后追加到 `.ccg/spec/{domain}/index.md`
152
+ 3. 无值得提炼的经验 → 跳过
153
+
154
+ **Task 更新**:`status → "archived"`
155
+
156
+ **归档任务**:
157
+ ```bash
158
+ mkdir -p .ccg/tasks/archive/$(date +%Y-%m) && mv .ccg/tasks/{task-name} .ccg/tasks/archive/$(date +%Y-%m)/
159
+ git add .ccg/tasks/ && git commit -m "chore: archive ccg task"
160
+ ```
148
161
 
149
162
  ---
150
163
 
@@ -225,21 +225,25 @@ SendMessage({ to: "reviewer", message: { type: "shutdown_request" } })
225
225
  2. 按 plan.md 中的 Layer 顺序逐文件实施
226
226
  3. 仍然遵守质量关卡
227
227
 
228
- ### Phase 5: 优化审查 + 质量关卡 [required]
228
+ ### Phase 5: 迭代审查 [required · Ralph Loop]
229
229
 
230
230
  `[模式:优化]`
231
231
 
232
232
  **Gate check**: 实施已完成
233
233
 
234
- **Task 更新**:`currentPhase → "5-optimization"`, `nextAction → "双模型审查 + 质量关卡"`
234
+ **Task 更新**:`currentPhase → "5-optimization"`, `nextAction → "Ralph Loop Round 1: 双模型审查 + 质量关卡"`
235
235
 
236
- #### 5a. 双模型交叉审查
236
+ 参考 `phase-guide.md § 10 Ralph Loop` 执行迭代审查。最多 3 轮。
237
237
 
238
- **并行调用**:
238
+ #### Round N 流程(N=1,2,3)
239
+
240
+ **5a. 双模型交叉审查(每轮 spawn 新 Agent,干净上下文)**
241
+
242
+ **并行调用**(`run_in_background: true`):
239
243
  - **backend 模型**:reviewer 角色 — 关注安全、性能、错误处理
240
244
  - **frontend 模型**:reviewer 角色 — 关注可访问性、设计一致性
241
245
 
242
- #### 5b. 质量关卡
246
+ **5b. 质量关卡**
243
247
 
244
248
  **⛔ 以下 Skill 必须逐个调用执行,不可跳过,不可用自己的判断替代:**
245
249
 
@@ -247,17 +251,35 @@ SendMessage({ to: "reviewer", message: { type: "shutdown_request" } })
247
251
  2. 调用 Skill `verify-quality` — 等待报告
248
252
  3. 调用 Skill `verify-change` — 等待报告
249
253
 
250
- 任何关卡报告 **Critical** → 必须修复后重新运行该关卡,才能进入 Phase 6。
251
-
252
- #### 5c. 综合报告
254
+ **5c. 综合报告**
253
255
 
254
256
  整合审查意见 + 质量关卡结果,按严重度分级:
255
257
  - **Critical**:必须修复(阻塞交付)
256
258
  - **Warning**:建议修复
257
259
  - **Info**:供参考
258
260
 
259
- 展示审查结果,用户确认后执行必要的优化。
260
- **持久化**:写入 `.ccg/tasks/{task-name}/review.md`
261
+ **持久化**:写入 `.ccg/tasks/{task-name}/review.md`(每轮覆盖)
262
+
263
+ 追加进度到 `.ccg/tasks/{task-name}/fix-log.jsonl`:
264
+ ```jsonl
265
+ {"round": N, "critical": X, "warning": Y, "info": Z, "ts": "ISO"}
266
+ ```
267
+
268
+ **5d. 用户决定(⛔ 必须等待)**
269
+
270
+ 展示审查结果后询问用户:
271
+ - 有 Critical → `发现 N 个 Critical 问题。修复后再审一轮?[Y/n]`
272
+ - 无 Critical 但有 Warning → `无 Critical 问题。需要再审一轮处理 Warning?[y/N]`
273
+ - 全部通过 → 直接进入 Phase 6
274
+
275
+ 用户选择继续 →
276
+ 1. spawn fix-dev(**新 Agent,干净上下文**)修复 Critical/Warning
277
+ 2. fix-dev 完成后回到 5a 开始 Round N+1
278
+ 3. 追加修复记录到 fix-log.jsonl
279
+
280
+ 用户选择停止 → 进入 Phase 6
281
+
282
+ **第 3 轮仍有 Critical** → 强制停止,建议回退到 Phase 3 重新规划。
261
283
 
262
284
  ### Phase 6: 最终验收
263
285
 
@@ -277,7 +299,28 @@ SendMessage({ to: "reviewer", message: { type: "shutdown_request" } })
277
299
  📍 Next: /ccg commit 提交,或查看 .ccg/tasks/{task-name}/ 中的完整记录
278
300
  ```
279
301
 
280
- **Task 更新**:`status → "completed"`, `nextAction → "可用 /ccg:commit 提交"`
302
+ #### Spec Evolution(归档前必须执行)
303
+
304
+ 参考 `phase-guide.md § 8 Spec Evolution Protocol` 执行:
305
+ 1. 分析本次 `git diff` + `review.md`,提炼可复用的编码约定和经验教训
306
+ 2. 如有值得记录的经验 → 草拟 Spec 条目,展示给用户确认后追加到 `.ccg/spec/{domain}/index.md`
307
+ 3. 无值得提炼的经验 → 跳过(不强行凑)
308
+
309
+ **Task 更新**:`status → "archived"`
310
+
311
+ **归档任务**:将 `.ccg/tasks/{task-name}/` 移动到 `.ccg/tasks/archive/YYYY-MM/{task-name}/`
312
+ ```bash
313
+ mkdir -p .ccg/tasks/archive/$(date +%Y-%m) && mv .ccg/tasks/{task-name} .ccg/tasks/archive/$(date +%Y-%m)/
314
+ ```
315
+
316
+ **自动提交归档**:
317
+ ```bash
318
+ git add .ccg/tasks/ && git commit -m "chore: archive ccg task {task-name}"
319
+ ```
320
+
321
+ ```
322
+ 📍 Next: /ccg:commit 提交产品代码
323
+ ```
281
324
 
282
325
  ---
283
326
 
@@ -161,34 +161,59 @@ TaskOutput({ task_id: "<id>", block: true, timeout: 600000 })
161
161
 
162
162
  **Task 更新**:`currentPhase → "5-implement"`, `nextAction → "按计划执行实施"`
163
163
 
164
- ### Phase 6: 验证 + 双模型审查 + 质量关卡
164
+ ### Phase 6: 迭代审查 [Ralph Loop]
165
165
 
166
166
  1. `git diff` 展示所有变更
167
167
  2. 运行测试(如果有)
168
168
 
169
- **⛔ 双模型交叉审查(变更 >30 行时必须执行):**
169
+ 参考 `phase-guide.md § 10 Ralph Loop` 执行迭代审查(变更 >30 行时,最多 3 轮)。
170
+
171
+ #### Round N 流程
172
+
173
+ **⛔ 双模型交叉审查(每轮 spawn 新调用,干净上下文):**
170
174
  3. 并行调用双模型(`run_in_background: true`,使用 model-router.md 模板):
171
175
  - backend 模型 + reviewer 角色 — 安全、性能、错误处理
172
176
  - frontend 模型 + reviewer 角色 — 设计一致性(如涉及前端)
173
177
  4. 综合审查意见
174
178
 
175
- **⛔ 质量关卡(变更 >30 行时必须逐个调用 Skill,不可跳过):**
179
+ **⛔ 质量关卡(必须逐个调用 Skill,不可跳过):**
176
180
  5. 调用 Skill `verify-quality` — 等待报告
177
181
  6. 调用 Skill `verify-security` — 等待报告(涉及 auth/input/crypto 时)
178
182
  7. 调用 Skill `verify-change` — 等待报告
179
- 8. Critical 问题必须修复
180
- 4. 检查是否满足验收标准
181
- 5. 输出结果:
183
+
184
+ **用户决定(⛔ 必须等待):**
185
+ - 有 Critical → `发现 N 个 Critical 问题。修复后再审一轮?[Y/n]`
186
+ - 无 Critical → `审查通过。需要再审一轮?[y/N]`
187
+ - 用户选择继续 → 修复 Critical 后回到 Round N+1
188
+ - 用户选择停止 → 退出审查循环
189
+
190
+ 追加进度到 `.ccg/tasks/{task-name}/fix-log.jsonl`。
191
+
192
+ 8. 检查是否满足验收标准
193
+ 9. 输出结果:
182
194
  ```
183
195
  ✅ 开发完成
184
196
  变更: [N] 文件,[M] 行
185
197
  实现: [摘要]
186
198
  测试: [通过/跳过/失败情况]
187
- 质量: [Critical: N, Warning: N, Info: N]
199
+ 审查: [N] 轮,[Critical: N, Warning: N, Info: N]
188
200
  📍 Next: 可以用 /ccg:commit 提交
189
201
  ```
190
202
 
191
- **Task 更新**:`status → "completed"`, `nextAction → "可用 /ccg:commit 提交"`
203
+ #### Spec Evolution(归档前必须执行)
204
+
205
+ 参考 `phase-guide.md § 8 Spec Evolution Protocol` 执行:
206
+ 1. 分析本次 `git diff` + 审查结果,提炼可复用的编码约定
207
+ 2. 如有值得记录的经验 → 草拟 Spec 条目,展示给用户确认后追加到 `.ccg/spec/{domain}/index.md`
208
+ 3. 无值得提炼的经验 → 跳过
209
+
210
+ **Task 更新**:`status → "archived"`
211
+
212
+ **归档任务**:
213
+ ```bash
214
+ mkdir -p .ccg/tasks/archive/$(date +%Y-%m) && mv .ccg/tasks/{task-name} .ccg/tasks/archive/$(date +%Y-%m)/
215
+ git add .ccg/tasks/ && git commit -m "chore: archive ccg task"
216
+ ```
192
217
 
193
218
  ---
194
219
 
@@ -110,12 +110,16 @@ Gate: 所有步骤已执行 ✓
110
110
  Step [N/M]: [描述] — ✅ 测试通过 / ❌ 测试失败
111
111
  ```
112
112
 
113
- ### Phase 5: 最终验证 + 双模型审查 + 质量关卡
113
+ ### Phase 5: 迭代审查 [Ralph Loop]
114
114
 
115
115
  1. 运行完整测试套件
116
116
  2. 对比基线:确保不引入新的失败
117
117
 
118
- **⛔ 双模型交叉审查(必须执行,使用 model-router.md 调用模板):**
118
+ 参考 `phase-guide.md § 10 Ralph Loop` 执行迭代审查(最多 3 轮)。
119
+
120
+ #### Round N 流程
121
+
122
+ **⛔ 双模型交叉审查(每轮 spawn 新调用,干净上下文):**
119
123
 
120
124
  3. 获取变更:`git diff` 全量输出
121
125
  4. 并行调用双模型审查(`run_in_background: true`):
@@ -129,23 +133,42 @@ Step [N/M]: [描述] — ✅ 测试通过 / ❌ 测试失败
129
133
  7. 调用 Skill `verify-security` — 等待报告
130
134
  8. 调用 Skill `verify-change` — 等待报告
131
135
 
132
- **综合报告**:双模型审查 + 质量关卡,按严重度分级:
133
- - Critical → 必须修复后重新验证
134
- - Warning → 建议修复
135
- - Info供参考
136
+ **综合报告**:双模型审查 + 质量关卡,按严重度分级
137
+
138
+ **用户决定(⛔ 必须等待):**
139
+ - Critical `发现 N 个 Critical 问题。修复后再审一轮?[Y/n]`
140
+ - 无 Critical → `审查通过。需要再审一轮?[y/N]`
141
+ - 用户选择继续 → 修复后回到 Round N+1
142
+ - 用户选择停止 → 退出审查循环
143
+
144
+ 追加进度到 `.ccg/tasks/{task-name}/fix-log.jsonl`。
136
145
 
137
146
  9. `git diff` 展示全部变更
138
- 5. 输出结果:
147
+ 10. 对比基线,确认无回归
148
+ 11. 输出结果:
139
149
  ```
140
150
  ✅ 重构完成
141
151
  步骤: [N] 步全部通过
142
152
  变更: [文件数] 文件,[行数] 行
143
153
  测试: 基线 [N] 通过 → 重构后 [N] 通过
144
- 质量: [Critical: N, Warning: N, Info: N]
154
+ 审查: [N] 轮,[Critical: N, Warning: N, Info: N]
145
155
  📍 Next: /ccg:commit 提交
146
156
  ```
147
157
 
148
- **Task 更新**:`status → "completed"`, `nextAction → "可用 /ccg:commit 提交"`
158
+ #### Spec Evolution(归档前必须执行)
159
+
160
+ 参考 `phase-guide.md § 8 Spec Evolution Protocol` 执行:
161
+ 1. 分析本次重构的 `git diff`,提炼可复用的重构模式和架构约定
162
+ 2. 如有值得记录的经验 → 草拟 Spec 条目,展示给用户确认后追加到 `.ccg/spec/{domain}/index.md`
163
+ 3. 无值得提炼的经验 → 跳过
164
+
165
+ **Task 更新**:`status → "archived"`
166
+
167
+ **归档任务**:
168
+ ```bash
169
+ mkdir -p .ccg/tasks/archive/$(date +%Y-%m) && mv .ccg/tasks/{task-name} .ccg/tasks/archive/$(date +%Y-%m)/
170
+ git add .ccg/tasks/ && git commit -m "chore: archive ccg task"
171
+ ```
149
172
 
150
173
  ---
151
174
 
@@ -106,6 +106,13 @@ Gate: 双模型审查已返回 ✓
106
106
 
107
107
  如果有 Critical 发现,询问用户是否立即修复(可切换到 `direct-fix` 策略)。
108
108
 
109
+ #### Spec Evolution(审查完成后执行)
110
+
111
+ 参考 `phase-guide.md § 8 Spec Evolution Protocol` 执行:
112
+ 1. 从审查发现中提炼可复用的编码规范(特别是 Critical/Warning 级反复出现的模式)
113
+ 2. 如有值得记录的经验 → 草拟 Spec 条目,展示给用户确认后追加到 `.ccg/spec/{domain}/index.md`
114
+ 3. 无值得提炼的经验 → 跳过
115
+
109
116
  ---
110
117
 
111
118
  ## 铁律
@@ -3,6 +3,8 @@
3
3
  // Injects spec + task context when:
4
4
  // 1. codeagent-wrapper is about to be called (Bash)
5
5
  // 2. Agent Team member is about to be spawned (Agent)
6
+ // Supports role-based filtering: context.jsonl entries with "roles" field
7
+ // are only injected when the agent's detected role matches.
6
8
 
7
9
  'use strict';
8
10
 
@@ -28,12 +30,11 @@ try {
28
30
  // Determine trigger type
29
31
  const command = toolInput.command || '';
30
32
  const teamName = toolInput.team_name || '';
31
- const agentPrompt = toolInput.prompt || '';
33
+ const agentName = toolInput.name || '';
32
34
 
33
35
  const isCodeagentCall = command.includes('codeagent-wrapper');
34
36
  const isTeamSpawn = !!teamName;
35
37
 
36
- // Only activate for codeagent-wrapper calls or Agent Team spawns
37
38
  if (!isCodeagentCall && !isTeamSpawn) {
38
39
  process.exit(0);
39
40
  }
@@ -45,20 +46,60 @@ try {
45
46
  const task = getActiveTask(root);
46
47
  if (!task) process.exit(0);
47
48
 
49
+ // --- Role detection ---
50
+ const ROLE_FILE_MAP = {
51
+ 'reviewer': 'review',
52
+ 'analyzer': 'research',
53
+ 'debugger': 'debug',
54
+ 'tester': 'review',
55
+ 'architect': 'implement',
56
+ 'optimizer': 'implement',
57
+ 'frontend': 'implement'
58
+ };
59
+ const AGENT_NAME_PATTERNS = [
60
+ { pattern: /dev|builder|fix|impl/i, role: 'implement' },
61
+ { pattern: /review|check|audit/i, role: 'review' },
62
+ { pattern: /research|scout|explore|analy/i, role: 'research' },
63
+ { pattern: /debug|diagnos/i, role: 'debug' }
64
+ ];
65
+
66
+ let detectedRole = 'implement'; // default
67
+
68
+ if (isCodeagentCall) {
69
+ const roleMatch = command.match(/ROLE_FILE:.*\/(\w+)\.md/);
70
+ if (roleMatch) {
71
+ detectedRole = ROLE_FILE_MAP[roleMatch[1]] || 'implement';
72
+ }
73
+ } else if (isTeamSpawn && agentName) {
74
+ for (const { pattern, role } of AGENT_NAME_PATTERNS) {
75
+ if (pattern.test(agentName)) {
76
+ detectedRole = role;
77
+ break;
78
+ }
79
+ }
80
+ }
81
+
48
82
  const contextParts = [];
49
83
 
50
- // Inject active task info for team members
51
84
  if (isTeamSpawn) {
52
85
  contextParts.push(`<ccg-active-task>
53
86
  Active task: ${task.dir}
54
87
  Task: ${task.title || task.id} (${task.status})
55
88
  Strategy: ${task.strategy}
56
89
  Phase: ${task.currentPhase}
90
+ Agent role: ${detectedRole}
57
91
  </ccg-active-task>`);
58
92
  }
59
93
 
60
- // Read context.jsonl entries (specs + research refs)
61
- const entries = readContextJsonl(task.dir);
94
+ // Read context.jsonl with role-based filtering
95
+ const allEntries = readContextJsonl(task.dir);
96
+ const entries = allEntries.filter(entry => {
97
+ if (!entry.roles || !Array.isArray(entry.roles) || entry.roles.length === 0) {
98
+ return true; // no roles field = inject to all
99
+ }
100
+ return entry.roles.includes(detectedRole) || entry.roles.includes('all');
101
+ });
102
+
62
103
  if (entries.length > 0) {
63
104
  const specContents = [];
64
105
  for (const entry of entries) {
@@ -75,7 +116,7 @@ Phase: ${task.currentPhase}
75
116
  }
76
117
  }
77
118
 
78
- // Read PRD and plan
119
+ // Read PRD and plan (always inject, role-independent)
79
120
  const prd = readFileSafe(path.join(task.dir, 'requirements.md'));
80
121
  const plan = readFileSafe(path.join(task.dir, 'plan.md'));
81
122
 
@@ -93,21 +134,23 @@ Phase: ${task.currentPhase}
93
134
  contextParts.push(taskContext.join('\n'));
94
135
  }
95
136
 
96
- // Read research files
97
- const researchDir = path.join(task.dir, 'research');
98
- if (fs.existsSync(researchDir)) {
99
- try {
100
- const researchFiles = fs.readdirSync(researchDir).filter(f => f.endsWith('.md'));
101
- if (researchFiles.length > 0) {
102
- const researchContents = researchFiles.map(f => {
103
- const content = readFileSafe(path.join(researchDir, f));
104
- return content ? `--- research/${f} ---\n${content.substring(0, 1500)}` : null;
105
- }).filter(Boolean);
106
- if (researchContents.length > 0) {
107
- contextParts.push(`<ccg-research>\n${researchContents.join('\n\n')}\n</ccg-research>`);
137
+ // Read research files (only for research + implement roles)
138
+ if (detectedRole === 'research' || detectedRole === 'implement') {
139
+ const researchDir = path.join(task.dir, 'research');
140
+ if (fs.existsSync(researchDir)) {
141
+ try {
142
+ const researchFiles = fs.readdirSync(researchDir).filter(f => f.endsWith('.md'));
143
+ if (researchFiles.length > 0) {
144
+ const researchContents = researchFiles.map(f => {
145
+ const content = readFileSafe(path.join(researchDir, f));
146
+ return content ? `--- research/${f} ---\n${content.substring(0, 1500)}` : null;
147
+ }).filter(Boolean);
148
+ if (researchContents.length > 0) {
149
+ contextParts.push(`<ccg-research>\n${researchContents.join('\n\n')}\n</ccg-research>`);
150
+ }
108
151
  }
109
- }
110
- } catch { /* silent */ }
152
+ } catch { /* silent */ }
153
+ }
111
154
  }
112
155
 
113
156
  if (contextParts.length === 0) process.exit(0);
@@ -25,9 +25,10 @@ function getActiveTask(projectRoot) {
25
25
  try {
26
26
  const dirs = fs.readdirSync(tasksDir)
27
27
  .filter(d => {
28
+ if (d === 'archive') return false;
28
29
  try {
29
- return fs.statSync(path.join(tasksDir, d)).isDirectory()
30
- && fs.existsSync(path.join(tasksDir, d, 'task.json'));
30
+ const full = path.join(tasksDir, d);
31
+ return fs.statSync(full).isDirectory() && fs.existsSync(path.join(full, 'task.json'));
31
32
  } catch { return false; }
32
33
  })
33
34
  .sort()
@@ -35,10 +36,12 @@ function getActiveTask(projectRoot) {
35
36
 
36
37
  for (const dir of dirs) {
37
38
  try {
38
- const raw = fs.readFileSync(path.join(tasksDir, dir, 'task.json'), 'utf-8');
39
+ const taskPath = path.join(tasksDir, dir, 'task.json');
40
+ if (!fs.existsSync(taskPath)) continue; // stale pointer detection
41
+ const raw = fs.readFileSync(taskPath, 'utf-8');
39
42
  const task = JSON.parse(raw);
40
43
  if (task.status !== 'completed' && task.status !== 'archived') {
41
- return { dir: path.join(tasksDir, dir), ...task };
44
+ return { dir: path.join(tasksDir, dir), ...task, _stale: false };
42
45
  }
43
46
  } catch { /* skip malformed */ }
44
47
  }
@@ -101,6 +104,75 @@ function outputHook(eventName, additionalContext) {
101
104
  }));
102
105
  }
103
106
 
107
+ function archiveTask(taskDir, projectRoot) {
108
+ try {
109
+ const now = new Date();
110
+ const month = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}`;
111
+ const archiveDir = path.join(projectRoot, '.ccg', 'tasks', 'archive', month);
112
+ if (!fs.existsSync(archiveDir)) fs.mkdirSync(archiveDir, { recursive: true });
113
+ const name = path.basename(taskDir);
114
+ const dest = path.join(archiveDir, name);
115
+ fs.renameSync(taskDir, dest);
116
+ return dest;
117
+ } catch { return null; }
118
+ }
119
+
120
+ function autoCommitTask(projectRoot, message) {
121
+ try {
122
+ const { execSync } = require('child_process');
123
+ execSync('git add .ccg/tasks/', { cwd: projectRoot, stdio: 'pipe' });
124
+ const diff = execSync('git diff --cached --quiet', { cwd: projectRoot, stdio: 'pipe' }).toString();
125
+ return false; // nothing to commit
126
+ } catch {
127
+ try {
128
+ const { execSync } = require('child_process');
129
+ execSync(`git commit -m "${message || 'chore: archive ccg task'}"`, { cwd: projectRoot, stdio: 'pipe' });
130
+ return true;
131
+ } catch { return false; }
132
+ }
133
+ }
134
+
135
+ function seedContextJsonl(taskDir, projectRoot) {
136
+ const jsonlPath = path.join(taskDir, 'context.jsonl');
137
+ if (fs.existsSync(jsonlPath)) return;
138
+ const specDir = path.join(projectRoot, '.ccg', 'spec');
139
+ const lines = ['{"_example": "Fill with {\\\"file\\\": \\\"path\\\", \\\"reason\\\": \\\"why\\\"}. One entry per line. Seed rows (with _example key) are skipped."}'];
140
+ if (fs.existsSync(specDir)) {
141
+ try {
142
+ const walk = (dir, prefix) => {
143
+ for (const e of fs.readdirSync(dir, { withFileTypes: true })) {
144
+ const rel = prefix ? `${prefix}/${e.name}` : e.name;
145
+ if (e.isDirectory()) walk(path.join(dir, e.name), rel);
146
+ else if (e.name.endsWith('.md')) lines.push(JSON.stringify({ file: `.ccg/spec/${rel}`, reason: 'project spec' }));
147
+ }
148
+ };
149
+ walk(specDir, '');
150
+ } catch { /* silent */ }
151
+ }
152
+ try { fs.writeFileSync(jsonlPath, lines.join('\n') + '\n', 'utf-8'); } catch { /* silent */ }
153
+ }
154
+
155
+ function trackTurn(taskDir, phase, nextAction) {
156
+ const turnsPath = path.join(taskDir, '.turns.json');
157
+ let turns = [];
158
+ try { turns = JSON.parse(fs.readFileSync(turnsPath, 'utf-8')); } catch { /* fresh */ }
159
+ turns.push({ phase: phase || '', next: nextAction || '', ts: Date.now() });
160
+ if (turns.length > 10) turns = turns.slice(-10);
161
+ try { fs.writeFileSync(turnsPath, JSON.stringify(turns), 'utf-8'); } catch { /* silent */ }
162
+ return turns;
163
+ }
164
+
165
+ function detectLoop(turns, threshold) {
166
+ threshold = threshold || 3;
167
+ if (turns.length < threshold) return null;
168
+ const recent = turns.slice(-threshold);
169
+ const key = `${recent[0].phase}|${recent[0].next}`;
170
+ const allSame = recent.every(t => `${t.phase}|${t.next}` === key);
171
+ if (!allSame) return null;
172
+ const elapsed = (recent[recent.length - 1].ts - recent[0].ts) / 1000;
173
+ return { phase: recent[0].phase, nextAction: recent[0].next, count: threshold, elapsedSec: Math.round(elapsed) };
174
+ }
175
+
104
176
  module.exports = {
105
177
  findProjectRoot,
106
178
  getActiveTask,
@@ -109,5 +181,10 @@ module.exports = {
109
181
  readContextJsonl,
110
182
  detectTechStack,
111
183
  getGitInfo,
112
- outputHook
184
+ outputHook,
185
+ archiveTask,
186
+ autoCommitTask,
187
+ seedContextJsonl,
188
+ trackTurn,
189
+ detectLoop
113
190
  };
@@ -1,12 +1,13 @@
1
1
  #!/usr/bin/env node
2
2
  // CCG Workflow State Hook — UserPromptSubmit
3
3
  // Injects per-turn breadcrumb based on active task state.
4
+ // Includes loop detection: warns when same phase+nextAction repeats 3+ turns.
4
5
  // Runs on EVERY user message. Must be fast (<1s) and never crash.
5
6
 
6
7
  'use strict';
7
8
 
8
9
  try {
9
- const { findProjectRoot, getActiveTask, outputHook } = require('./task-utils.js');
10
+ const { findProjectRoot, getActiveTask, outputHook, trackTurn, detectLoop } = require('./task-utils.js');
10
11
 
11
12
  const cwd = process.env.CLAUDE_PROJECT_DIR || process.cwd();
12
13
  const root = findProjectRoot(cwd);
@@ -19,6 +20,9 @@ try {
19
20
  process.exit(0);
20
21
  }
21
22
 
23
+ const turns = trackTurn(task.dir, task.currentPhase, task.nextAction);
24
+ const loop = detectLoop(turns, 3);
25
+
22
26
  const lines = [
23
27
  '<ccg-state>',
24
28
  `Task: ${task.title || task.id} (${task.status})`,
@@ -31,6 +35,18 @@ try {
31
35
  }
32
36
 
33
37
  lines.push(`Next: ${task.nextAction || 'Continue current phase'}`);
38
+
39
+ if (loop) {
40
+ lines.push('');
41
+ lines.push(`⚠️ LOOP DETECTED: Phase "${loop.phase}" with same nextAction repeated ${loop.count} turns (${loop.elapsedSec}s).`);
42
+ lines.push('🔄 BREAK-LOOP PROTOCOL:');
43
+ lines.push(' 1. STOP current approach immediately');
44
+ lines.push(' 2. Root-cause analysis: why is this phase not progressing?');
45
+ lines.push(' 3. Options: (a) try alternative approach, (b) escalate to user, (c) upgrade strategy');
46
+ lines.push(' 4. If blocked by external dependency → tell user explicitly');
47
+ lines.push(' 5. Do NOT repeat the same action — that is what caused this loop');
48
+ }
49
+
34
50
  lines.push('</ccg-state>');
35
51
 
36
52
  outputHook('UserPromptSubmit', lines.join('\n'));
@@ -4,7 +4,11 @@ When the user's request matches trigger keywords below, automatically READ the c
4
4
 
5
5
  **IMPORTANT**: Read the skill file FIRST, then respond. Do NOT fabricate domain knowledge from training data when a skill file exists.
6
6
 
7
- ## Security Domain (`domains/security/`)
7
+ ## Security Domain (`domains/security/`) — NOT installed by default
8
+
9
+ > Security domain files contain red team/pentest reference content that may trigger antivirus false positives.
10
+ > They are NOT installed by default. To enable, manually copy from the npm package:
11
+ > `cp -r $(npm root -g)/ccg-workflow/templates/skills/domains/security/ ~/.claude/skills/ccg/domains/security/`
8
12
 
9
13
  | Trigger Keywords | Skill File | Description |
10
14
  |------------------|-----------|-------------|