@perrylink/dsh-github 0.4.1 → 0.6.1

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.
Files changed (65) hide show
  1. package/README.es.md +30 -13
  2. package/README.hi.md +30 -13
  3. package/README.md +53 -13
  4. package/README.pt.md +31 -14
  5. package/README.zh-CN.md +53 -13
  6. package/action.yml +161 -0
  7. package/lib/approval-gate.d.ts +14 -9
  8. package/lib/approval-gate.d.ts.map +1 -1
  9. package/lib/approval-gate.js +39 -1
  10. package/lib/approval-gate.js.map +1 -1
  11. package/lib/ci/bot.d.ts +50 -0
  12. package/lib/ci/bot.d.ts.map +1 -0
  13. package/lib/ci/bot.js +168 -0
  14. package/lib/ci/bot.js.map +1 -0
  15. package/lib/ci/pipeline.d.ts +84 -0
  16. package/lib/ci/pipeline.d.ts.map +1 -0
  17. package/lib/ci/pipeline.js +380 -0
  18. package/lib/ci/pipeline.js.map +1 -0
  19. package/lib/ci/review-rules.d.ts +41 -0
  20. package/lib/ci/review-rules.d.ts.map +1 -0
  21. package/lib/ci/review-rules.js +108 -0
  22. package/lib/ci/review-rules.js.map +1 -0
  23. package/lib/ci/tool.d.ts +4 -0
  24. package/lib/ci/tool.d.ts.map +1 -0
  25. package/lib/ci/tool.js +155 -0
  26. package/lib/ci/tool.js.map +1 -0
  27. package/lib/commands.d.ts.map +1 -1
  28. package/lib/commands.js +5 -2
  29. package/lib/commands.js.map +1 -1
  30. package/lib/config.d.ts +65 -1
  31. package/lib/config.d.ts.map +1 -1
  32. package/lib/config.js +75 -2
  33. package/lib/config.js.map +1 -1
  34. package/lib/github.d.ts +11 -7
  35. package/lib/github.d.ts.map +1 -1
  36. package/lib/github.js +28 -7
  37. package/lib/github.js.map +1 -1
  38. package/lib/index.d.ts +15 -9
  39. package/lib/index.d.ts.map +1 -1
  40. package/lib/index.js +13 -1
  41. package/lib/index.js.map +1 -1
  42. package/lib/jobs.js +4 -3
  43. package/lib/jobs.js.map +1 -1
  44. package/lib/present.d.ts +146 -0
  45. package/lib/present.d.ts.map +1 -1
  46. package/lib/present.js +140 -1
  47. package/lib/present.js.map +1 -1
  48. package/lib/review.d.ts +11 -1
  49. package/lib/review.d.ts.map +1 -1
  50. package/lib/review.js +12 -9
  51. package/lib/review.js.map +1 -1
  52. package/lib/state.d.ts +3 -1
  53. package/lib/state.d.ts.map +1 -1
  54. package/lib/state.js +11 -6
  55. package/lib/state.js.map +1 -1
  56. package/lib/tools.d.ts +71 -0
  57. package/lib/tools.d.ts.map +1 -1
  58. package/lib/tools.js +379 -16
  59. package/lib/tools.js.map +1 -1
  60. package/package.json +6 -2
  61. package/scripts/action-patch.mjs +152 -0
  62. package/scripts/action-post.mjs +61 -0
  63. package/scripts/check-readmes.mjs +113 -0
  64. package/scripts/local-test.mjs +259 -0
  65. package/scripts/prepare.mjs +34 -0
package/README.pt.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  <p align="center">
4
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.
5
+ Crie pull requests · revise PRs com comentários inline ou de resumo · gerencie issues · pesquise — toda gravação exige aprovação humana e o token nunca é registrado em log.
6
6
  </p>
7
7
 
8
8
  <p align="center">
@@ -24,10 +24,11 @@
24
24
 
25
25
  ---
26
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.
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, mesclar e atualizar PRs, ler metadados de repositórios e arquivos, comentar e fechar issues, e pesquisar** — enquanto um humano aprova toda gravação e o token permanece em segredo.
28
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`
29
+ - 🛠 **12 ferramentas** — `pr_create` · `pr_merge` · `pr_update` · `gh_review` · `review_post` · `gh_issue` · `issue_open` · `issue_comment` · `issue_close` · `gh_search` · `gh_repo` · `gh_file`, todas com JSON canônico via `defineTool`
30
30
  - ⌨️ **3 famílias de comandos** — `/pr create` · `/review` (start/stop/post) · `/issue open`
31
+ - 🔀 **Ciclo de vida completo do PR** — criar → revisar → atualizar (título/corpo/estado/rama base) → mesclar (merge/squash/rebase, exclusão opcional da rama head)
31
32
  - 📝 **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
33
  - 🔒 **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
34
  - 🗝 **Sigilo do token** — camada de credenciais → ambiente → CLI `gh`, resolvido por operação, nunca em logs, eventos, renderizações ou erros
@@ -60,8 +61,8 @@
60
61
  # 1. instalar (registro npm — o mais simples; ou use o canal tarball abaixo)
61
62
  dsh plugin --profile <name> add @perrylink/dsh-github
62
63
  # 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
64
+ # pnpm pack → dsh-github-<version>.tgz
65
+ # dsh plugin --profile <name> add ./dsh-github-<version>.tgz
65
66
 
66
67
  # 2. configure a GitHub token (recommended: the credentials seam)
67
68
  # $DSH_HOME/.credentials.yaml
@@ -81,9 +82,13 @@ Verifique: `dsh --profile <name> --dump-config` deve mostrar a seção `# == dsh
81
82
  | Área | O que você obtém |
82
83
  |---|---|
83
84
  | **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 |
85
+ | **Atualizar PRs** | `pr_update` edita título, corpo, estado ou rama de destino — com aprovação, como toda outra gravação |
86
+ | **Mesclar PRs** | `pr_merge` mescla com `merge`/`squash`/`rebase`, título/mensagem de commit opcionais e exclusão da rama head após a mesclagem |
84
87
  | **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
88
  | **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
89
  | **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 |
90
+ | **Ler repositórios** | `gh_repo` lê os metadados do repositório: descrição, rama padrão, visibilidade, estrelas, forks, issues abertas, linguagem, licença, tópicos |
91
+ | **Ler arquivos** | `gh_file` lê um arquivo em uma rama/tag/commit com decodificação base64 e um limite configurável; diretórios reportam um erro estruturado |
87
92
  | **Ler issues** | `gh_issue` lista / obtém / comenta; os pull requests nas listagens são marcados como `kind: "pr"` |
88
93
  | **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
94
  | **Pesquisar** | `gh_search` consulta issues e pull requests com a sintaxe de busca do GitHub, exibindo a cota de busca separada |
@@ -99,7 +104,7 @@ Quatro canais documentados — escolha um.
99
104
  | Canal | Comando | Observações |
100
105
  |---|---|---|
101
106
  | **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 |
107
+ | **tarball npm** | `dsh plugin --profile <name> add ./dsh-github-<version>.tgz` | Envia com `lib/` compilado — sem permissão de build |
103
108
  | **fonte git** | `dsh plugin --profile <name> add "github:PerryLink/dsh-github#<sha>"` | Requer `prepare` + `allowBuilds` (veja abaixo); fixe o commit |
104
109
  | **link local** | `pnpm link --dir .` e depois `dsh plugin add @perrylink/dsh-github` | Desenvolvimento |
105
110
 
@@ -131,21 +136,30 @@ Validado pelo Schemastery no momento do carregamento (falha em alto e bom som).
131
136
  | `maxComments` | `20` | Limite para comentários de PR listados por `gh_review` |
132
137
  | `reviewJobTimeoutMs` | `600000` | Prazo para um job de revisão em segundo plano (falha com `timeout`) |
133
138
  | `maxReviewRecords` | `50` | Limite para registros em memória de jobs de revisão; os registros concluídos mais antigos são removidos primeiro |
139
+ | `maxFileChars` | `12000` | Limite de caracteres para o conteúdo de arquivos lido por `gh_file` |
140
+ | `maxFindings` | `50` | Limite de achados do analisador por revisão |
141
+ | `maxLineLength` | `300` | Comprimento de linha a partir do qual o analisador marca um achado de linha longa |
134
142
  | `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
143
  | `modelReviewProvider` | — | Nome do provedor de subagente para `reviewMode: "model"`; usa, por padrão, o primeiro provedor registrado |
136
144
  | `maxRetries` | `3` | Tentativas de nova tentativa em 429 por requisição |
137
145
  | `retryBaseMs` | `500` | Base do backoff de nova tentativa (dobra a cada tentativa) |
138
146
  | `retryMaxWaitMs` | `60000` | Teto do backoff de nova tentativa |
147
+ | `requestTimeoutMs` | `30000` | Timeout rígido por requisição; aborta o fetch ao exceder |
139
148
  | `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 |
149
+ | `allowedActions` | `['pr.create','pr.merge','pr.update','review.post','issue.create','issue.comment','issue.close','ci.run']` | Allowlist de ações de gravação; qualquer outra coisa é negada antes da aprovação |
141
150
  | `workspaceDir` | process cwd | Diretório de trabalho para inspeção somente leitura do git |
151
+ | `ci` | `{ enabled: false, … }` | Seção de integração CI: bot de revisão por polling, gate de status-check e a ferramenta de execução única `ci_run` (contém todas as chaves `ci.*`) |
142
152
 
143
153
  ## 🛠 Ferramentas
144
154
 
145
155
  | Ferramenta | Tipo | Parâmetros | Retorna |
146
156
  |---|---|---|---|
147
157
  | `pr_create` | gravação | `title*`, `body?`, `base?`, `head?`, `draft?`, `ownerRepo?` | `{status:'created', url, number, title, state, draft, base, head, rateLimit}` ou erro estruturado |
158
+ | `pr_merge` | gravação | `pr*` (number / `#n` / `o/r#n` / URL), `mergeMethod?`, `commitTitle?`, `commitMessage?`, `deleteBranch?` | `{status:'merged', merged, sha?, message, url, branchDeleted, branchDeleteNote?, rateLimit}` ou erro estruturado |
159
+ | `pr_update` | gravação | `pr*` (number / `#n` / `o/r#n` / URL), `title?`, `body?`, `state?` (`open`/`closed`), `base?` | `{status:'updated', url, number, title, state, base, rateLimit}` ou erro estruturado |
148
160
  | `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 |
161
+ | `gh_repo` | leitura | `ownerRepo?` | `{repo, description, defaultBranch, visibility, stars, forks, openIssues, language, license, topics, url, updatedAt, rateLimit}` ou erro estruturado |
162
+ | `gh_file` | leitura | `ownerRepo?`, `path*`, `ref?`, `maxChars?` | `{repo, path, ref, size, truncated, content, sha, url, rateLimit}` ou erro estruturado |
149
163
  | `gh_issue` | leitura | `action*` (`list`/`get`/`comments`), `ownerRepo?`, `issueNumber?`, `state?`, `limit?` | itens normalizados (cada um marcado `kind: issue/pr/comment`) + limite de taxa |
150
164
  | `review_post` | gravação | `jobId*`, `mode?` (`summary`/`inline`), `body?` | `{status:'posted', mode, url, commentId?, reviewId?, findings, rateLimit}` ou erro estruturado |
151
165
  | `issue_open` | gravação | `title*`, `body?`, `labels?`, `ownerRepo?` | `{status:'created', url, number, title, rateLimit}` ou erro estruturado |
@@ -176,8 +190,9 @@ Validado pelo Schemastery no momento do carregamento (falha em alto e bom som).
176
190
  /review ───┼──► ctx.jobs.start("github-review") ──► job │
177
191
  /issue ────┼──► agent.followup │
178
192
  │ │
179
- modelo ─── pr_create / gh_review / gh_issue / review_post / │
180
- issue_open / issue_comment / issue_close / gh_search │
193
+ modelo ─── pr_create / pr_merge / pr_update / gh_review / │
194
+ review_post / gh_issue / issue_open / issue_comment / │
195
+ issue_close / gh_search / gh_repo / gh_file │
181
196
  (defineTool, canonical JSON only) │
182
197
  │ │
183
198
  └───────┬───────────────┬───────────────┬───────┘
@@ -189,7 +204,7 @@ Validado pelo Schemastery no momento do carregamento (falha em alto e bom som).
189
204
  ```
190
205
 
191
206
  - **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.
207
+ - **Aprovação.** Todas as gravações passam pelas ferramentas do modelo. Um listener waterfall `tools/pre-execute` retorna `ask` para as sete 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, métodos de mesclagem 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
208
  - **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
209
  - **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
210
  - **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.
@@ -201,7 +216,7 @@ Validado pelo Schemastery no momento do carregamento (falha em alto e bom som).
201
216
  - `/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
217
  - 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
218
  - 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.
219
+ - O conteúdo de arquivos lido por `gh_file` e 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
220
  - Limites de taxa: os 429 são repetidos com backoff e a cota restante é exibida ao modelo em todo resultado, incluindo falhas.
206
221
 
207
222
  ## ⚠️ Limitações conhecidas
@@ -210,7 +225,7 @@ Validado pelo Schemastery no momento do carregamento (falha em alto e bom som).
210
225
  - **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
226
  - **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
227
  - **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.
228
+ - **CI / GitHub Action** — incluído neste repositório (v0.6.0): uma ação composta (`action.yml`) que revisa PRs, corrige CI e escreve o relatório; um bot de revisão por polling com comentários inline idempotentes; e um gate de status-check. Toda gravação permanece sujeita a aprovação.
214
229
 
215
230
  ## 🧪 Desenvolvimento
216
231
 
@@ -220,11 +235,13 @@ pnpm test # vitest: config, credentials, 429/retry, tools, commands, jo
220
235
  pnpm typecheck
221
236
  pnpm build # tsc → lib/ (noEmitOnError)
222
237
  pnpm pack # installable tarball
223
- pnpm run check:readmes # cross-checks TOC anchors in all 5 READMEs
238
+ pnpm run check:readmes # cross-checks TOC anchors, tools, and config keys in all 5 READMEs
224
239
  ```
225
240
 
226
241
  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 `DSH_GITHUB_E2E_TOKEN` esteja definido (apenas endpoints somente leitura).
227
242
 
243
+ Para exercitar a ação composta localmente, execute `node scripts/local-test.mjs --owner-repo you/repo --pr 42` (veja `--help` para todas as opções). O simulador fixa explicitamente `DSH_HOME`, `DSH_PROFILE_DIR`, `RUNNER_TEMP` e o diretório de saída em um sandbox novo do diretório temporário do sistema para cada etapa — seu dsh home real nunca é lido nem gravado, mesmo que exista um `DSH_HOME` de escopo de máquina — e reproduz os passos install → prepare → execução headless → post do `action.yml`. `action-patch.mjs` e `action-post.mjs` se recusam a executar fora de um runner do GitHub Actions, de modo que a ação não pode gravar overlays de perfil nem relatórios em locais desconhecidos.
244
+
228
245
  ## 🗂 Estrutura do repositório
229
246
 
230
247
  ```
@@ -237,7 +254,7 @@ src/git.ts read-only git inspection + origin parsing for any API host
237
254
  src/review.ts deterministic diff analyzer + sanitized comment drafting
238
255
  src/jobs.ts github-review background job producer (metadata + diff + CI + comments)
239
256
  src/approval-gate.ts tools/pre-execute ask/deny gate with write previews
240
- src/tools.ts the eight model-facing tools
257
+ src/tools.ts the twelve model-facing tools
241
258
  src/commands.ts /pr, /review, /issue
242
259
  src/present.ts pure UI-card presenters
243
260
  test/ vitest suite + mock host scaffolding + opt-in e2e smoke
package/README.zh-CN.md CHANGED
@@ -23,10 +23,11 @@
23
23
 
24
24
  ---
25
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 全程保密。
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、合并与更新 PR、读仓库元数据与文件、评论与关闭 issue、搜索**——写操作由人类审批,token 全程保密。
27
27
 
28
- - 🛠 **8 个工具** —— `pr_create` · `gh_review` · `review_post` · `gh_issue` · `issue_open` · `issue_comment` · `issue_close` · `gh_search`,全部经 `defineTool` 返回规范 JSON
28
+ - 🛠 **12 个工具** —— `pr_create` · `pr_merge` · `pr_update` · `gh_review` · `review_post` · `gh_issue` · `issue_open` · `issue_comment` · `issue_close` · `gh_search` · `gh_repo` · `gh_file`,全部经 `defineTool` 返回规范 JSON
29
29
  - ⌨️ **3 族命令** —— `/pr create` · `/review`(启动/停止/发布)· `/issue open`
30
+ - 🔀 **完整 PR 生命周期** —— 创建 → 审查 → 更新(标题/正文/状态/目标分支)→ 合并(merge/squash/rebase,可选合并后删源分支)
30
31
  - 📝 **行级审查评论** —— `review_post` 既可发布单条汇总评论,也可按行锚定到 PR head commit 发布行级 review 评论
31
32
  - 🔒 **写操作审批** —— 每个 GitHub 写操作都经 `ctx.approval`(默认 `ask`,fail-closed);审批理由预览标题、正文长度与评论覆盖内容
32
33
  - 🗝 **token 保密** —— credentials seam → 环境变量 → `gh` CLI 三级解析,逐操作执行,绝不进日志/事件/渲染/错误
@@ -52,6 +53,7 @@
52
53
  - [目录结构](#🗂-目录结构)
53
54
  - [Topics](#🏷-topics)
54
55
  - [许可证](#许可证)
56
+ - [PerryLink DSH 插件家族](#perrylink-dsh-插件家族)
55
57
 
56
58
  ## 🚀 快速上手
57
59
 
@@ -59,8 +61,8 @@
59
61
  # 1. 安装(npm registry —— 最简单;也可用下方 tarball 通道)
60
62
  dsh plugin --profile <name> add @perrylink/dsh-github
61
63
  # tarball 通道(不需要 registry):
62
- # pnpm pack → dsh-github-0.4.0.tgz
63
- # dsh plugin --profile <name> add ./dsh-github-0.4.0.tgz
64
+ # pnpm pack → dsh-github-<version>.tgz
65
+ # dsh plugin --profile <name> add ./dsh-github-<version>.tgz
64
66
 
65
67
  # 2. 配置 GitHub token(推荐:credentials seam)
66
68
  # $DSH_HOME/.credentials.yaml
@@ -80,9 +82,13 @@ dsh plugin --profile <name> add @perrylink/dsh-github
80
82
  | 领域 | 你能得到什么 |
81
83
  |---|---|
82
84
  | **创建 PR** | `/pr create [标题]` 读取 git 状态(分支、变更文件、未推送提交),把草稿交给 agent;`pr_create` 创建 PR 并返回 URL |
85
+ | **更新 PR** | `pr_update` 修改标题、正文、状态或目标分支——与其它写操作一样经审批门 |
86
+ | **合并 PR** | `pr_merge` 以 `merge`/`squash`/`rebase` 合并,可选提交标题/消息与合并后删除源分支 |
83
87
  | **审查 PR** | `gh_review` 汇总元数据、截断 diff(canonical 值含完整 diff 文本、渲染面为有界摘要)、评论、CI 状态与静态发现;各分节抓取失败以 `diff.error` / `comments.error` / `ci.error` 显式上报 |
84
88
  | **发布审查** | `review_post` 发布单条 issue 级汇总评论(`mode: "summary"`,默认)或按行锚定 PR head commit 的行级评论(`mode: "inline"`);`body` 覆盖参数让模型先润色评论——发布前必须审批 |
85
89
  | **后台审查** | `/review <pr>` 在 `ctx.jobs` job 内抓取元数据、截断 diff、CI 检查与既有评论;完成输出带发现汇总、CI 状态与评论数。`reviewMode: "model"` 改为把 diff 交给一次性 subagent 评审 |
90
+ | **读仓库** | `gh_repo` 读取仓库元数据:描述、默认分支、可见性、star、fork、开放 issue、语言、license、topics |
91
+ | **读文件** | `gh_file` 按分支/tag/commit 读取单个文件,base64 解码、上限可配;目录返回结构化错误 |
86
92
  | **读取 issue** | `gh_issue` 支持 list / get / comments;列表中的 PR 以 `kind: "pr"` 标记 |
87
93
  | **管理 issue** | `issue_open` 创建、`issue_comment` 评论(对 PR 同样可用)、`issue_close` 关闭并可选记录关闭原因——全部审批门控 |
88
94
  | **搜索** | `gh_search` 以 GitHub 搜索语法查询 issue 与 PR,暴露独立的搜索配额 |
@@ -98,7 +104,7 @@ dsh plugin --profile <name> add @perrylink/dsh-github
98
104
  | 通道 | 命令 | 说明 |
99
105
  |---|---|---|
100
106
  | **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/`——无需构建许可 |
107
+ | **npm tarball** | `dsh plugin --profile <name> add ./dsh-github-<version>.tgz` | 自带构建好的 `lib/`——无需构建许可 |
102
108
  | **git 源** | `dsh plugin --profile <name> add "github:PerryLink/dsh-github#<sha>"` | 需 `prepare` + `allowBuilds`(见下);请钉住 commit |
103
109
  | **本地 link** | `pnpm link --dir .` 后 `dsh plugin add @perrylink/dsh-github` | 开发用 |
104
110
 
@@ -130,21 +136,30 @@ allowBuilds:
130
136
  | `maxComments` | `20` | `gh_review` 列出 PR 评论的上限 |
131
137
  | `reviewJobTimeoutMs` | `600000` | 单个后台审查 job 的截止时间(超时以 `timeout` 失败) |
132
138
  | `maxReviewRecords` | `50` | 内存审查 job 记录上限;最旧的已终态记录先淘汰 |
139
+ | `maxFileChars` | `12000` | `gh_file` 读取文件内容的字符数上限 |
140
+ | `maxFindings` | `50` | 每次审查分析器发现数上限 |
141
+ | `maxLineLength` | `300` | 行长度超过该值时分析器报超长行发现 |
133
142
  | `reviewMode` | `static` | 评审引擎:`static`(确定性分析器)或 `model`(经宿主 `subagents` 接缝的一次性 subagent;接缝缺失时响亮失败) |
134
143
  | `modelReviewProvider` | — | `reviewMode: "model"` 使用的 subagent provider 名;缺省用第一个注册的 provider |
135
144
  | `maxRetries` | `3` | 单请求的 429 重试次数 |
136
145
  | `retryBaseMs` | `500` | 重试退避基数(逐次翻倍) |
137
146
  | `retryMaxWaitMs` | `60000` | 重试退避上限 |
147
+ | `requestTimeoutMs` | `30000` | 单次请求硬超时;超时即中止 fetch |
138
148
  | `apiBaseUrl` | `https://api.github.com` | GitHub REST 基地址(GitHub Enterprise) |
139
- | `allowedActions` | `['pr.create','review.post','issue.create','issue.comment','issue.close']` | 写动作白名单;名单外直接拒绝 |
149
+ | `allowedActions` | `['pr.create','pr.merge','pr.update','review.post','issue.create','issue.comment','issue.close','ci.run']` | 写动作白名单;名单外直接拒绝 |
140
150
  | `workspaceDir` | 进程 cwd | 只读 git 检查的工作目录 |
151
+ | `ci` | `{ enabled: false, … }` | CI 集成段:轮询式审查机器人、状态检查门禁与一次性 `ci_run` 工具(其下为全部 `ci.*` 子键) |
141
152
 
142
153
  ## 🛠 工具
143
154
 
144
155
  | 工具 | 类型 | 参数 | 返回 |
145
156
  |---|---|---|---|
146
157
  | `pr_create` | 写 | `title*`、`body?`、`base?`、`head?`、`draft?`、`ownerRepo?` | `{status:'created', url, number, title, state, draft, base, head, rateLimit}` 或结构化错误 |
158
+ | `pr_merge` | 写 | `pr*`(数字 / `#n` / `o/r#n` / URL)、`mergeMethod?`、`commitTitle?`、`commitMessage?`、`deleteBranch?` | `{status:'merged', merged, sha?, message, url, branchDeleted, branchDeleteNote?, rateLimit}` 或结构化错误 |
159
+ | `pr_update` | 写 | `pr*`(数字 / `#n` / `o/r#n` / URL)、`title?`、`body?`、`state?`(`open`/`closed`)、`base?` | `{status:'updated', url, number, title, state, base, rateLimit}` 或结构化错误 |
147
160
  | `gh_review` | 读 | `pr*`(数字 / `#n` / `o/r#n` / URL)、`fields?`、`maxDiffChars?` | 元数据、截断 diff(完整 `diff.text` + 有界 `diff.excerpt` + 逐文件统计)、评论、CI、静态发现、各分节 `error` 字段、配额 |
161
+ | `gh_repo` | 读 | `ownerRepo?` | `{repo, description, defaultBranch, visibility, stars, forks, openIssues, language, license, topics, url, updatedAt, rateLimit}` 或结构化错误 |
162
+ | `gh_file` | 读 | `ownerRepo?`、`path*`、`ref?`、`maxChars?` | `{repo, path, ref, size, truncated, content, sha, url, rateLimit}` 或结构化错误 |
148
163
  | `gh_issue` | 读 | `action*`(`list`/`get`/`comments`)、`ownerRepo?`、`issueNumber?`、`state?`、`limit?` | 归一化条目(每条带 `kind: issue/pr/comment`)+ 配额 |
149
164
  | `review_post` | 写 | `jobId*`、`mode?`(`summary`/`inline`)、`body?` | `{status:'posted', mode, url, commentId?, reviewId?, findings, rateLimit}` 或结构化错误 |
150
165
  | `issue_open` | 写 | `title*`、`body?`、`labels?`、`ownerRepo?` | `{status:'created', url, number, title, rateLimit}` 或结构化错误 |
@@ -175,8 +190,9 @@ allowBuilds:
175
190
  /review ───┼──► ctx.jobs.start("github-review") ──► job │
176
191
  /issue ────┼──► agent.followup │
177
192
  │ │
178
- 模型 ─── pr_create / gh_review / gh_issue / review_post / │
179
- issue_open / issue_comment / issue_close / gh_search │
193
+ 模型 ─── pr_create / pr_merge / pr_update / gh_review / │
194
+ review_post / gh_issue / issue_open / issue_comment / │
195
+ issue_close / gh_search / gh_repo / gh_file │
180
196
  (defineTool,只返回规范 JSON) │
181
197
  │ │
182
198
  └───────┬───────────────┬───────────────┬───────┘
@@ -187,7 +203,7 @@ allowBuilds:
187
203
  ```
188
204
 
189
205
  - **凭证接缝。** `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 内调用受审批门保护的工具。
206
+ - **审批。** 所有写操作都经模型工具。`tools/pre-execute` waterfall 监听器对七个写工具返回 `ask`,注册表即通过 `ctx.approval` 询问人类(宿主自动落 `approval/asked` + `approval/decided` 审计对),无应答者时 fail-closed。审批理由预览将要发布的内容(标题、正文长度、合并方式、覆盖评论的首行)。命令本身从不直接写:命令 handler 运行时没有开启的 turn,审批 seam 对命令在结构上不可用——写命令先收集只读上下文,再唤醒 agent(空闲 `followup`、忙碌 `inject`),让模型在 turn 内调用受审批门保护的工具。
191
207
  - **后台审查。** `/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
208
  - **模型可见 ⟺ 已记录。** 本插件**不新增任何自定义会话事件类型**。仓库外插件的事件类型不在宿主 `KNOWN_SESSION_EVENT_TYPES` 中,未知的 required 事件会让宿主拒绝读取会话日志(宿主明确把外部插件事件注册面推迟到未来)。因此所有模型可见内容都走宿主已记录的表面:`tool/result` 规范值、经 `agent.inject`/`agent.followup` 的 `user/message` 通知、`command/run` + `command/done` 生命周期对、`approval/asked` + `approval/decided` 审计对。
193
209
  - **纯 presenter。** `presentCall`/`presentResult` 是 `args`(+ 持久化的 `result.meta`)的纯函数,实时流与日志回放行为一致。PR 创建结果以 generic 卡片展示 PR 链接。
@@ -199,7 +215,7 @@ allowBuilds:
199
215
  - `/pr create` 自己从不 commit/push;`autoCommit: true` 时模型通过 bash 工具(其自身审批门)执行这些写操作。dsh-github **不**管理 git 提交身份(dsh-git-identity 的职责)、**不**做 worktree(dsh-worktree 的职责)。
200
216
  - 审查 job 零写操作:只读 diff、把报告存进进程内存;只有 `review_post` 在审批后发布。
201
217
  - 发布的评论会插入 diff 中的文件名——这是不可信的仓库内容:`formatPostBody` 对文件名做反引号转义与 HTML 转义,恶意 PR 无法向审查评论注入 Markdown。
202
- - 从 GitHub 读到的 issue/PR 正文、评论与搜索结果都是进入模型上下文的外部不可信内容——与网页抓取同属固有权衡;插件在渲染中把它们标注为外部内容。
218
+ - `gh_file` 读到的文件内容以及从 GitHub 读到的 issue/PR 正文、评论与搜索结果都是进入模型上下文的外部不可信内容——与网页抓取同属固有权衡;插件在渲染中把它们标注为外部内容。
203
219
  - 配额:429 带退避重试,剩余配额在包括失败在内的每个结果上对模型可见。
204
220
 
205
221
  ## ⚠️ 已知局限
@@ -208,7 +224,7 @@ allowBuilds:
208
224
  - **默认静态分析器** —— 确定性规则集(`src/review.ts`),零 token、可复现。`reviewMode: "model"` 会把截断后的 diff 交给宿主 `subagents` 接缝的一次性 subagent 做 LLM 评审(消耗 token;需要接缝与已注册的 provider)。
209
225
  - **job 与记录是进程内状态** —— 审查报告按 job id 存于插件内存,与宿主 job 注册表同为进程级生命周期;记录表受 `maxReviewRecords` 上限约束(最旧已终态记录先淘汰)。
210
226
  - **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 配套仓库。
227
+ - **CI / GitHub Action** — 随本仓库发布(v0.6.0):复合动作(`action.yml`)负责审查 PR、修复 CI 并产出报告;轮询式审查机器人带幂等行内评论;外加状态检查门禁。所有写动作仍需审批。
212
228
 
213
229
  ## 🧪 开发
214
230
 
@@ -218,11 +234,13 @@ pnpm test # vitest:配置、凭证、429 重试、工具、命令、j
218
234
  pnpm typecheck
219
235
  pnpm build # tsc → lib/(noEmitOnError)
220
236
  pnpm pack # 可安装 tarball
221
- pnpm run check:readmes # 交叉检查 5 个 README 的目录锚点
237
+ pnpm run check:readmes # 交叉检查 5 个 README 的目录锚点、工具与配置键
222
238
  ```
223
239
 
224
240
  测试通过注入的 runner mock 掉 GitHub API、`gh` CLI 与 git——不联网、不用真实凭证。`test/security.test.ts` 断言 token 字符串不出现在任何模型或人类可见输出中。`test/e2e.test.ts` 是可选真实 API 冒烟测试:未设置 `DSH_GITHUB_E2E_TOKEN` 时自动跳过(只打只读端点;独立变量保证单测套件与环境隔离)。
225
241
 
242
+ 要在本地演练 composite action,运行 `node scripts/local-test.mjs --owner-repo you/repo --pr 42`(全部选项见 `--help`)。模拟器为每个子进程显式写死 `DSH_HOME`、`DSH_PROFILE_DIR`、`RUNNER_TEMP` 与输出目录——全部位于全新的系统临时目录沙箱内,即使存在 Machine 级 `DSH_HOME` 也绝不读写你真实的 dsh home——并按 `action.yml` 的顺序回放 install → prepare → headless run → post 四步。`action-patch.mjs` 与 `action-post.mjs` 在 GitHub Actions runner 之外一律拒绝运行,因此 action 不会把 profile overlay 或报告写到未知的本地位置。
243
+
226
244
  ## 🗂 目录结构
227
245
 
228
246
  ```
@@ -235,7 +253,7 @@ src/git.ts 只读 git 检查 + 任意 API 主机的 origin 解析
235
253
  src/review.ts 确定性 diff 分析器 + 转义后的评论草稿
236
254
  src/jobs.ts github-review 后台 job 生产者(元数据 + diff + CI + 评论)
237
255
  src/approval-gate.ts tools/pre-execute ask/deny 门(带写操作预览)
238
- src/tools.ts 八个模型可调工具
256
+ src/tools.ts 十二个模型可调工具
239
257
  src/commands.ts /pr、/review、/issue
240
258
  src/present.ts 纯 UI 卡片 presenter
241
259
  test/ vitest 套件 + mock 宿主脚手架 + 可选 e2e 冒烟
@@ -252,3 +270,25 @@ scripts/prepare.mjs git 安装用的自包含构建
252
270
  ## 许可证
253
271
 
254
272
  [Apache License 2.0](LICENSE)
273
+
274
+ ## PerryLink DSH 插件家族
275
+
276
+ 本项目是 [PerryLink](https://github.com/PerryLink) 维护的 [15 个 DeepSeek Harness 插件](https://github.com/PerryLink)之一。如果你觉得这个插件有用,其余的很可能同样有用:
277
+
278
+ | 插件 | 一句话说明 |
279
+ |---|---|
280
+ | [dsh-mcp-panel](https://github.com/PerryLink/dsh-mcp-panel) | 只读 MCP 运行时面板:/mcp 命令 + 设置页,状态/工具/错误一览 |
281
+ | [dsh-doublecheck](https://github.com/PerryLink/dsh-doublecheck) | 工程纪律守门:需求审讯、测试证据门、对抗评审 |
282
+ | [dsh-background-agents](https://github.com/PerryLink/dsh-background-agents) | 持久化后台子代理:Web 侧边栏进度、随时留言与打断 |
283
+ | [dsh-lsp-actions](https://github.com/PerryLink/dsh-lsp-actions) | 基于语言服务器的诊断/格式化/补全/代码动作/重命名 |
284
+ | [dsh-output-styles](https://github.com/PerryLink/dsh-output-styles) | 对标 Claude Code outputStyles 的运行时风格切换 |
285
+ | [dsh-checkpoint-rewind](https://github.com/PerryLink/dsh-checkpoint-rewind) | 对标 Claude Code /rewind:快照、会话 fork、一键回退 |
286
+ | [dsh-permission-rules](https://github.com/PerryLink/dsh-permission-rules) | Claude Code 风格声明式 allow/deny/ask 权限规则,带审计 |
287
+ | [dsh-auto-review](https://github.com/PerryLink/dsh-auto-review) | 审批链上的第二模型自动审查,默认 fail-closed |
288
+ | [dsh-memento](https://github.com/PerryLink/dsh-memento) | 带审批门的跨会话记忆:ctx.memory + SQLite + memory 工具 |
289
+ | [dsh-skill-pack-security](https://github.com/PerryLink/dsh-skill-pack-security) | 安全审计技能包:密钥扫描、依赖与供应链审查 |
290
+ | [dsh-session-pin](https://github.com/PerryLink/dsh-session-pin) | 在 Web 侧边栏置顶会话,持久排序 |
291
+ | [dsh-composer-history](https://github.com/PerryLink/dsh-composer-history) | Web 作曲器终端式输入历史:方向键、Ctrl+R 搜索 |
292
+ | **[dsh-github](https://github.com/PerryLink/dsh-github)** | DSH 的 GitHub PR/issue 集成,所有写操作经审批门 |
293
+ | [dsh-plugin-guide](https://github.com/PerryLink/dsh-plugin-guide) | 插件开发知识库,随 bundle 安装的按需 agent 技能 |
294
+ | [dsh-claude-move](https://github.com/PerryLink/dsh-claude-move) | 把 Claude Code 会话、记忆、技能和 CLAUDE.md 迁入 DSH |
package/action.yml ADDED
@@ -0,0 +1,161 @@
1
+ # dsh-github — official-grade GitHub CI integration for DeepSeek Harness.
2
+ #
3
+ # Runs `dsh --profile headless` with the dsh-github CI surface composed on top,
4
+ # to review pull requests, investigate CI failures, or generate reports. The
5
+ # plugin publishes the status-check gate and writes structured JSON + Markdown
6
+ # reports; this action exposes them as outputs and enforces the blocking gate.
7
+ #
8
+ # Shell contract: every step uses only bash builtins, node, npm, and the dsh
9
+ # CLI — no curl, gh, jq, or third-party CLI tools. Secrets travel only through
10
+ # environment variables and the plugin's per-operation token resolution; they
11
+ # are never logged or interpolated into prompts.
12
+ name: 'dsh-github — DeepSeek Harness GitHub CI'
13
+ description: 'Run dsh headless to review PRs, investigate CI failures, or generate reports, with inline review comments, a status-check gate, and structured JSON/Markdown outputs'
14
+ author: 'PerryLink'
15
+
16
+ branding:
17
+ icon: 'git-pull-request'
18
+ color: 'purple'
19
+
20
+ inputs:
21
+ task:
22
+ description: 'CI task: review (analyze + comments + status check), fix-ci (investigate CI failures and post a fix plan), or report (generate a repository/PR report).'
23
+ required: false
24
+ default: 'review'
25
+ pr:
26
+ description: 'Pull request number. Defaults to the current pull_request event number; required for review and fix-ci.'
27
+ required: false
28
+ default: ${{ github.event.pull_request.number }}
29
+ owner-repo:
30
+ description: 'Repository as owner/repo. Defaults to github.repository.'
31
+ required: false
32
+ default: ${{ github.repository }}
33
+ task-prompt:
34
+ description: 'Complete replacement for the per-mode default headless task text (advanced).'
35
+ required: false
36
+ model:
37
+ description: 'DeepSeek model id for the headless session.'
38
+ required: false
39
+ default: 'deepseek-v4-flash'
40
+ engine:
41
+ description: "Review engine: 'static' (deterministic analyzer, zero review tokens) or 'model' (the agent authors the review body)."
42
+ required: false
43
+ default: 'static'
44
+ deepseek-api-key:
45
+ description: 'DeepSeek API key. Always pass a secret, never a literal.'
46
+ required: true
47
+ github-token:
48
+ description: 'GitHub token for the review pipeline. Use the auto-expiring job token with the minimal permissions shown in the README.'
49
+ required: false
50
+ default: ${{ github.token }}
51
+ check-name:
52
+ description: 'Name of the status check published on the PR head commit.'
53
+ required: false
54
+ default: 'dsh-github-review'
55
+ blocking:
56
+ description: "When the verdict is needs-changes: 'true' fails the check (and this step), 'false' publishes a neutral (non-blocking) conclusion."
57
+ required: false
58
+ default: 'true'
59
+ fail-on:
60
+ description: "Lowest finding severity that flips the verdict to needs-changes: 'error' or 'warning'."
61
+ required: false
62
+ default: 'error'
63
+ label-filters:
64
+ description: 'Comma-separated labels; a PR must carry at least one to be reviewed. Empty matches all PRs.'
65
+ required: false
66
+ path-filters:
67
+ description: 'Comma-separated path globs (e.g. src/**,*.md); a PR must touch at least one matching path. Empty matches all PRs.'
68
+ required: false
69
+ max-diff-chars:
70
+ description: 'Character cap for the PR diff read into the review.'
71
+ required: false
72
+ default: '8000'
73
+ post-comments:
74
+ description: "Whether the pipeline posts the inline review comments ('true'/'false')."
75
+ required: false
76
+ default: 'true'
77
+ post-check:
78
+ description: "Whether the pipeline publishes the status check ('true'/'false')."
79
+ required: false
80
+ default: 'true'
81
+ request-timeout-ms:
82
+ description: 'Per GitHub API request timeout in milliseconds (plugin-side).'
83
+ required: false
84
+ default: '30000'
85
+ node-version:
86
+ description: 'Node.js version for actions/setup-node.'
87
+ required: false
88
+ default: '22'
89
+ dsh-version:
90
+ description: '@deepseek-ai/dsh version to install.'
91
+ required: false
92
+ default: 'latest'
93
+ plugin-version:
94
+ description: '@perrylink/dsh-github version to install.'
95
+ required: false
96
+ default: 'latest'
97
+ output-dir:
98
+ description: 'Directory for dsh-github-ci-result.json and dsh-github-ci-summary.md.'
99
+ required: false
100
+ default: ${{ runner.temp }}/dsh-github
101
+
102
+ outputs:
103
+ verdict:
104
+ description: "Review verdict: pass | needs-changes | skipped | error."
105
+ value: ${{ steps.post.outputs.verdict }}
106
+ report-json:
107
+ description: 'Path of the structured JSON report.'
108
+ value: ${{ steps.post.outputs.report-json }}
109
+ report-markdown:
110
+ description: 'Path of the Markdown summary.'
111
+ value: ${{ steps.post.outputs.report-markdown }}
112
+ check-url:
113
+ description: 'URL of the published status check (when published).'
114
+ value: ${{ steps.post.outputs.check-url }}
115
+
116
+ runs:
117
+ using: composite
118
+ steps:
119
+ - name: Set up Node
120
+ uses: actions/setup-node@v4
121
+ with:
122
+ node-version: ${{ inputs.node-version }}
123
+
124
+ - name: Install dsh and dsh-github
125
+ shell: bash
126
+ env:
127
+ DSH_HOME: ${{ runner.temp }}/dsh-home
128
+ DSH_PROFILE_DIR: ${{ runner.temp }}/dsh-home/profiles/headless
129
+ run: |
130
+ npm install --global --no-audit --no-fund "@deepseek-ai/dsh@${{ inputs.dsh-version }}"
131
+ mkdir -p "$DSH_PROFILE_DIR"
132
+ printf '{"private": true}\n' > "$DSH_PROFILE_DIR/package.json"
133
+ npm install --prefix "$DSH_PROFILE_DIR" --legacy-peer-deps --package-lock=false --no-save --no-audit --no-fund "@perrylink/dsh-github@${{ inputs.plugin-version }}"
134
+
135
+ - name: Generate the dsh profile overlay and task
136
+ id: prepare
137
+ shell: bash
138
+ run: node "${{ github.action_path }}/scripts/action-patch.mjs"
139
+
140
+ - name: Run dsh headless
141
+ id: run
142
+ shell: bash
143
+ env:
144
+ DSH_HOME: ${{ runner.temp }}/dsh-home
145
+ DEEPSEEK_API_KEY: ${{ inputs.deepseek-api-key }}
146
+ DSH_GITHUB_TOKEN: ${{ inputs.github-token }}
147
+ DSH_GITHUB_CI_DRIVER: '1'
148
+ DSH_GITHUB_CI_OUTPUT_DIR: ${{ inputs.output-dir }}
149
+ DSH_TELEMETRY_DISABLED: '1'
150
+ run: |
151
+ mkdir -p "${{ inputs.output-dir }}"
152
+ set +e
153
+ dsh --profile headless --patch "${{ inputs.output-dir }}/dsh-github-ci.cordis.yml" "$(cat "${{ inputs.output-dir }}/task.txt")" \
154
+ > "${{ inputs.output-dir }}/dsh-github-stdout.log" 2> "${{ inputs.output-dir }}/dsh-github-stderr.log"
155
+ echo "$?" > "${{ inputs.output-dir }}/dsh-github-exit.txt"
156
+ cat "${{ inputs.output-dir }}/dsh-github-stdout.log"
157
+
158
+ - name: Publish outputs and enforce the gate
159
+ id: post
160
+ shell: bash
161
+ run: node "${{ github.action_path }}/scripts/action-post.mjs"
@@ -1,15 +1,15 @@
1
1
  /**
2
2
  * The write-action approval gate: a `tools/pre-execute` waterfall listener.
3
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.
4
+ * Every dsh-github write tool (`pr_create`, `pr_merge`, `pr_update`,
5
+ * `review_post`, `issue_open`, `issue_comment`, `issue_close`) asks the human
6
+ * through the registry-owned approval path (`ask` → ctx.approval), which
7
+ * appends the approval/asked + approval/decided audit pair and fails closed
8
+ * without an answerer. Actions missing from the `allowedActions` whitelist are
9
+ * denied before any prompt. Every other tool passes through via `next()` — the
10
+ * waterfall contract requires it. Approval reasons preview what would be
11
+ * published (titles, body lengths, and the first line of an overridden review
12
+ * body) without ever containing the token.
13
13
  * @module dsh-github/approval-gate
14
14
  */
15
15
  import type { Context } from '@deepseek-ai/cordis';
@@ -17,6 +17,11 @@ import type { GithubState } from './state.ts';
17
17
  /**
18
18
  * Register the approval gate. Registration is an effect: disposing the plugin
19
19
  * fiber removes the listener.
20
+ *
21
+ * Unattended CI runs (`DSH_GITHUB_CI_DRIVER=1`) auto-allow exactly the
22
+ * actions listed in `ci.autoApprove` — the composite action composes that
23
+ * allowlist for the write the pipeline needs. Interactive sessions always
24
+ * ask, and actions missing from `allowedActions` stay denied everywhere.
20
25
  * @param ctx - plugin context; the listener lives on the shared tools pipeline.
21
26
  * @param state - plugin state used to enrich approval reasons.
22
27
  * @returns the effect disposer.
@@ -1 +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"}
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;AAyF7C;;;;;;;;;;;GAWG;AACH,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,OAAO,EAAE,KAAK,EAAE,WAAW,GAAG,MAAM,IAAI,CAYjF"}