---
name: unidesk-gh
description: UniDesk GitHub CLI - 通过 `bun scripts/cli.ts gh ...` 管理 GitHub issue/PR，不依赖原生 `gh` binary。用户提到 gh、GitHub issue、GitHub PR、创建 issue、评论 issue、合并 PR、preflight、看板操作时使用。
---

# UniDesk GitHub CLI

UniDesk 受控 GitHub 操作入口，所有 issue/PR 读写走 `bun scripts/cli.ts gh <subcommand>`，不依赖原生 `gh` binary，不手写 `curl`/GraphQL。

**固定入口前缀**: `cd /root/unidesk && bun scripts/cli.ts gh ...`

## 写入语言

- Issue/PR 的正式写入一律使用中文，包括标题、正文、评论、关闭/重开说明、PR 描述、PR 评论和 merge closeout 说明。
- 命令、路径、trace/session/job id、API path、代码标识符、英文专有名词、原始日志摘录和外部错误原文可以保留原样；解释性文字、结论、风险、计划和验收说明必须中文。
- 除非用户明确要求外文，不要用英文撰写 issue/PR closeout、进展评论或阻塞说明。

---

## Issue 目标分支 / Lane

- 创建 issue、补充正式 issue 评论、关闭/重开说明或把 trace 发现的问题登记成 issue 时，正文必须显式写明目标合并分支或运行 lane。
- 推荐固定字段：`目标合并分支: <repo> <branch/lane>`；例如 `目标合并分支: HWLAB v0.2`、`目标合并分支: AgentRun v0.1`、`目标合并分支: UniDesk master`。
- 如果该 issue 只记录外部依赖、运维观察或不需要代码合并，仍必须写 `目标合并分支: 不适用`，并用一句话说明原因。
- 看到用户或上下文已明确目标 lane 时必须照写；不明确时按目标仓库长期 source truth 选择：HWLAB 按当前 runtime lane（如 `v0.2`/`v0.3`）、AgentRun 默认 `v0.1`、UniDesk 默认 `master`。不能确定时写 `目标合并分支: 待确认` 并说明待确认点，不能省略字段。

---

## 认证探测

```bash
bun scripts/cli.ts gh auth status [--repo owner/name]
```

探测 token 来源（`GH_TOKEN`/`GITHUB_TOKEN`/`gh auth token`）、GitHub REST egress、repo 可见性、issue 可读性。不打印 token。

---

## Issue 命令

### 列表

```bash
bun scripts/cli.ts gh issue list [owner/repo] \
  [--state open|closed|all] [--limit N] [--search text] \
  [--label label[,label...]] [--repo owner/name] \
  [--json number,title,state,url,updatedAt,createdAt,author,labels] [--raw|--full]
```

默认 `state=open`、`limit=30`。`owner/repo` 位置参数等价 `--repo`。`--search` 走 GitHub Search API 做查重。

### 查看

```bash
bun scripts/cli.ts gh issue view <number|url|owner/repo#number> \
  [--repo owner/name] [--json body,title,state,closed,closedAt,comments,commentCount] [--raw|--full]
```

`read` 是兼容别名。支持 `owner/repo#number` shorthand（如 `pikasTech/HWLAB#1024`）。

### 创建

```bash
bun scripts/cli.ts gh issue create \
  --repo owner/name \
  --title "标题" \
  --label "bug,v0.2" \
  --body-stdin <<'EOF'
目标合并分支: HWLAB v0.2

正文（Markdown，支持换行、反引号、表格）
EOF
```

`--body-stdin` 是 heredoc/stdin 第一等入口。`--dry-run` 只输出计划不写 GitHub。

### 更新正文

```bash
# 替换正文
bun scripts/cli.ts gh issue update <number> --repo owner/name \
  --mode replace --body-stdin <<'EOF'
新正文
EOF

# 追加正文
bun scripts/cli.ts gh issue update <number> --repo owner/name \
  --mode append --body-stdin <<'EOF'
追加内容
EOF
```

`edit` 是 `update --mode replace` 的兼容别名。正式写入默认先读当前 issue 做 body guard。

### 评论

```bash
# 创建评论
bun scripts/cli.ts gh issue comment create <number|owner/repo#number> \
  --repo owner/name --body-stdin <<'EOF'
评论正文
EOF

# 原地修正评论
bun scripts/cli.ts gh issue comment update <commentId> \
  --repo owner/name --body-stdin <<'EOF'
新的评论正文
EOF

# 删除评论
bun scripts/cli.ts gh issue comment delete <commentId>
```

`edit` 是 `comment update` 的兼容别名。`--body <short-text>` 仅适合短单行。日常修正文案优先用 `update/edit` 保留评论 ID 和时间线；`delete` 只用于确实需要删除的评论。

### 关闭/重开

```bash
bun scripts/cli.ts gh issue close <number|owner/repo#number> \
  --repo owner/name [--comment "关闭原因"]

bun scripts/cli.ts gh issue reopen <number|owner/repo#number> \
  --repo owner/name [--comment "重开原因"]
```

附长证据时先用 `comment create` 写证据评论，再用 `close --comment <短引用>` 关闭。不支持 `delete`（走 `close`）。

### 批量过期关闭

```bash
bun scripts/cli.ts gh issue stale-close \
  --repo owner/name [--inactive-hours 48] [--limit N] \
  [--label label] [--dry-run]
```

关闭 `updatedAt < observedAt - inactiveHours` 的 open issue。先 `--dry-run` 观察，再正式执行。

### 转义扫描

```bash
bun scripts/cli.ts gh issue scan-escape \
  --repo owner/name [--limit N] [--dry-run]
```

只读扫描 issue 正文/评论中的字面量 `\n`、`\t`、ANSI escape 等污染。

---

## PR 命令

### 列表

```bash
bun scripts/cli.ts gh pr list [owner/repo] \
  [--state open|closed|all] [--json ...] [--raw|--full]
```

### 查看

```bash
bun scripts/cli.ts gh pr view <number|url|owner/repo#number> \
  [--repo owner/name] \
  [--json body,title,state,stateDetail,headRefName,baseRefName,mergeable,mergeStateStatus,statusCheckRollup] \
  [--raw|--full]
```

`stateDetail` 归一化为 `open|closed|merged`。`mergeable`/`mergeStateStatus`/`statusCheckRollup` 走 GraphQL。

### 文件变更

```bash
bun scripts/cli.ts gh pr files <number> [--repo owner/name] [--limit N]
```

返回 changed files 统计，不输出 raw diff。`gh pr diff <number> --stat` 是兼容别名。

### 收口预检

```bash
bun scripts/cli.ts gh pr preflight <number|owner/repo#number> \
  [--repo owner/name] [--full|--raw]
```

`preflight`/`closeout` 是别名。只读检查 state/draft/conflict/mergeable/statusCheck，不写 GitHub。默认只给 status check 计数与失败预览。

### 创建

```bash
bun scripts/cli.ts gh pr create \
  --repo owner/name \
  --title "标题" \
  --body-stdin --base master --head <branch> \
  [--draft] [--dry-run] <<'EOF'
PR 正文
EOF
```

### 合并

```bash
bun scripts/cli.ts gh pr merge <number> \
  [--repo owner/name] [--merge|--squash|--rebase] \
  [--delete-branch] [--dry-run]
```

guarded merge：先做 closeout 预检，拒绝非 ready PR。已 merged 返回 `alreadyMerged=true`。

### 编辑 / 评论 / 关闭 / 重开

```bash
bun scripts/cli.ts gh pr update <number> --mode replace|append --body-stdin [--title ...]
bun scripts/cli.ts gh pr comment create <number> --body-stdin
bun scripts/cli.ts gh pr comment update <commentId> --body-stdin
bun scripts/cli.ts gh pr comment edit <commentId> --body-stdin
bun scripts/cli.ts gh pr comment delete <commentId>
bun scripts/cli.ts gh pr close <number> [--comment ...]
bun scripts/cli.ts gh pr reopen <number> [--comment ...]
```

`pr comment edit` 是 `pr comment update` 的兼容别名。与 issue 对应命令行为一致；PR 评论也是 GitHub issue comment，更新目标使用 commentId。

---

## 虚拟文件系统 (`trans gh:`)

```bash
# 列出 repo
trans gh:/pikasTech/HWLAB ls

# 列出 PR / Issue
trans gh:/pikasTech/HWLAB/pr ls [--limit N] [--full]
trans gh:/pikasTech/HWLAB/issue ls [--limit N] [--full]

# 查看单个条目
trans gh:/pikasTech/HWLAB/pr/507 ls
trans gh:/pikasTech/HWLAB/505/1 cat

# patch-apply 写回正文（走 gh issue/pr update）
trans gh:/pikasTech/HWLAB/pr/507 apply-patch <<'PATCH'
*** Begin Patch
*** Update File: body.md
@@
 old line
-old line
+new line
 more context
*** End Patch
PATCH
```

正文一楼映射为 `body.md`。写回走 `gh issue/pr update` 的 guard 规则。`rm` 对正文结构化拒绝。

---

## 看板命令 (#20 总看板)

```bash
# 结构审计
bun scripts/cli.ts gh issue board-audit \
  --repo pikasTech/unidesk --board-issue 20 [--dry-run]

# 行列表
bun scripts/cli.ts gh issue board-row list \
  --board-issue 20 [--state open|closed|all]

# 行查看
bun scripts/cli.ts gh issue board-row get <issueNumber> --board-issue 20

# 行更新（单字段）
bun scripts/cli.ts gh issue board-row update <issueNumber> \
  --board-issue 20 \
  --field progress|status|validation|branch|tasks|focus \
  --value <text> \
  [--expect-body-sha <sha>|--expect-updated-at <ts>]

# 行新增/插入（upsert）
bun scripts/cli.ts gh issue board-row upsert <issueNumber> \
  --board-issue 20 --section open|closed \
  --branch <branch> --tasks <task> --summary <text> \
  --focus <text> --validation <text> --progress <text> \
  [--expect-body-sha <sha>|--expect-updated-at <ts>]

# 行移动 / 删除
bun scripts/cli.ts gh issue board-row move <issueNumber> \
  --board-issue 20 --to open|closed [--status OPEN|CLOSED]
bun scripts/cli.ts gh issue board-row delete <issueNumber> \
  --board-issue 20
```

写操作默认 dry-run，正式 PATCH 必须带 `--expect-body-sha` 或 `--expect-updated-at`。

---

## 关键约定

### heredoc 优先

多行正文/评论一律用 `--body-stdin <<'EOF'`（quoted heredoc），不用 `--body` 内联长文本、不用临时文件、不用 `gh api -f body=...`：

```bash
bun scripts/cli.ts gh issue create --repo pikasTech/HWLAB \
  --title "标题" --body-stdin <<'EOF'
正文，支持 `code`、**bold**、表格等 Markdown
EOF
```

### owner/repo#number shorthand

所有接受 issue/PR number 的命令都支持：

```bash
bun scripts/cli.ts gh issue view pikasTech/HWLAB#1024
bun scripts/cli.ts gh pr preflight pikasTech/HWLAB#1020
bun scripts/cli.ts gh issue comment create pikasTech/HWLAB#1024 --body-stdin <<'EOF'
...
EOF
```

shorthand 与显式 `--repo` 冲突时结构化失败。

### 干跑优先

写操作前先 `--dry-run`：

```bash
bun scripts/cli.ts gh issue update 20 --mode append --body-stdin --dry-run <<'EOF'
...
EOF
```

### debug 任务入口

```bash
bun scripts/cli.ts codex pr-preflight [--remote] [--push-dry-run ...] [--pr-create-dry-run ...] [--issue N] [--full|--raw]
```

用于 PR 型派单 admission，检查 scheduler auth、runner GH token、git worktree、GitHub egress 等。
