diff --git a/.gitea/workflows/claude.yml.placeholder b/.gitea/workflows/claude.yml.placeholder new file mode 100644 index 0000000..fd32d98 --- /dev/null +++ b/.gitea/workflows/claude.yml.placeholder @@ -0,0 +1,35 @@ +# 占位 / 模板 —— 实际 workflow 会在 Milestone A2 阶段填充并改名为 claude.yml +# 这里只展示目标形态,让任何来读仓库的人都能立刻理解最终调用方式 + +name: Claude Code Agent + +on: + issues: + types: [opened, edited] + issue_comment: + types: [created] + pull_request: + types: [opened, synchronize] + +permissions: + contents: write + issues: write + pull-requests: write + +jobs: + claude: + # 仅在评论/正文含 @claude 时跑 + if: contains(github.event.comment.body, '@claude') || contains(github.event.issue.body, '@claude') || contains(github.event.pull_request.body, '@claude') + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Run claude-gitea-agent + uses: pangtiankai/claude-gitea-agent@main + with: + gitea_token: ${{ secrets.GITEA_TOKEN }} + anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }} + # 可选:限制 Claude 能用的工具 + # allowed_tools: 'Read,Edit,Write,Bash' diff --git a/README.md b/README.md index bd80273..9110f22 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,48 @@ # claude-gitea-agent -Claude Code agent that responds to Gitea issues — interactive requirements discussion + auto PR (path A: ported claude-code-action) \ No newline at end of file +> Claude Code 自动响应 Gitea issue 的代理 —— 在 issue 里和你交互式确认需求,对齐后自动开发并提 PR。 + +## 项目目标 + +复刻 Anthropic 官方 [`claude-code-action`](https://github.com/anthropics/claude-code-action) 的 GitHub 体验,但跑在 Gitea 上: + +- **触发**:在 Gitea issue 或 PR 评论里 `@claude ...` +- **交互**:Claude 在评论里和你来回讨论需求(superpowers brainstorming 风格) +- **执行**:对齐后自动建分支、写代码、跑测试、提 PR +- **运行环境**:你的本地 act_runner(已部署) + +## 当前状态 + +🟡 **筹划期** —— 仓库刚建好,docs 在写,代码移植还没开始。 + +| 阶段 | 状态 | +|---|---| +| 调研开源方案 | ✅ 完成([docs/02-roadmap.md](docs/02-roadmap.md)) | +| 仓库脚手架 | ✅ 完成 | +| Gitea API 客户端移植 | ⏳ 待开始 | +| 触发器 + 评论交互 | ⏳ 待开始 | +| PR 生成流程 | ⏳ 待开始 | +| 端到端跑通 | ⏳ 待开始 | +| 升级到 webhook 长驻服务(路线 B) | 🔮 未来 | + +## 架构选择 + +**路线 A**:fork 上游 `claude-code-action`,替换 GitHub API 调用为 Gitea API,跑在 Gitea Actions 上。 + +- 优点:站在巨人肩膀上,提示词/上下文构造/PR 流程沿用官方逻辑 +- 缺点:Gitea Actions 冷启动 ~30s,每轮交互有延迟,不算"真实时" +- 后续可升级到路线 B(长驻 webhook 服务 + Claude Agent SDK),细节见 [docs/02-roadmap.md](docs/02-roadmap.md) + +详见 [docs/01-architecture.md](docs/01-architecture.md)。 + +## 快速链接 + +- [01 - 架构](docs/01-architecture.md) +- [02 - 路线图](docs/02-roadmap.md) +- [03 - 移植计划](docs/03-porting-plan.md) +- [04 - 设计决策](docs/04-decisions.md) +- 上游参考:[anthropics/claude-code-action](https://github.com/anthropics/claude-code-action) + +## License + +MIT diff --git a/docs/01-architecture.md b/docs/01-architecture.md new file mode 100644 index 0000000..91df6e7 --- /dev/null +++ b/docs/01-architecture.md @@ -0,0 +1,110 @@ +# 01 - 架构 + +## 总体数据流 + +``` +┌─────────────┐ ┌────────────┐ +│ 开发者 │ ① @claude 在 issue/PR 评论提需求 │ Gitea │ +│ (浏览器) │ ───────────────────────────────────────▶│ (3000) │ +└─────────────┘ └─────┬──────┘ + │ ② webhook → workflow_dispatch + ▼ + ┌───────────────┐ + │ act_runner │ + │ (本机容器) │ + └─────┬─────────┘ + │ ③ 启动 Action + ▼ + ┌────────────────────────────────────┐ + │ claude-gitea-agent Action │ + │ ┌──────────────────────────────┐ │ + │ │ 1. parse trigger │ │ + │ │ 2. fetch issue/PR context │ │ + │ │ 3. build prompt + skills │ │ + │ │ 4. exec `claude` CLI │ │ + │ │ 5. post comment / open PR │ │ + │ └──────────────────────────────┘ │ + └──────┬─────────────────────────────┘ + │ ④ Gitea API (评论/分支/PR) + ▼ + ┌────────────┐ + │ Gitea │ ⑤ 用户在评论里看到 Claude 回复 + └────────────┘ +``` + +## 关键组件 + +### 1. 触发器(trigger) + +| 入口 | 上游 | Gitea 对应 | +|---|---|---| +| `issue_comment` | `github.event.comment.body` | `gitea.event.comment.body` ✅ 一致 | +| `issues` (opened/edited) | 一致 | 一致 | +| `pull_request_review_comment` | 一致 | 一致 | +| `workflow_dispatch` | 手动触发 | 一致 | + +触发短语:`@claude` 在评论/issue 正文里出现。 + +### 2. Gitea API 适配层(src/gitea/) + +**对应上游 `src/github/` 的 1:1 重写**: + +| 上游模块 | Gitea 移植 | 改造点 | +|---|---|---| +| `github/api/` | `gitea/api/` | 用 Gitea Swagger SDK 或 fetch;endpoint 90% 相似 | +| `github/data/` | `gitea/data/` | 数据 schema 重写(少量字段差异) | +| `github/operations/` | `gitea/operations/` | 评论 / branch / PR 创建逻辑 | +| `github/validation/` | `gitea/validation/` | webhook 签名校验、权限检查 | +| `github/utils/` | `gitea/utils/` | 工具函数 | + +### 3. 提示词层(src/create-prompt/) + +**几乎不动**。生成给 Claude 的初始 prompt(包含 issue 标题/正文、相关代码、历史评论)。 + +### 4. 模式层(src/modes/) + +- `tag` 模式:被 @claude 提到时响应(**主用**) +- `agent` 模式:显式 prompt 启动(高级用法,后期支持) + +**几乎不动**。 + +### 5. Claude 执行层 + +调用本机 `claude` CLI 或 `@anthropic-ai/claude-agent-sdk`。携带: + +- `CLAUDE_AGENT_SDK_AUTH`:用户的 API key(Gitea Secret) +- `--add-dir`:仓库 checkout 后的工作目录 +- `--allowed-tools`:默认 Read/Edit/Write/Bash,可通过 inputs 限制 + +## Gitea Actions 兼容性预判 + +| 上游依赖 | Gitea Actions 表现 | 处理 | +|---|---|---| +| `@actions/core` | ✅ 完全兼容 | 原样用 | +| `@actions/github` | ❌ 调用 GitHub API | 替换为 Gitea API | +| `@octokit/rest` | ❌ GitHub-only | 替换为 Gitea SDK 或 fetch | +| `@octokit/graphql` | ❌ Gitea 暂无 GraphQL | 改写为 REST 拼接 | +| `@octokit/webhooks-types` | 类型定义 | 自己写 Gitea 的 types | +| `@modelcontextprotocol/sdk` | ✅ 与平台无关 | 原样用 | +| `@anthropic-ai/claude-agent-sdk` | ✅ 与平台无关 | 原样用 | +| `actions/checkout@v4` | ✅ Gitea Actions 兼容 | 原样用 | + +## 关键路径估算(路线 A) + +| 阶段 | 工作量 | 关键风险 | +|---|---|---| +| 搭基础(types/auth/api 骨架) | 2-3 天 | Gitea SDK 选型 | +| 端口 issue 评论 + PR 评论场景 | 3-4 天 | webhook payload 字段差异 | +| 端口 branch 创建 + PR 提交 | 2-3 天 | Gitea PR API 偶尔有差异 | +| 跑通 end-to-end demo | 1-2 天 | act_runner Docker socket 权限 | +| 文档 + 自部署指南 | 1 天 | - | +| **合计** | **~2 周** | | + +## 升级到路线 B 的接口 + +设计时**保留这两个边界**便于以后切到长驻服务: + +1. `EventDispatcher` 接口:当前实现读 `GITEA_EVENT_PATH` 文件;未来实现订阅 webhook +2. `SessionStore` 接口:当前每次 Action run 都重新 build prompt;未来用同一个 Claude session 持续追加消息 + +具体见 [02-roadmap.md](02-roadmap.md)。 diff --git a/docs/02-roadmap.md b/docs/02-roadmap.md new file mode 100644 index 0000000..9c9a1e5 --- /dev/null +++ b/docs/02-roadmap.md @@ -0,0 +1,111 @@ +# 02 - 路线图 + +## 调研结论(2026-05-25) + +调研了 4 个候选 OSS: + +| 项目 | Stars | 契合度 | 结论 | +|---|---|---|---| +| anthropics/claude-code-action | 7.7k | ⭐⭐⭐⭐⭐ | **GitHub Only**,移植到 Gitea 是这次项目的核心工作 | +| All-Hands-AI/OpenHands | 40k+ | ⭐⭐⭐ | 非 Claude 原生,Gitea 支持弱,引入了大量不需要的东西 | +| SWE-agent/SWE-agent | 19.3k | ⭐⭐ | 学术为主,一次性自动修,不擅长多轮交互 | +| Turrain/claude-gitea-app | 1 | ⭐⭐⭐ | 名字对路但太小(20KB),不能依赖 | + +**Gitea 生态周边可复用**: + +- [Sqcows/forgejo-mcp](https://github.com/Sqcows/forgejo-mcp) — Forgejo/Gitea MCP server,103 工具 +- [pkulik0/gitea-skill](https://github.com/pkulik0/gitea-skill) — `tea` CLI 的 Claude Code Skill + +**结论**:复刻 `claude-code-action` 是最直接的路径,OpenHands 等通用方案在交互性上不及。 + +--- + +## 路线 A:移植 `claude-code-action` 到 Gitea Actions(当前阶段) + +**目标**:端到端跑通最小可用版本,体感先达到 "issue 里说话 → Claude 在评论里回复 → 同意后 PR 自动开"。 + +### Milestone A1:基础设施(第 1 周) + +- [ ] fork upstream,确定 TS/Bun 工具链能在 act_runner 里跑 +- [ ] 在 Gitea 控制面板创建 `@claude` bot 用户 + 对应 access token +- [ ] 验证 Gitea webhook → workflow_dispatch 链路 +- [ ] 写 Gitea API 客户端(`src/gitea/api/`),覆盖: + - 评论 CRUD + - issue/PR 读取 + - 分支创建 + push + - PR 创建 + +### Milestone A2:tag 模式跑通(第 2 周) + +- [ ] 移植 `src/modes/tag/`(@claude 触发的最常用模式) +- [ ] 移植 `src/create-prompt/`(基本不动,确认 context 字段映射) +- [ ] 移植 `src/entrypoints/`(适配 Gitea 事件载荷格式) +- [ ] `.gitea/workflows/claude.yml` 示例工作流 +- [ ] 在本仓库自己开 issue 验证"对话+提 PR"流程 + +### 验收标准 + +``` +1. 我在某个 Gitea 仓库开 issue:「加一个返回当前时间的 /now 接口」 +2. issue 正文末尾 @claude +3. 1 分钟内出现 Claude 评论:「先确认几点... a/b/c」 +4. 我回复评论:「a 这样,b 那样」 +5. Claude 再回复对齐 + 开始执行 +6. Claude push 分支 + 开 PR,PR 描述里贴着完整对话和实现说明 +``` + +--- + +## 路线 B:长驻 webhook 服务(未来) + +**触发条件**:路线 A 跑通后,发现每轮交互 30s+ 冷启动太难受,再启动 B。 + +### 架构差异 + +``` +路线 A:每次评论 → 启动 Action → 加载 context → 退出 (冷) +路线 B:每次评论 → POST 到长驻服务 → 复用 Claude session → 立刻回复 (热) +``` + +### 主要工作 + +- 用 Python 或 Node 写一个常驻 webhook server(FastAPI / Hono) +- 用 `@anthropic-ai/claude-agent-sdk` 维护 `{issue_id: ClaudeSession}` 字典 +- 每条 Gitea 评论 webhook → 找到对应 session → `session.send_message()` → 回写评论 +- 长会话超时回收策略(24h 不活跃释放) +- 加载 superpowers skills([skills 目录注入](https://docs.claude.com/api/agent-sdk)) +- 部署:和 Gitea 同机 docker-compose,加一个 `claude-agent` 服务 + +### 升级路径 + +设计路线 A 代码时已保留两个抽象接口: + +```typescript +interface EventDispatcher { + // A: read GITEA_EVENT_PATH file + // B: subscribe to webhook stream + next(): Promise +} + +interface SessionStore { + // A: fresh prompt each run + // B: persistent ClaudeSession per issue + get(key: string): Promise + save(key: string, session: Session): Promise +} +``` + +切换路线时只需新实现这两个接口,核心业务逻辑(trigger 解析、Gitea API、PR 流程)不动。 + +--- + +## 不做的事(明确拒绝) + +- ❌ **不**追求支持 GitHub + Gitea + GitLab 多平台。Gitea-only,避免抽象税 +- ❌ **不**在路线 A 阶段做 Web UI / dashboard。命令行 + Gitea 评论已够 +- ❌ **不**做"全自动决策合并 PR"。Claude 提 PR,人审核合并 +- ❌ **不**移植上游所有 mode(review/agent 等),只做 tag mode + +## 决策记录 + +非显然的设计选择记在 [04-decisions.md](04-decisions.md)。 diff --git a/docs/03-porting-plan.md b/docs/03-porting-plan.md new file mode 100644 index 0000000..9a55cf6 --- /dev/null +++ b/docs/03-porting-plan.md @@ -0,0 +1,139 @@ +# 03 - 移植计划 + +> 把 `anthropics/claude-code-action` 源代码逐文件迁移到本仓库的清单。 +> 上游版本:master @ 2026-05-23(克隆在 `/home/pangtiankai/projects/claude-code-action-upstream/`) + +## 目录映射 + +| 上游 | 本仓库 | 移植量 | +|---|---|---| +| `action.yml` | `action.yml` | 🟡 改 inputs:去掉 GitHub 特有字段(如 `github_token` → `gitea_token`) | +| `package.json` | `package.json` | 🟡 替换 `@octokit/*` 依赖 | +| `bunfig.toml` | `bunfig.toml` | ✅ 原样 | +| `tsconfig.json` | `tsconfig.json` | ✅ 原样 | +| `src/entrypoints/` | `src/entrypoints/` | 🟡 适配 Gitea 事件载荷字段 | +| `src/modes/tag/` | `src/modes/tag/` | 🟢 90% 原样,少量字段重命名 | +| `src/modes/agent/` | — | ⛔ **暂不移植**(后期再说) | +| `src/create-prompt/` | `src/create-prompt/` | 🟢 90% 原样 | +| `src/github/api/` | `src/gitea/api/` | 🔴 **完全重写**(Gitea API client) | +| `src/github/data/` | `src/gitea/data/` | 🔴 重写 schema | +| `src/github/operations/` | `src/gitea/operations/` | 🔴 重写(评论、branch、PR) | +| `src/github/validation/` | `src/gitea/validation/` | 🔴 重写 | +| `src/github/utils/` | `src/gitea/utils/` | 🟡 部分通用,部分重写 | +| `src/auth/` | `src/auth/` | 🟢 90% 原样(Anthropic API key 认证不变) | +| `src/mcp/` | `src/mcp/` | ✅ 原样 | +| `src/utils/` | `src/utils/` | ✅ 原样 | +| `base-action/` | `base-action/` | 🟢 90% 原样 | + +**统计**:约 30% 文件要重写,60% 只改字段或导入路径,10% 完全不动。 + +## 重写优先级(P0 = 第一周必交付) + +### P0 — Gitea API client(`src/gitea/api/`) + +最低要凑齐的 endpoint: + +| 操作 | Gitea API | 备注 | +|---|---|---| +| 获取 issue | `GET /repos/{owner}/{repo}/issues/{number}` | | +| 获取 issue 评论列表 | `GET /repos/{owner}/{repo}/issues/{number}/comments` | | +| 创建评论 | `POST /repos/{owner}/{repo}/issues/{number}/comments` | | +| 更新评论 | `PATCH /repos/{owner}/{repo}/issues/comments/{id}` | 用于流式更新("思考中..."→ 最终回复) | +| 获取 PR | `GET /repos/{owner}/{repo}/pulls/{number}` | | +| 获取 PR diff | `GET /repos/{owner}/{repo}/pulls/{number}.diff` | | +| 创建分支 | `POST /repos/{owner}/{repo}/branches` | | +| 创建 PR | `POST /repos/{owner}/{repo}/pulls` | | +| 获取仓库内容 | `GET /repos/{owner}/{repo}/contents/{filepath}` | | + +**选择**:直接用 `fetch` + zod schema,不引入完整 Gitea SDK(依赖最小化)。 + +### P0 — Trigger 解析(`src/modes/tag/`) + +需要适配的事件类型: + +| Gitea 事件 | trigger 条件 | +|---|---| +| `issues.opened` | issue 正文含 `@claude` | +| `issues.edited` | 同上 | +| `issue_comment.created` | 评论正文含 `@claude` | +| `pull_request.opened` | PR 正文含 `@claude` | +| `pull_request_comment.created` | PR 评论含 `@claude` | + +### P0 — Entrypoint(`src/entrypoints/`) + +读取 `GITHUB_EVENT_PATH` → 改成 `GITEA_EVENT_PATH`(Gitea Actions 自动注入)。 +事件载荷字段: + +``` +github.event.issue.user.login → gitea.event.issue.user.login ✅ 一致 +github.event.comment.user.login → gitea.event.comment.user.login ✅ 一致 +github.repository → gitea.repository ✅ 一致 +github.event.pull_request.head.sha → 一致 +``` + +### P1 — Status comment("我在干活" 进度评论) + +上游用 GitHub Checks API 做进度展示,Gitea 没有等价 API([Gitea Issue #28381](https://github.com/go-gitea/gitea/issues/28381) 是 commit status,不一样)。 + +替代方案:**用一条评论的连续 edit 模拟**。 +- 第一条评论:"🤔 Claude 正在分析需求..." +- 边干边 PATCH 更新这条评论的正文 +- 完成后定格成最终内容 + +### P2 — `agent` mode、`review` mode + +后期再说。 + +## 依赖替换 + +### package.json + +去掉: +```json +"@actions/github": "^...", +"@octokit/rest": "^...", +"@octokit/graphql": "^...", +"@octokit/webhooks-types": "^..." +``` + +新增(如果需要): +```json +// 如果用生成的 SDK +"gitea-js": "latest" +// 否则只用 fetch + zod,零新增 +``` + +保留原样: +```json +"@actions/core": "...", // 跟 Gitea Actions 兼容 +"@anthropic-ai/claude-agent-sdk": "...", +"@modelcontextprotocol/sdk": "...", +"shell-quote": "...", +"zod": "..." +``` + +## webhook payload 差异速查 + +Gitea 的 webhook payload 大部分字段和 GitHub 一致(这是有意为之的),主要差异: + +| GitHub | Gitea | 说明 | +|---|---|---| +| `pull_request.head.repo` | `pull_request.head.repo` | Gitea 始终有此字段(GH 偶尔 null) | +| `installation` | — | Gitea 没有 GitHub App 概念 | +| `sender.type === "Bot"` | `sender.type === "user"` | Gitea 暂无 Bot 用户类型 | + +完整差异参考:[Gitea webhook 文档](https://docs.gitea.com/usage/webhooks)。 + +## 测试策略 + +1. **本仓库自身做 dogfooding**:在 `claude-gitea-agent` 自己的 issue 里 @claude,让它给自己写功能 +2. **mock 测试**:`test/fixtures/` 放几个 Gitea webhook payload 样本 +3. **CI**:暂不上 CI,跑通 happy path 后再加 + +## 当前阻塞 / 风险 + +| 风险 | 缓解 | +|---|---| +| Gitea Actions 跑 bun 容器可能要装 bun | runner image 用 `catthehacker/ubuntu:act-latest`(已包含 node,bun 安装 1 行命令) | +| Gitea webhook 评论事件名是 `issue_comment` 还是 `issues_comment`? | 写 entrypoint 时先 dump payload 验证 | +| Anthropic API rate limit 在循环交互场景下会不会爆 | 暂不优化,到了再说 | diff --git a/docs/04-decisions.md b/docs/04-decisions.md new file mode 100644 index 0000000..34a3f1e --- /dev/null +++ b/docs/04-decisions.md @@ -0,0 +1,110 @@ +# 04 - 设计决策(ADR) + +> 仅记录"非显然"的选择 —— 同一份代码读者扫一眼就懂的东西不在这里。 + +## ADR-001:fork 上游而不是从零写 + +**日期**:2026-05-25 +**状态**:✅ 采纳 + +**背景**:要做"issue 触发 Claude → 交互 → PR"的工具,候选方案有从 0 写、用 OpenHands、复刻 SWE-agent、移植 claude-code-action。 + +**决策**:移植 anthropics/claude-code-action。 + +**原因**: +- 上游的 prompt 构造、context 收集、PR 生成流程是 Anthropic 内部生产用的,质量靠谱 +- TypeScript 实现,比起从 0 写省 2-3 周 +- Gitea API 与 GitHub API 大量相似,移植量约 30% 文件 +- 不引入 OpenHands 等大型框架的认知和维护成本 + +**代价**: +- 引入 TS/Bun 工具链(如果团队更熟 Python,是个学习成本) +- 跟随上游升级有维护成本(mitigated by:上游 API 变化不会太频繁) + +--- + +## ADR-002:路线 A(Actions)先行,路线 B(webhook 服务)后置 + +**日期**:2026-05-25 +**状态**:✅ 采纳 + +**背景**:Gitea Actions 冷启动 ~30s,每轮交互体感差。一开始就上长驻服务能解决,但工作量翻倍。 + +**决策**:先做路线 A,跑通验收标准后再决定是否升级 B。 + +**原因**: +- 体感差但能用 ≫ 体感好但跑不起来 +- 真实跑起来才知道哪些环节实际是瓶颈(可能 30s 启动里实际只有 5s 在等 Claude,瓶颈是别的) +- 留好抽象接口(`EventDispatcher` / `SessionStore`),升级不重写业务 + +**代价**: +- 用户首次体验差,可能让人产生"这玩意没用"的错觉 +- mitigated by:文档明说"路线 A 是过渡,B 才是终态" + +--- + +## ADR-003:不引入完整 Gitea SDK,用 fetch + zod + +**日期**:2026-05-25 +**状态**:✅ 采纳 + +**背景**:Gitea 有 `gitea-js` SDK,但维护活跃度一般。 + +**决策**:直接 `fetch` + zod schema 做 API 调用。 + +**原因**: +- 需要的 endpoint 只有 ~10 个,自己写 client 不到 200 行 +- zod schema 比 SDK 自带类型更可控(出问题好排查) +- 少一个依赖,少一份升级负担 +- 上游 `@octokit/rest` 本来也只用了 1% 的功能 + +**代价**: +- 失去了 SDK 的 retry / rate-limit 内置逻辑 +- mitigated by:路线 A 阶段流量很小,需要时再加 wrapper + +--- + +## ADR-004:Status comment 用"连续 edit 单条评论"模拟 GitHub Checks + +**日期**:2026-05-25 +**状态**:✅ 采纳 + +**背景**:上游用 GitHub Checks API 做进度展示(带 ✅ / ⏳ 复选框)。Gitea 没有等价 API。 + +**决策**:发一条评论作为状态板,全程 PATCH 这条评论的正文模拟进度更新。 + +**原因**: +- 唯一可行的方案(除非等 Gitea 实现 Checks API) +- 用户看到的视觉效果差不多(一条会变的评论 vs 一个会变的 Check) +- 实现简单 + +**代价**: +- 不像 Checks 那样能在 PR 顶部显眼地显示状态 +- mitigated by:评论会自动滚到底部,user 自然能看到最新进度 + +--- + +## ADR-005:暂不支持 GitHub App 等价的 bot 身份 + +**日期**:2026-05-25 +**状态**:✅ 采纳 + +**背景**:上游支持 GitHub App 安装,bot 评论时会有 "Claude (bot)" 标识。Gitea 暂无原生 bot 用户类型。 + +**决策**:用普通用户 `@claude`(或任意命名)作为 bot 身份,access token 模式调用。 + +**原因**: +- Gitea 没有 App 概念,硬模拟没必要 +- 普通用户 + token 模式简单直接,权限可控 + +**代价**: +- 评论里没有 bot 标识,看起来像普通用户在说话 +- mitigated by:评论里加固定 footer "🤖 Powered by claude-gitea-agent" + +--- + +## 待决策 + +- 长会话状态存哪?路线 B 时再选(候选:SQLite / Redis / 文件) +- 是否支持私有仓库?默认支持,token 权限够就行 +- 多仓库共享 runner 还是每仓库独立?运行时按需启动,act_runner 已支持