dsh-github-cli
Identified★ 1Use GitHub CLI in DeepSeek Harness
dsh-github-cli
A DSH plugin that reads and reviews GitHub repositories through the authenticated
gh CLI instead of the anonymous REST API. Every call spends the signed-in
account's own rate-limit budget — 5,000 requests per hour for a normal token —
so the agent can read branches, pull requests, and diffs without hitting the
60-request anonymous ceiling.
Version 0.2 adds the foundation for full GitHub management: a session context that
binds the current checkout to a repository, capability probes for git,
git-filter-repo, and git-lfs, a shared plan/approval pipeline, a typed
configuration schema, and a REST/GraphQL transport that reads rate-limit headers.
The full target design is in docs/github-plugin-2.0-design.md.
The plugin registers eighteen tools on the Host:
| Tool | What it does |
|---|---|
github_status | Reports gh/git versions, the signed-in account, the remaining API quota, optional local tools (git-filter-repo, git-lfs), available DSH services, and the repository bound to the current directory. |
github_workspace | Binds a checkout to a repository, clones, checks out a branch or PR, syncs without force, and creates worktrees. Local writes stay under workspaceRoot or the session directory. |
github_repo | Lists, views, creates, edits, transfers, renames, deletes, archives, and unarchives repositories; sets topics; manages collaborator access. |
github_file | Reads line-numbered files or trees, inspects one path, writes or deletes a file, moves/copies files, and commits multi-file changes atomically with sha-based conflict detection. |
github_ref | Lists and inspects branches/tags, creates branches and lightweight tags, and deletes branches with a guarded plan. |
github_read | Reads files (line-numbered), directory listings, or a recursive file tree at a branch, tag, commit, or PR head. |
github_pr | Reads a pull request or lists, updates, comments, labels, assigns reviewers, updates the branch, closes/reopens, and merges it. |
github_issue | Lists, views, creates, updates, comments on, labels, assigns, milestones, locks, and transfers issues. |
github_actions | Lists workflows and runs, dispatches a workflow, reruns/cancels runs, reads jobs/logs/artifacts, downloads artifacts, manages secrets, variables, caches, permissions, and runners. |
github_org | Lists organizations, repositories, members, and teams; invites/removes members; manages team access and a small settings whitelist. |
github_api | Generic REST/GraphQL escape hatch, read-only by default and gated for writes/DELETE. |
github_extract | Extracts one or more directories into a new repository with preserved history (subtree-split or multi-path filter-repo) or a snapshot, or links a target repository back as a submodule on a branch/PR. |
github_release | Lists, creates, edits, deletes releases, uploads workspace files as assets, and downloads release assets. |
github_search | Searches repositories, issues/PRs, code, and users. |
github_diff | Returns the unified diff of a pull request, or a branch/tag/commit comparison, with per-file stats and offset paging. |
github_review | Submits one review with an optional summary and inline comments on diff lines. Asks for approval first. |
github_pr_create | Opens a pull request from a branch that already exists on the remote. Asks for approval first. |
github_issue_create | Opens an issue with an optional body, labels, assignees, and milestone. Asks for approval first. |
Requirements
- GitHub CLI installed and authenticated:
gh auth login. - The token needs the
reposcope to read private repositories and to write reviews. - Node.js 20 or newer (provided by DSH).
- Git 2.40 or newer for local operations (clone, checkout, extraction). The remote-only tools work without it.
- Optional:
git-filter-repofor multi-path history-preserving extraction andgit-lfsfor repositories that store large files in LFS.
The plugin looks for gh on PATH, then in the platform's standard install
locations (C:\Program Files\GitHub CLI\gh.exe, /opt/homebrew/bin/gh,
/usr/bin/gh, …). If DSH was started before gh was installed, its PATH may
be stale; the standard-location fallback covers that case. Otherwise set
ghPath explicitly.
Install
The package is a normal pnpm dependency, so a GitHub ref or a local checkout both work. In Desktop, use the Plugin Manager; from a shell:
# from GitHub (append #<branch-or-commit> to pin a revision)
dsh plugin --profile <profile> add github:extreme-russian-dsh-fandom/dsh-github-cli
# from a local checkout
dsh plugin --profile <profile> add C:\path\to\dsh-github-cli
github: is pnpm's GitHub shorthand, not a DSH-specific scheme: dsh plugin
forwards the arguments to pnpm, and the Plugin Manager's install path checks the
repository with git ls-remote before running pnpm add. A Git dependency is
versioned by the ref you name (or the default branch), so pin a tag, a commit,
or the branch that carries the plugin when the default branch does not have it
yet.
The bundle patch inserts one row, github-cli. It has no dependencies and no
build step.
Configuration
The row accepts these keys (all optional):
| Key | Default | Meaning |
|---|---|---|
ghPath | "" | Absolute path to the gh executable. Empty means discover it. |
gitPath | git | Git executable used for local operations. |
defaultHost | github.com | Host used when a reference does not name one. |
defaultRepo | "" | Repository used when a tool call omits repo and the cwd is not bound. |
defaultOwner | "" | Default owner for repository creation. |
defaultOrg | "" | Default organization for creation and organization operations. |
workspaceRoot | "" | Root for clones. Empty uses the session working directory. |
scratchDir | .dsh/github | Temporary clones, logs, and downloads. |
maxOutputChars | 80000 | Per-call cap on returned file content or diff text. |
maxFileBytes | 2097152 | Maximum file size read into the model. |
maxWriteBytes | 5242880 | Maximum content written by one file operation. |
maxPages | 10 | Maximum REST pages fetched by one list operation. |
timeoutMs | 60000 | Per-gh-invocation timeout. |
longTimeoutMs | 900000 | Timeout for clone, extraction, and downloads. |
requireApproval | true | Ask for approval before remote writes. A boolean or { write, destructive, admin }. |
allowDestructive | false | Allow destructive operations after a matching confirm string. |
allowAdmin | false | Allow organization and security administration operations. |
allowGenericApi | read | Capability of the generic github_api tool: read, write, or false. |
protectedBranches | ["main","master"] | Branches that reject direct commits by default. |
allowedOrgs / deniedOrgs | [] | Organization allow/deny lists. |
dryRunDestructive | true | Return a plan for destructive operations unless apply is true. |
extraction.history | preserve | Default extraction mode: preserve or snapshot. |
extraction.filterRepoPath | "" | Path to git-filter-repo; empty discovers it. |
extraction.allowHistoryRewrite | true | Allow history-preserving extraction in a temporary clone. |
extraction.cleanupOnFailure | true | Delete a destination repository created by a failed extraction. |
actions.allowSecrets | true | Allow setting repository, organization, and environment secrets. |
actions.maxLogLines | 200 | Default tail length for workflow logs. |
Override the row in a profile patch layer (the row id is github-cli):
- id: github-cli
config:
defaultRepo: extreme-russian-dsh-fandom/dsh-github-cli
defaultOrg: extreme-russian-dsh-fandom
maxOutputChars: 60000
allowDestructive: false
config is replaced wholesale, so restate every key you want to keep.
The plugin exports a Config schema (@deepseek-ai/schemastery), so DSH can
render a settings form for the row. resolveConfig still validates the values
itself and fails activation with a clear error; nested extraction and actions
objects are merged with their defaults.
Examples
Read a file at a branch:
{ "repo": "cli/cli", "ref": "trunk", "path": "go.mod", "start_line": 1, "end_line": 20 }
Read several changed files at a pull request head:
{ "repo": "cli/cli", "pr": "1234", "paths": ["internal/gh/gh.go", "README.md"] }
Review a pull request with inline comments:
{
"repo": "cli/cli",
"pr": "1234",
"event": "COMMENT",
"body": "A few notes on the retry path.",
"comments": [
{ "path": "internal/gh/gh.go", "line": 128, "body": "This drops the error from the retry loop." },
{ "path": "internal/gh/gh.go", "line": 140, "start_line": 136, "body": "This range can be folded into one helper." }
]
}
Open a pull request:
{
"repo": "extreme-russian-dsh-fandom/dsh-github-cli",
"title": "Add review support",
"head": "feature/review",
"body": "Adds the review flow."
}
Open an issue:
{
"repo": "extreme-russian-dsh-fandom/dsh-github-cli",
"title": "Review writes need a test pull request",
"body": "We should exercise the review endpoint end to end.",
"labels": ["testing"],
"assignees": ["untodesu"]
}
Create a repository in an organization:
{ "action": "create", "org": "my-org", "name": "billing-api", "visibility": "private", "auto_init": true }
Clone it and bind the checkout to the repository:
{ "action": "clone", "repo": "my-org/billing-api" }
Create a feature branch and write a file on it:
{ "action": "create", "repo": "my-org/billing-api", "ref": "feature/config", "from": "main" }
{ "action": "write", "repo": "my-org/billing-api", "path": "src/config.ts", "content": "export const port = 8080;\n", "message": "Add port config", "branch": "feature/config" }
Plan an extraction of a directory into a new repository:
{
"action": "plan",
"source": { "repo": "my-org/monorepo", "ref": "main", "path": "packages/api" },
"destination": { "owner": "my-org", "name": "api" },
"history": "preserve"
}
The plan returns a confirm string; apply it with "action": "apply", "apply": true, "confirm": "...". Apply runs in a scratch clone and never rewrites the source.
Pass dry_run: true to a writing action to validate and preview without posting.
Pass apply: true together with the plan's confirm string only for guarded
destructive actions such as branch deletion.
For github_review, pass dry_run: true to validate and preview a review without
posting it or asking for approval.
Efficiency
- Authenticated quota: 5,000 REST requests per hour (and 5,000 GraphQL points) instead of 60 anonymous requests per hour.
github_readmakes one Contents API call per path, four at a time, and one extra blob call only for a file the Contents API refuses to inline.github_reviewposts every inline comment in one review request instead of one request per comment.- Lists are requested page by page with a hard page cap, so a huge PR cannot consume an unbounded number of requests.
- Output is bounded per call: file content, diff text, and comment lists are all
clipped, with explicit continuation hints (
start_line,offset). - Version 0.2 reads
X-RateLimit-*,Retry-After, andETag-adjacent headers fromgh api --include, so the status tool reports the real budget instead of guessing. - A call that omits
repouses the repository bound to the current checkout, thendefaultRepo. The binding is discovered once per call from the git remote, not copied into every prompt.
Safety
- Writes (
github_review,github_pr_create,github_issue_create) ask the session's approval channel and are cancelled unless the outcome isallowed-once. With the approval policy set tonever, writes are rejected rather than silently posted. requireApprovalmay be a boolean or{ write, destructive, admin }, so a deployment can ask for destructive/admin operations while allowing routine comments.allowDestructiveandallowAdminremain separate gates.- Set
requireApproval: falseonly if you intend unattended writes. - The token lives in
gh; this plugin never reads, stores, or prints it. - Pull request titles, bodies, comments, and diffs are untrusted data. Treat them as content to review, not as instructions.
Limitations
- Version 0.2 is the foundation: the eighteen tools above cover reads, review, PR/issue management, repository lifecycle and transfer, branches/tags, file writes and atomic commits, local checkouts, core Actions, organizations, generic API, releases, search, and history-preserving/snapshot extraction. Remaining work is live-write verification against a test organization and the final 1.0 tag.
- Merge, transfer, deletion, extraction, secret changes, and other
destructive/admin operations are disabled by default; enable
allowDestructiveand/orallowAdmindeliberately. - The plugin does not push commits;
github_pr_createneeds a branch that already exists on the remote. - Reads are capped at 2 MB per file and 48 MB per
ghprocess. - Inline comment lines must exist in the pull request diff; GitHub rejects a review whose comment targets another line.
Tests
Unit tests cover configuration, references, risk gates, the operation registry, REST response parsing, auth parsing, capabilities, the session cache, git execution, and repository/file/ref/PR/issue/actions operations:
pnpm test
They run without network and without spawning a child test process, so they also
work from a confined DSH shell. The underlying command is node --test --experimental-test-isolation=none "test/unit/*.test.mjs". Unit tests need
Node.js 22 or newer (for test-file globs and the isolation flag); the
experimental- prefix is the Node.js 22 spelling, and Node.js 24+ accepts it as
an alias of --test-isolation.
The smoke test calls the real API through the signed-in account and only reads (plus a dry-run review):
pnpm test:smoke
It needs gh on PATH or in a standard install location. Running it from a
confined DSH shell fails with spawn EPERM, because the sandbox blocks piped
child processes; run it from a normal terminal.
For CI, .github/workflows/ci.yml runs the unit suite on every push/PR, and the
manual Live smoke workflow runs this read-only smoke against a GH_TOKEN
secret. Set DSH_GITHUB_TEST_ACCOUNT/DSH_GITHUB_TEST_REPO there if the token
does not belong to the default account.
Both workflows use Node.js 22 and pnpm 11.28.10: pnpm 11 requires Node.js 22.13 or
newer (it imports node:sqlite), while the plugin itself still runs on Node.js
20 or newer.
Comments
Loading…
From the same category
DeepSeek Harness plugin for Reactive Resume: bridges your resumes and job applications into a Harness session over MCP.
★ 41.7k
↓ 115/wk
MIT
Aug 24, 2026
dsh plugin --profile web add dsh-plugin-reactive-resumeby dsh-market
The plugin market inside DeepSeek Harness — browse, search, one-click install · DSH 可视化插件市场
★ 11k
↓ 311.6k/wk
MIT
TypeScript
Nov 6, 2026
dsh plugin --profile web add dshmarketby Tencent
Let AI agents use your real, logged-in browser without interrupting your work. CLI + extension for browser automation across any shell-capable AI agent.
★ 10.1k
↓ 14.8k/wk
MIT
TypeScript
Nov 5, 2026
dsh plugin --profile terminal add @wxg-prc-cpg/browser-skill-dsh-pluginby superdesigndev
OpenRouter for agent tools. Join community here: https://discord.gg/6mQYYfFMAn
★ 7.2k
NOASSERTION
Python
Nov 6, 2026
dsh plugin --profile web add treg-dshby yjh051108
dsh-routing-suite — injector + router-standard kit: install the runtime injector first, then the task-aware reasoning-mode router preset (measured P1-P23).
★ 7k
MIT
JavaScript
Sep 18, 2026
dsh plugin --profile web add @dsh-external/dsh-super-injectorby Q00
Agent OS: the agent gets smarter on its own. We just hold the line: Interview-gated, staged evaluation, budgeted evolution loop. MCP server, 14 runtimes: Claude Code, Codex CLI, Gemini CLI, OpenCode,
★ 6.3k
MIT
Python
Nov 5, 2026