@perrylink/dsh-github 0.6.0 → 0.6.2

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/README.md CHANGED
@@ -1,131 +1,90 @@
1
- <h1 align="center">dsh-github</h1>
2
-
3
- <p align="center">
4
- <b>Bring GitHub into DeepSeek Harness.</b><br/>
5
- Create pull requests · review PRs with inline or summary comments · manage issues · search — every write gated by human approval, token never logged.
6
- </p>
7
-
8
- <p align="center">
9
- <a href="README.zh-CN.md">中文</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>
1
+ <div align="center">
23
2
 
24
- ---
3
+ # dsh-github
4
+
5
+ **GitHub PRs, reviews, issues, and CI for DeepSeek Harness — every write gated by human approval, token never logged.**
6
+
7
+ *Create, review, merge, and search GitHub from the agent, with a CI composite action, polling review bot, and status-check gate.*
8
+
9
+ [![License](https://img.shields.io/badge/license-Apache%202.0-blue.svg)](LICENSE)
10
+ [![DSH plugin](https://img.shields.io/badge/dsh-plugin-✅-green)](https://github.com/topics/dsh-plugin)
11
+ [![Node](https://img.shields.io/badge/node-%5E22.19%20%7C%7C%20%3E%3D24-brightgreen.svg)](#)
12
+ [![CI](https://img.shields.io/github/actions/workflow/status/PerryLink/dsh-github/ci.yml?branch=main&label=CI)](https://github.com/PerryLink/dsh-github/actions)
13
+ [![Version](https://img.shields.io/github/v/tag/PerryLink/dsh-github?label=version)](https://github.com/PerryLink/dsh-github/releases)
14
+ [![npm version](https://img.shields.io/npm/v/%40perrylink%2Fdsh-github)](https://www.npmjs.com/package/@perrylink/dsh-github)
15
+ [![npm downloads](https://img.shields.io/npm/dm/%40perrylink%2Fdsh-github)](https://www.npmjs.com/package/@perrylink/dsh-github)
25
16
 
26
- **dsh-github** is a bundle plugin for [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) (`dsh`) — the "everything is a plugin" agent harness. It fills the GitHub gap between dsh and tools like [Claude Code](https://github.com/anthropics/claude-code) (`gh claude` / [claude-code-action](https://github.com/anthropics/claude-code-action)) and [Codex](https://github.com/openai/codex) (`@codex review` / Autofix CI): your agent can **read a PR, review a PR, open a PR, merge and update PRs, read repo metadata and files, comment on and close issues, and search** — while a human approves every write and the token stays secret.
17
+ [English](README.md) · [简体中文](README.zh.md) · [Español](README.es.md) · [Português](README.pt.md) · [हिन्दी](README.hi.md)
27
18
 
28
- - 🛠 **12 tools** — `pr_create` · `pr_merge` · `pr_update` · `gh_review` · `review_post` · `gh_issue` · `issue_open` · `issue_comment` · `issue_close` · `gh_search` · `gh_repo` · `gh_file`, all canonical-JSON via `defineTool`
29
- - ⌨️ **3 command families** — `/pr create` · `/review` (start/stop/post) · `/issue open`
30
- - 🔀 **Full PR lifecycle** — create → review → update (title/body/state/base) → merge (merge/squash/rebase, optional head-branch delete)
31
- - 📝 **Inline reviews** — `review_post` posts either one summary comment or line-anchored review comments against the PR head commit
32
- - 🔒 **Approval-gated writes** — every GitHub write goes through `ctx.approval` (default `ask`, fail-closed); approval reasons preview titles, body sizes, and comment overrides
33
- - 🗝 **Token secrecy** — credentials seam → environment → `gh` CLI, resolved per operation, never in logs, events, renders, or errors
34
- - 🖥 **Background review jobs** — `/review` runs on `ctx.jobs` with the host's own `job_list` / `job_output` / `job_kill` surface, and reports CI status and comment counts alongside the findings
35
- - 🤖 **Model review option** — `reviewMode: "model"` delegates the capped diff to a one-shot subagent through the host's `subagents` seam; the default `static` mode stays deterministic and token-free
36
- - 🚦 **429 backoff + quota surfacing** — the model sees the remaining rate limit on every result, including failures; per-section fetch errors are surfaced instead of swallowed
37
- - 🌐 **5-language docs** — English · 中文 · Español · Português · हिन्दी
19
+ </div>
38
20
 
39
21
  ---
40
22
 
41
23
  ## 📚 Table of contents
42
24
 
43
- - [Quick start](#🚀-quick-start)
44
- - [Features](#✨-features)
45
- - [Installation](#📦-installation)
46
- - [Configuration](#⚙️-configuration)
47
- - [Tools](#🛠-tools)
48
- - [Commands](#⌨️-commands)
49
- - [Architecture](#🏗-architecture)
50
- - [Security boundaries](#🔒-security-boundaries)
51
- - [Known limitations](#⚠️-known-limitations)
52
- - [Development](#🧪-development)
53
- - [Repository layout](#🗂-repository-layout)
54
- - [Topics](#🏷-topics)
55
- - [License](#license)
25
+ - [Compatibility](#compatibility)
26
+ - [What you get](#what-you-get)
27
+ - [Quick start](#quick-start)
28
+ - [Install & uninstall](#install-&-uninstall)
29
+ - [Configuration](#configuration)
30
+ - [Tools & surfaces](#tools-&-surfaces)
31
+ - [Architecture](#architecture)
32
+ - [Permissions & data](#permissions-&-data)
33
+ - [Security boundaries](#security-boundaries)
34
+ - [Known limitations](#known-limitations)
35
+ - [Development](#development)
36
+ - [Repository layout](#repository-layout)
37
+ - [Topics](#topics)
38
+ - [Contributors](#contributors)
56
39
  - [PerryLink DSH Plugin Family](#perrylink-dsh-plugin-family)
40
+ - [License](#license)
57
41
 
58
- ## 🚀 Quick start
42
+ ## Compatibility
59
43
 
60
- ```sh
61
- # 1. install (npm registry — simplest; or use the tarball channel below)
62
- dsh plugin --profile <name> add @perrylink/dsh-github
63
- # tarball channel (no registry needed):
64
- # pnpm pack → dsh-github-<version>.tgz
65
- # dsh plugin --profile <name> add ./dsh-github-<version>.tgz
66
-
67
- # 2. configure a GitHub token (recommended: the credentials seam)
68
- # $DSH_HOME/.credentials.yaml
69
- # GITHUB_TOKEN: <your token>
70
-
71
- # 3. use it — in the dsh web UI or headless
72
- # /pr create "add dark mode" → agent drafts & opens the PR (approval required)
73
- # /review 42 → background review job, read it with job_output
74
- # /review post github-review-1 → publish the review comment (approval required)
75
- # /issue open "crash on startup" → agent opens the issue (approval required)
76
- ```
44
+ | Surface | Status |
45
+ |---|---|
46
+ | Harness | DeepSeek Harness `0.1.0-rc.6` (compat declared for `0.1.0-rc.5`–`0.1.0-rc.6`) |
47
+ | Node | `^22.19.0 \|\| >=24.0.0` |
48
+ | Platforms | All (host plugin; outbound network to GitHub) |
49
+ | Model | Any (static review is deterministic; `reviewMode: "model"` is optional) |
77
50
 
78
- Verify: `dsh --profile <name> --dump-config` must show the `# == dsh-github` section with **no FAILED lines**.
51
+ ## What you get
79
52
 
80
- ## ✨ Features
53
+ `dsh-github` fills the GitHub gap between `dsh` and tools like Claude Code and Codex: your agent can read, review, open, update, and merge pull requests, read repository metadata and files, comment on and close issues, and search — while a human approves every write and the token stays secret.
81
54
 
82
- | Area | What you get |
83
- |---|---|
84
- | **Create PRs** | `/pr create [title]` reads git state (branch, changed files, commits ahead) and hands the agent a draft; `pr_create` opens the PR and returns its URL |
85
- | **Update PRs** | `pr_update` edits title, body, state, or target branch — approval-gated like every other write |
86
- | **Merge PRs** | `pr_merge` merges with `merge`/`squash`/`rebase`, optional commit title/message and head-branch deletion after the merge |
87
- | **Review PRs** | `gh_review` summarizes metadata, capped diff (full text in the canonical value, bounded excerpt in the render), comments, CI status, and static findings — per-section fetch failures are reported as `diff.error` / `comments.error` / `ci.error` |
88
- | **Post reviews** | `review_post` publishes one aggregated issue-level comment (`mode: "summary"`, default) or line-anchored review comments on the PR head commit (`mode: "inline"`); a `body` override lets the model polish the comment first — after human approval |
89
- | **Background reviews** | `/review <pr>` fetches metadata, the capped diff, CI checks, and existing comments in a `ctx.jobs` job; the completion output carries the findings summary, CI status, and comment count. `reviewMode: "model"` delegates the diff to a one-shot subagent instead of the static analyzer |
90
- | **Read repos** | `gh_repo` reads repository metadata: description, default branch, visibility, stars, forks, open issues, language, license, topics |
91
- | **Read files** | `gh_file` reads one file at a branch/tag/commit with base64 decoding and a configurable cap; directories report a structured error |
92
- | **Read issues** | `gh_issue` lists / gets / comments; pull requests in listings are marked `kind: "pr"` |
93
- | **Manage issues** | `issue_open` creates, `issue_comment` comments (also works on PRs), `issue_close` closes with an optional state reason — all approval-gated |
94
- | **Search** | `gh_search` queries issues and pull requests with GitHub search syntax, surfacing the separate search quota |
95
- | **Approval** | `tools/pre-execute` asks `ctx.approval` for every write; `allowedActions` whitelist denies before prompting |
96
- | **Secret safety** | Token is read per operation and sent only in the Authorization header; a dedicated test asserts it never appears in any visible output |
97
- | **Resilience** | 429 retry with `Retry-After`/`x-ratelimit-reset` backoff; read tools are concurrency-safe; all calls honor cancellation |
98
- | **Observability** | Model-visible ⇔ logged: everything the model sees flows through the host's own session events (`tool/result`, `user/message`, `command/run`, `approval/asked`…) |
99
-
100
- ## 📦 Installation
101
-
102
- Four documented channels — pick one.
103
-
104
- | Channel | Command | Notes |
105
- |---|---|---|
106
- | **npm registry** | `dsh plugin --profile <name> add @perrylink/dsh-github` | Published package — the simplest channel |
107
- | **npm tarball** | `dsh plugin --profile <name> add ./dsh-github-<version>.tgz` | Ships with `lib/` built — no build permission |
108
- | **git source** | `dsh plugin --profile <name> add "github:PerryLink/dsh-github#<sha>"` | Needs `prepare` + `allowBuilds` (see below); pin the commit |
109
- | **local link** | `pnpm link --dir .` then `dsh plugin add @perrylink/dsh-github` | Development |
55
+ - **12 tools** — `pr_create`, `pr_merge`, `pr_update`, `gh_review`, `review_post`, `gh_issue`, `issue_open`, `issue_comment`, `issue_close`, `gh_search`, `gh_repo`, `gh_file`, all canonical JSON via `defineTool`.
56
+ - **3 command families** — `/pr create`, `/review` (start/stop/post), `/issue open`.
57
+ - **Full PR lifecycle** — create → review → update (title/body/state/base) → merge (merge/squash/rebase, optional head-branch delete).
58
+ - **Inline reviews** — `review_post` posts one summary comment or line-anchored review comments against the PR head commit.
59
+ - **Approval-gated writes** — every GitHub write goes through `ctx.approval` (default `ask`, fail-closed); approval reasons preview titles, body sizes, and comment overrides.
60
+ - **Token secrecy** — credentials seam → environment → `gh` CLI, resolved per operation, never in logs, events, renders, or errors.
61
+ - **Background review jobs** — `/review` runs on `ctx.jobs` with the host's own `job_list` / `job_output` / `job_kill` surface.
62
+ - **Resilience** — 429 retry with `Retry-After`/`x-ratelimit-reset` backoff; read tools are concurrency-safe; all calls honor cancellation.
63
+ - **CI surface** — the one-shot `ci_run` tool, a polling review bot, and a status-check gate (composite action `action.yml`).
110
64
 
111
- > The npm package is published under the `@perrylink` scope because the
112
- > unscoped `dsh-github` name is owned by an unrelated project on the registry.
113
- > The plugin's module name stays `dsh-github`.
65
+ ## Quick start
114
66
 
115
- Git installs: pnpm ≥10 refuses a git dependency's `prepare` until allowlisted — `dsh` prints the exact key; copy it into the profile's `pnpm-workspace.yaml`:
67
+ ```sh
68
+ # 1. install the bundle into your profile
69
+ dsh plugin --profile web add "github:PerryLink/dsh-github#main"
70
+
71
+ # or from npm (published releases)
72
+ dsh plugin --profile web add @perrylink/dsh-github
116
73
 
117
- ```yaml
118
- allowBuilds:
119
- '@perrylink/dsh-github': true
74
+ # 2. restart and verify the row
75
+ dsh --profile web --dump-config | grep -A3 'id: dsh-github'
120
76
  ```
121
77
 
122
- The `prepare` script (`scripts/prepare.mjs`) is self-contained: it builds with TypeScript when a compiler is resolvable, otherwise falls back to the **committed `lib/` artifacts**, and fails loud with neither.
78
+ ## Install & uninstall
123
79
 
124
- **Uninstall:** `dsh plugin --profile <name> remove @perrylink/dsh-github`.
80
+ - **git channel** (latest `main`): `dsh plugin --profile web add "github:PerryLink/dsh-github#main"` — the `prepare` script builds with production dependencies only.
81
+ - **npm channel** (published releases): `dsh plugin --profile web add @perrylink/dsh-github`.
82
+ - **tarball channel**: `pnpm pack` in this repo, then `dsh plugin --profile web add ./dsh-github-<version>.tgz`.
83
+ - **uninstall**: `dsh plugin --profile web remove dsh-github` (or remove the row from the profile patch).
125
84
 
126
- ## ⚙️ Configuration
85
+ ## Configuration
127
86
 
128
- Schemastery-validated at load time (fail loud). Override any key in the profile's `cordis.patch.yml` (the whole row config is replaced, never deep-merged).
87
+ All tunables are Schemastery `Config` fields (changeable from cordis.yml). An id-targeted override replaces the whole row — restate every key you need. `cordis.patch.yml` documents each key inline.
129
88
 
130
89
  | Key | Default | Meaning |
131
90
  |---|---|---|
@@ -152,97 +111,71 @@ Schemastery-validated at load time (fail loud). Override any key in the profile'
152
111
  | `workspaceDir` | process cwd | Working directory for read-only git inspection |
153
112
  | `ci` | `{ enabled: false, … }` | CI integration section: polling review bot, status-check gate, and the one-shot `ci_run` tool (all `ci.*` keys live inside it) |
154
113
 
155
- ## 🛠 Tools
156
-
157
- | Tool | Kind | Parameters | Returns |
158
- |---|---|---|---|
159
- | `pr_create` | write | `title*`, `body?`, `base?`, `head?`, `draft?`, `ownerRepo?` | `{status:'created', url, number, title, state, draft, base, head, rateLimit}` or structured error |
160
- | `pr_merge` | write | `pr*` (number / `#n` / `o/r#n` / URL), `mergeMethod?`, `commitTitle?`, `commitMessage?`, `deleteBranch?` | `{status:'merged', merged, sha?, message, url, branchDeleted, branchDeleteNote?, rateLimit}` or structured error |
161
- | `pr_update` | write | `pr*` (number / `#n` / `o/r#n` / URL), `title?`, `body?`, `state?` (`open`/`closed`), `base?` | `{status:'updated', url, number, title, state, base, rateLimit}` or structured error |
162
- | `gh_review` | read | `pr*` (number / `#n` / `o/r#n` / URL), `fields?`, `maxDiffChars?` | metadata, capped diff (full `diff.text` + bounded `diff.excerpt` + per-file stats), comments, CI, static findings, per-section `error` fields, rate limit |
163
- | `gh_repo` | read | `ownerRepo?` | `{repo, description, defaultBranch, visibility, stars, forks, openIssues, language, license, topics, url, updatedAt, rateLimit}` or structured error |
164
- | `gh_file` | read | `ownerRepo?`, `path*`, `ref?`, `maxChars?` | `{repo, path, ref, size, truncated, content, sha, url, rateLimit}` or structured error |
165
- | `gh_issue` | read | `action*` (`list`/`get`/`comments`), `ownerRepo?`, `issueNumber?`, `state?`, `limit?` | normalized items (each marked `kind: issue/pr/comment`) + rate limit |
166
- | `review_post` | write | `jobId*`, `mode?` (`summary`/`inline`), `body?` | `{status:'posted', mode, url, commentId?, reviewId?, findings, rateLimit}` or structured error |
167
- | `issue_open` | write | `title*`, `body?`, `labels?`, `ownerRepo?` | `{status:'created', url, number, title, rateLimit}` or structured error |
168
- | `issue_comment` | write | `issueNumber*`, `body*`, `ownerRepo?` | `{status:'commented', url, commentId, issueNumber, rateLimit}` or structured error |
169
- | `issue_close` | write | `issueNumber*`, `ownerRepo?`, `stateReason?` (`completed`/`not_planned`) | `{status:'closed', url, number, title, rateLimit}` or structured error |
170
- | `gh_search` | read | `q*`, `sort?`, `order?`, `perPage?` | `{query, total, items[{number,title,state,kind,author,url,repo,comments,createdAt}], rateLimit}` or structured error |
114
+ ## Tools & surfaces
171
115
 
172
- `execute` returns only the canonical JSON declared by `output.schema`. Missing-token and GitHub-API failures are structured error variants carrying rate-limit facts; infrastructure failures throw (→ `isError`). `exec.signal` is honored everywhere.
173
-
174
- ## ⌨️ Commands
175
-
176
- | Command | Effect |
177
- |---|---|
178
- | `/pr create [title]` | Reads git state and queues a `pr_create` instruction for the model (draft body, defaults, no commit/push unless `autoCommit`). Creating the PR asks for approval. |
179
- | `/review <pr>` | Starts a background review job; prints the job id. Completion is announced by the host; read it with `job_output`. |
180
- | `/review <pr> --max-diff <n> --no-ci --no-comments` | Per-job overrides: diff cap and which supplementary sections the job fetches. |
181
- | `/review stop <jobId>` | Cancels the job (local control, no GitHub write). |
182
- | `/review post <jobId>` | Queues a `review_post` instruction for the model (summary or inline); posting asks for approval. |
183
- | `/issue open <title>` | Queues an `issue_open` instruction for the model; creating asks for approval. |
116
+ | Surface | Kind | Notes |
117
+ |---|---|---|
118
+ | `pr_create` | tool | Create a pull request (write; approval-gated) |
119
+ | `pr_merge` | tool | Merge a PR (merge/squash/rebase, optional head-branch delete) |
120
+ | `pr_update` | tool | Update a PR (title/body/state/base) |
121
+ | `gh_review` | tool | Read a PR: metadata, capped diff, comments, CI, static findings |
122
+ | `review_post` | tool | Publish a review comment (summary or line-anchored inline) |
123
+ | `gh_issue` | tool | List / get / comment on issues (PRs marked `kind: "pr"`) |
124
+ | `issue_open` | tool | Create an issue |
125
+ | `issue_comment` | tool | Comment on an issue or PR |
126
+ | `issue_close` | tool | Close an issue (optional state reason) |
127
+ | `gh_search` | tool | Search issues and PRs (separate search quota) |
128
+ | `gh_repo` | tool | Read repository metadata |
129
+ | `gh_file` | tool | Read one file at a branch/tag/commit |
130
+ | `/pr create` | command | Read git state and queue a `pr_create` instruction |
131
+ | `/review` | command | Start / stop / post a background review job |
132
+ | `/issue open` | command | Queue an `issue_open` instruction |
133
+ | `ci_run` | tool | One-shot CI review run by the composite action / CI driver |
134
+ | review bot | surface | Polling review bot with idempotent inline comments (`ci.*`) |
135
+ | status-check gate | surface | Publishes the `success` / `needs-changes` verdict per PR head commit (`action.yml`) |
136
+
137
+ ## Architecture
184
138
 
185
- ## 🏗 Architecture
139
+ - **Credential seam.** `tokenSource: auto` resolves per operation in the order credentials seam (`GITHUB_TOKEN` reference) → environment variable → `gh` CLI token. The value is a local variable handed to the REST client; it never enters canonical values, renders, cards, command outputs, injected notices, job output, approval reasons, or error messages.
140
+ - **Approval gate.** All writes flow through model tools. A `tools/pre-execute` waterfall listener returns `ask` for the write tools, so the registry asks the human through `ctx.approval` (the host logs the `approval/asked` + `approval/decided` audit pair) and fails closed without an answerer. Commands never write directly: a write command gathers read-only context, then wakes the agent so the model runs the gated tool inside a turn.
141
+ - **Background review job.** `/review <pr>` starts a `github-review` job on `ctx.jobs`; the job fetches metadata (capturing the head-commit SHA for inline posting), the capped diff, CI checks, and existing comments, then runs the deterministic multi-file analyzer (`src/review.ts`). With `reviewMode: "model"`, the job hands the capped diff to a one-shot subagent through the host's `subagents` seam. Completion reaches the session through the host's `dsh-tool-jobs` consumer; the model reads it with `job_output` and publishes it with `review_post`.
142
+ - **CI composite action / review bot / status-check gate.** The repo ships a composite action (`action.yml`) that reviews PRs, fixes CI, and writes the report; a polling review bot posts idempotent inline comments; and a status-check gate publishes the verdict per PR head commit. The one-shot `ci_run` tool drives the headless run. Every write stays approval-gated.
186
143
 
187
- ```
188
- ┌───────────────────────────────────────────────┐
189
- │ dsh-github │
190
- │ │
191
- humans ─── /pr ────┼──► git reader (read-only) ──► agent.followup │
192
- /review ───┼──► ctx.jobs.start("github-review") ──► job │
193
- /issue ────┼──► agent.followup │
194
- │ │
195
- model ─── pr_create / pr_merge / pr_update / gh_review / │
196
- review_post / gh_issue / issue_open / issue_comment / │
197
- issue_close / gh_search / gh_repo / gh_file │
198
- (defineTool, canonical JSON only) │
199
- │ │
200
- └───────┬───────────────┬───────────────┬───────┘
201
- │ │ │
202
- tools/pre-execute credential GitHub REST
203
- approval gate resolution client (fetch,
204
- (ask | deny) (seam → env → 429 retry,
205
- gh CLI, per-op) rate-limit)
206
- ```
144
+ ## Permissions & data
207
145
 
208
- - **Credential seam.** `tokenSource: auto` resolves per operation in the order credentials seam (`GITHUB_TOKEN` reference) → environment variable → `gh` CLI token. The value is a local variable handed to the REST client; it never enters canonical values, renders, cards, command outputs, injected notices, job output, approval reasons, or error messages.
209
- - **Approval.** All writes flow through model tools. A `tools/pre-execute` waterfall listener returns `ask` for the seven write tools, so the registry asks the human through `ctx.approval` (the host logs the `approval/asked` + `approval/decided` audit pair) and fails closed without an answerer. Approval reasons preview what would be published (titles, body sizes, merge methods, and the first line of an overridden review body). Commands never write directly: command handlers run with no open turn, so the approval seam is structurally closed to them — a write command gathers read-only context, then wakes the agent (`followup` when idle, `inject` when busy) so the model runs the gated tool inside a turn.
210
- - **Background review.** `/review <pr>` starts a `github-review` job on `ctx.jobs` (label, owner, timeout, cancelable). The job resolves the token per operation, fetches the PR metadata (capturing the head-commit SHA for inline posting), the capped diff, and — unless disabled — CI check runs and existing review comments, then runs a deterministic multi-file analyzer (`src/review.ts`: hardcoded secrets, Google API keys, credential assignments, debug artifacts, eval, TODO markers, long lines, oversized changes) — zero tokens spent, fully testable. With `reviewMode: "model"`, the job instead hands the capped diff to a one-shot subagent through the host's `subagents` seam (the owning agent is the parent) and stores the child's Markdown output as the postable report; a missing seam or provider fails loud. Supplementary fetch failures are noted in the output without failing the job. Completion notices reach the initiating session through the host's `dsh-tool-jobs` consumer; the model reads the report via the existing `job_output` tool and publishes it with `review_post` — approval required.
211
- - **Model-visible ⇔ logged.** The plugin appends **no custom session event types**. Out-of-repo event types are not in the host's `KNOWN_SESSION_EVENT_TYPES`, so an unknown required event would make the session log unreadable after plugin removal (the host deliberately defers a registration surface for external plugins). All model-visible content therefore flows through host-logged surfaces: `tool/result` canonical values, `user/message` notices via `agent.inject`/`agent.followup`, the `command/run` + `command/done` lifecycle pair, and the `approval/asked` + `approval/decided` audit pair.
212
- - **Pure presenters.** `presentCall`/`presentResult` are pure functions of `args` (+ the persisted `result.meta`), identical on live streaming and log replay. PR creation shows a generic card with the PR URL.
146
+ - **Permissions**: writes ride the official approval seam; nothing is re-implemented or bypassed. The plugin declares `network:outbound` and `filesystem:write` in its workshop manifest.
147
+ - **Data**: the review report lives in process memory keyed by job id; nothing durable is written to disk.
148
+ - **Session log**: the plugin adds no custom session event types; all model-visible content flows through host-logged surfaces (`tool/result`, `user/message`, `command/run`, `approval/asked`…).
213
149
 
214
- ## 🔒 Security boundaries
150
+ ## Security boundaries
215
151
 
216
- - The token is read per operation from the configured source (credentials seam, environment, or `gh` CLI) and sent only in the REST client's Authorization header. It is never logged, never rendered, never injected, never appended to the session log, and never appears in error messages.
217
- - Every GitHub write requires `allowed-once` from `ctx.approval` (default policy `ask`); `rejected`, `cancelled`, and `unavailable` all fail closed.
218
- - `/pr create` never commits or pushes by itself; with `autoCommit: true` the model performs those writes through the bash tool's own approval gate. dsh-github does **not** manage git identity (dsh-git-identity's job) or worktrees (dsh-worktree's job).
219
- - The review job performs no writes: it reads a diff and stores a report in process memory; only `review_post` publishes, after approval.
220
- - Posted comments interpolate diff-derived file names, which are untrusted repository content: `formatPostBody` backtick-escapes and HTML-escapes file names so a hostile PR cannot inject Markdown into the review comment.
221
- - File contents read by `gh_file` and issue/PR bodies, comments, and search results read from GitHub are external untrusted content that enters model context — the same inherent tradeoff as web fetching; the plugin marks them as external content in its renders.
222
- - Rate limits: 429s are retried with backoff and the remaining quota is surfaced to the model on every result, including failures.
152
+ - **Approval, not enforcement.** Writes only produce `ask`/deny decisions on the official seam; the sandbox and approval systems remain the enforcement authorities.
153
+ - **Fail closed.** Missing approval answerer degrades to the strictest decision — never to silent pass-through.
154
+ - **The token never leaves the process.** It is read per operation and sent only in the Authorization header; never logged, rendered, injected, or surfaced in errors.
155
+ - **No writes outside approval.** `/pr create` never commits or pushes by itself; with `autoCommit: true`, the model performs those writes through the bash tool's own approval gate. The review job performs no writes; only `review_post` publishes, after approval.
156
+ - **Untrusted content is escaped and marked.** `formatPostBody` backtick- and HTML-escapes diff-derived file names, and external GitHub content (files, bodies, comments, search results) is marked as external in renders.
157
+ - **Bounded work and rate limits.** 429s are retried with backoff; the remaining quota is surfaced on every result, including failures.
223
158
 
224
- ## ⚠️ Known limitations
159
+ ## Known limitations
225
160
 
226
161
  - **No custom session events** — deliberate (see Architecture); audit trails rely on the host's own event vocabulary.
227
- - **Static analyzer by default** — deterministic rules (`src/review.ts`), zero tokens, reproducible. `reviewMode: "model"` delegates the capped diff to a one-shot subagent through the host's `subagents` seam for an LLM review (costs tokens; requires the seam and a registered provider).
228
- - **Jobs and records are process-local** — the review report lives in plugin memory keyed by job id, matching the host job registry's lifetime; the record map is capped by `maxReviewRecords` (oldest settled records evict first).
229
- - **npm `latest` dist-tags are stale** — the plugin declares `^0.1.0-rc.5` peer ranges so it resolves against the profile closure that `dsh-base` provides, and pins `0.1.0-rc.6` for development. Never install by bare `npm i @deepseek-ai/dsh-tools`.
230
- - **CI / GitHub Action** — ships in this repository (v0.6.0): a composite action (`action.yml`) that reviews PRs, fixes CI, and writes the report; a polling review bot with idempotent inline comments; and a status-check gate. Every write stays approval-gated.
162
+ - **Static analyzer by default** — deterministic rules (`src/review.ts`), zero tokens, reproducible. `reviewMode: "model"` costs tokens and requires the `subagents` seam and a registered provider.
163
+ - **Jobs and records are process-local** — the review report lives in plugin memory keyed by job id; the record map is capped by `maxReviewRecords` (oldest settled records evict first).
164
+ - **npm `latest` dist-tags are stale** — install through the profile closure `dsh-base` provides; never bare `npm i @deepseek-ai/dsh-tools`.
231
165
 
232
- ## 🧪 Development
166
+ ## Development
233
167
 
234
168
  ```sh
235
- pnpm install
236
- pnpm test # vitest: config, credentials, 429/retry, tools, commands, jobs, approval gate, token non-leakage
237
- pnpm typecheck
238
- pnpm build # tsc → lib/ (noEmitOnError)
239
- pnpm pack # installable tarball
169
+ pnpm install # node ^22.19 || >=24
170
+ pnpm run build # tsc --noEmitOnError → lib/
171
+ pnpm run prepare # self-contained git-install build (scripts/prepare.mjs)
172
+ pnpm run prepublishOnly # build + test before publishing
173
+ pnpm test # vitest run
174
+ pnpm run typecheck # tsc --noEmit
240
175
  pnpm run check:readmes # cross-checks TOC anchors, tools, and config keys in all 5 READMEs
241
176
  ```
242
177
 
243
- Tests mock the GitHub API, the `gh` CLI, and git through injected runners — no network, no real credentials. `test/security.test.ts` asserts the token string never appears in any model- or human-visible output. `test/e2e.test.ts` contains opt-in real-API smoke tests that self-skip unless `DSH_GITHUB_E2E_TOKEN` is set (read-only endpoints only; the dedicated variable keeps the unit suite hermetic).
244
-
245
- ## 🗂 Repository layout
178
+ ## Repository layout
246
179
 
247
180
  ```
248
181
  src/index.ts plugin entry (name/inject/apply, applyWithDeps for tests)
@@ -262,15 +195,13 @@ cordis.patch.yml bundle patch (one insert row)
262
195
  scripts/prepare.mjs self-contained git-install build
263
196
  ```
264
197
 
265
- ## 🏷 Topics
266
-
267
- Recommended GitHub repository topics (set them in the repo settings — they power the [`dsh-plugin` topic page](https://github.com/topics/dsh-plugin) and the DSH plugin marketplaces):
198
+ ## Topics
268
199
 
269
200
  `dsh` · `dsh-plugin` · `deepseek-harness` · `github` · `pull-request` · `code-review` · `issue-tracker`
270
201
 
271
- ## License
202
+ ## Contributors
272
203
 
273
- [Apache License 2.0](LICENSE)
204
+ - [@PerryLink](https://github.com/PerryLink) — creator and maintainer: the GitHub tool surface, approval gate, background review jobs, CI composite action, review bot, status-check gate, and the five-language docs.
274
205
 
275
206
  ## PerryLink DSH Plugin Family
276
207
 
@@ -293,3 +224,7 @@ This project is one of the [15 DeepSeek Harness plugins](https://github.com/Perr
293
224
  | **[dsh-github](https://github.com/PerryLink/dsh-github)** | GitHub PR/issues integration for DSH, every write gated by approval |
294
225
  | [dsh-plugin-guide](https://github.com/PerryLink/dsh-plugin-guide) | Plugin-development knowledge base as an on-demand agent skill |
295
226
  | [dsh-claude-move](https://github.com/PerryLink/dsh-claude-move) | Migrate Claude Code sessions, memory, skills and CLAUDE.md into DSH |
227
+
228
+ ## License
229
+
230
+ [Apache License 2.0](LICENSE) © 2026 dsh-github contributors