From fa6cc32dfc87b1d63c0fd126b8039baeb6f3a62c Mon Sep 17 00:00:00 2001 From: Delicious233 Date: Sun, 7 Jun 2026 01:56:24 +0800 Subject: [PATCH 01/39] =?UTF-8?q?docs(agent):=20=E5=AF=B9=E9=BD=90?= =?UTF-8?q?=E6=9C=80=E6=96=B0=20subagent=20=E5=88=86=E5=B7=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .agents/skills/dev-loop/SKILL.md | 33 +++++++++-------- .../dev-loop/references/model-strategy.md | 36 ++++++++++--------- .agents/skills/dev-team/SKILL.md | 26 +++++++------- AGENTS.md | 16 +++++---- 4 files changed, 60 insertions(+), 51 deletions(-) diff --git a/.agents/skills/dev-loop/SKILL.md b/.agents/skills/dev-loop/SKILL.md index c11dad3f..bfc6fb1f 100644 --- a/.agents/skills/dev-loop/SKILL.md +++ b/.agents/skills/dev-loop/SKILL.md @@ -14,15 +14,17 @@ description: "自主开发推进引擎——ROADMAP 驱动、模型分配、并 | 入口 | 别名/模型 | 上下文 | 强项 | 派发策略 | |---|---|---:|---|---| -| Codex 自带 agent 工具 | GPT-5.5 | 256k | 全方面强,代码、agentic 执行、审查都稳 | 中等上下文内的核心实现、跨前后端小集成、关键 review | -| Claude CLI | **opus** = DeepSeek-V4-Pro | 1M | 速度快、强推理、长上下文 | 大范围阅读、路线图/架构判断、复杂设计评审、安全/方案审查 | -| Claude CLI | **sonnet** = GLM-5.1 | 200k | 强代码和 agentic 能力 | 窄范围代码实现、测试修复、Go/TS 小切片 | +| Codex 自带 agent 工具 | GPT-5.5 low/mid | 256k | 前端、看图、UI/视觉判断、常规实现和审查 | 前端 UI、截图对比、局部体验判断、常规 code review | +| Codex 自带 agent 工具 | GPT-5.5 xhigh | 256k | 最强架构推理和复杂工程设计 | 复杂架构、关键方案、跨模块取舍、高强度 sidecar | +| Claude CLI | **sonnet** = DeepSeek-V4-Pro | 1M | 长上下文、找东西、长文本整理、架构整理 | 大范围阅读、文档整理、roadmap/architecture 归纳、竞品/仓库查找 | +| Claude CLI | **opus** = GLM-5.1 | 200k | 强代码和 agentic 能力 | 窄范围代码实现、测试修复、Go/TS 小切片 | | Claude CLI | **haiku** = DeepSeek-V4-Flash | 200k | 速度快、轻量反馈 | 快速检查、轻量 review、日志/文档/小范围 UI 可读性审查 | - **主 Agent**:设计决策、审查输出、编辑核心文件(AGENTS.md/STATE.md/ROADMAP.md)。 -- **Codex GPT-5.5 subagent**:工具可用时优先派给高价值代码实现和强 review;不要给超 256k 的大仓库研究。 -- **Claude opus**:DeepSeek-V4-Pro,1M 上下文,长上下文推理、竞品研究、安全/架构审查。 -- **Claude sonnet**:GLM-5.1,200k 上下文,明确路径内的实现和 focused tests;prompt 精简,只传必要文件。 +- **Codex GPT-5.5 low/mid**:前端、看图、截图对比、常规 UI/UX 判断。 +- **Codex GPT-5.5 xhigh**:复杂架构推理、关键方案和高风险设计复核。 +- **Claude sonnet**:DeepSeek-V4-Pro,1M 上下文,长文本、找东西、简单文档、架构整理和大范围归纳。 +- **Claude opus**:GLM-5.1,200k 上下文,明确路径内的实现和 focused tests;prompt 精简,只传必要文件。 - **Claude haiku**:DeepSeek-V4-Flash,200k 上下文,快速检查、轻量 review、日志/文档/小范围 UI 可读性审查,不作为代码主力。 ## CC 原生工具配合 @@ -59,14 +61,15 @@ dev-loop 配合两个 CC 内置命令使用效果最好: ### 3. 执行 - **自己(主 session)**:设计决策、审查输出、编辑核心文件(AGENTS.md/STATE.md/ROADMAP.md) -- **派 Codex GPT-5.5 subagent**:中等上下文内的核心实现、跨模块小集成、关键代码 review -- **派 Claude opus**:复杂架构推理、长上下文研究、安全审查、多维度审计 -- **派 Claude sonnet**:窄范围编码实现、bug 修复、focused tests +- **派 GPT-5.5 low/mid**:前端 UI、看图、截图对比、局部体验判断 +- **派 GPT-5.5 xhigh**:复杂架构、关键方案、跨模块取舍 +- **派 Claude sonnet**:长文本、找东西、简单文档、架构整理、大范围归纳 +- **派 Claude opus**:窄范围编码实现、bug 修复、focused tests - **派 Claude haiku**:快速检查、轻量 review、日志/文档/小范围 UI 可读性审查 - 每次 subagent 完成后审查其输出 ### 4. 审查 -- 完成一批变更后启动交叉审查:按维度混用 Codex GPT-5.5、Claude opus、Claude sonnet、Claude haiku +- 完成一批变更后启动交叉审查:按维度混用 GPT-5.5 low/mid/xhigh、Claude opus、Claude sonnet、Claude haiku - 维度:结构、文档、安全、架构、易用性、视觉 QA - 让其他 agent 提问题:"审查这个变更,列出你担心的问题" - 修复高优先级项 @@ -101,11 +104,11 @@ dev-loop 配合两个 CC 内置命令使用效果最好: | 维度 | 模型 | 为什么 | |---|---|---| | 结构 | sonnet | 机械检查,批量扫文件 | -| 文档 | sonnet | 一致性检查,不重推理 | -| 安全 | **opus** | 必须深度推理 | -| 架构 | **opus** | 需要设计判断 | -| 易用性 | sonnet | 清单式检查 | -| 业务逻辑 | **haiku** | 简短复杂逻辑审查 | +| 文档 | sonnet | 一致性检查、整理和归纳 | +| 安全 | **GPT-5.5 xhigh** | 必须深度推理 | +| 架构 | **GPT-5.5 xhigh** | 需要设计判断 | +| 易用性 | GPT-5.5 low/mid | 前端体验和截图判断 | +| 业务逻辑 | **haiku** | 快速轻量逻辑检查 | 审查 agent 的 prompt 要具体:告诉它查什么、怎么报告、文件在哪。 diff --git a/.agents/skills/dev-loop/references/model-strategy.md b/.agents/skills/dev-loop/references/model-strategy.md index 56600bb3..2baf8c76 100644 --- a/.agents/skills/dev-loop/references/model-strategy.md +++ b/.agents/skills/dev-loop/references/model-strategy.md @@ -6,15 +6,16 @@ | 入口 | 别名/模型 | 上下文 | 优势 | 限制 | |---|---|---:|---|---| -| Codex 自带 agent 工具 | GPT-5.5 | 256k | 全方面强,代码、agentic 执行、审查都稳 | 上下文不如 Claude opus,不能吃超大仓库研究 | -| Claude CLI | **opus** = DeepSeek-V4-Pro | 1M | 速度快、强推理、长上下文,适合架构设计、安全审查、竞品仓库研究 | 代码实现不作为首选 | -| Claude CLI | **sonnet** = GLM-5.1 | 200k | 强代码和 agentic 能力,适合聚焦实现 | 不要给大批量阅读 | +| Codex 自带 agent 工具 | GPT-5.5 low/mid | 256k | 前端、看图、UI/视觉判断、常规实现和审查 | 不吃超大仓库研究 | +| Codex 自带 agent 工具 | GPT-5.5 xhigh | 256k | 最强架构推理和复杂工程设计 | 上下文仍不适合超大仓库全文阅读 | +| Claude CLI | **sonnet** = DeepSeek-V4-Pro | 1M | 长上下文、找东西、长文本整理、架构整理 | 代码实现不作为首选 | +| Claude CLI | **opus** = GLM-5.1 | 200k | 强代码和 agentic 能力,适合聚焦实现 | 不要给大批量阅读 | | Claude CLI | **haiku** = DeepSeek-V4-Flash | 200k | 速度快、轻量反馈,适合快速检查、轻量 review、日志/文档/小范围 UI 可读性审查 | 不作为代码主力 | ## 选择原则 - **先看入口**:Codex 自带 agent 工具和 Claude CLI 是两套执行面,不能把别名混用。 -- **先限上下文**:超过 256k 的研究、竞品仓库阅读、跨大量文件审查优先 Claude opus;不超过 256k 的核心代码实现优先 Codex GPT-5.5。 +- **先限上下文**:超过 256k 的研究、竞品仓库阅读、跨大量文件审查优先 Claude sonnet;复杂架构判断优先 GPT-5.5 xhigh;明确文件集代码实现优先 Claude opus。 - **先限写入范围**:任何编码 subagent 都必须有允许路径、禁止范围、验收命令和证据输出。 - **轻量检查单独派发**:快速 sanity check、日志/文档/小范围 UI 可读性审查优先 Claude haiku,不让代码主力消耗在低风险扫读上。 @@ -23,22 +24,22 @@ ``` 任务类型? ├── 核心实现 / 跨前后端小集成 -│ ├── 上下文 <= 256k → Codex GPT-5.5 subagent -│ └── 上下文 > 256k → 拆小;设计交给 Claude opus,代码交给 GPT-5.5/sonnet +│ ├── 上下文 <= 256k → GPT-5.5 low/mid 或 xhigh,按复杂度选择 +│ └── 上下文 > 256k → Claude sonnet 先整理,再拆给实现 agent ├── 窄范围代码修复(明确 1-3 个文件) -│ ├── Go/TS/测试小切片 → Claude sonnet(GLM-5.1) -│ └── 高风险实现 review → Codex GPT-5.5 或 Claude opus 复核 +│ ├── Go/TS/测试小切片 → Claude opus(GLM-5.1) +│ └── 高风险实现 review → GPT-5.5 xhigh 或主 Agent 复核 ├── 长上下文推理 / 架构 / 安全 / 竞品仓库研究 -│ └── Claude opus(DeepSeek-V4-Pro, 1M) +│ └── GPT-5.5 xhigh(复杂架构)或 Claude sonnet(长文本/找东西/整理) ├── 截图 / 竞品图 / 视觉 QA / UI 可读性 │ └── Claude haiku(DeepSeek-V4-Flash,200k,快速轻量反馈) ├── 机械批量文档或格式统一 -│ ├── 中等上下文 → Codex GPT-5.5 -│ └── 超大上下文或需要归纳 → Claude opus 先规划,再分片执行 +│ ├── 中等上下文 → GPT-5.5 low/mid +│ └── 超大上下文或需要归纳 → Claude sonnet 先整理,再分片执行 └── 交叉审查 - ├── 安全/架构/长期方向 → Claude opus - ├── 代码正确性/集成风险 → Codex GPT-5.5 - ├── 小范围实现细节 → Claude sonnet + ├── 安全/架构/长期方向 → GPT-5.5 xhigh + ├── 代码正确性/集成风险 → GPT-5.5 xhigh 或主 Agent + ├── 小范围实现细节 → Claude opus └── 小范围 UI 可读性/布局文字检查 → Claude haiku ``` @@ -46,9 +47,10 @@ | Agent | 上限 | 策略 | |---|---:|---| -| Codex GPT-5.5 | 256k | 给完整任务卡 + 必要文件;适合强实现和强 review | -| Claude opus | 1M | DeepSeek-V4-Pro;可给大仓库、大量文档、竞品源码;产出方案/审查,不直接机械改大批文件 | -| Claude sonnet | 200k | GLM-5.1;prompt 精简,只传相关文件;适合窄范围代码和测试 | +| GPT-5.5 low/mid | 256k | 前端、看图、截图对比、常规 UI/UX 判断 | +| GPT-5.5 xhigh | 256k | 复杂架构、关键方案、高风险设计复核 | +| Claude sonnet | 1M | DeepSeek-V4-Pro;长文本、找东西、简单文档、架构整理、大范围归纳 | +| Claude opus | 200k | GLM-5.1;prompt 精简,只传相关文件;适合窄范围代码和测试 | | Claude haiku | 200k | DeepSeek-V4-Flash;快速检查、轻量 review、日志/文档/小范围 UI 可读性审查 | ## 并行度 diff --git a/.agents/skills/dev-team/SKILL.md b/.agents/skills/dev-team/SKILL.md index 4bb3d706..a2ce92d3 100644 --- a/.agents/skills/dev-team/SKILL.md +++ b/.agents/skills/dev-team/SKILL.md @@ -11,12 +11,12 @@ description: 多 Team 并行开发引擎 — 大规模 Issue 修复、跨模块 ``` 你(主 Agent) - ├── Team Leader 1 (Codex GPT-5.5 或 Claude opus) → Worktree A - │ ├── Worker 1 (GPT-5.5 / Claude sonnet) → 修 1-3 issues - │ ├── Worker 2 (GPT-5.5 / Claude sonnet) → 修 1-3 issues + ├── Team Leader 1 (主 Agent 或 GPT-5.5 xhigh) → Worktree A + │ ├── Worker 1 (Claude opus / GPT-5.5 low-mid) → 修 1-3 issues + │ ├── Worker 2 (Claude opus / GPT-5.5 low-mid) → 修 1-3 issues │ ├── Worker 3 (Claude haiku) → 快速检查 / 轻量 review(如需要) - │ └── Worker 4 (GPT-5.5 / opus) → 测试 + 审查 - ├── Team Leader 2 (Codex GPT-5.5 或 Claude opus) → Worktree B + │ └── Worker 4 (GPT-5.5 xhigh / 主 Agent) → 测试 + 审查 + ├── Team Leader 2 (主 Agent 或 GPT-5.5 xhigh) → Worktree B │ └── ... (同上) └── ... (最多 5 个 Team 并行) ``` @@ -25,9 +25,10 @@ description: 多 Team 并行开发引擎 — 大规模 Issue 修复、跨模块 | Agent | 上下文 | 定位 | |---|---:|---| -| Codex GPT-5.5 subagent | 256k | 全方面强,适合核心实现、跨模块小集成、强代码 review | -| Claude opus = DeepSeek-V4-Pro | 1M | 速度快、强推理、长上下文;适合架构、安全、竞品仓库研究 | -| Claude sonnet = GLM-5.1 | 200k | 强代码和 agentic 能力;适合明确文件范围内的实现和测试 | +| GPT-5.5 low/mid | 256k | 前端、看图、截图对比、常规 UI/UX 判断 | +| GPT-5.5 xhigh | 256k | 复杂架构、关键方案、高风险设计复核 | +| Claude sonnet = DeepSeek-V4-Pro | 1M | 长文本、找东西、简单文档、架构整理、大范围归纳 | +| Claude opus = GLM-5.1 | 200k | 强代码和 agentic 能力;适合明确文件范围内的实现和测试 | | Claude haiku = DeepSeek-V4-Flash | 200k | 速度快、轻量反馈;适合快速检查、轻量 review、日志/文档/小范围 UI 可读性审查 | ## 何时使用 @@ -91,10 +92,11 @@ You are Team Leader for {team_name}. Fix {N} issues ({batch_name}). 1. Create worktree: git worktree add .worktrees/{worktree_name} -b feat/{branch_name} 2. Read key source files: {file_list} 3. Spawn workers by task type: - - Codex GPT-5.5: core implementation / integration review (<=256k context) - - Claude sonnet: narrow code fixes with explicit file whitelist + - GPT-5.5 low/mid: frontend, screenshots, visual/UI review + - GPT-5.5 xhigh: complex architecture and high-risk review + - Claude sonnet: long-text search, docs, architecture整理 + - Claude opus: narrow code fixes with explicit file whitelist - Claude haiku: fast lightweight checks and narrow UI readability review - - Claude opus: long-context architecture/security review 4. Each worker: read → write failing test → implement fix → go test passes 5. Review all work, resolve conflicts, go test -race, commit 6. Push branch @@ -152,7 +154,7 @@ git branch -d feat/team-* ``` 输入:129 个 Issue,按 label 分组为 5 个批次 Team 数:5 -每个 Team:1 Leader + 3-4 Workers,按任务类型混用 GPT-5.5 / opus / sonnet / haiku +每个 Team:1 Leader + 3-4 Workers,按任务类型混用 GPT-5.5 low/mid/xhigh、opus、sonnet、haiku 总 agent 数:约 20-25 Worktree 数:5 diff --git a/AGENTS.md b/AGENTS.md index 68793cd6..b7b47713 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -184,15 +184,17 @@ git status --short --branch # 确认只改了允许的路径 | 入口 | 别名/模型 | 上下文 | 强项 | 优先使用场景 | |---|---|---:|---|---| -| Codex 自带 agent 工具 | GPT-5.5 | 256k | 全方面强,代码、agentic 执行、审查都稳 | 中等上下文内的核心实现、跨前后端小集成、主 Agent 复核前的强力 sidecar | -| Claude CLI | **opus** = DeepSeek-V4-Pro | 1M | 速度快、强推理、长上下文 | 大范围阅读、路线图/架构判断、复杂设计评审、安全/方案审查 | -| Claude CLI | **sonnet** = GLM-5.1 | 200k | 强代码和 agentic 能力 | 窄范围代码实现、测试修复、Go/TS 小切片、明确文件集的重构 | +| Codex 自带 agent 工具 | GPT-5.5 low/mid | 256k | 前端、看图、UI/视觉判断、常规实现和审查 | 前端 UI、截图对比、局部体验判断、常规 code review | +| Codex 自带 agent 工具 | GPT-5.5 xhigh | 256k | 最强架构推理和复杂工程设计 | 复杂架构、关键方案、跨模块取舍、主 Agent 复核前的高强度 sidecar | +| Claude CLI | **sonnet** = DeepSeek-V4-Pro | 1M | 长上下文、找东西、长文本整理、架构整理 | 大范围阅读、文档整理、roadmap/architecture 归纳、竞品/仓库查找 | +| Claude CLI | **opus** = GLM-5.1 | 200k | 强代码和 agentic 能力 | 窄范围代码实现、测试修复、Go/TS 小切片、明确文件集的重构 | | Claude CLI | **haiku** = DeepSeek-V4-Flash | 200k | 速度快、轻量反馈 | 快速检查、轻量 review、日志/文档/小范围 UI 可读性审查 | -- **主 Agent(本 session)**:负责决策、分支治理、提交、roadmap、比赛材料和最终验收。 -- **Codex GPT-5.5 subagent**:工具可用时优先用于高价值代码实现或关键 review;上下文 256k,不承担超大仓库研究。 -- **Claude opus**:DeepSeek-V4-Pro,1M 上下文,用于长上下文推理、竞品仓库研究、架构/安全审查。 -- **Claude sonnet**:GLM-5.1,200k 上下文,用于明确路径内的代码实现和 focused tests;每次只给必要文件。 +- **主 Agent(本 session)**:负责架构设计、规划、分支治理、文档、开发进度管理、整体工程化设计和任务拆解。 +- **Codex GPT-5.5 low/mid**:用于看图、前端 UI、截图对比和常规前端判断。 +- **Codex GPT-5.5 xhigh**:用于复杂架构推理、关键方案和高风险设计复核。 +- **Claude sonnet**:DeepSeek-V4-Pro,1M 上下文,用于长文本、找东西、简单文档、架构整理和大范围归纳。 +- **Claude opus**:GLM-5.1,200k 上下文,用于明确路径内的代码实现和 focused tests;每次只给必要文件。 - **Claude haiku**:DeepSeek-V4-Flash,200k 上下文,用于快速检查、轻量 review、日志/文档/小范围 UI 可读性审查,不派它做代码主力。 ### Agent 间进度同步 From f1a6d1c983b294c64c8d01014fdb68acfdbfb840 Mon Sep 17 00:00:00 2001 From: Delicious233 Date: Sun, 7 Jun 2026 02:03:53 +0800 Subject: [PATCH 02/39] =?UTF-8?q?docs(agent):=20=E5=AF=B9=E9=BD=90?= =?UTF-8?q?=E6=9C=80=E6=96=B0=20subagent=20=E5=88=86=E5=B7=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .agents/skills/dev-loop/SKILL.md | 22 +++++++++---------- .../dev-loop/references/model-strategy.md | 20 ++++++++--------- .agents/skills/dev-team/SKILL.md | 12 +++++----- AGENTS.md | 8 +++---- 4 files changed, 31 insertions(+), 31 deletions(-) diff --git a/.agents/skills/dev-loop/SKILL.md b/.agents/skills/dev-loop/SKILL.md index bfc6fb1f..ad72e311 100644 --- a/.agents/skills/dev-loop/SKILL.md +++ b/.agents/skills/dev-loop/SKILL.md @@ -16,15 +16,15 @@ description: "自主开发推进引擎——ROADMAP 驱动、模型分配、并 |---|---|---:|---|---| | Codex 自带 agent 工具 | GPT-5.5 low/mid | 256k | 前端、看图、UI/视觉判断、常规实现和审查 | 前端 UI、截图对比、局部体验判断、常规 code review | | Codex 自带 agent 工具 | GPT-5.5 xhigh | 256k | 最强架构推理和复杂工程设计 | 复杂架构、关键方案、跨模块取舍、高强度 sidecar | -| Claude CLI | **sonnet** = DeepSeek-V4-Pro | 1M | 长上下文、找东西、长文本整理、架构整理 | 大范围阅读、文档整理、roadmap/architecture 归纳、竞品/仓库查找 | -| Claude CLI | **opus** = GLM-5.1 | 200k | 强代码和 agentic 能力 | 窄范围代码实现、测试修复、Go/TS 小切片 | +| Claude CLI | **opus** = DeepSeek-V4-Pro | 1M | 速度快、强推理、长上下文 | 大范围阅读、文档整理、roadmap/architecture 归纳、竞品/仓库查找、复杂方案审查 | +| Claude CLI | **sonnet** = GLM-5.1 | 200k | 强代码和 agentic 能力 | 窄范围代码实现、测试修复、Go/TS 小切片 | | Claude CLI | **haiku** = DeepSeek-V4-Flash | 200k | 速度快、轻量反馈 | 快速检查、轻量 review、日志/文档/小范围 UI 可读性审查 | - **主 Agent**:设计决策、审查输出、编辑核心文件(AGENTS.md/STATE.md/ROADMAP.md)。 - **Codex GPT-5.5 low/mid**:前端、看图、截图对比、常规 UI/UX 判断。 - **Codex GPT-5.5 xhigh**:复杂架构推理、关键方案和高风险设计复核。 -- **Claude sonnet**:DeepSeek-V4-Pro,1M 上下文,长文本、找东西、简单文档、架构整理和大范围归纳。 -- **Claude opus**:GLM-5.1,200k 上下文,明确路径内的实现和 focused tests;prompt 精简,只传必要文件。 +- **Claude opus**:DeepSeek-V4-Pro,1M 上下文,速度快、强推理,适合长文本、找东西、简单文档、架构整理、大范围归纳和复杂方案审查。 +- **Claude sonnet**:GLM-5.1,200k 上下文,强代码模型,适合明确路径内的实现和 focused tests;prompt 精简,只传必要文件。 - **Claude haiku**:DeepSeek-V4-Flash,200k 上下文,快速检查、轻量 review、日志/文档/小范围 UI 可读性审查,不作为代码主力。 ## CC 原生工具配合 @@ -48,7 +48,7 @@ dev-loop 配合两个 CC 内置命令使用效果最好: ## 标准工作循环 ### 1. 理解 -- 读 `AGENTS.md` / `docs/handoffs/STATE.md` / `docs/roadmap.md` +- 读 `AGENTS.md`、`docs/roadmap.md` 和当前任务关联的设计/架构文档 - 理解现有架构、约定、当前进度 - STATE.md 是跨 session 状态文件,每次接手先读 @@ -63,8 +63,8 @@ dev-loop 配合两个 CC 内置命令使用效果最好: - **自己(主 session)**:设计决策、审查输出、编辑核心文件(AGENTS.md/STATE.md/ROADMAP.md) - **派 GPT-5.5 low/mid**:前端 UI、看图、截图对比、局部体验判断 - **派 GPT-5.5 xhigh**:复杂架构、关键方案、跨模块取舍 -- **派 Claude sonnet**:长文本、找东西、简单文档、架构整理、大范围归纳 -- **派 Claude opus**:窄范围编码实现、bug 修复、focused tests +- **派 Claude opus**:长文本、找东西、简单文档、架构整理、大范围归纳、复杂方案审查 +- **派 Claude sonnet**:窄范围编码实现、bug 修复、focused tests - **派 Claude haiku**:快速检查、轻量 review、日志/文档/小范围 UI 可读性审查 - 每次 subagent 完成后审查其输出 @@ -76,7 +76,7 @@ dev-loop 配合两个 CC 内置命令使用效果最好: ### 5. 同步 - AGENTS.md / CLAUDE.md(规则变更) -- `docs/handoffs/STATE.md`(事实变更:进度/阻塞/部署状态) +- `docs/roadmap.md` 或当前任务计划文档(事实变更:进度、阻塞、下一步) - ROADMAP.md(标记完成、记录阻塞、写下一步) - 运行 `neat-freak` 清理过时文档 - 运行 `memory-management` 同步 memory(如有跨系统需求) @@ -103,12 +103,12 @@ dev-loop 配合两个 CC 内置命令使用效果最好: ### 交叉审查维度与模型 | 维度 | 模型 | 为什么 | |---|---|---| -| 结构 | sonnet | 机械检查,批量扫文件 | -| 文档 | sonnet | 一致性检查、整理和归纳 | +| 结构 | opus | 长上下文整理和跨文件一致性检查 | +| 文档 | opus | 一致性检查、整理和归纳 | | 安全 | **GPT-5.5 xhigh** | 必须深度推理 | | 架构 | **GPT-5.5 xhigh** | 需要设计判断 | | 易用性 | GPT-5.5 low/mid | 前端体验和截图判断 | -| 业务逻辑 | **haiku** | 快速轻量逻辑检查 | +| 业务逻辑 | **sonnet** | 强代码模型,适合 focused 逻辑检查 | 审查 agent 的 prompt 要具体:告诉它查什么、怎么报告、文件在哪。 diff --git a/.agents/skills/dev-loop/references/model-strategy.md b/.agents/skills/dev-loop/references/model-strategy.md index 2baf8c76..a1c27ea6 100644 --- a/.agents/skills/dev-loop/references/model-strategy.md +++ b/.agents/skills/dev-loop/references/model-strategy.md @@ -8,14 +8,14 @@ |---|---|---:|---|---| | Codex 自带 agent 工具 | GPT-5.5 low/mid | 256k | 前端、看图、UI/视觉判断、常规实现和审查 | 不吃超大仓库研究 | | Codex 自带 agent 工具 | GPT-5.5 xhigh | 256k | 最强架构推理和复杂工程设计 | 上下文仍不适合超大仓库全文阅读 | -| Claude CLI | **sonnet** = DeepSeek-V4-Pro | 1M | 长上下文、找东西、长文本整理、架构整理 | 代码实现不作为首选 | -| Claude CLI | **opus** = GLM-5.1 | 200k | 强代码和 agentic 能力,适合聚焦实现 | 不要给大批量阅读 | +| Claude CLI | **opus** = DeepSeek-V4-Pro | 1M | 速度快、强推理、长上下文,适合找东西、长文本整理、架构整理和复杂方案审查 | 代码实现不作为首选 | +| Claude CLI | **sonnet** = GLM-5.1 | 200k | 强代码和 agentic 能力,适合聚焦实现 | 不要给大批量阅读 | | Claude CLI | **haiku** = DeepSeek-V4-Flash | 200k | 速度快、轻量反馈,适合快速检查、轻量 review、日志/文档/小范围 UI 可读性审查 | 不作为代码主力 | ## 选择原则 - **先看入口**:Codex 自带 agent 工具和 Claude CLI 是两套执行面,不能把别名混用。 -- **先限上下文**:超过 256k 的研究、竞品仓库阅读、跨大量文件审查优先 Claude sonnet;复杂架构判断优先 GPT-5.5 xhigh;明确文件集代码实现优先 Claude opus。 +- **先限上下文**:超过 256k 的研究、竞品仓库阅读、跨大量文件审查优先 Claude opus;复杂架构判断优先 GPT-5.5 xhigh;明确文件集代码实现优先 Claude sonnet。 - **先限写入范围**:任何编码 subagent 都必须有允许路径、禁止范围、验收命令和证据输出。 - **轻量检查单独派发**:快速 sanity check、日志/文档/小范围 UI 可读性审查优先 Claude haiku,不让代码主力消耗在低风险扫读上。 @@ -25,21 +25,21 @@ 任务类型? ├── 核心实现 / 跨前后端小集成 │ ├── 上下文 <= 256k → GPT-5.5 low/mid 或 xhigh,按复杂度选择 -│ └── 上下文 > 256k → Claude sonnet 先整理,再拆给实现 agent +│ └── 上下文 > 256k → Claude opus 先整理,再拆给实现 agent ├── 窄范围代码修复(明确 1-3 个文件) -│ ├── Go/TS/测试小切片 → Claude opus(GLM-5.1) +│ ├── Go/TS/测试小切片 → Claude sonnet(GLM-5.1) │ └── 高风险实现 review → GPT-5.5 xhigh 或主 Agent 复核 ├── 长上下文推理 / 架构 / 安全 / 竞品仓库研究 -│ └── GPT-5.5 xhigh(复杂架构)或 Claude sonnet(长文本/找东西/整理) +│ └── GPT-5.5 xhigh(复杂架构)或 Claude opus(长文本/找东西/整理) ├── 截图 / 竞品图 / 视觉 QA / UI 可读性 │ └── Claude haiku(DeepSeek-V4-Flash,200k,快速轻量反馈) ├── 机械批量文档或格式统一 │ ├── 中等上下文 → GPT-5.5 low/mid -│ └── 超大上下文或需要归纳 → Claude sonnet 先整理,再分片执行 +│ └── 超大上下文或需要归纳 → Claude opus 先整理,再分片执行 └── 交叉审查 ├── 安全/架构/长期方向 → GPT-5.5 xhigh ├── 代码正确性/集成风险 → GPT-5.5 xhigh 或主 Agent - ├── 小范围实现细节 → Claude opus + ├── 小范围实现细节 → Claude sonnet └── 小范围 UI 可读性/布局文字检查 → Claude haiku ``` @@ -49,8 +49,8 @@ |---|---:|---| | GPT-5.5 low/mid | 256k | 前端、看图、截图对比、常规 UI/UX 判断 | | GPT-5.5 xhigh | 256k | 复杂架构、关键方案、高风险设计复核 | -| Claude sonnet | 1M | DeepSeek-V4-Pro;长文本、找东西、简单文档、架构整理、大范围归纳 | -| Claude opus | 200k | GLM-5.1;prompt 精简,只传相关文件;适合窄范围代码和测试 | +| Claude opus | 1M | DeepSeek-V4-Pro;速度快、强推理;长文本、找东西、简单文档、架构整理、大范围归纳 | +| Claude sonnet | 200k | GLM-5.1;强代码模型;prompt 精简,只传相关文件;适合窄范围代码和测试 | | Claude haiku | 200k | DeepSeek-V4-Flash;快速检查、轻量 review、日志/文档/小范围 UI 可读性审查 | ## 并行度 diff --git a/.agents/skills/dev-team/SKILL.md b/.agents/skills/dev-team/SKILL.md index a2ce92d3..36c512f1 100644 --- a/.agents/skills/dev-team/SKILL.md +++ b/.agents/skills/dev-team/SKILL.md @@ -12,8 +12,8 @@ description: 多 Team 并行开发引擎 — 大规模 Issue 修复、跨模块 ``` 你(主 Agent) ├── Team Leader 1 (主 Agent 或 GPT-5.5 xhigh) → Worktree A - │ ├── Worker 1 (Claude opus / GPT-5.5 low-mid) → 修 1-3 issues - │ ├── Worker 2 (Claude opus / GPT-5.5 low-mid) → 修 1-3 issues + │ ├── Worker 1 (Claude sonnet / GPT-5.5 low-mid) → 修 1-3 issues + │ ├── Worker 2 (Claude sonnet / GPT-5.5 low-mid) → 修 1-3 issues │ ├── Worker 3 (Claude haiku) → 快速检查 / 轻量 review(如需要) │ └── Worker 4 (GPT-5.5 xhigh / 主 Agent) → 测试 + 审查 ├── Team Leader 2 (主 Agent 或 GPT-5.5 xhigh) → Worktree B @@ -27,8 +27,8 @@ description: 多 Team 并行开发引擎 — 大规模 Issue 修复、跨模块 |---|---:|---| | GPT-5.5 low/mid | 256k | 前端、看图、截图对比、常规 UI/UX 判断 | | GPT-5.5 xhigh | 256k | 复杂架构、关键方案、高风险设计复核 | -| Claude sonnet = DeepSeek-V4-Pro | 1M | 长文本、找东西、简单文档、架构整理、大范围归纳 | -| Claude opus = GLM-5.1 | 200k | 强代码和 agentic 能力;适合明确文件范围内的实现和测试 | +| Claude opus = DeepSeek-V4-Pro | 1M | 速度快、强推理、长上下文;适合长文本、找东西、简单文档、架构整理、大范围归纳 | +| Claude sonnet = GLM-5.1 | 200k | 强代码和 agentic 能力;适合明确文件范围内的实现和测试 | | Claude haiku = DeepSeek-V4-Flash | 200k | 速度快、轻量反馈;适合快速检查、轻量 review、日志/文档/小范围 UI 可读性审查 | ## 何时使用 @@ -94,8 +94,8 @@ You are Team Leader for {team_name}. Fix {N} issues ({batch_name}). 3. Spawn workers by task type: - GPT-5.5 low/mid: frontend, screenshots, visual/UI review - GPT-5.5 xhigh: complex architecture and high-risk review - - Claude sonnet: long-text search, docs, architecture整理 - - Claude opus: narrow code fixes with explicit file whitelist + - Claude opus: long-text search, docs, architecture整理 + - Claude sonnet: narrow code fixes with explicit file whitelist - Claude haiku: fast lightweight checks and narrow UI readability review 4. Each worker: read → write failing test → implement fix → go test passes 5. Review all work, resolve conflicts, go test -race, commit diff --git a/AGENTS.md b/AGENTS.md index b7b47713..ffb5d96f 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -186,15 +186,15 @@ git status --short --branch # 确认只改了允许的路径 |---|---|---:|---|---| | Codex 自带 agent 工具 | GPT-5.5 low/mid | 256k | 前端、看图、UI/视觉判断、常规实现和审查 | 前端 UI、截图对比、局部体验判断、常规 code review | | Codex 自带 agent 工具 | GPT-5.5 xhigh | 256k | 最强架构推理和复杂工程设计 | 复杂架构、关键方案、跨模块取舍、主 Agent 复核前的高强度 sidecar | -| Claude CLI | **sonnet** = DeepSeek-V4-Pro | 1M | 长上下文、找东西、长文本整理、架构整理 | 大范围阅读、文档整理、roadmap/architecture 归纳、竞品/仓库查找 | -| Claude CLI | **opus** = GLM-5.1 | 200k | 强代码和 agentic 能力 | 窄范围代码实现、测试修复、Go/TS 小切片、明确文件集的重构 | +| Claude CLI | **opus** = DeepSeek-V4-Pro | 1M | 速度快、强推理、长上下文 | 大范围阅读、文档整理、roadmap/architecture 归纳、竞品/仓库查找、复杂方案审查 | +| Claude CLI | **sonnet** = GLM-5.1 | 200k | 强代码和 agentic 能力 | 窄范围代码实现、测试修复、Go/TS 小切片、明确文件集的重构 | | Claude CLI | **haiku** = DeepSeek-V4-Flash | 200k | 速度快、轻量反馈 | 快速检查、轻量 review、日志/文档/小范围 UI 可读性审查 | - **主 Agent(本 session)**:负责架构设计、规划、分支治理、文档、开发进度管理、整体工程化设计和任务拆解。 - **Codex GPT-5.5 low/mid**:用于看图、前端 UI、截图对比和常规前端判断。 - **Codex GPT-5.5 xhigh**:用于复杂架构推理、关键方案和高风险设计复核。 -- **Claude sonnet**:DeepSeek-V4-Pro,1M 上下文,用于长文本、找东西、简单文档、架构整理和大范围归纳。 -- **Claude opus**:GLM-5.1,200k 上下文,用于明确路径内的代码实现和 focused tests;每次只给必要文件。 +- **Claude opus**:DeepSeek-V4-Pro,1M 上下文,速度快、强推理,用于长文本、找东西、简单文档、架构整理、大范围归纳和复杂方案审查。 +- **Claude sonnet**:GLM-5.1,200k 上下文,强代码模型,用于明确路径内的代码实现和 focused tests;每次只给必要文件。 - **Claude haiku**:DeepSeek-V4-Flash,200k 上下文,用于快速检查、轻量 review、日志/文档/小范围 UI 可读性审查,不派它做代码主力。 ### Agent 间进度同步 From 63047a99e1918322d34eb1027e30bc5edf526030 Mon Sep 17 00:00:00 2001 From: Delicious233 Date: Sun, 7 Jun 2026 02:15:20 +0800 Subject: [PATCH 03/39] =?UTF-8?q?feat(shared):=20=E5=BB=BA=E7=AB=8B=20v4?= =?UTF-8?q?=20workbench=20=E9=A6=96=E7=89=87=E9=AA=A8=E6=9E=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/shared/package.json | 4 + .../src/composer/composerReducer.test.ts | 51 ++++++ app/shared/src/composer/composerReducer.ts | 89 +++++++++ app/shared/src/composer/index.ts | 16 ++ app/shared/src/composer/types.ts | 43 +++++ app/shared/src/index.ts | 56 ++++++ .../src/platform/createMockPlatform.test.ts | 41 +++++ app/shared/src/platform/createMockPlatform.ts | 56 ++++++ app/shared/src/platform/index.ts | 11 ++ app/shared/src/platform/types.ts | 34 ++++ app/shared/src/transcript/index.ts | 14 ++ .../src/transcript/transcriptEvidence.test.ts | 63 +++++++ .../src/transcript/transcriptEvidence.ts | 16 ++ app/shared/src/transcript/types.ts | 58 ++++++ .../workbench/AgentHubWorkbench.module.css | 170 ++++++++++++++++++ .../src/workbench/AgentHubWorkbench.test.tsx | 70 ++++++++ .../src/workbench/AgentHubWorkbench.tsx | 148 +++++++++++++++ app/shared/src/workbench/index.ts | 2 + docs/roadmap.md | 15 +- 19 files changed, 952 insertions(+), 5 deletions(-) create mode 100644 app/shared/src/composer/composerReducer.test.ts create mode 100644 app/shared/src/composer/composerReducer.ts create mode 100644 app/shared/src/composer/index.ts create mode 100644 app/shared/src/composer/types.ts create mode 100644 app/shared/src/platform/createMockPlatform.test.ts create mode 100644 app/shared/src/platform/createMockPlatform.ts create mode 100644 app/shared/src/platform/index.ts create mode 100644 app/shared/src/platform/types.ts create mode 100644 app/shared/src/transcript/index.ts create mode 100644 app/shared/src/transcript/transcriptEvidence.test.ts create mode 100644 app/shared/src/transcript/transcriptEvidence.ts create mode 100644 app/shared/src/transcript/types.ts create mode 100644 app/shared/src/workbench/AgentHubWorkbench.module.css create mode 100644 app/shared/src/workbench/AgentHubWorkbench.test.tsx create mode 100644 app/shared/src/workbench/AgentHubWorkbench.tsx create mode 100644 app/shared/src/workbench/index.ts diff --git a/app/shared/package.json b/app/shared/package.json index 4bb98248..86ac0eae 100644 --- a/app/shared/package.json +++ b/app/shared/package.json @@ -12,6 +12,10 @@ "./errors": "./src/errors.ts", "./hubEvents": "./src/hubEvents.ts", "./hubClient": "./src/hubClient.ts", + "./composer": "./src/composer/index.ts", + "./platform": "./src/platform/index.ts", + "./transcript": "./src/transcript/index.ts", + "./workbench": "./src/workbench/index.ts", "./surfaceMetadata": "./src/surfaceMetadata.ts", "./workbenchState": "./src/workbenchState.ts", "./tree": "./src/tree.ts", diff --git a/app/shared/src/composer/composerReducer.test.ts b/app/shared/src/composer/composerReducer.test.ts new file mode 100644 index 00000000..9df0d965 --- /dev/null +++ b/app/shared/src/composer/composerReducer.test.ts @@ -0,0 +1,51 @@ +import { describe, expect, it } from 'vitest'; +import { + buildComposerIntent, + canSubmitComposer, + composerReducer, + createInitialComposerState, +} from './composerReducer'; + +describe('composerReducer', () => { + it('tracks text, mode, mentions and approval mode for a conversation', () => { + let state = createInitialComposerState('team'); + + state = composerReducer(state, { type: 'setText', text: '请 @Builder 重构 shared shell' }); + state = composerReducer(state, { type: 'setMode', mode: 'code' }); + state = composerReducer(state, { type: 'addMention', agentId: 'builder' }); + state = composerReducer(state, { type: 'setApprovalMode', approvalMode: 'workspace-write' }); + + expect(state).toMatchObject({ + conversationId: 'team', + text: '请 @Builder 重构 shared shell', + mode: 'code', + mentions: ['builder'], + approvalMode: 'workspace-write', + }); + }); + + it('only submits non-empty text or attachments and builds a platform intent', () => { + const empty = createInitialComposerState('team'); + expect(canSubmitComposer(empty)).toBe(false); + + const ready = composerReducer(empty, { type: 'setText', text: ' build v4 ' }); + expect(canSubmitComposer(ready)).toBe(true); + expect(buildComposerIntent(ready)).toEqual( + expect.objectContaining({ + conversationId: 'team', + text: 'build v4', + mode: 'ask', + approvalMode: 'suggest', + }), + ); + }); + + it('resets text and transient submit state after successful submit', () => { + let state = composerReducer(createInitialComposerState('team'), { type: 'setText', text: 'ship' }); + state = composerReducer(state, { type: 'setSubmitState', submitState: 'submitting' }); + state = composerReducer(state, { type: 'resetAfterSubmit' }); + + expect(state.text).toBe(''); + expect(state.submitState).toBe('idle'); + }); +}); diff --git a/app/shared/src/composer/composerReducer.ts b/app/shared/src/composer/composerReducer.ts new file mode 100644 index 00000000..6bee47c2 --- /dev/null +++ b/app/shared/src/composer/composerReducer.ts @@ -0,0 +1,89 @@ +import type { ComposerAction, ComposerIntent, ComposerState } from './types'; + +export function createInitialComposerState(conversationId: string): ComposerState { + return { + conversationId, + text: '', + mode: 'ask', + mentions: [], + attachments: [], + approvalMode: 'suggest', + submitState: 'idle', + }; +} + +export function composerReducer( + state: ComposerState, + action: ComposerAction, +): ComposerState { + switch (action.type) { + case 'setText': + return { + ...state, + text: action.text, + }; + case 'setMode': + return { + ...state, + mode: action.mode, + }; + case 'addMention': + if (state.mentions.includes(action.agentId)) return state; + return { + ...state, + mentions: [...state.mentions, action.agentId], + }; + case 'removeMention': + return { + ...state, + mentions: state.mentions.filter((agentId) => agentId !== action.agentId), + }; + case 'setApprovalMode': + return { + ...state, + approvalMode: action.approvalMode, + }; + case 'setSubmitState': + return { + ...state, + submitState: action.submitState, + }; + case 'addAttachment': + if (state.attachments.some((attachment) => attachment.id === action.attachment.id)) { + return state; + } + return { + ...state, + attachments: [...state.attachments, action.attachment], + }; + case 'removeAttachment': + return { + ...state, + attachments: state.attachments.filter((attachment) => attachment.id !== action.attachmentId), + }; + case 'resetAfterSubmit': + return { + ...state, + text: '', + attachments: [], + submitState: 'idle', + }; + default: + return state; + } +} + +export function canSubmitComposer(state: ComposerState): boolean { + return state.text.trim().length > 0 || state.attachments.length > 0; +} + +export function buildComposerIntent(state: ComposerState): ComposerIntent { + return { + conversationId: state.conversationId, + text: state.text.trim(), + mode: state.mode, + mentions: [...state.mentions], + attachments: [...state.attachments], + approvalMode: state.approvalMode, + }; +} diff --git a/app/shared/src/composer/index.ts b/app/shared/src/composer/index.ts new file mode 100644 index 00000000..a4e43850 --- /dev/null +++ b/app/shared/src/composer/index.ts @@ -0,0 +1,16 @@ +export { + buildComposerIntent, + canSubmitComposer, + composerReducer, + createInitialComposerState, +} from './composerReducer'; +export type { + ApprovalMode, + ComposerAction, + ComposerAttachment, + ComposerIntent, + ComposerMode, + ComposerState, + ComposerSubmitResult, + ComposerSubmitState, +} from './types'; diff --git a/app/shared/src/composer/types.ts b/app/shared/src/composer/types.ts new file mode 100644 index 00000000..f5f15cdc --- /dev/null +++ b/app/shared/src/composer/types.ts @@ -0,0 +1,43 @@ +export type ComposerMode = 'ask' | 'code'; +export type ComposerSubmitState = 'idle' | 'submitting' | 'error'; +export type ApprovalMode = 'suggest' | 'workspace-write' | 'read-only'; + +export interface ComposerAttachment { + id: string; + name: string; + kind?: string; +} + +export interface ComposerState { + conversationId: string; + text: string; + mode: ComposerMode; + mentions: string[]; + attachments: ComposerAttachment[]; + approvalMode: ApprovalMode; + submitState: ComposerSubmitState; +} + +export interface ComposerIntent { + conversationId: string; + text: string; + mode: ComposerMode; + mentions: string[]; + attachments: ComposerAttachment[]; + approvalMode: ApprovalMode; +} + +export interface ComposerSubmitResult { + intentId: string; +} + +export type ComposerAction = + | { type: 'setText'; text: string } + | { type: 'setMode'; mode: ComposerMode } + | { type: 'addMention'; agentId: string } + | { type: 'removeMention'; agentId: string } + | { type: 'setApprovalMode'; approvalMode: ApprovalMode } + | { type: 'setSubmitState'; submitState: ComposerSubmitState } + | { type: 'addAttachment'; attachment: ComposerAttachment } + | { type: 'removeAttachment'; attachmentId: string } + | { type: 'resetAfterSubmit' }; diff --git a/app/shared/src/index.ts b/app/shared/src/index.ts index 34accb68..e473c577 100644 --- a/app/shared/src/index.ts +++ b/app/shared/src/index.ts @@ -294,6 +294,62 @@ export type { SurfaceStatusMetadata, } from './surfaceMetadata'; +export { + buildComposerIntent, + canSubmitComposer, + composerReducer, + createInitialComposerState, +} from './composer'; +export type { + ApprovalMode, + ComposerAction, + ComposerAttachment, + ComposerIntent, + ComposerMode, + ComposerState, + ComposerSubmitResult, + ComposerSubmitState, +} from './composer'; + +export { + createMockPlatform, +} from './platform'; +export type { + AgentHubPlatform, + AgentHubSurface, + ConversationKind, + ConversationPort, + MockPlatform, + MockPlatformSeed, + RunPort, + SurfaceCapabilities, + WorkbenchConversation, +} from './platform'; + +export { + collectTranscriptEvidence, +} from './transcript'; +export type { + ApprovalTranscriptBlock, + ArtifactTranscriptBlock, + DiffTranscriptBlock, + EvidenceRef, + EvidenceRefKind, + EvidenceRefStatus, + TextTranscriptBlock, + ToolCallTranscriptBlock, + TranscriptAuthor, + TranscriptAuthorRole, + TranscriptBlock, +} from './transcript'; + +export { + AgentHubWorkbench, +} from './workbench'; +export type { + AgentHubWorkbenchProps, +} from './workbench'; + export { mockProject, mockProjects, diff --git a/app/shared/src/platform/createMockPlatform.test.ts b/app/shared/src/platform/createMockPlatform.test.ts new file mode 100644 index 00000000..14e3baae --- /dev/null +++ b/app/shared/src/platform/createMockPlatform.test.ts @@ -0,0 +1,41 @@ +import { describe, expect, it } from 'vitest'; +import { createMockPlatform } from './createMockPlatform'; + +describe('createMockPlatform', () => { + it('exposes surface capabilities and conversations through ports', async () => { + const platform = createMockPlatform({ + surface: 'desktop', + capabilities: { localEdge: true, localFiles: true, browserPreview: false }, + conversations: [ + { id: 'builder', title: 'Builder', kind: 'direct', subtitle: 'Claude Code' }, + { id: 'team', title: 'Agent 协作群', kind: 'group', unreadCount: 3 }, + ], + }); + + await expect(platform.conversations.list()).resolves.toHaveLength(2); + expect(platform.surface).toBe('desktop'); + expect(platform.capabilities.localEdge).toBe(true); + expect(platform.capabilities.browserPreview).toBe(false); + }); + + it('records submitted composer intents for adapter verification', async () => { + const platform = createMockPlatform({ + surface: 'web', + conversations: [{ id: 'team', title: 'Agent 协作群', kind: 'group' }], + }); + + const result = await platform.runs.submitComposerIntent({ + conversationId: 'team', + text: '重构 shared workbench', + mode: 'code', + mentions: ['builder'], + attachments: [], + approvalMode: 'workspace-write', + }); + + expect(result.intentId).toMatch(/^mock-intent-/); + expect(platform.submittedIntents).toEqual([ + expect.objectContaining({ conversationId: 'team', mode: 'code', mentions: ['builder'] }), + ]); + }); +}); diff --git a/app/shared/src/platform/createMockPlatform.ts b/app/shared/src/platform/createMockPlatform.ts new file mode 100644 index 00000000..67d47295 --- /dev/null +++ b/app/shared/src/platform/createMockPlatform.ts @@ -0,0 +1,56 @@ +import type { ComposerIntent, ComposerSubmitResult } from '../composer/types'; +import type { + AgentHubPlatform, + AgentHubSurface, + SurfaceCapabilities, + WorkbenchConversation, +} from './types'; + +export interface MockPlatformSeed { + surface?: AgentHubSurface; + capabilities?: Partial; + conversations?: WorkbenchConversation[]; +} + +export interface MockPlatform extends AgentHubPlatform { + seed: { + conversations: WorkbenchConversation[]; + }; + submittedIntents: ComposerIntent[]; +} + +const defaultCapabilities: SurfaceCapabilities = { + localEdge: false, + localFiles: false, + browserPreview: false, +}; + +export function createMockPlatform(seed: MockPlatformSeed = {}): MockPlatform { + const conversations = seed.conversations ?? []; + const submittedIntents: ComposerIntent[] = []; + + return { + surface: seed.surface ?? 'web', + capabilities: { + ...defaultCapabilities, + ...seed.capabilities, + }, + seed: { + conversations, + }, + submittedIntents, + conversations: { + async list() { + return conversations; + }, + }, + runs: { + async submitComposerIntent(intent: ComposerIntent): Promise { + submittedIntents.push(intent); + return { + intentId: `mock-intent-${submittedIntents.length}`, + }; + }, + }, + }; +} diff --git a/app/shared/src/platform/index.ts b/app/shared/src/platform/index.ts new file mode 100644 index 00000000..8548525f --- /dev/null +++ b/app/shared/src/platform/index.ts @@ -0,0 +1,11 @@ +export { createMockPlatform } from './createMockPlatform'; +export type { MockPlatform, MockPlatformSeed } from './createMockPlatform'; +export type { + AgentHubPlatform, + AgentHubSurface, + ConversationKind, + ConversationPort, + RunPort, + SurfaceCapabilities, + WorkbenchConversation, +} from './types'; diff --git a/app/shared/src/platform/types.ts b/app/shared/src/platform/types.ts new file mode 100644 index 00000000..3304e52b --- /dev/null +++ b/app/shared/src/platform/types.ts @@ -0,0 +1,34 @@ +import type { ComposerIntent, ComposerSubmitResult } from '../composer/types'; + +export type AgentHubSurface = 'desktop' | 'web'; + +export interface SurfaceCapabilities { + localEdge: boolean; + localFiles: boolean; + browserPreview: boolean; +} + +export type ConversationKind = 'direct' | 'group'; + +export interface WorkbenchConversation { + id: string; + title: string; + kind: ConversationKind; + subtitle?: string; + unreadCount?: number; +} + +export interface ConversationPort { + list(): Promise; +} + +export interface RunPort { + submitComposerIntent(intent: ComposerIntent): Promise; +} + +export interface AgentHubPlatform { + surface: AgentHubSurface; + capabilities: SurfaceCapabilities; + conversations: ConversationPort; + runs: RunPort; +} diff --git a/app/shared/src/transcript/index.ts b/app/shared/src/transcript/index.ts new file mode 100644 index 00000000..fa42bf59 --- /dev/null +++ b/app/shared/src/transcript/index.ts @@ -0,0 +1,14 @@ +export { collectTranscriptEvidence } from './transcriptEvidence'; +export type { + ApprovalTranscriptBlock, + ArtifactTranscriptBlock, + DiffTranscriptBlock, + EvidenceRef, + EvidenceRefKind, + EvidenceRefStatus, + TextTranscriptBlock, + ToolCallTranscriptBlock, + TranscriptAuthor, + TranscriptAuthorRole, + TranscriptBlock, +} from './types'; diff --git a/app/shared/src/transcript/transcriptEvidence.test.ts b/app/shared/src/transcript/transcriptEvidence.test.ts new file mode 100644 index 00000000..ef1bb782 --- /dev/null +++ b/app/shared/src/transcript/transcriptEvidence.test.ts @@ -0,0 +1,63 @@ +import { describe, expect, it } from 'vitest'; +import { collectTranscriptEvidence } from './transcriptEvidence'; +import type { TranscriptBlock } from './types'; + +describe('collectTranscriptEvidence', () => { + it('collects evidence refs from transcript blocks in render order', () => { + const blocks: TranscriptBlock[] = [ + { + id: 'msg-1', + kind: 'text', + author: { id: 'user', name: 'User', role: 'human' }, + text: '请实现 v4 shell', + }, + { + id: 'tool-1', + kind: 'tool_call', + author: { id: 'builder', name: 'Builder', role: 'agent' }, + toolName: 'rg', + status: 'completed', + evidenceRefs: [ + { id: 'ev-tool', kind: 'tool', label: 'rg desktop shell', status: 'completed' }, + { id: 'ev-file', kind: 'file', label: 'app/shared/src/workbench/AgentHubWorkbench.tsx' }, + ], + }, + { + id: 'artifact-1', + kind: 'artifact', + author: { id: 'builder', name: 'Builder', role: 'agent' }, + title: 'v4 workbench skeleton', + evidenceRefs: [{ id: 'ev-artifact', kind: 'artifact', label: 'Workbench skeleton' }], + }, + ]; + + expect(collectTranscriptEvidence(blocks).map((item) => item.id)).toEqual([ + 'ev-tool', + 'ev-file', + 'ev-artifact', + ]); + }); + + it('deduplicates repeated evidence ids without changing first occurrence order', () => { + const blocks: TranscriptBlock[] = [ + { + id: 'diff-1', + kind: 'diff', + author: { id: 'builder', name: 'Builder', role: 'agent' }, + title: 'Shared shell diff', + files: ['app/shared/src/workbench/AgentHubWorkbench.tsx'], + evidenceRefs: [{ id: 'ev-file', kind: 'file', label: 'Workbench file' }], + }, + { + id: 'approval-1', + kind: 'approval', + author: { id: 'edge', name: 'Edge', role: 'system' }, + title: 'Apply workbench patch', + status: 'pending', + evidenceRefs: [{ id: 'ev-file', kind: 'file', label: 'Workbench file' }], + }, + ]; + + expect(collectTranscriptEvidence(blocks)).toHaveLength(1); + }); +}); diff --git a/app/shared/src/transcript/transcriptEvidence.ts b/app/shared/src/transcript/transcriptEvidence.ts new file mode 100644 index 00000000..ba0f4253 --- /dev/null +++ b/app/shared/src/transcript/transcriptEvidence.ts @@ -0,0 +1,16 @@ +import type { EvidenceRef, TranscriptBlock } from './types'; + +export function collectTranscriptEvidence(blocks: TranscriptBlock[]): EvidenceRef[] { + const seen = new Set(); + const evidence: EvidenceRef[] = []; + + for (const block of blocks) { + for (const ref of block.evidenceRefs ?? []) { + if (seen.has(ref.id)) continue; + seen.add(ref.id); + evidence.push(ref); + } + } + + return evidence; +} diff --git a/app/shared/src/transcript/types.ts b/app/shared/src/transcript/types.ts new file mode 100644 index 00000000..f770bef8 --- /dev/null +++ b/app/shared/src/transcript/types.ts @@ -0,0 +1,58 @@ +export type TranscriptAuthorRole = 'human' | 'agent' | 'system'; + +export interface TranscriptAuthor { + id: string; + name: string; + role: TranscriptAuthorRole; +} + +export type EvidenceRefKind = 'tool' | 'file' | 'artifact'; +export type EvidenceRefStatus = 'pending' | 'running' | 'completed' | 'failed'; + +export interface EvidenceRef { + id: string; + kind: EvidenceRefKind; + label: string; + status?: EvidenceRefStatus; +} + +interface TranscriptBlockBase { + id: string; + author: TranscriptAuthor; + evidenceRefs?: EvidenceRef[]; +} + +export interface TextTranscriptBlock extends TranscriptBlockBase { + kind: 'text'; + text: string; +} + +export interface ToolCallTranscriptBlock extends TranscriptBlockBase { + kind: 'tool_call'; + toolName: string; + status: EvidenceRefStatus; +} + +export interface ArtifactTranscriptBlock extends TranscriptBlockBase { + kind: 'artifact'; + title: string; +} + +export interface DiffTranscriptBlock extends TranscriptBlockBase { + kind: 'diff'; + title: string; + files: string[]; +} + +export interface ApprovalTranscriptBlock extends TranscriptBlockBase { + kind: 'approval'; + title: string; + status: EvidenceRefStatus; +} + +export type TranscriptBlock = + | TextTranscriptBlock + | ToolCallTranscriptBlock + | ArtifactTranscriptBlock + | DiffTranscriptBlock + | ApprovalTranscriptBlock; diff --git a/app/shared/src/workbench/AgentHubWorkbench.module.css b/app/shared/src/workbench/AgentHubWorkbench.module.css new file mode 100644 index 00000000..26145ece --- /dev/null +++ b/app/shared/src/workbench/AgentHubWorkbench.module.css @@ -0,0 +1,170 @@ +.shell { + display: grid; + grid-template-columns: 64px minmax(192px, 260px) minmax(0, 1fr) minmax(220px, 300px); + min-height: 640px; + background: var(--td-surface, #f6f7f8); + color: var(--td-text, #14161a); +} + +.rail, +.sidebar, +.inspector { + border-right: 1px solid rgba(20, 22, 26, 0.1); + background: rgba(255, 255, 255, 0.72); +} + +.rail { + display: flex; + flex-direction: column; + align-items: center; + gap: 12px; + padding: 14px 10px; +} + +.mark { + display: grid; + width: 36px; + height: 36px; + place-items: center; + border-radius: 8px; + background: #14161a; + color: #fff; + font-weight: 700; +} + +.sidebar, +.inspector { + padding: 18px; +} + +.sidebarTitle, +.inspectorTitle { + margin: 0 0 14px; + font-size: 13px; + font-weight: 650; +} + +.conversationList, +.evidenceList, +.transcript { + display: grid; + gap: 8px; + margin: 0; + padding: 0; + list-style: none; +} + +.conversationButton { + width: 100%; + padding: 10px; + border: 1px solid rgba(20, 22, 26, 0.1); + border-radius: 8px; + background: #fff; + color: inherit; + text-align: left; +} + +.conversationButton[aria-current='true'] { + border-color: rgba(0, 113, 227, 0.4); + background: rgba(0, 113, 227, 0.08); +} + +.conversationTitle, +.conversationSubtitle, +.evidenceLabel, +.blockAuthor { + display: block; +} + +.conversationTitle, +.evidenceLabel { + font-size: 13px; + font-weight: 650; +} + +.conversationSubtitle, +.blockAuthor, +.blockMeta { + color: #5b6472; + font-size: 12px; +} + +.workspace { + display: grid; + grid-template-rows: auto minmax(0, 1fr) auto; + min-width: 0; + padding: 18px; +} + +.toolbar { + display: flex; + justify-content: flex-end; + padding-bottom: 14px; +} + +.previewButton, +.sendButton { + min-height: 36px; + padding: 0 14px; + border: 1px solid rgba(20, 22, 26, 0.14); + border-radius: 8px; + background: #fff; + color: inherit; +} + +.previewButton:disabled, +.sendButton:disabled { + color: #8b94a3; + cursor: not-allowed; +} + +.transcriptRegion { + min-width: 0; + overflow: auto; +} + +.block { + padding: 12px 0; + border-bottom: 1px solid rgba(20, 22, 26, 0.08); +} + +.blockText, +.blockTitle { + margin: 4px 0 0; + font-size: 14px; + line-height: 1.55; +} + +.composer { + display: grid; + grid-template-columns: minmax(0, 1fr) auto; + gap: 10px; + padding-top: 14px; +} + +.composerInput { + min-height: 40px; + resize: vertical; + border: 1px solid rgba(20, 22, 26, 0.14); + border-radius: 8px; + padding: 10px 12px; + font: inherit; +} + +.evidenceItem { + padding: 10px; + border: 1px solid rgba(20, 22, 26, 0.1); + border-radius: 8px; + background: #fff; +} + +@media (max-width: 860px) { + .shell { + grid-template-columns: 52px minmax(0, 1fr); + } + + .workspace, + .inspector { + grid-column: 1 / -1; + } +} diff --git a/app/shared/src/workbench/AgentHubWorkbench.test.tsx b/app/shared/src/workbench/AgentHubWorkbench.test.tsx new file mode 100644 index 00000000..17aaf902 --- /dev/null +++ b/app/shared/src/workbench/AgentHubWorkbench.test.tsx @@ -0,0 +1,70 @@ +import React from 'react'; +import { fireEvent, render, screen, waitFor } from '@testing-library/react'; +import { describe, expect, it } from 'vitest'; +import { createMockPlatform } from '../platform/createMockPlatform'; +import type { TranscriptBlock } from '../transcript/types'; +import { AgentHubWorkbench } from './AgentHubWorkbench'; + +describe('AgentHubWorkbench', () => { + const transcript: TranscriptBlock[] = [ + { + id: 'msg-1', + kind: 'text', + author: { id: 'user', name: 'Delicious233', role: 'human' }, + text: '全面参考 agenthub-design/desktop', + }, + { + id: 'tool-1', + kind: 'tool_call', + author: { id: 'builder', name: 'Builder', role: 'agent' }, + toolName: 'Read', + status: 'completed', + evidenceRefs: [{ id: 'ev-tool', kind: 'tool', label: 'Read desktop/index.html', status: 'completed' }], + }, + ]; + + it('renders the v4 shell regions from one shared workbench', () => { + const platform = createMockPlatform({ + surface: 'desktop', + capabilities: { browserPreview: false }, + conversations: [{ id: 'builder', title: 'Builder', kind: 'direct', subtitle: 'Claude Code' }], + }); + + render(); + + expect(screen.getByRole('navigation', { name: 'Global rail' })).toBeInTheDocument(); + expect(screen.getByRole('complementary', { name: 'Conversation sidebar' })).toBeInTheDocument(); + expect(screen.getByRole('main', { name: 'Workspace' })).toHaveAttribute('data-surface', 'desktop'); + expect(screen.getByRole('complementary', { name: 'Right inspector' })).toBeInTheDocument(); + expect(screen.getByText('全面参考 agenthub-design/desktop')).toBeInTheDocument(); + expect(screen.getByText('Read desktop/index.html')).toBeInTheDocument(); + expect(screen.getByRole('button', { name: '浏览器预览' })).toBeDisabled(); + }); + + it('submits composer intents through the platform adapter', async () => { + const platform = createMockPlatform({ + surface: 'web', + conversations: [{ id: 'team', title: 'Agent 协作群', kind: 'group' }], + }); + + render( + , + ); + + fireEvent.change(screen.getByRole('textbox', { name: 'Composer input' }), { + target: { value: '开始 v4 shared workbench' }, + }); + fireEvent.click(screen.getByRole('button', { name: '发送消息' })); + + await waitFor(() => { + expect(platform.submittedIntents).toEqual([ + expect.objectContaining({ conversationId: 'team', text: '开始 v4 shared workbench' }), + ]); + }); + }); +}); diff --git a/app/shared/src/workbench/AgentHubWorkbench.tsx b/app/shared/src/workbench/AgentHubWorkbench.tsx new file mode 100644 index 00000000..92e2058b --- /dev/null +++ b/app/shared/src/workbench/AgentHubWorkbench.tsx @@ -0,0 +1,148 @@ +import React, { FormEvent, useReducer } from 'react'; +import { + buildComposerIntent, + canSubmitComposer, + composerReducer, + createInitialComposerState, +} from '../composer'; +import type { AgentHubPlatform, WorkbenchConversation } from '../platform'; +import { collectTranscriptEvidence } from '../transcript'; +import type { EvidenceRef, TranscriptBlock } from '../transcript'; +import styles from './AgentHubWorkbench.module.css'; + +export interface AgentHubWorkbenchProps { + platform: AgentHubPlatform; + conversations: WorkbenchConversation[]; + activeConversationId?: string; + transcript: TranscriptBlock[]; +} + +export function AgentHubWorkbench({ + platform, + conversations, + activeConversationId, + transcript, +}: AgentHubWorkbenchProps): React.ReactElement { + const fallbackConversationId = conversations[0]?.id ?? 'default'; + const currentConversationId = activeConversationId ?? fallbackConversationId; + const [composer, dispatchComposer] = useReducer( + composerReducer, + currentConversationId, + createInitialComposerState, + ); + const evidence = collectTranscriptEvidence(transcript); + + async function submitComposer(event: FormEvent): Promise { + event.preventDefault(); + if (!canSubmitComposer(composer)) return; + + dispatchComposer({ type: 'setSubmitState', submitState: 'submitting' }); + await platform.runs.submitComposerIntent(buildComposerIntent(composer)); + dispatchComposer({ type: 'resetAfterSubmit' }); + } + + return ( +
+ + + + +
+
+ +
+ +
+
    + {transcript.map((block) => ( +
  1. + {block.author.name} + {renderTranscriptBlock(block)} +
  2. + ))} +
+
+ +
+