@perrylink/dsh-github 0.4.0
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/LICENSE +201 -0
- package/README.es.md +256 -0
- package/README.hi.md +256 -0
- package/README.md +257 -0
- package/README.pt.md +256 -0
- package/README.zh-CN.md +254 -0
- package/cordis.patch.yml +9 -0
- package/lib/approval-gate.d.ts +25 -0
- package/lib/approval-gate.d.ts.map +1 -0
- package/lib/approval-gate.js +75 -0
- package/lib/approval-gate.js.map +1 -0
- package/lib/commands.d.ts +12 -0
- package/lib/commands.d.ts.map +1 -0
- package/lib/commands.js +228 -0
- package/lib/commands.js.map +1 -0
- package/lib/config.d.ts +60 -0
- package/lib/config.d.ts.map +1 -0
- package/lib/config.js +64 -0
- package/lib/config.js.map +1 -0
- package/lib/credential.d.ts +42 -0
- package/lib/credential.d.ts.map +1 -0
- package/lib/credential.js +80 -0
- package/lib/credential.js.map +1 -0
- package/lib/git.d.ts +52 -0
- package/lib/git.d.ts.map +1 -0
- package/lib/git.js +113 -0
- package/lib/git.js.map +1 -0
- package/lib/github.d.ts +66 -0
- package/lib/github.d.ts.map +1 -0
- package/lib/github.js +153 -0
- package/lib/github.js.map +1 -0
- package/lib/index.d.ts +55 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +44 -0
- package/lib/index.js.map +1 -0
- package/lib/jobs.d.ts +34 -0
- package/lib/jobs.d.ts.map +1 -0
- package/lib/jobs.js +255 -0
- package/lib/jobs.js.map +1 -0
- package/lib/present.d.ts +254 -0
- package/lib/present.d.ts.map +1 -0
- package/lib/present.js +149 -0
- package/lib/present.js.map +1 -0
- package/lib/review.d.ts +53 -0
- package/lib/review.d.ts.map +1 -0
- package/lib/review.js +158 -0
- package/lib/review.js.map +1 -0
- package/lib/state.d.ts +96 -0
- package/lib/state.d.ts.map +1 -0
- package/lib/state.js +86 -0
- package/lib/state.js.map +1 -0
- package/lib/tools.d.ts +21 -0
- package/lib/tools.d.ts.map +1 -0
- package/lib/tools.js +937 -0
- package/lib/tools.js.map +1 -0
- package/lib/types.d.ts +147 -0
- package/lib/types.d.ts.map +1 -0
- package/lib/types.js +2 -0
- package/lib/types.js.map +1 -0
- package/package.json +78 -0
package/README.pt.md
ADDED
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
<h1 align="center">dsh-github</h1>
|
|
2
|
+
|
|
3
|
+
<p align="center">
|
|
4
|
+
<b>Traga o GitHub para o DeepSeek Harness.</b><br/>
|
|
5
|
+
Crie pull requests · revise PRs com comentários inline ou de resumo · gerencie issues · pesquise — toda gravação passa pela aprovação humana, e o token nunca é registrado.
|
|
6
|
+
</p>
|
|
7
|
+
|
|
8
|
+
<p align="center">
|
|
9
|
+
<a href="README.md">English</a> ·
|
|
10
|
+
<a href="README.zh-CN.md">中文</a> ·
|
|
11
|
+
<a href="README.es.md">Español</a> ·
|
|
12
|
+
Português ·
|
|
13
|
+
<a href="README.hi.md">हिन्दी</a>
|
|
14
|
+
</p>
|
|
15
|
+
|
|
16
|
+
<p align="center">
|
|
17
|
+
<img src="https://img.shields.io/badge/license-Apache%202.0-blue.svg" alt="License: Apache 2.0">
|
|
18
|
+
<img src="https://img.shields.io/badge/dsh-0.1.0--rc.6-4D6BFE" alt="dsh: 0.1.0-rc.6">
|
|
19
|
+
<img src="https://img.shields.io/badge/dsh-dsh--plugin-4D6BFE" alt="dsh-plugin">
|
|
20
|
+
<img src="https://img.shields.io/badge/node-%5E22.19%20%7C%7C%20%3E%3D24-brightgreen" alt="Node: ^22.19 || >=24">
|
|
21
|
+
<img src="https://github.com/PerryLink/dsh-github/actions/workflows/ci.yml/badge.svg" alt="CI">
|
|
22
|
+
<img src="https://img.shields.io/badge/documents-EN%2FZH%2FES%2FPT%2FHI-8257D0" alt="Documents: EN/ZH/ES/PT/HI">
|
|
23
|
+
</p>
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
**dsh-github** é um plugin de bundle para o [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) (`dsh`) — o harness de agentes "tudo é um plugin". Ele preenche a lacuna do GitHub entre o dsh e ferramentas como o [Claude Code](https://github.com/anthropics/claude-code) (`gh claude` / [claude-code-action](https://github.com/anthropics/claude-code-action)) e o [Codex](https://github.com/openai/codex) (`@codex review` / Autofix CI): seu agente pode **ler um PR, revisar um PR, abrir um PR, comentar e fechar issues, e pesquisar** — enquanto um humano aprova toda gravação e o token permanece em segredo.
|
|
28
|
+
|
|
29
|
+
- 🛠 **8 ferramentas** — `pr_create` · `gh_review` · `review_post` · `gh_issue` · `issue_open` · `issue_comment` · `issue_close` · `gh_search`, todas com JSON canônico via `defineTool`
|
|
30
|
+
- ⌨️ **3 famílias de comandos** — `/pr create` · `/review` (start/stop/post) · `/issue open`
|
|
31
|
+
- 📝 **Revisões inline** — `review_post` publica um único comentário de resumo ou comentários de revisão ancorados por linha no commit head do PR
|
|
32
|
+
- 🔒 **Gravações com aprovação obrigatória** — toda gravação no GitHub passa por `ctx.approval` (padrão `ask`, falha fechada); os motivos de aprovação pré-visualizam títulos, tamanhos de corpo e substituições de comentários
|
|
33
|
+
- 🗝 **Sigilo do token** — camada de credenciais → ambiente → CLI `gh`, resolvido por operação, nunca em logs, eventos, renderizações ou erros
|
|
34
|
+
- ⏱ **Jobs de revisão em segundo plano** — `/review` roda em `ctx.jobs` com a própria superfície `job_list` / `job_output` / `job_kill` do host, e reporta o status de CI e a contagem de comentários junto com os achados
|
|
35
|
+
- 🤖 **Opção de revisão por modelo** — `reviewMode: "model"` delega o diff limitado a um subagente de uso único pela seam `subagents` do host; o modo `static` padrão permanece determinístico e sem gasto de tokens
|
|
36
|
+
- 🚦 **Backoff de 429 + exibição de cota** — o modelo vê o limite de taxa restante em todo resultado, incluindo falhas; os erros de busca por seção são exibidos em vez de engolidos
|
|
37
|
+
- 🌐 **Documentação em 5 idiomas** — English · 中文 · Español · Português · हिन्दी
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
## 📚 Índice
|
|
42
|
+
|
|
43
|
+
- [Início rápido](#🚀-início-rápido)
|
|
44
|
+
- [Funcionalidades](#✨-funcionalidades)
|
|
45
|
+
- [Instalação](#📦-instalação)
|
|
46
|
+
- [Configuração](#⚙️-configuração)
|
|
47
|
+
- [Ferramentas](#🛠-ferramentas)
|
|
48
|
+
- [Comandos](#⌨️-comandos)
|
|
49
|
+
- [Arquitetura](#🏗-arquitetura)
|
|
50
|
+
- [Limites de segurança](#🔒-limites-de-segurança)
|
|
51
|
+
- [Limitações conhecidas](#⚠️-limitações-conhecidas)
|
|
52
|
+
- [Desenvolvimento](#🧪-desenvolvimento)
|
|
53
|
+
- [Estrutura do repositório](#🗂-estrutura-do-repositório)
|
|
54
|
+
- [Tópicos](#🏷-tópicos)
|
|
55
|
+
- [Licença](#licença)
|
|
56
|
+
|
|
57
|
+
## 🚀 Início rápido
|
|
58
|
+
|
|
59
|
+
```sh
|
|
60
|
+
# 1. instalar (registro npm — o mais simples; ou use o canal tarball abaixo)
|
|
61
|
+
dsh plugin --profile <name> add @perrylink/dsh-github
|
|
62
|
+
# canal tarball (sem necessidade de registro):
|
|
63
|
+
pnpm pack # inside this repo → dsh-github-0.4.0.tgz
|
|
64
|
+
dsh plugin --profile <name> add ./dsh-github-0.4.0.tgz
|
|
65
|
+
|
|
66
|
+
# 2. configure a GitHub token (recommended: the credentials seam)
|
|
67
|
+
# $DSH_HOME/.credentials.yaml
|
|
68
|
+
# GITHUB_TOKEN: <your token>
|
|
69
|
+
|
|
70
|
+
# 3. use it — in the dsh web UI or headless
|
|
71
|
+
# /pr create "add dark mode" → agent drafts & opens the PR (approval required)
|
|
72
|
+
# /review 42 → background review job, read it with job_output
|
|
73
|
+
# /review post github-review-1 → publish the review comment (approval required)
|
|
74
|
+
# /issue open "crash on startup" → agent opens the issue (approval required)
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Verifique: `dsh --profile <name> --dump-config` deve mostrar a seção `# == dsh-github` sem **nenhuma linha FAILED**.
|
|
78
|
+
|
|
79
|
+
## ✨ Funcionalidades
|
|
80
|
+
|
|
81
|
+
| Área | O que você obtém |
|
|
82
|
+
|---|---|
|
|
83
|
+
| **Criar PRs** | `/pr create [title]` lê o estado do git (branch, arquivos alterados, commits à frente) e entrega um rascunho ao agente; `pr_create` abre o PR e retorna sua URL |
|
|
84
|
+
| **Revisar PRs** | `gh_review` resume metadados, diff limitado (texto completo no valor canônico, trecho limitado na renderização), comentários, status de CI e achados estáticos — as falhas de busca por seção são reportadas como `diff.error` / `comments.error` / `ci.error` |
|
|
85
|
+
| **Publicar revisões** | `review_post` publica um comentário agregado no nível da issue (`mode: "summary"`, padrão) ou comentários de revisão ancorados por linha no commit head do PR (`mode: "inline"`); uma substituição de `body` permite que o modelo refine o comentário primeiro — após a aprovação humana |
|
|
86
|
+
| **Revisões em segundo plano** | `/review <pr>` busca metadados, o diff limitado, as verificações de CI e os comentários existentes em um job de `ctx.jobs`; a saída de conclusão traz o resumo dos achados, o status de CI e a contagem de comentários; `reviewMode: "model"` delega o diff a um subagente de uso único em vez do analisador estático |
|
|
87
|
+
| **Ler issues** | `gh_issue` lista / obtém / comenta; os pull requests nas listagens são marcados como `kind: "pr"` |
|
|
88
|
+
| **Gerenciar issues** | `issue_open` cria, `issue_comment` comenta (também funciona em PRs), `issue_close` fecha com um motivo de estado opcional — todos com aprovação obrigatória |
|
|
89
|
+
| **Pesquisar** | `gh_search` consulta issues e pull requests com a sintaxe de busca do GitHub, exibindo a cota de busca separada |
|
|
90
|
+
| **Aprovação** | `tools/pre-execute` consulta `ctx.approval` para toda gravação; a allowlist `allowedActions` nega antes de perguntar |
|
|
91
|
+
| **Segurança do segredo** | O token é lido por operação e enviado apenas no cabeçalho Authorization; um teste dedicado garante que ele nunca aparece em nenhuma saída visível |
|
|
92
|
+
| **Resiliência** | Nova tentativa em 429 com backoff `Retry-After`/`x-ratelimit-reset`; as ferramentas de leitura são seguras para concorrência; todas as chamadas respeitam o cancelamento |
|
|
93
|
+
| **Observabilidade** | Visível ao modelo ⇔ registrado: tudo o que o modelo vê flui pelos próprios eventos de sessão do host (`tool/result`, `user/message`, `command/run`, `approval/asked`…) |
|
|
94
|
+
|
|
95
|
+
## 📦 Instalação
|
|
96
|
+
|
|
97
|
+
Quatro canais documentados — escolha um.
|
|
98
|
+
|
|
99
|
+
| Canal | Comando | Observações |
|
|
100
|
+
|---|---|---|
|
|
101
|
+
| **npm registry** | `dsh plugin --profile <name> add @perrylink/dsh-github` | Publicado no npm — o canal mais simples |
|
|
102
|
+
| **tarball npm** | `dsh plugin --profile <name> add ./dsh-github-0.4.0.tgz` | Envia com `lib/` compilado — sem permissão de build |
|
|
103
|
+
| **fonte git** | `dsh plugin --profile <name> add "github:PerryLink/dsh-github#<sha>"` | Requer `prepare` + `allowBuilds` (veja abaixo); fixe o commit |
|
|
104
|
+
| **link local** | `pnpm link --dir .` e depois `dsh plugin add @perrylink/dsh-github` | Desenvolvimento |
|
|
105
|
+
|
|
106
|
+
> O pacote npm é publicado sob o escopo `@perrylink` porque o nome sem escopo `dsh-github` pertence a um projeto alheio no registro. O nome de módulo do plugin permanece `dsh-github`.
|
|
107
|
+
|
|
108
|
+
Instalações via git: o pnpm ≥10 recusa o `prepare` de uma dependência git até que ela seja incluída na allowlist — o `dsh` imprime a chave exata; copie-a para o `pnpm-workspace.yaml` do perfil:
|
|
109
|
+
|
|
110
|
+
```yaml
|
|
111
|
+
allowBuilds:
|
|
112
|
+
'@perrylink/dsh-github': true
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
O script `prepare` (`scripts/prepare.mjs`) é autocontido: ele compila com TypeScript quando um compilador é resolvível, caso contrário recorre aos **artefatos `lib/` commitados** e falha em alto e bom som se não houver nenhum dos dois.
|
|
116
|
+
|
|
117
|
+
**Desinstalar:** `dsh plugin --profile <name> remove @perrylink/dsh-github`.
|
|
118
|
+
|
|
119
|
+
## ⚙️ Configuração
|
|
120
|
+
|
|
121
|
+
Validado pelo Schemastery no momento do carregamento (falha em alto e bom som). Sobrescreva qualquer chave no `cordis.patch.yml` do perfil (toda a configuração da linha é substituída, nunca mesclada profundamente).
|
|
122
|
+
|
|
123
|
+
| Chave | Padrão | Significado |
|
|
124
|
+
|---|---|---|
|
|
125
|
+
| `tokenSource` | `auto` | `auto` (credenciais → ambiente → gh) ou um de `credentials` / `env` / `gh` |
|
|
126
|
+
| `tokenRef` | `GITHUB_TOKEN` | Referência da camada de credenciais / nome da variável de ambiente |
|
|
127
|
+
| `defaultOwnerRepo` | — | Fallback `owner/repo` quando uma chamada não nomeia nenhum e o git não tem origin |
|
|
128
|
+
| `autoCommit` | `false` | Se `/pr create` pode instruir o modelo a fazer commit+push primeiro |
|
|
129
|
+
| `maxDiffChars` | `8000` | Limite de caracteres para diffs de PR lidos nas revisões |
|
|
130
|
+
| `renderExcerptChars` | `2000` | Limite de caracteres para o trecho de diff renderizado na saída da ferramenta |
|
|
131
|
+
| `maxComments` | `20` | Limite para comentários de PR listados por `gh_review` |
|
|
132
|
+
| `reviewJobTimeoutMs` | `600000` | Prazo para um job de revisão em segundo plano (falha com `timeout`) |
|
|
133
|
+
| `maxReviewRecords` | `50` | Limite para registros em memória de jobs de revisão; os registros concluídos mais antigos são removidos primeiro |
|
|
134
|
+
| `reviewMode` | `static` | Motor de revisão: `static` (analisador determinístico) ou `model` (subagente de uso único pela seam `subagents` do host; falha em alto e bom som quando a seam está ausente) |
|
|
135
|
+
| `modelReviewProvider` | — | Nome do provedor de subagente para `reviewMode: "model"`; usa, por padrão, o primeiro provedor registrado |
|
|
136
|
+
| `maxRetries` | `3` | Tentativas de nova tentativa em 429 por requisição |
|
|
137
|
+
| `retryBaseMs` | `500` | Base do backoff de nova tentativa (dobra a cada tentativa) |
|
|
138
|
+
| `retryMaxWaitMs` | `60000` | Teto do backoff de nova tentativa |
|
|
139
|
+
| `apiBaseUrl` | `https://api.github.com` | URL base da API REST do GitHub (GitHub Enterprise) |
|
|
140
|
+
| `allowedActions` | `['pr.create','review.post','issue.create','issue.comment','issue.close']` | Allowlist de ações de gravação; qualquer outra coisa é negada antes da aprovação |
|
|
141
|
+
| `workspaceDir` | process cwd | Diretório de trabalho para inspeção somente leitura do git |
|
|
142
|
+
|
|
143
|
+
## 🛠 Ferramentas
|
|
144
|
+
|
|
145
|
+
| Ferramenta | Tipo | Parâmetros | Retorna |
|
|
146
|
+
|---|---|---|---|
|
|
147
|
+
| `pr_create` | gravação | `title*`, `body?`, `base?`, `head?`, `draft?`, `ownerRepo?` | `{status:'created', url, number, title, state, draft, base, head, rateLimit}` ou erro estruturado |
|
|
148
|
+
| `gh_review` | leitura | `pr*` (number / `#n` / `o/r#n` / URL), `fields?`, `maxDiffChars?` | metadados, diff limitado (texto completo `diff.text` + trecho limitado `diff.excerpt` + estatísticas por arquivo), comentários, CI, achados estáticos, campos de `error` por seção, limite de taxa |
|
|
149
|
+
| `gh_issue` | leitura | `action*` (`list`/`get`/`comments`), `ownerRepo?`, `issueNumber?`, `state?`, `limit?` | itens normalizados (cada um marcado `kind: issue/pr/comment`) + limite de taxa |
|
|
150
|
+
| `review_post` | gravação | `jobId*`, `mode?` (`summary`/`inline`), `body?` | `{status:'posted', mode, url, commentId?, reviewId?, findings, rateLimit}` ou erro estruturado |
|
|
151
|
+
| `issue_open` | gravação | `title*`, `body?`, `labels?`, `ownerRepo?` | `{status:'created', url, number, title, rateLimit}` ou erro estruturado |
|
|
152
|
+
| `issue_comment` | gravação | `issueNumber*`, `body*`, `ownerRepo?` | `{status:'commented', url, commentId, issueNumber, rateLimit}` ou erro estruturado |
|
|
153
|
+
| `issue_close` | gravação | `issueNumber*`, `ownerRepo?`, `stateReason?` (`completed`/`not_planned`) | `{status:'closed', url, number, title, rateLimit}` ou erro estruturado |
|
|
154
|
+
| `gh_search` | leitura | `q*`, `sort?`, `order?`, `perPage?` | `{query, total, items[{number,title,state,kind,author,url,repo,comments,createdAt}], rateLimit}` ou erro estruturado |
|
|
155
|
+
|
|
156
|
+
`execute` retorna apenas o JSON canônico declarado por `output.schema`. Falhas de token ausente e da API do GitHub são variantes de erro estruturado que carregam fatos do limite de taxa; falhas de infraestrutura lançam exceção (→ `isError`). `exec.signal` é respeitado em todos os lugares.
|
|
157
|
+
|
|
158
|
+
## ⌨️ Comandos
|
|
159
|
+
|
|
160
|
+
| Comando | Efeito |
|
|
161
|
+
|---|---|
|
|
162
|
+
| `/pr create [title]` | Lê o estado do git e enfileira uma instrução `pr_create` para o modelo (corpo rascunhado, padrões, sem commit/push a menos que `autoCommit`). Criar o PR solicita aprovação. |
|
|
163
|
+
| `/review <pr>` | Inicia um job de revisão em segundo plano; imprime o id do job. A conclusão é anunciada pelo host; leia-a com `job_output`. |
|
|
164
|
+
| `/review <pr> --max-diff <n> --no-ci --no-comments` | Substituições por job: limite de diff e quais seções suplementares o job busca. |
|
|
165
|
+
| `/review stop <jobId>` | Cancela o job (controle local, sem gravação no GitHub). |
|
|
166
|
+
| `/review post <jobId>` | Enfileira uma instrução `review_post` para o modelo (resumo ou inline); publicar solicita aprovação. |
|
|
167
|
+
| `/issue open <title>` | Enfileira uma instrução `issue_open` para o modelo; criar solicita aprovação. |
|
|
168
|
+
|
|
169
|
+
## 🏗 Arquitetura
|
|
170
|
+
|
|
171
|
+
```
|
|
172
|
+
┌───────────────────────────────────────────────┐
|
|
173
|
+
│ dsh-github │
|
|
174
|
+
│ │
|
|
175
|
+
humanos ─── /pr ────┼──► git reader (read-only) ──► agent.followup │
|
|
176
|
+
/review ───┼──► ctx.jobs.start("github-review") ──► job │
|
|
177
|
+
/issue ────┼──► agent.followup │
|
|
178
|
+
│ │
|
|
179
|
+
modelo ─── pr_create / gh_review / gh_issue / review_post / │
|
|
180
|
+
issue_open / issue_comment / issue_close / gh_search │
|
|
181
|
+
(defineTool, canonical JSON only) │
|
|
182
|
+
│ │
|
|
183
|
+
└───────┬───────────────┬───────────────┬───────┘
|
|
184
|
+
│ │ │
|
|
185
|
+
tools/pre-execute credential GitHub REST
|
|
186
|
+
approval gate resolution client (fetch,
|
|
187
|
+
(ask | deny) (seam → env → 429 retry,
|
|
188
|
+
gh CLI, per-op) rate-limit)
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
- **Camada de credenciais.** `tokenSource: auto` resolve por operação na ordem camada de credenciais (referência `GITHUB_TOKEN`) → variável de ambiente → token da CLI `gh`. O valor é uma variável local entregue ao cliente REST; ele nunca entra em valores canônicos, renderizações, cards, saídas de comandos, avisos injetados, saídas de jobs, motivos de aprovação ou mensagens de erro.
|
|
192
|
+
- **Aprovação.** Todas as gravações passam pelas ferramentas do modelo. Um listener waterfall `tools/pre-execute` retorna `ask` para as cinco ferramentas de gravação, de modo que o registro pergunta ao humano por meio de `ctx.approval` (o host registra o par de auditoria `approval/asked` + `approval/decided`) e falha fechado sem um respondedor. Os motivos de aprovação pré-visualizam o que será publicado (títulos, tamanhos de corpo e a primeira linha de um corpo de revisão substituído). Comandos nunca gravam diretamente: os handlers de comando rodam sem um turno aberto, então a camada de aprovação é estruturalmente fechada para eles — um comando de gravação coleta contexto somente leitura e então acorda o agente (`followup` quando ocioso, `inject` quando ocupado) para que o modelo execute a ferramenta com aprovação dentro de um turno.
|
|
193
|
+
- **Revisão em segundo plano.** `/review <pr>` inicia um job `github-review` em `ctx.jobs` (label, owner, timeout, cancelável). O job resolve o token por operação, busca os metadados do PR (capturando o SHA do commit head para a publicação inline), o diff limitado e — a menos que desativado — as execuções de verificação de CI e os comentários de revisão existentes, e então executa um analisador determinístico de múltiplos arquivos (`src/review.ts`: segredos hardcoded, chaves de API do Google, atribuições de credenciais, artefatos de debug, eval, marcadores TODO, linhas longas, mudanças grandes demais) — zero tokens gastos, totalmente testável. Com `reviewMode: "model"`, o job entrega o diff limitado a um subagente de uso único pela seam `subagents` do host (o agente proprietário é o pai) e armazena a saída Markdown do filho como o relatório publicável; uma seam ou provedor ausente falha em alto e bom som. As falhas de busca de seções suplementares são anotadas na saída sem fazer o job falhar. Os avisos de conclusão chegam à sessão de origem por meio do consumidor `dsh-tool-jobs` do host; o modelo lê o relatório por meio da ferramenta existente `job_output` e o publica com `review_post` — aprovação necessária.
|
|
194
|
+
- **Visível ao modelo ⇔ registrado.** O plugin não acrescenta **nenhum tipo de evento de sessão personalizado**. Tipos de evento fora do repositório não estão em `KNOWN_SESSION_EVENT_TYPES` do host, então um evento obrigatório desconhecido tornaria o log da sessão ilegível após a remoção do plugin (o host deliberadamente adia uma superfície de registro para plugins externos). Todo conteúdo visível ao modelo, portanto, flui por superfícies registradas pelo host: valores canônicos de `tool/result`, avisos `user/message` via `agent.inject`/`agent.followup`, o par de ciclo de vida `command/run` + `command/done` e o par de auditoria `approval/asked` + `approval/decided`.
|
|
195
|
+
- **Presenters puros.** `presentCall`/`presentResult` são funções puras de `args` (+ o `result.meta` persistido), idênticas no streaming ao vivo e na reprodução do log. A criação de PR mostra um card genérico com a URL do PR.
|
|
196
|
+
|
|
197
|
+
## 🔒 Limites de segurança
|
|
198
|
+
|
|
199
|
+
- O token é lido por operação da fonte configurada (camada de credenciais, ambiente ou CLI `gh`) e enviado apenas no cabeçalho Authorization do cliente REST. Ele nunca é registrado, nunca é renderizado, nunca é injetado, nunca é anexado ao log da sessão e nunca aparece nas mensagens de erro.
|
|
200
|
+
- Toda gravação no GitHub exige `allowed-once` de `ctx.approval` (política padrão `ask`); `rejected`, `cancelled` e `unavailable` falham todos de forma fechada.
|
|
201
|
+
- `/pr create` nunca faz commit ou push por conta própria; com `autoCommit: true`, o modelo realiza essas gravações pela própria barreira de aprovação da ferramenta bash. O dsh-github **não** gerencia a identidade do git (trabalho do dsh-git-identity) nem worktrees (trabalho do dsh-worktree).
|
|
202
|
+
- O job de revisão não realiza gravações: ele lê um diff e armazena um relatório na memória do processo; apenas `review_post` publica, após aprovação.
|
|
203
|
+
- Os comentários publicados interpolam nomes de arquivo derivados do diff, que são conteúdo de repositório não confiável: `formatPostBody` escapa as crases e escapa em HTML os nomes de arquivo para que uma PR hostil não possa injetar Markdown no comentário de revisão.
|
|
204
|
+
- Os corpos de issues/PRs, os comentários e os resultados de busca lidos do GitHub são conteúdo externo não confiável que entra no contexto do modelo — a mesma contrapartida inerente à busca na web; o plugin os marca como conteúdo externo em suas renderizações.
|
|
205
|
+
- Limites de taxa: os 429 são repetidos com backoff e a cota restante é exibida ao modelo em todo resultado, incluindo falhas.
|
|
206
|
+
|
|
207
|
+
## ⚠️ Limitações conhecidas
|
|
208
|
+
|
|
209
|
+
- **Sem eventos de sessão personalizados** — deliberado (veja Arquitetura); as trilhas de auditoria dependem do próprio vocabulário de eventos do host.
|
|
210
|
+
- **Analisador estático por padrão** — regras determinísticas (`src/review.ts`), zero tokens, reproduzível. `reviewMode: "model"` delega o diff limitado a um subagente de uso único pela seam `subagents` do host para uma revisão por LLM (consome tokens; requer a seam e um provedor registrado).
|
|
211
|
+
- **Jobs e registros são locais ao processo** — o relatório de revisão vive na memória do plugin, indexado pelo id do job, acompanhando o tempo de vida do registro de jobs do host; o mapa de registros é limitado por `maxReviewRecords` (os registros concluídos mais antigos são removidos primeiro).
|
|
212
|
+
- **As dist-tags `latest` do npm estão desatualizadas** — o plugin declara faixas de peer `^0.1.0-rc.5` para resolver contra o fechamento de perfil que o `dsh-base` fornece, e fixa `0.1.0-rc.6` para desenvolvimento. Nunca instale por meio de um `npm i @deepseek-ai/dsh-tools` simples.
|
|
213
|
+
- **CI / GitHub Action** (`dsh-github-action`, loop headless revisão→comentário no espírito do claude-code-action / codex-action) é um repositório complementar v2 planejado.
|
|
214
|
+
|
|
215
|
+
## 🧪 Desenvolvimento
|
|
216
|
+
|
|
217
|
+
```sh
|
|
218
|
+
pnpm install
|
|
219
|
+
pnpm test # vitest: config, credentials, 429/retry, tools, commands, jobs, approval gate, token non-leakage
|
|
220
|
+
pnpm typecheck
|
|
221
|
+
pnpm build # tsc → lib/ (noEmitOnError)
|
|
222
|
+
pnpm pack # installable tarball
|
|
223
|
+
pnpm run check:readmes # cross-checks TOC anchors in all 5 READMEs
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
Os testes simulam a API do GitHub, a CLI `gh` e o git por meio de runners injetados — sem rede, sem credenciais reais. `test/security.test.ts` garante que a string do token nunca aparece em nenhuma saída visível ao modelo ou ao humano. `test/e2e.test.ts` contém testes de fumaça optativos da API real que se pulam automaticamente a menos que `GITHUB_TOKEN` esteja definido (apenas endpoints somente leitura).
|
|
227
|
+
|
|
228
|
+
## 🗂 Estrutura do repositório
|
|
229
|
+
|
|
230
|
+
```
|
|
231
|
+
src/index.ts plugin entry (name/inject/apply, applyWithDeps for tests)
|
|
232
|
+
src/config.ts Schemastery Config
|
|
233
|
+
src/types.ts local structural views of host services + Context merging
|
|
234
|
+
src/credential.ts token resolution (seam → env → gh), per operation
|
|
235
|
+
src/github.ts REST client: 429 retry, rate limits, diff media type
|
|
236
|
+
src/git.ts read-only git inspection + origin parsing for any API host
|
|
237
|
+
src/review.ts deterministic diff analyzer + sanitized comment drafting
|
|
238
|
+
src/jobs.ts github-review background job producer (metadata + diff + CI + comments)
|
|
239
|
+
src/approval-gate.ts tools/pre-execute ask/deny gate with write previews
|
|
240
|
+
src/tools.ts the eight model-facing tools
|
|
241
|
+
src/commands.ts /pr, /review, /issue
|
|
242
|
+
src/present.ts pure UI-card presenters
|
|
243
|
+
test/ vitest suite + mock host scaffolding + opt-in e2e smoke
|
|
244
|
+
cordis.patch.yml bundle patch (one insert row)
|
|
245
|
+
scripts/prepare.mjs self-contained git-install build
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
## 🏷 Tópicos
|
|
249
|
+
|
|
250
|
+
Tópicos recomendados do repositório GitHub (defina-os nas configurações do repositório — eles alimentam a [página de tópicos `dsh-plugin`](https://github.com/topics/dsh-plugin) e os marketplaces de plugins DSH):
|
|
251
|
+
|
|
252
|
+
`dsh` · `dsh-plugin` · `deepseek-harness` · `github` · `pull-request` · `code-review` · `issue-tracker`
|
|
253
|
+
|
|
254
|
+
## Licença
|
|
255
|
+
|
|
256
|
+
[Apache License 2.0](LICENSE)
|
package/README.zh-CN.md
ADDED
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
<h1 align="center">dsh-github</h1>
|
|
2
|
+
|
|
3
|
+
<p align="center">
|
|
4
|
+
<b>把 GitHub 接入 DeepSeek Harness。</b><br/>
|
|
5
|
+
创建 PR · 行级或汇总评论审查 PR · 管理 issue · 搜索 —— 每个写操作都经人类审批,token 永不落日志。
|
|
6
|
+
</p>
|
|
7
|
+
|
|
8
|
+
<p align="center">
|
|
9
|
+
<a href="README.md">English</a> ·
|
|
10
|
+
<a href="README.es.md">Español</a> ·
|
|
11
|
+
<a href="README.pt.md">Português</a> ·
|
|
12
|
+
<a href="README.hi.md">हिन्दी</a>
|
|
13
|
+
</p>
|
|
14
|
+
|
|
15
|
+
<p align="center">
|
|
16
|
+
<img src="https://img.shields.io/badge/license-Apache%202.0-blue.svg" alt="License: Apache 2.0">
|
|
17
|
+
<img src="https://img.shields.io/badge/dsh-0.1.0--rc.6-4D6BFE" alt="dsh: 0.1.0-rc.6">
|
|
18
|
+
<img src="https://img.shields.io/badge/dsh-dsh--plugin-4D6BFE" alt="dsh-plugin">
|
|
19
|
+
<img src="https://img.shields.io/badge/node-%5E22.19%20%7C%7C%20%3E%3D24-brightgreen" alt="Node: ^22.19 || >=24">
|
|
20
|
+
<img src="https://github.com/PerryLink/dsh-github/actions/workflows/ci.yml/badge.svg" alt="CI">
|
|
21
|
+
<img src="https://img.shields.io/badge/documents-EN%2FZH%2FES%2FPT%2FHI-8257D0" alt="Documents: EN/ZH/ES/PT/HI">
|
|
22
|
+
</p>
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
**dsh-github** 是 [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness)(`dsh`,"一切皆插件"的 agent 框架)的 bundle 插件。它填补了 dsh 相对 [Claude Code](https://github.com/anthropics/claude-code)(`gh claude` / [claude-code-action](https://github.com/anthropics/claude-code-action))与 [Codex](https://github.com/openai/codex)(`@codex review` / Autofix CI)的 GitHub 集成空白:agent 能**看 PR、审 PR、开 PR、评论与关闭 issue、搜索**——写操作由人类审批,token 全程保密。
|
|
27
|
+
|
|
28
|
+
- 🛠 **8 个工具** —— `pr_create` · `gh_review` · `review_post` · `gh_issue` · `issue_open` · `issue_comment` · `issue_close` · `gh_search`,全部经 `defineTool` 返回规范 JSON
|
|
29
|
+
- ⌨️ **3 族命令** —— `/pr create` · `/review`(启动/停止/发布)· `/issue open`
|
|
30
|
+
- 📝 **行级审查评论** —— `review_post` 既可发布单条汇总评论,也可按行锚定到 PR head commit 发布行级 review 评论
|
|
31
|
+
- 🔒 **写操作审批** —— 每个 GitHub 写操作都经 `ctx.approval`(默认 `ask`,fail-closed);审批理由预览标题、正文长度与评论覆盖内容
|
|
32
|
+
- 🗝 **token 保密** —— credentials seam → 环境变量 → `gh` CLI 三级解析,逐操作执行,绝不进日志/事件/渲染/错误
|
|
33
|
+
- ⏱ **后台审查 job** —— `/review` 跑在 `ctx.jobs` 上,复用宿主自带 `job_list` / `job_output` / `job_kill` 工具面;输出除发现外还带 CI 状态与既有评论数
|
|
34
|
+
- 🤖 **可选模型评审** —— `reviewMode: "model"` 把截断后的 diff 交给宿主 `subagents` 接缝的一次性 subagent 评审;默认 `static` 模式保持确定性、零 token
|
|
35
|
+
- 🚦 **429 退避 + 配额可见** —— 每个结果(含失败)都向模型暴露剩余配额;各分节抓取失败显式上报,不再静默吞掉
|
|
36
|
+
- 🌐 **5 语文档** —— English · 中文 · Español · Português · हिन्दी
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
## 📚 目录
|
|
41
|
+
|
|
42
|
+
- [快速上手](#🚀-快速上手)
|
|
43
|
+
- [特性](#✨-特性)
|
|
44
|
+
- [安装](#📦-安装)
|
|
45
|
+
- [配置](#⚙️-配置)
|
|
46
|
+
- [工具](#🛠-工具)
|
|
47
|
+
- [命令](#⌨️-命令)
|
|
48
|
+
- [架构](#🏗-架构)
|
|
49
|
+
- [安全边界](#🔒-安全边界)
|
|
50
|
+
- [已知局限](#⚠️-已知局限)
|
|
51
|
+
- [开发](#🧪-开发)
|
|
52
|
+
- [目录结构](#🗂-目录结构)
|
|
53
|
+
- [Topics](#🏷-topics)
|
|
54
|
+
- [许可证](#许可证)
|
|
55
|
+
|
|
56
|
+
## 🚀 快速上手
|
|
57
|
+
|
|
58
|
+
```sh
|
|
59
|
+
# 1. 安装(npm registry —— 最简单;也可用下方 tarball 通道)
|
|
60
|
+
dsh plugin --profile <name> add @perrylink/dsh-github
|
|
61
|
+
# tarball 通道(不需要 registry):
|
|
62
|
+
# pnpm pack → dsh-github-0.4.0.tgz
|
|
63
|
+
# dsh plugin --profile <name> add ./dsh-github-0.4.0.tgz
|
|
64
|
+
|
|
65
|
+
# 2. 配置 GitHub token(推荐:credentials seam)
|
|
66
|
+
# $DSH_HOME/.credentials.yaml
|
|
67
|
+
# GITHUB_TOKEN: <你的 token>
|
|
68
|
+
|
|
69
|
+
# 3. 使用 —— dsh Web UI 或 headless 均可
|
|
70
|
+
# /pr create "add dark mode" → agent 起草并创建 PR(需审批)
|
|
71
|
+
# /review 42 → 后台审查 job,用 job_output 读结论
|
|
72
|
+
# /review post github-review-1 → 发布审查评论(需审批)
|
|
73
|
+
# /issue open "crash on startup" → agent 创建 issue(需审批)
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
验证:`dsh --profile <name> --dump-config` 应显示 `# == dsh-github` 段且**无 FAILED 行**。
|
|
77
|
+
|
|
78
|
+
## ✨ 特性
|
|
79
|
+
|
|
80
|
+
| 领域 | 你能得到什么 |
|
|
81
|
+
|---|---|
|
|
82
|
+
| **创建 PR** | `/pr create [标题]` 读取 git 状态(分支、变更文件、未推送提交),把草稿交给 agent;`pr_create` 创建 PR 并返回 URL |
|
|
83
|
+
| **审查 PR** | `gh_review` 汇总元数据、截断 diff(canonical 值含完整 diff 文本、渲染面为有界摘要)、评论、CI 状态与静态发现;各分节抓取失败以 `diff.error` / `comments.error` / `ci.error` 显式上报 |
|
|
84
|
+
| **发布审查** | `review_post` 发布单条 issue 级汇总评论(`mode: "summary"`,默认)或按行锚定 PR head commit 的行级评论(`mode: "inline"`);`body` 覆盖参数让模型先润色评论——发布前必须审批 |
|
|
85
|
+
| **后台审查** | `/review <pr>` 在 `ctx.jobs` job 内抓取元数据、截断 diff、CI 检查与既有评论;完成输出带发现汇总、CI 状态与评论数。`reviewMode: "model"` 改为把 diff 交给一次性 subagent 评审 |
|
|
86
|
+
| **读取 issue** | `gh_issue` 支持 list / get / comments;列表中的 PR 以 `kind: "pr"` 标记 |
|
|
87
|
+
| **管理 issue** | `issue_open` 创建、`issue_comment` 评论(对 PR 同样可用)、`issue_close` 关闭并可选记录关闭原因——全部审批门控 |
|
|
88
|
+
| **搜索** | `gh_search` 以 GitHub 搜索语法查询 issue 与 PR,暴露独立的搜索配额 |
|
|
89
|
+
| **审批** | `tools/pre-execute` 对每个写操作向 `ctx.approval` 发起 `ask`;`allowedActions` 白名单在询问前拒绝 |
|
|
90
|
+
| **密钥安全** | token 逐操作读取、只写入 Authorization 头;专项测试断言它不出现在任何可见输出中 |
|
|
91
|
+
| **韧性** | 按 `Retry-After`/`x-ratelimit-reset` 退避重试 429;读工具并发安全;所有调用尊重取消信号 |
|
|
92
|
+
| **可观测** | 模型可见 ⟺ 已记录:模型看到的一切都经宿主自有会话事件(`tool/result`、`user/message`、`command/run`、`approval/asked`…) |
|
|
93
|
+
|
|
94
|
+
## 📦 安装
|
|
95
|
+
|
|
96
|
+
四条通道,全部有文档——任选其一。
|
|
97
|
+
|
|
98
|
+
| 通道 | 命令 | 说明 |
|
|
99
|
+
|---|---|---|
|
|
100
|
+
| **npm registry** | `dsh plugin --profile <name> add @perrylink/dsh-github` | 已发布到 npm —— 最简单的通道 |
|
|
101
|
+
| **npm tarball** | `dsh plugin --profile <name> add ./dsh-github-0.4.0.tgz` | 自带构建好的 `lib/`——无需构建许可 |
|
|
102
|
+
| **git 源** | `dsh plugin --profile <name> add "github:PerryLink/dsh-github#<sha>"` | 需 `prepare` + `allowBuilds`(见下);请钉住 commit |
|
|
103
|
+
| **本地 link** | `pnpm link --dir .` 后 `dsh plugin add @perrylink/dsh-github` | 开发用 |
|
|
104
|
+
|
|
105
|
+
> npm 包发布在 `@perrylink` 作用域下,因为裸名 `dsh-github` 已被 registry 上另一个无关项目占用。插件模块名仍是 `dsh-github`。
|
|
106
|
+
|
|
107
|
+
git 安装:pnpm ≥10 默认拒绝运行 git 依赖的 `prepare`,直到放行——`dsh` 会打印确切包键,复制进 profile 的 `pnpm-workspace.yaml`:
|
|
108
|
+
|
|
109
|
+
```yaml
|
|
110
|
+
allowBuilds:
|
|
111
|
+
'@perrylink/dsh-github': true
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
`prepare` 脚本(`scripts/prepare.mjs`)自包含:能找到 TypeScript 编译器就构建,否则回退到**仓库内已提交的 `lib/` 产物**,两者都没有才响亮失败。
|
|
115
|
+
|
|
116
|
+
**卸载:** `dsh plugin --profile <name> remove @perrylink/dsh-github`。
|
|
117
|
+
|
|
118
|
+
## ⚙️ 配置
|
|
119
|
+
|
|
120
|
+
加载期由 Schemastery 校验(非法即响亮失败)。可在 profile 的 `cordis.patch.yml` 覆盖任意键(整行 config 被替换,不深合并)。
|
|
121
|
+
|
|
122
|
+
| 键 | 默认值 | 含义 |
|
|
123
|
+
|---|---|---|
|
|
124
|
+
| `tokenSource` | `auto` | `auto`(credentials → env → gh)或指定 `credentials` / `env` / `gh` |
|
|
125
|
+
| `tokenRef` | `GITHUB_TOKEN` | credentials seam 引用名 / 环境变量名 |
|
|
126
|
+
| `defaultOwnerRepo` | — | 调用未指定且 git 无 origin 时的兜底 `owner/repo` |
|
|
127
|
+
| `autoCommit` | `false` | `/pr create` 是否允许指示模型先 commit+push |
|
|
128
|
+
| `maxDiffChars` | `8000` | 审查读取 PR diff 的字符数上限 |
|
|
129
|
+
| `renderExcerptChars` | `2000` | 渲染进工具输出的 diff 摘要字符数上限 |
|
|
130
|
+
| `maxComments` | `20` | `gh_review` 列出 PR 评论的上限 |
|
|
131
|
+
| `reviewJobTimeoutMs` | `600000` | 单个后台审查 job 的截止时间(超时以 `timeout` 失败) |
|
|
132
|
+
| `maxReviewRecords` | `50` | 内存审查 job 记录上限;最旧的已终态记录先淘汰 |
|
|
133
|
+
| `reviewMode` | `static` | 评审引擎:`static`(确定性分析器)或 `model`(经宿主 `subagents` 接缝的一次性 subagent;接缝缺失时响亮失败) |
|
|
134
|
+
| `modelReviewProvider` | — | `reviewMode: "model"` 使用的 subagent provider 名;缺省用第一个注册的 provider |
|
|
135
|
+
| `maxRetries` | `3` | 单请求的 429 重试次数 |
|
|
136
|
+
| `retryBaseMs` | `500` | 重试退避基数(逐次翻倍) |
|
|
137
|
+
| `retryMaxWaitMs` | `60000` | 重试退避上限 |
|
|
138
|
+
| `apiBaseUrl` | `https://api.github.com` | GitHub REST 基地址(GitHub Enterprise) |
|
|
139
|
+
| `allowedActions` | `['pr.create','review.post','issue.create','issue.comment','issue.close']` | 写动作白名单;名单外直接拒绝 |
|
|
140
|
+
| `workspaceDir` | 进程 cwd | 只读 git 检查的工作目录 |
|
|
141
|
+
|
|
142
|
+
## 🛠 工具
|
|
143
|
+
|
|
144
|
+
| 工具 | 类型 | 参数 | 返回 |
|
|
145
|
+
|---|---|---|---|
|
|
146
|
+
| `pr_create` | 写 | `title*`、`body?`、`base?`、`head?`、`draft?`、`ownerRepo?` | `{status:'created', url, number, title, state, draft, base, head, rateLimit}` 或结构化错误 |
|
|
147
|
+
| `gh_review` | 读 | `pr*`(数字 / `#n` / `o/r#n` / URL)、`fields?`、`maxDiffChars?` | 元数据、截断 diff(完整 `diff.text` + 有界 `diff.excerpt` + 逐文件统计)、评论、CI、静态发现、各分节 `error` 字段、配额 |
|
|
148
|
+
| `gh_issue` | 读 | `action*`(`list`/`get`/`comments`)、`ownerRepo?`、`issueNumber?`、`state?`、`limit?` | 归一化条目(每条带 `kind: issue/pr/comment`)+ 配额 |
|
|
149
|
+
| `review_post` | 写 | `jobId*`、`mode?`(`summary`/`inline`)、`body?` | `{status:'posted', mode, url, commentId?, reviewId?, findings, rateLimit}` 或结构化错误 |
|
|
150
|
+
| `issue_open` | 写 | `title*`、`body?`、`labels?`、`ownerRepo?` | `{status:'created', url, number, title, rateLimit}` 或结构化错误 |
|
|
151
|
+
| `issue_comment` | 写 | `issueNumber*`、`body*`、`ownerRepo?` | `{status:'commented', url, commentId, issueNumber, rateLimit}` 或结构化错误 |
|
|
152
|
+
| `issue_close` | 写 | `issueNumber*`、`ownerRepo?`、`stateReason?`(`completed`/`not_planned`) | `{status:'closed', url, number, title, rateLimit}` 或结构化错误 |
|
|
153
|
+
| `gh_search` | 读 | `q*`、`sort?`、`order?`、`perPage?` | `{query, total, items[{number,title,state,kind,author,url,repo,comments,createdAt}], rateLimit}` 或结构化错误 |
|
|
154
|
+
|
|
155
|
+
`execute` 只返回 `output.schema` 声明的规范 JSON。缺 token 与 GitHub API 失败是携带配额事实的结构化错误分支,基础设施故障抛出(→ `isError`)。全程尊重 `exec.signal`。
|
|
156
|
+
|
|
157
|
+
## ⌨️ 命令
|
|
158
|
+
|
|
159
|
+
| 命令 | 效果 |
|
|
160
|
+
|---|---|
|
|
161
|
+
| `/pr create [标题]` | 读取 git 状态并为模型排队 `pr_create` 指令(描述草稿、默认值;除非 `autoCommit`,否则不 commit/push)。创建 PR 需审批。 |
|
|
162
|
+
| `/review <pr>` | 启动后台审查 job 并打印 job id;完成后宿主会通知,用 `job_output` 读取。 |
|
|
163
|
+
| `/review <pr> --max-diff <n> --no-ci --no-comments` | 单 job 覆盖:diff 上限与是否抓取补充分节。 |
|
|
164
|
+
| `/review stop <jobId>` | 取消 job(本地控制,非 GitHub 写操作)。 |
|
|
165
|
+
| `/review post <jobId>` | 为模型排队 `review_post` 指令(汇总或行级);发布需审批。 |
|
|
166
|
+
| `/issue open <标题>` | 为模型排队 `issue_open` 指令;创建需审批。 |
|
|
167
|
+
|
|
168
|
+
## 🏗 架构
|
|
169
|
+
|
|
170
|
+
```
|
|
171
|
+
┌───────────────────────────────────────────────┐
|
|
172
|
+
│ dsh-github │
|
|
173
|
+
│ │
|
|
174
|
+
人类 ─── /pr ──────┼──► git 读取(只读)──► agent.followup │
|
|
175
|
+
/review ───┼──► ctx.jobs.start("github-review") ──► job │
|
|
176
|
+
/issue ────┼──► agent.followup │
|
|
177
|
+
│ │
|
|
178
|
+
模型 ─── pr_create / gh_review / gh_issue / review_post / │
|
|
179
|
+
issue_open / issue_comment / issue_close / gh_search │
|
|
180
|
+
(defineTool,只返回规范 JSON) │
|
|
181
|
+
│ │
|
|
182
|
+
└───────┬───────────────┬───────────────┬───────┘
|
|
183
|
+
│ │ │
|
|
184
|
+
tools/pre-execute 凭证解析 GitHub REST
|
|
185
|
+
审批门(ask|deny) (seam→env→ 客户端(fetch、
|
|
186
|
+
gh CLI,逐次解析) 429 重试、配额)
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
- **凭证接缝。** `tokenSource: auto` 每次操作按「credentials seam 引用(`GITHUB_TOKEN`)→ 环境变量 → `gh` CLI 登录态」顺序解析。token 值只是交给 REST 客户端的局部变量,绝不进入规范值、渲染文本、UI 卡片、命令输出、注入通知、job 输出、审批理由或错误消息。
|
|
190
|
+
- **审批。** 所有写操作都经模型工具。`tools/pre-execute` waterfall 监听器对五个写工具返回 `ask`,注册表即通过 `ctx.approval` 询问人类(宿主自动落 `approval/asked` + `approval/decided` 审计对),无应答者时 fail-closed。审批理由预览将要发布的内容(标题、正文长度、覆盖评论的首行)。命令本身从不直接写:命令 handler 运行时没有开启的 turn,审批 seam 对命令在结构上不可用——写命令先收集只读上下文,再唤醒 agent(空闲 `followup`、忙碌 `inject`),让模型在 turn 内调用受审批门保护的工具。
|
|
191
|
+
- **后台审查。** `/review <pr>` 在 `ctx.jobs` 上启动 `github-review` job(label、owner、超时、可取消)。job 逐操作解析 token,抓取 PR 元数据(记录 head-commit SHA,供行级发布使用)、截断后的 diff,以及(除非关闭)CI 检查与既有评论,然后运行确定性的多文件静态分析器(`src/review.ts`:硬编码密钥、Google API key、凭证赋值、调试语句、eval、TODO 标记、超长行、超大改动)——零 token、完全可测。`reviewMode: "model"` 时,job 改为把截断后的 diff 交给宿主 `subagents` 接缝的一次性 subagent(parent 为发起 job 的 agent),把子 agent 的 Markdown 输出存为可发布的报告;接缝或 provider 缺失时响亮失败。补充分节抓取失败只在输出中注明,不使 job 失败。完成通知由宿主的 `dsh-tool-jobs` 消费者送回发起会话;模型用自带 `job_output` 工具读取结论,用 `review_post` 发布——发布前必须审批。
|
|
192
|
+
- **模型可见 ⟺ 已记录。** 本插件**不新增任何自定义会话事件类型**。仓库外插件的事件类型不在宿主 `KNOWN_SESSION_EVENT_TYPES` 中,未知的 required 事件会让宿主拒绝读取会话日志(宿主明确把外部插件事件注册面推迟到未来)。因此所有模型可见内容都走宿主已记录的表面:`tool/result` 规范值、经 `agent.inject`/`agent.followup` 的 `user/message` 通知、`command/run` + `command/done` 生命周期对、`approval/asked` + `approval/decided` 审计对。
|
|
193
|
+
- **纯 presenter。** `presentCall`/`presentResult` 是 `args`(+ 持久化的 `result.meta`)的纯函数,实时流与日志回放行为一致。PR 创建结果以 generic 卡片展示 PR 链接。
|
|
194
|
+
|
|
195
|
+
## 🔒 安全边界
|
|
196
|
+
|
|
197
|
+
- token 逐操作从配置的源(credentials seam、环境变量或 `gh` CLI)读取,只写入 REST 客户端的 Authorization 头;从不落日志、不渲染、不注入、不进会话日志、不进错误消息。
|
|
198
|
+
- 每次 GitHub 写操作都需要 `ctx.approval` 的 `allowed-once`(默认策略 `ask`);`rejected`、`cancelled`、`unavailable` 一律 fail-closed。
|
|
199
|
+
- `/pr create` 自己从不 commit/push;`autoCommit: true` 时模型通过 bash 工具(其自身审批门)执行这些写操作。dsh-github **不**管理 git 提交身份(dsh-git-identity 的职责)、**不**做 worktree(dsh-worktree 的职责)。
|
|
200
|
+
- 审查 job 零写操作:只读 diff、把报告存进进程内存;只有 `review_post` 在审批后发布。
|
|
201
|
+
- 发布的评论会插入 diff 中的文件名——这是不可信的仓库内容:`formatPostBody` 对文件名做反引号转义与 HTML 转义,恶意 PR 无法向审查评论注入 Markdown。
|
|
202
|
+
- 从 GitHub 读到的 issue/PR 正文、评论与搜索结果都是进入模型上下文的外部不可信内容——与网页抓取同属固有权衡;插件在渲染中把它们标注为外部内容。
|
|
203
|
+
- 配额:429 带退避重试,剩余配额在包括失败在内的每个结果上对模型可见。
|
|
204
|
+
|
|
205
|
+
## ⚠️ 已知局限
|
|
206
|
+
|
|
207
|
+
- **无自定义会话事件** —— 刻意为之(见架构);审计依赖宿主自有事件词汇。
|
|
208
|
+
- **默认静态分析器** —— 确定性规则集(`src/review.ts`),零 token、可复现。`reviewMode: "model"` 会把截断后的 diff 交给宿主 `subagents` 接缝的一次性 subagent 做 LLM 评审(消耗 token;需要接缝与已注册的 provider)。
|
|
209
|
+
- **job 与记录是进程内状态** —— 审查报告按 job id 存于插件内存,与宿主 job 注册表同为进程级生命周期;记录表受 `maxReviewRecords` 上限约束(最旧已终态记录先淘汰)。
|
|
210
|
+
- **npm `latest` 标签过期** —— 本插件用 `^0.1.0-rc.5` peer 范围对齐 `dsh-base` 提供的 profile 闭包,开发时钉 `0.1.0-rc.6`。不要裸跑 `npm i @deepseek-ai/dsh-tools`。
|
|
211
|
+
- **CI / GitHub Action**(`dsh-github-action`,对标 claude-code-action / codex-action 的 headless「审查 PR → 评论」闭环)是计划中的 v2 配套仓库。
|
|
212
|
+
|
|
213
|
+
## 🧪 开发
|
|
214
|
+
|
|
215
|
+
```sh
|
|
216
|
+
pnpm install
|
|
217
|
+
pnpm test # vitest:配置、凭证、429 重试、工具、命令、job、审批门、token 不泄露
|
|
218
|
+
pnpm typecheck
|
|
219
|
+
pnpm build # tsc → lib/(noEmitOnError)
|
|
220
|
+
pnpm pack # 可安装 tarball
|
|
221
|
+
pnpm run check:readmes # 交叉检查 5 个 README 的目录锚点
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
测试通过注入的 runner mock 掉 GitHub API、`gh` CLI 与 git——不联网、不用真实凭证。`test/security.test.ts` 断言 token 字符串不出现在任何模型或人类可见输出中。`test/e2e.test.ts` 是可选真实 API 冒烟测试:未设置 `GITHUB_TOKEN` 时自动跳过(只打只读端点)。
|
|
225
|
+
|
|
226
|
+
## 🗂 目录结构
|
|
227
|
+
|
|
228
|
+
```
|
|
229
|
+
src/index.ts 插件入口(name/inject/apply,applyWithDeps 供测试注入)
|
|
230
|
+
src/config.ts Schemastery 配置
|
|
231
|
+
src/types.ts 宿主服务的最小结构视图 + Context 声明合并
|
|
232
|
+
src/credential.ts token 解析(seam → env → gh),逐操作解析
|
|
233
|
+
src/github.ts REST 客户端:429 重试、配额、diff 媒体类型
|
|
234
|
+
src/git.ts 只读 git 检查 + 任意 API 主机的 origin 解析
|
|
235
|
+
src/review.ts 确定性 diff 分析器 + 转义后的评论草稿
|
|
236
|
+
src/jobs.ts github-review 后台 job 生产者(元数据 + diff + CI + 评论)
|
|
237
|
+
src/approval-gate.ts tools/pre-execute ask/deny 门(带写操作预览)
|
|
238
|
+
src/tools.ts 八个模型可调工具
|
|
239
|
+
src/commands.ts /pr、/review、/issue
|
|
240
|
+
src/present.ts 纯 UI 卡片 presenter
|
|
241
|
+
test/ vitest 套件 + mock 宿主脚手架 + 可选 e2e 冒烟
|
|
242
|
+
cordis.patch.yml bundle patch(单行 insert)
|
|
243
|
+
scripts/prepare.mjs git 安装用的自包含构建
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
## 🏷 Topics
|
|
247
|
+
|
|
248
|
+
推荐的 GitHub 仓库 Topics(在仓库设置里添加——它们驱动 [`dsh-plugin` 话题页](https://github.com/topics/dsh-plugin) 与各 DSH 插件市场):
|
|
249
|
+
|
|
250
|
+
`dsh` · `dsh-plugin` · `deepseek-harness` · `github` · `pull-request` · `code-review` · `issue-tracker`
|
|
251
|
+
|
|
252
|
+
## 许可证
|
|
253
|
+
|
|
254
|
+
[Apache License 2.0](LICENSE)
|
package/cordis.patch.yml
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# dsh-github bundle patch: installs the GitHub integration plugin row.
|
|
2
|
+
# All configuration keys carry schema defaults; override any of them here
|
|
3
|
+
# by replacing this row's config (the whole row is replaced, never deep-merged).
|
|
4
|
+
- insert:
|
|
5
|
+
- id: dsh-github
|
|
6
|
+
name: dsh-github
|
|
7
|
+
config:
|
|
8
|
+
tokenSource: auto
|
|
9
|
+
autoCommit: false
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The write-action approval gate: a `tools/pre-execute` waterfall listener.
|
|
3
|
+
*
|
|
4
|
+
* Every dsh-github write tool (`pr_create`, `review_post`, `issue_open`,
|
|
5
|
+
* `issue_comment`, `issue_close`) asks the human through the registry-owned
|
|
6
|
+
* approval path (`ask` → ctx.approval), which appends the approval/asked +
|
|
7
|
+
* approval/decided audit pair and fails closed without an answerer. Actions
|
|
8
|
+
* missing from the `allowedActions` whitelist are denied before any prompt.
|
|
9
|
+
* Every other tool passes through via `next()` — the waterfall contract
|
|
10
|
+
* requires it. Approval reasons preview what would be published (titles,
|
|
11
|
+
* body lengths, and the first line of an overridden review body) without ever
|
|
12
|
+
* containing the token.
|
|
13
|
+
* @module dsh-github/approval-gate
|
|
14
|
+
*/
|
|
15
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
16
|
+
import type { GithubState } from './state.ts';
|
|
17
|
+
/**
|
|
18
|
+
* Register the approval gate. Registration is an effect: disposing the plugin
|
|
19
|
+
* fiber removes the listener.
|
|
20
|
+
* @param ctx - plugin context; the listener lives on the shared tools pipeline.
|
|
21
|
+
* @param state - plugin state used to enrich approval reasons.
|
|
22
|
+
* @returns the effect disposer.
|
|
23
|
+
*/
|
|
24
|
+
export declare function registerApprovalGate(ctx: Context, state: GithubState): () => void;
|
|
25
|
+
//# sourceMappingURL=approval-gate.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"approval-gate.d.ts","sourceRoot":"","sources":["../src/approval-gate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAGlD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AA+D7C;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,OAAO,EAAE,KAAK,EAAE,WAAW,GAAG,MAAM,IAAI,CASjF"}
|