Skip to content

Commit 539ea2c

Browse files
authored
Merge branch 'main' into fix/result-idle-timeout
2 parents a1ad254 + 7107288 commit 539ea2c

85 files changed

Lines changed: 8374 additions & 1472 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
---
2+
name: maker-chats
3+
description: "根据 Maker App ID 拉取该应用的 chat 列表,并生成可直接打开的 maker/fuping URL 表格。当用户提供 Maker App ID(UUID 形式,如 99b00f3e-e64a-455a-b000-9ec2c95297d7)并想查看该应用下所有 chat 会话、复盘历史对话、获取带 bypassAuth 的直达链接时使用。默认先查生产,生产无数据时回落到 fuping;也可显式指定 prod/fuping/both。"
4+
argument-hint: "<app_id> [prod|fuping|both]"
5+
---
6+
7+
# Maker Chats
8+
9+
根据 Maker App ID 拉取生产或 fuping 环境的 chat 列表,输出包含直达链接、标题、最后活跃时间的表格。
10+
11+
## API
12+
13+
| 环境 | API URL | 直达链接 base |
14+
|------|---------|---------------|
15+
| prod | `https://maker.taptap.cn/api/v1/gmtools/apps/<app_id>/chats` | `https://maker.taptap.cn/app/<app_id>?chatId=<chat_id>&bypassAuth=true` |
16+
| fuping | `https://fuping.agnt.xd.com/api/v1/gmtools/apps/<app_id>/chats` | `https://fuping.agnt.xd.com/app/<app_id>?chatId=<chat_id>&bypassAuth=true` |
17+
18+
**分页参数**`?limit=<N>&offset=<M>`。响应顶层 `hasMore: true` 表示还有下一页。默认页大小 `limit=20`
19+
无需鉴权,直接 GET。响应示例:
20+
21+
```json
22+
{
23+
"appId": "99b00f3e-e64a-455a-b000-9ec2c95297d7",
24+
"limit": 20,
25+
"offset": 0,
26+
"hasMore": false,
27+
"chats": [
28+
{
29+
"id": "6825614d-1ad0-4084-9cb0-6acc7212e285",
30+
"title": "类似霓虹深渊的游戏...",
31+
"lastActivity": "2026-05-11T13:12:19.062Z"
32+
}
33+
]
34+
}
35+
```
36+
37+
## 执行步骤
38+
39+
### 1. 解析输入
40+
41+
`$ARGUMENTS` 或用户消息中提取:
42+
43+
- `app_id`:UUID 格式(必须)。如未提供则询问用户。
44+
- 环境:`prod` / `fuping` / `both`**未指定时默认行为:先查 prod,命中则只展示 prod;prod 无数据(chats 为空、404 Not Found 或请求失败)时再查 fuping。** 用户显式指定时按指定执行(`both` 才并行两个环境)。
45+
46+
### 2. 查询 chats(按需翻页)
47+
48+
**单次只拉一页**。接口虽支持分页,但一页通常足够使用,不要循环抓取——等用户明确要下一页时再翻。
49+
50+
**默认(fallback)**:先单独查 prod 第一页,若无数据则查 fuping 第一页。
51+
52+
```bash
53+
curl -s 'https://maker.taptap.cn/api/v1/gmtools/apps/<app_id>/chats?limit=20&offset=0'
54+
# 若响应为 {"error":"Not Found"} / 网络失败 / chats 为空 → 再查 fuping
55+
curl -s 'https://fuping.agnt.xd.com/api/v1/gmtools/apps/<app_id>/chats?limit=20&offset=0'
56+
```
57+
58+
**显式 `both`**:并行调两个环境的第一页。
59+
60+
**显式 `prod` / `fuping`**:只调对应环境第一页,不回落。
61+
62+
判断"无数据":响应非 JSON、缺少 `chats` 字段、`chats` 为空、HTTP 非 2xx。
63+
64+
**翻页**:当用户说"下一页"、"再来 N 条"、"翻到第 X 页"等时,复用上次命中的环境与 `limit`,按 `offset += limit` 调用:
65+
66+
```bash
67+
curl -s '<base>/api/v1/gmtools/apps/<app_id>/chats?limit=<limit>&offset=<next_offset>'
68+
```
69+
70+
跨轮记住:当前环境、`limit`、下一个 `offset`。如果用户切换 app_id,重置状态。
71+
72+
### 3. 输出结果
73+
74+
每个查询的环境输出一张表格。链接 base 必须**与查询环境一致**(fuping 的 chat 不能用 maker.taptap.cn 链接,反之亦然)。
75+
76+
```markdown
77+
## Maker Chats — `<app_id>`
78+
### Production (`maker.taptap.cn`) — 第 1 页 (offset 0, N 条)
79+
80+
| URL | Title | Last Activity |
81+
|-----|-------|---------------|
82+
| https://maker.taptap.cn/app/<app_id>?chatId=<chat_id>&bypassAuth=true | <title> | <lastActivity> |
83+
| ... | ... | ... |
84+
85+
### Fuping (`fuping.agnt.xd.com`) — 第 1 页 (offset 0, N 条)
86+
87+
| URL | Title | Last Activity |
88+
|-----|-------|---------------|
89+
| https://fuping.agnt.xd.com/app/<app_id>?chatId=<chat_id>&bypassAuth=true | <title> | <lastActivity> |
90+
| URL | Title | Last Activity |
91+
|-----|-------|---------------|
92+
| https://fuping.agnt.xd.com/app/<app_id>?chatId=<chat_id>&bypassAuth=true | <title> | <lastActivity> |
93+
```
94+
95+
排序:按 `lastActivity` 倒序(最近活跃在前)。
96+
97+
### 4. 注意事项
98+
99+
- `lastActivity` 保留 API 返回的原始 ISO 字符串,不做时区转换。
100+
- 表格表头显示**当前页条数**与分页位置,例如 `Production (maker.taptap.cn) — 第 1 页 (offset 0, 20 条)`
101+
-`hasMore=true`,在该环境表格下方追加一行提示:`> 还有更多 chat(hasMore=true),告诉我"下一页"继续翻。`
102+
- 翻页结果同样按上述格式输出,标明当前 offset。
103+
- 仅输出表格与必要说明,不要追加"是否需要还原项目"等额外建议(用户需要时会显式触发 `restore-project` 等技能)。

.claude/skills/pr-fixup/SKILL.md

Lines changed: 68 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,23 @@ gh run view RUN_ID --log-failed 2>&1 | tail -100
141141

142142
### Step 3: 获取未解决的 Review 评论
143143

144-
通过 GraphQL 获取所有 review threads:
144+
Review 反馈分布在三个地方,必须**都检查**,不能只看 inline review threads:
145+
146+
1. **Inline review threads** — reviewer 在具体代码行上的评论(GraphQL `reviewThreads`
147+
2. **PR 顶层 issue comments** — review bot 经常把发现的问题汇总成一条整体评论发到 PR 主时间线(`gh pr view --json comments`
148+
3. **PR description** — 部分 review bot 会把 summary 写进 PR description 而非 comment
149+
150+
先获取 PR 作者、body、head SHA,以及最后一次推送对应的 commit 时间:
151+
152+
```bash
153+
gh pr view PR_NUMBER --json author,body,headRefOid -q '{author: .author.login, body: .body, sha: .headRefOid}'
154+
# 用 GitHub 上 head commit 的 committer date 作为"最后一次 push 时间"的近似值
155+
# 不要用本地 git log(rebase/amend 后本地时间和 GitHub 上不一致)
156+
HEAD_SHA=$(gh pr view PR_NUMBER --json headRefOid -q .headRefOid)
157+
LAST_PUSH=$(gh api repos/OWNER/REPO/commits/$HEAD_SHA --jq .commit.committer.date)
158+
```
159+
160+
**3a. Inline review threads**(GraphQL):
145161

146162
```bash
147163
gh api graphql -f query='{
@@ -158,6 +174,7 @@ gh api graphql -f query='{
158174
author { login }
159175
path
160176
line
177+
createdAt
161178
}
162179
}
163180
}
@@ -167,19 +184,45 @@ gh api graphql -f query='{
167184
}'
168185
```
169186

170-
先获取 PR 作者:`gh pr view PR_NUMBER --json author -q .author.login``PR_AUTHOR`
171-
172187
过滤条件:
173188
- `isResolved == false`(未解决)
174-
- 发起评论(第一条 comment)的 `author.login` **不是** PR 作者(排除自己的评论,处理所有 reviewer 的反馈,包括 bot 和人类 reviewer)
189+
- 第一条 comment 的 `author.login` **不是** PR 作者
190+
191+
**3b. PR 顶层 issue comments**
192+
193+
```bash
194+
gh pr view PR_NUMBER --json comments -q '.comments[] | select(.author.login != "PR_AUTHOR") | {id, body, author: .author.login, createdAt}'
195+
```
196+
197+
过滤条件:
198+
- `author.login` 不是 PR 作者
199+
- `createdAt` 在最后一次 push 之后(处理新增反馈,忽略已被旧 commit 处理的历史评论)
200+
201+
**3c. PR description**
202+
203+
检查 PR body 中是否包含 review summary。判定标准(避免把普通 PR 说明误判为 review):
204+
205+
- **强信号**(出现任一即可判定):`## Review Summary``### Issues Found``## Review Notes``Suggested Action`
206+
- **弱信号**(需同时出现 ≥2 个才算):`🟡``🔴``nit``confidence``severity`
207+
208+
满足上述任一规则的,把 review summary 段落里的条目当作待处理 review feedback,与 3a/3b 一起进入 Step 4。
209+
210+
**去重(重要)**:3a 靠 `isResolved` 去重、3b 靠 `createdAt > LAST_PUSH` 去重,但 PR description 是静态的,Claude 修完代码 push 后 description 内容并不会变。为避免同一 `/pr-fixup` 调用内同一条 3c 条目被反复处理,必须做以下两件事之一:
175211

176-
如果没有 CI 失败(Step 2 已全部通过)且没有未解决的非作者评论 → 输出 "✅ 所有 CI checks 通过,PR review 无阻塞问题" 并结束循环。
212+
- **本轮内存记录**:在当前 `/pr-fixup` 执行流程中维护一个集合(如条目正文的前 50 字符 hash),处理过的 3c 条目下一轮直接跳过
213+
- **镜像到顶层 comment**:处理完 3c 条目后调用 `gh pr comment` 写一条 "Addressed (3c): <条目摘要>" 到 PR 主时间线,让后续轮次靠 3b 的 `LAST_PUSH` 过滤自动跳过
214+
215+
推荐第一种(更便宜,不污染 PR 时间线)。
216+
217+
如果没有 CI 失败(Step 2 已全部通过)且 3a/3b/3c 都没有未处理的反馈 → 输出 "✅ 所有 CI checks 通过,PR review 无阻塞问题" 并结束循环。
177218

178219
### Step 4: 分析并处理 Review 评论
179220

180-
对于每个未解决的评论:
221+
对于每个未解决的评论(来自 3a inline、3b issue comment、3c PR description summary)
181222

182-
1. **读取完整源文件**:用 Read 工具读取评论所在的 `path` 文件
223+
1. **读取相关源文件**
224+
- inline 评论:用 Read 工具读取评论所在的 `path` 文件
225+
- issue comment / PR description summary:从 body 中解析出涉及的文件路径(通常是 `src/foo.ts:123` 格式),逐个 Read
183226
2. **理解评论内容**:仔细阅读 `body` 中指出的具体问题
184227
3. **结合上下文判断**:评论是否正确?
185228

@@ -200,11 +243,19 @@ gh api graphql -f query='{
200243
- 回复评论确认修复:
201244

202245
```bash
246+
# inline review comment(来自 3a)
203247
gh api repos/OWNER/REPO/pulls/PR_NUMBER/comments/COMMENT_DATABASE_ID/replies \
204248
-f body="Fixed — <简述修改内容>"
249+
250+
# PR 顶层 issue comment(来自 3b)— 没有 thread,直接在 PR 主时间线新增一条
251+
# 回复链接用完整 URL(GitHub 不会把 #COMMENT_ID 解析成 comment 跳转)
252+
gh pr comment PR_NUMBER --body "Fixed — <简述修改内容>(回复 [评论](https://github.com/OWNER/REPO/pull/PR_NUMBER#issuecomment-ISSUE_COMMENT_ID))"
253+
254+
# PR description summary 条目(来自 3c)— 同样在 PR 主时间线回复
255+
gh pr comment PR_NUMBER --body "Addressed — <简述修改内容>"
205256
```
206257

207-
- Resolve 该 thread:
258+
- Resolve 该 thread(仅 inline review thread 适用,issue comment 和 description summary 无 thread 可 resolve)
208259

209260
```bash
210261
gh api graphql -f query='mutation {
@@ -219,11 +270,15 @@ gh api graphql -f query='mutation {
219270
1. 回复评论说明原因:
220271

221272
```bash
273+
# inline review comment(来自 3a)
222274
gh api repos/OWNER/REPO/pulls/PR_NUMBER/comments/COMMENT_DATABASE_ID/replies \
223275
-f body="Not an issue — <具体解释,引用代码说明 reviewer 的判断为什么不适用于此场景>"
276+
277+
# PR 顶层 issue comment / description summary(来自 3b/3c)
278+
gh pr comment PR_NUMBER --body "Not an issue — <具体解释>"
224279
```
225280

226-
2. Resolve 该 thread:
281+
2. Resolve 该 thread(仅 inline review thread 适用)
227282

228283
```bash
229284
gh api graphql -f query='mutation {
@@ -246,10 +301,12 @@ gh api graphql -f query='mutation {
246301
- 输出 "🔄 第 N 轮:修复 X 个 CI 问题 + Y 个 review 问题,反驳 Z 个误报,等待新一轮 checks..."
247302
- 回到 Step 1
248303

249-
**如果只有误报被 resolve(无代码修复)且 CI 全部通过:**
250-
- 输出 "✅ 第 N 轮:反驳 Y 个误报并 resolve,所有 CI checks 通过"
304+
**如果本轮所有 review 反馈都已处理(inline 已 reply+resolve、3b/3c 已 reply)且无代码修复且 CI 全部通过:**
305+
- 输出 "✅ 第 N 轮:处理 Y 个 review 反馈(含 Z 个反驳),所有 CI checks 通过"
251306
- 结束循环
252307

308+
> 注意:3b/3c 没有 thread 可以 resolve,"已处理"的标准是已经发出 `gh pr comment` 回复。不要因为"没有 resolve 动作"就误判为未处理而陷入死循环。
309+
253310
---
254311

255312
## 完成汇总

.env.example

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@ ALLOWED_USER_IDS=
2020
# 为空时自动将首个发消息的用户设为管理员
2121
OWNER_USER_ID=
2222

23+
# === Session Fork 配置 (Plan 8) ===
24+
# /fork 命令开关,默认开启。设为 false 可关闭
25+
# FORK_ENABLED=false
26+
2327
# === Claude Code 配置 ===
2428
# Anthropic API Key (Claude Code 需要)
2529
ANTHROPIC_API_KEY=sk-ant-xxxxxxxx

.github/workflows/claude-comment.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,8 @@ jobs:
5656
run: bun install --frozen-lockfile
5757

5858
- uses: anthropics/claude-code-action@v1
59+
env:
60+
ANTHROPIC_BASE_URL: https://llm-proxy.tapsvc.com
5961
with:
6062
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
6163
trigger_phrase: "@claude"

.github/workflows/pr-review.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ jobs:
4040
run: bun install --frozen-lockfile
4141

4242
- uses: anthropics/claude-code-action@v1
43+
env:
44+
ANTHROPIC_BASE_URL: https://llm-proxy.tapsvc.com
4345
with:
4446
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
4547
github_token: ${{ github.token }}

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
node_modules/
22
dist/
33
data/
4+
!src/cron/holidays/data/
45
logs/
56
.env
67
*.log

CLAUDE.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ Feishu User → Feishu Platform → Bridge Server → Claude Agent SDK → Claud
7272
- **`settingSources: ['project']`** loads `.claude/settings.local.json` from cwd, including `permissions.allow` whitelist. This whitelist is checked *before* `canUseTool`, so unlisted tools (including MCP) get blocked. Currently `canUseTool` with proper `updatedInput` overrides this.
7373
- **Skills require `allowedTools: ['Skill']`** — SDK 默认不启用 Skill 工具。仅有 `settingSources: ['project']` 不够,还需在 `allowedTools` 中显式包含 `'Skill'`,否则 `.claude/skills/` 中的 SKILL.md 不会被加载。
7474
- **Feishu rich text breaks URLs**`github.com:user/repo` gets auto-linked by Feishu as `[github.com:](http://github.com/)user/repo`. The `workspace/manager.ts` `normalizeRepoUrl()` handles SSH shorthand normalization.
75+
- **`options.env` 完全替换子进程环境(SDK 0.3.x)** — 一旦传入 `env`,子进程**不再继承**父进程环境,必须自行展开:`{ ...process.env, ANTHROPIC_BASE_URL: ... }`。否则会丢失 `ANTHROPIC_API_KEY` / `PATH` / `HOME`,导致 Claude Code 返回 `Not logged in · Please run /login`。仅在配了代理 `ANTHROPIC_BASE_URL` 时才需要传 `env`(见 `claude/executor.ts`)。注意 0.2.x 是**合并**语义(`{...process.env, ...你的}`),升级到 0.3.x 时这是隐蔽的破坏性变更。
7576

7677
## Configuration
7778

@@ -104,6 +105,7 @@ Loaded via dotenv (see `.env.example`):
104105
## Deployment
105106

106107
- 部署方式取决于运行环境(PM2、systemd、Docker 等),服务启动时自动检测进程管理器类型(见 `src/utils/runtime.ts`)。
108+
- **升级/拉取后必须跑 `npm install`**`@anthropic-ai/claude-agent-sdk` 0.3.x 起,Claude Code CLI 二进制改为通过 `optionalDependencies` 按平台分发(`@anthropic-ai/claude-agent-sdk-<platform>`,约 240MB)。只 `git pull` + 重新 `npm run build` + 拷 `dist/` **不会**拉到该二进制,服务会起不来。`npm install` 会按 OS/CPU/libc 自动选包(glibc/musl、x64/arm64 均有)。同理,`@anthropic-ai/sdk``@modelcontextprotocol/sdk` 在 0.3.x 是 peerDependencies,已提为本仓库的直接依赖。
107109
- **自重启须在对话最后一步执行** — Claude 是服务的子进程,使用 `sleep 5 && <restart command> &` 脱离当前进程,避免 kill 自己的父进程导致对话中断。
108110

109111
## Tech Stack

0 commit comments

Comments
 (0)