gh_issue(action: "comments")now returns the comment bodies instead of an 80-character headline (issue #26). The execute path already mappedbodyinto the canonical value and the output schema already declared it; onlyrenderdiscarded it. The comments action now appends each body under its one-line header and caps it with the configuredmaxFileChars(with an explicit truncation marker), whilelistandgetkeep the one-line summary. Added a render regression test.
- Full-access sessions no longer fail every write with "the user rejected" (issue #15). The gate returned
askfor every interactive write, but the host's ApprovalService short-circuitsasktorejectedwhen a session's approval policy isnever, before any answerer runs. In a session that is bothdanger-full-accessandnever— the host's full-access preset — the model saw a rejection the human never made and no GitHub write could run. The gate now allows the in-flight write only when the livesandboxPolicy.resolve({ session }).modeisdanger-full-accessandctx.approval.effectivePolicy(session)isnever;allowedActionsis still checked first, every other combination still asks, and a missing service, missing policy reader, or thrown lookup fails closed. Tests cover the matrix and the fail-closed paths. Thanks to @ljyzww for the root-cause analysis and the patch direction.
- The browser bundle registers under the package name, so installing 0.7.24 no longer resets the profile (issue #16).
scripts/bundle-client.mjshardcodedid: "dsh-github", while DSH 0.2.1-rc.2 keys client-module rows by package name. The lookup missed, the loader re-executed the bundle from the single-resource URL, andduplicate factory registrationaborted web boot; Desktop's fatal recovery then backed upcordis.patch.ymland rewrotedsh.profile.bundlesto the two official bundles. The wrapper id now comes frompackage.json.name, andverify:artifactsplustest/client-bundle.test.tsassert it. Thanks to @fishdivinity for the fix (#24) and @N-o-c-t-i-s for the independent patch (#25).
@deepseek-ai/schemasteryis a peer, not a bundled dependency. The package already declared it in devDependencies; keeping a second copy in dependencies made a profile install carry two schemastery instances. The host-provided peer remains the only runtime copy.
- Model review now uses the 0.2.1+
startActivationseam.ctx.subagents.startwas removed from the host; the plugin's local type still declared it, soreviewMode: "model"threw before any review ran. The job now feature-detectsstartActivationand keeps the legacystartpath for older hosts, with a caller-delivery activation test.
- Add a 1280×720 hero and a 960×540, 4-second terminal GIF rendered from the test run;
screenshots.jsonis an object array andcapture.jsonrecords the command.
- Browser half is a
window.__ModuleLoader__.loadfactory, not bare ESM.lib/client.jswas tsc output with top-levelimport/export; the host executes the file as a classic script, so the Plugins page failed to load. Ascripts/bundle-client.mjspost-build step wraps the compiled module with the host loader and externalizesreact/dsh-client-ui-primitivesthroughrequire. - Compatibility:
engines.dsh->>=0.2.1-0 <0.3.0; devDependencies to0.2.1-rc.2; add@deepseek-ai/dsh-working-directory.
All notable changes to this project are documented in this file. The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
-
The README now carries the terminal demo and an animated version of it: the same real run, shown assembling line by line. The static image stays as the primary one, because a crawler or an assistant can read a PNG and cannot read a GIF.
Both frames are that plugin's own output — the card is generated by running its code over its own fixture, not drawn by hand.
- Correct the release date in the previous section, which was stamped with the literal string
undefinedby the release stamper. No content or behaviour change; the version is bumped only because npm will not republish an existing version.
githubToken— a token field the Plugins page can edit. DSH 0.2.1 serves a settings namespace only for a plugin whose Config marks at least one field.volatile();dsh-githubhad none, so the settings service skipped the whole namespace and the row had no editable form. The card now writes this field, andtokenSourcegains aconfigmember thatautotries before the credentials file, the environment, and the gh CLI. The value sits in the profile configuration as plain text; the credentials file andgh auth loginremain the alternatives.
- The shipped browser bundle registers under the package name.
scripts/bundle-client.mjshardcodedid: "dsh-github"; DSH 0.2.1 keys client rows by package name (@perrylink/dsh-github), so the loader fell back to the single-resource URL and re-executed the bundle, aborting web boot withduplicate factory registration. The wrapper now readspackage.json.name, andverify:artifacts+test/client-bundle.test.tsassert the registration id. - The GitHub card mounts again on DSH 0.2.1. The browser half required the browser-side credentials remote, which 0.2.1 removed (
@deepseek-ai/dsh-credentialsships no./remote): the client entry stayed pending — a fatal web boot in 0.2.x — and the defensive variant returned beforectx.slots.register, so no card appeared in the Plugins panel. It now reads the page-suppliedform({ state, mutate }), registers onplugins.itemunconditionally, renders throughSettingsForm/SettingsSecretFieldso themes and installed skins restyle it, and adds a short guide to both GitHub token pages.
undefined
Adds the versioned five-language interoperability declaration to the READMEs: the plugin records, against DSH 0.2.0-rc.2, every injection point it owns and why none of them collide with another plugin — no tool-name, service-key, slot, HTTP-route, patch-layer or global-mutation overlap, and, for each ordering-sensitive event it listens on, that the listener delegates through next() so a shared waterfall chain is never short-circuited. Verified by dsh-plugin-doctor K10-K14 (checkset R0-R8+K1-K14+D0-D3,D9+CC1-CC5/3). Documentation only; no behaviour change.
- Host pins move to
0.2.1-alpha.1; re-verified against that host line. Every@deepseek-ai/dsh-*dev/test dependency now pins0.2.1-alpha.1, thedshWorkshop.compatibility.dshVersionstimeline appends0.2.1-alpha.1, and the compatibility baseline in every README records thedsh-v0.2.1-alpha.1host. The declared host ranges (engines.dshand thepeerDependenciesunion) gain the|| >=0.2.0-0 <0.3.0 || >=0.2.1-0 <0.3.0clauses: the previous upper bound was<0.2.0, which under semver rejects every 0.2.x host, so the probe host itself was not installable. Nothing was narrowed — the0.1.xclauses are unchanged, in place and in order.
- Host pins move to
0.1.7-rc.2; re-verified against that host line. Every@deepseek-ai/dsh-*dev/test dependency now pins0.1.7-rc.2, thedshWorkshop.compatibility.dshVersionstimeline appends0.1.7-rc.2, and the compatibility baseline in every README records thedsh-v0.1.7-rc.2host. The declared host ranges (engines.dshand thepeerDependenciesunion) are deliberately unchanged — they already admit0.1.7-rc.2, and a range is what the manifest accepts, not what has been tested.
- The save-in-progress spinner crashed on every host from
0.1.7-alpha.1on. The browser half importedIconLoadingOutline16from@deepseek-ai/dsh-client-ui-primitives, which no longer exports it: the client-wide visual unification dropped the size-in-the-name form, so the same icon is now exported once per weight asIconLoadingOutlineRegular/IconLoadingOutlineMediumand carries its size as asizeprop. The destructured binding was thereforeundefined, andReact.createElement(undefined, …)throwsElement type is invalidthe moment the save control rendered its spinner. Only the identifier changed — the new component takes the same{ size: 16 }prop — so there is no behavioural difference.src/client.tsand the rebuiltlib/client.jsboth move toIconLoadingOutlineRegular. - The type gate could not have caught the above. Three
pnpm-workspace.yamloverrides still pinned the client line to0.1.1-rc.2:@deepseek-ai/dsh-client-ui-primitives@>=0.1.0-rc.8 <0.2.0,@deepseek-ai/dsh-client-ui-settings@>=0.1.1-rc.2 <0.2.0and@deepseek-ai/dsh-tool-jobs@>=0.1.0-rc.8 <0.2.0. The first of those is the very package the browser half compiles against, sotscresolved the stale0.1.1-rc.2type surface, which still exported the removed name, and the build stayed green on a call that could only throw at run time (lib/client.jsis a build artifact ofsrc/client.ts, and nothing in the test suite renders it). All three rows now follow the0.1.7-rc.1devDep line, and re-running the gate against the corrected graph reproduces the defect directly:src/client.ts(25,11): error TS2724: '"@deepseek-ai/dsh-client-ui-primitives"' has no exported member named 'IconLoadingOutline16'.
- Move the
@deepseek-ai/dsh-*dev/test pins to the published0.1.7-rc.1line and record0.1.7-rc.1indshWorkshop.compatibility.dshVersions; the monthly Compat workflow now installs the0.1.7-rc.1host (@deepseek-ai/dsh,dsh-base+dsh-headless) instead of0.1.7-alpha.2. The four self-referentialpnpm-workspace.yamloverride values follow the pins, as in previous line moves. - The
peerDependenciesranges are unchanged, so no supported host line is dropped. The existing four-clause band already ends in|| >=0.1.7-0 <0.2.0, which admits0.1.7-rc.1;engines.dshis likewise untouched.
- Move the
@deepseek-ai/dsh-*dev/test pins to the published0.1.7-alpha.2line and record0.1.7-alpha.2indshWorkshop.compatibility.dshVersions; the monthly Compat workflow now installs the0.1.7-alpha.2host (dsh-base+dsh-headless) instead of0.1.6-alpha.2. - Append the fourth host clause
|| >=0.1.7-0 <0.2.0toengines.dshand to all thirteen@deepseek-ai/dsh-*peer ranges, and raise the@deepseek-ai/cordispeer and dev/test pin to^4.0.4. Under semver's prerelease rule a range whose only prerelease comparators sit on earlier tuples cannot admit a later alpha, so the three-clause band excluded the very host line this release targets. No previously supported host line is dropped. - Move the
pnpm-workspace.yamloverridesblock with the pins. Its four self-referential rows exist so a transitive peer's prerelease request resolves onto ONE copy of the host type graph instead of a second one; their values now follow the devDep pin to0.1.7-alpha.2. The KEYS stay the ranges a transitive peer spells (@deepseek-ai/dsh-agent@^0.1.7-alpha.1,@deepseek-ai/dsh-llm@^0.1.7-alpha.1,@deepseek-ai/dsh-llm@^0.1.2-alpha.3,@deepseek-ai/dsh-llm@^0.1.2-alpha.4) — they match what asks, while the value is what it resolves to. Moving the devDeps alone would have left those requests resolving to the previous line and split this package'sUserMessage/ContentBlockfrom the host'sAgent, which is the failure the block was written for.
- Adapt to host
dsh-v0.1.7-alpha.1. Five confirmed breakages are fixed; the plugin no longer compiles or runs against the0.1.6-alpha.2faces:ctx.jobscontract rewritten. The review job is now owned by the calling agent's bareSessionId(spec.owner: input.owner.id), because the registry dropped itsAgent | SessionIdunion and fences access onSessionIdalone — the oldas unknown as NonNullable<JobStartSpec['owner']>cast is gone.JobStart/JobSnapshotbecameJobSpec/JobView, andJobOutcome.outputbecameJobOutcome.result(the job's report text now travels as the producer result rather than a stream payload —job_outputstill surfaces it)./review stoppassesinvocation.agent.idtojobs.kill.JobRegistry.startrefuses when no attached controller serves the owner, so@deepseek-ai/dsh-tool-jobsmust be composed: the CI bot keeps its one-time-warn interval fallback for exactly that case.@deepseek-ai/dsh-settings-filedeleted,ctx.settingsScopegone. The plugin no longer registers a settings namespace (ctx.settings.configure()is now a page policy, not a namespace registration) and no longer injectssettings.tokenSource/tokenRefstay ordinary cordis.ymlConfigfields — they never had independent persistence semantics — so behavior is unchanged; the exportedGithubSettingsSchemaandGITHUB_SETTINGS_NAMESPACEremain for importers.@deepseek-ai/dsh-agent-presets→@deepseek-ai/dsh-agent-preset-registry. This plugin never imported the renamed package (only two now-deadpnpm-workspace.yamloverride rows named it), so no source change was needed; those rows were removed and theagentPresetsservice key is untouched.- Message source catch-all removed. The notices commands queue now carry a plugin-owned source kind, declared by the new
src/message-source.ts(declare module '@deepseek-ai/dsh-llm/message' { interface MessageSourceMap { 'dsh-github': { kind: 'dsh-github' } & ContextFormed } }) as{ kind: 'dsh-github', form: 'notice', summary }. The host has nokind: 'plugin', anddsh-session-format-v3-to-v4refuses a durable row carrying one outright. - Browser slot moves.
settings.plugin.itemandctx.settingsScopeare gone. The card registers onplugins.item— now declared by@deepseek-ai/dsh-client-ui-plugin-manager, added todsh.client.inject— and readstokenReffrom theformthe page hands the entry instead of binding a scope of its own. The token control is unchanged: it still reports and writes the credential throughremote.credentials, never the settings document.turnTail/conversation.hero.agentPreset/ transcript view modes were audited and are not used by this plugin (0 hits), as isturn/end.reason(forkedneeds no branch here).
- Consume the host's own
ApprovalServiceinstead of a hand-written mirror:@deepseek-ai/dsh-user-approvalnow augmentsctx.approval, so the plugin's duplicateContext.approvaldeclaration (and itsApprovalRequest/ApprovalServicetypes) is deleted and the CI driver'ssetPolicy(exec.agent, 'never')call is cast-free. - Pin the
@deepseek-ai/dsh-*dev/test dependencies to the published0.1.7-alpha.1alpha line, adding@deepseek-ai/dsh-agentand@deepseek-ai/dsh-user-approval, and record the two new peer entries (dsh-agent,dsh-user-approval) with the same loose range. ThepeerDependenciesranges for the pre-existing host packages are unchanged, so no supported host line is dropped.pnpm-workspace.yamlgains self-referential@deepseek-ai/dsh-agent@^0.1.7-alpha.1and@deepseek-ai/dsh-llm@^0.1.7-alpha.1override rows and loses the two dead@deepseek-ai/dsh-agent-presetsrows: pnpm matches a prerelease range only against its own[major, minor, patch]tuple, so without the self-referential rows a transitive peer installs a second copy of the host type graph andAgent.followup'sUserMessagestops matching this package's. - Explicit deviation from the
0.1.5-rc.3pin: the@deepseek-ai/dsh-*dev/test line is0.1.7-alpha.1.0.1.5-rc.3is the old-contract next line — it still carriesSettingsProvider/installSection/SettingsScope/ctx.settingsScope,kind: 'plugin'message sources, andJobSnapshot/onJobDone— so adapting to it would produce code that cannot compile or run against the0.1.7-alpha.1host this plugin targets.0.1.7-alpha.1is the only published line carrying the contracts this package now consumes. - Raise the
@deepseek-ai/cordisrange from^4.0.2to^4.0.3, and@deepseek-ai/schemastery(dependency + devDependency) from^3.18.2to^3.18.3, plus@deepseek-ai/cordis-plugin-loaderfrom^1.0.3to^1.0.4. The caret already permitted the newer versions, but the lockfile resolved4.0.2/3.18.2/1.0.3, and the0.1.7-alpha.1host packages declare peers ofcordis ^4.0.3/schemastery ^3.18.3/cordis-plugin-loader ^1.0.4. A raised floor is a strictly narrower requirement, so no supported host line is dropped — but it is a floor change and is recorded as one. Verified after install:cordis 4.0.3(which exportsVolatile/VolatileSnapshotfrom@deepseek-ai/cosmokit),schemastery 3.18.3(which providesSchema.volatile()),cosmokit 1.8.4resolved. This package itself declares no.volatile()field. - Move the settings card from the Settings→plugins tab to the Plugins page (Official group): the
0.1.6-alpha.2host deletes thesettings.plugin.itemslot, so the card registered there silently disappears. The card now registers on the keyedplugins.itemslot (id: dsh-github,order: 100,labelas a function,locale) and renders one component in two states — a one-line summary (description + token badge) and the full editable form (view: 'summary' | 'page') — with the builtlib/client.jsrebuilt and committed. - Default the CI composite action model to
deepseek-flash: thedeepseek-v4-flashid the action, its patch script, and the local test script referenced is removed from the harness catalog, which would silently downgrade GitHub Actions users to a text-only model. - Drive the CI review bot's polling through
ctx.jobs.start(kindgithub-ci) instead ofsetInterval, with a one-time-warnsetIntervalfallback when no job controller serves the agent. - Auto-approve CI-driver writes through the official policy seam:
ctx.approval.setPolicy(agent, 'never')pins the calling agent instead of the previous gate-only bypass. - Consume the official
@deepseek-ai/dsh-jobsand@deepseek-ai/dsh-subagent(SubagentRun,SubagentStartRequest) type faces instead of the hand-copied mirrors, and drop the plugin's ownsubagentscontext augmentation (the official package augments it). - Declare
dshWorkshop.manifestVersion: 1and theengines.dshrange>=0.1.2-rc.1 <0.2.0 || >=0.1.5-alpha.1 <0.2.0 || >=0.1.6-0 <0.2.0(the added third clause keeps the0.1.6alpha line accepted).
- Close the type gate over the client half:
src/client.tsis no longer excluded fromtsconfig.json(react dev deps and the DOM lib added), and the newtsconfig.ci.json+typecheck:ciruler covers the published line. Thelib/client.d.ts/lib/client.d.ts.map/lib/client.js.mapbuild artifacts are now committed alongsidelib/client.js. - Cover the two identity seams the
0.1.7-alpha.1migration rewrote: a unit test asserts the review job'sowneris the bare session-id string (not the agent object), and another asserts every queued notice carries{ kind: 'dsh-github', form: 'notice', summary }rather than the retiredkind: 'plugin'.
- Rename the four translated READMEs to
README-<lang>.md. npm selects the package-page readme as the first markdown file matching its{README,README.*}glob (@npmcli/package-json, publish path), and that glob order putsREADME.<lang>.mdahead ofREADME.md— so npm was serving the Simplified-Chinese file for this package too (measured on 15/15 sampled packages of the family). The new names sit outside the glob, so the English source is served again. No content changed apart from the language-switcher link each translation holds to its siblings, and the repo readme gate still passes. Takes effect with the next release; an already-published version cannot gain a corrected readme retroactively. - Pin the
@deepseek-ai/dsh-*dev/test dependencies to the published0.1.5-rc.2line and record0.1.5-rc.2indshWorkshop.compatibility.dshVersions; the monthly Compat workflow now runs against0.1.5-rc.2. The peer range>=0.1.2-rc.1 <0.2.0 || >=0.1.5-alpha.1 <0.2.0is unchanged, so no supported host line is dropped.
- The monthly Endpoint liveness workflow never probed anything:
actions/setup-node@v5auto-enables package-manager caching frompackage.json#packageManager(pnpm here), so the step failed withUnable to locate executable file: pnpmand the probe step was skipped on every scheduled run (observed on the 2026-09-01 run). The job only runsnode, so the automatic cache is now disabled withpackage-manager-cache: falseinstead of installing a package manager it does not use.
- Pin the
@deepseek-ai/dsh-*dev/test dependencies to the published0.1.5-rc.1line and record0.1.5-rc.1indshWorkshop.compatibility.dshVersions; the monthly Compat workflow now runs against0.1.5-rc.1. The peer range>=0.1.2-rc.1 <0.2.0 || >=0.1.5-alpha.1 <0.2.0is unchanged, so no supported host line is dropped.
- Refresh the five-language README compatibility baseline to
dsh-v0.1.5-rc.1(verified 2026-09-10).
- Align the
@deepseek-ai/dsh-*peer ranges to>=0.1.2-rc.1 <0.2.0 || >=0.1.5-alpha.1 <0.2.0and pin the dev/test dependencies to the published0.1.5-alpha.1line: adaptation to DeepSeek Harnessdsh-v0.1.5-alpha.1(session format V3,ctx.agentremoval,Inboxtype-only interface); runtime behavior is unchanged for every supported host line. - Record
0.1.5-alpha.1indshWorkshop.compatibility.dshVersions.
- Refresh the five-language README compatibility baseline to
dsh-v0.1.5-alpha.1(verified 2026-09-09).
- Fix the DSH plugin badge URL: shields.io rejects the four-segment static badge form with "404 badge not found"; the label now uses the documented double-dash form (
dsh--plugin), rendering identically; no behavior change.
- Align the
@deepseek-ai/dsh-*peer ranges to>=0.1.2-rc.1 <0.2.0: the older>=0.1.0-rc.8 <0.2.0band resolved to only the0.1.0-rc.8prerelease under registry-driven resolution and broke fresh tarball installs; no behavior change.
- Refresh the five-language README support-version wording: the verified GitHub tag
dsh-v0.1.3-alpha.1now leads the compatibility claim, while npm0.1.2-rc.1stays the published dependency-pin line (peers>=0.1.2-rc.1 <0.2.0); no behavior change.
- Align the devDependency pins and the workspace override table to the published dsh
0.1.2-rc.1line, move the compat CI harness probes from0.1.2-alpha.5to0.1.2-rc.1, and re-verify the adaptation claims; no behavior change.
- Align the devDependency pins to the published dsh 0.1.2-alpha.5 line and re-verify the adaptation claims; no behavior change.
- Host half: drop the removed
settingsNamespaceimport from@deepseek-ai/dsh-settings(absent from the published0.1.2-alpha.2line onward) and register the settings namespace with the'dsh-github'literal, which the settings service brands internally. - Browser half: route the credential card off the retired
connection.apiface (absent on the alpha client) onto theremote.credentialsRemote namespace (describe/set);injectnow namesremote.credentials, and the rebuiltlib/client.jscarries the new wire calls. Thecredentials/reference-updatedlistener stays onremote.$on.
- Dev pins bumped to
0.1.2-alpha.3(@deepseek-ai/dsh-util-values,dsh-attachment,dsh-credentials,dsh-llm,dsh-session,dsh-settings,dsh-tools),@deepseek-ai/schemasteryto^3.18.2, and the@deepseek-ai/cordispeer to^4.0.2; the peer ranges stay>=0.1.0-rc.8 <0.2.0(and their rc.2/UI companions) — no tightening. - Compat declared for
0.1.2-alpha.3: five-language READMEs,dshWorkshop.compatibility, and the compat workflow (CLI/base/headless).
- Migrate the browser half off the removed
dsh-client-runtime: the settings scope and the snapshot store are local structural contracts (the store's previous homedsh-client-storeis not on the published0.1.1-rc.2line), the client context is the plain cordisContext, and thedsh.client.injectlist now namesdsh-client-ui-settings— the package that provides thesettingsScopeservice on both host lines. The committedlib/client.jsbundle no longer requires@deepseek-ai/dsh-client-runtime/client.
- GraphQL client additions for search, batch, and PR checks over a unified REST+GraphQL transport.
- Declared
packageManager: [email protected]so Corepack and CI resolve a reproducible pnpm version.
- DSH dev dependencies pinned to
0.1.1-rc.2(@deepseek-ai/dsh-credentials,@deepseek-ai/dsh-llm,@deepseek-ai/dsh-session,@deepseek-ai/dsh-settings,@deepseek-ai/dsh-tools); the peer ranges stay>=0.1.0-rc.8 <0.2.0because no rc.2-only API is required. - Compat declaration updated to DeepSeek Harness
0.1.1-rc.2(READMEs,dshWorkshop.compatibility, and the compat workflow).
test/ci.test.ts"no token" case now injects arunGhfake so it stays hermetic against a locally-authenticatedghCLI.
- DSH dev/peer dependencies pinned to
0.1.0-rc.8(@deepseek-ai/dsh-credentials,@deepseek-ai/dsh-llm,@deepseek-ai/dsh-session,@deepseek-ai/dsh-tools); the peer ranges are widened to>=0.1.0-rc.8 <0.2.0. - Compat declaration updated to DeepSeek Harness
0.1.0-rc.8(READMEs,dshWorkshop.compatibility, and the compat workflow).
- The bundle patch row now names the package exactly as published (
@perrylink/dsh-github). The previous bare name made the row unresolvable in a clean profile, so the plugin failed to load withERR_MODULE_NOT_FOUND.
- Local-machine protection for the action scripts:
action-patch.mjsandaction-post.mjsrefuse to run outside a GitHub Actions runner (noRUNNER_TEMP/GITHUB_WORKSPACE), and the newscripts/local-test.mjssimulates the composite action in a fully isolated sandbox —DSH_HOME,DSH_PROFILE_DIR, and the output directory are hardcoded under the system temp directory, overriding any inherited (machine-scope)DSH_HOME. - Regression coverage for the CI-context guards in
test/action-guard.test.ts, plus local-testing notes in all five READMEs.
- CI integration surface: a composite GitHub 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 published per PR head commit; and the one-shotci_runtool plus the/cicommand family. ciconfig section: master switch, review engine (static/model), CI-driver auto-approve list, status-check name and blocking behavior, fail-on severity, poll interval, label/path filters, sensitive-path and test-existence rules, change-size caps, concurrency cap, comment posting, and report directory.ci.runwrite action joins theallowedActionsdefault list.requestTimeoutMsconfig: hard per-request timeout that aborts the fetch when exceeded.
- The README "planned v2 companion repository" note is replaced: the CI / GitHub Action surface now ships in this repository.
pr_mergetool: merge a pull request withmerge/squash/rebase, optional commit title/message, and optional head-branch deletion after the merge (approval-gated, actionpr.merge). The tool passes the head-commit SHA for consistency and reports branch-deletion failures as a note without failing the merge.pr_updatetool: edit a pull request's title, body, state (open/closed), or target branch (approval-gated, actionpr.update; at least one field required).gh_repotool: read a repository's metadata (description, default branch, visibility, stars, forks, open issues, language, license, topics, last update). Concurrency-safe.gh_filetool: read one file from a repository at a branch, tag, or commit (base64-decoded, capped by the newmaxFileCharsconfig, per-callmaxCharsoverride); directories are reported as a structuredis-directoryerror. Concurrency-safe.- Analyzer tunables moved into configuration:
maxFindings(default 50) andmaxLineLength(default 300) replace the hardcoded constants insrc/review.ts. - Secondary-rate-limit resilience: the REST client now retries 403 responses carrying a
Retry-Afterheader (GitHub's secondary-limit / abuse-detection signal) under the samemaxRetries/ backoff / signal rules as 429s; 403s withoutRetry-Afterstill fail fast as permission denials. - 204 No Content responses are handled by the JSON client (needed by the branch-deletion call).
test/present.test.ts: pure-function coverage for the new UI cards plus thereview_postbody-size indicator.- The
check:readmesgate now also asserts that every README mentions every tool and config key from the source, that no README pins a versioned tarball name, and that CHANGELOG.md carries a section for the current version. - Real-API e2e smoke for the contents endpoint (base64 decode path).
/pr createrefuses detached-HEAD checkouts with a structuredno-headerror instead of sending the literal branch nameHEADto the API./review postand thereview_postapproval reason label model-review jobs "model review" instead of "0 finding(s)".review_postinline mode omits thecommentskey entirely for body-only reviews (no line-anchored findings) instead of posting an empty array.- The
review_postpending card records the body override as a character count (bodyChars) rather than dropping it or dumping the text. allowedActionsdefaults now includepr.mergeandpr.update.- README install channels reference
dsh-github-<version>.tgzinstead of a stale pinned version.
- The unit suite is now hermetic against the environment: a developer's or CI's
GITHUB_TOKENno longer leaks into "no token" tests (a setup file removes it before unit tests run). The opt-in real-API smoke tests now gate on the dedicatedDSH_GITHUB_E2E_TOKENvariable instead.
-
Published to npm as
@perrylink/dsh-github(the unscopeddsh-githubname is owned by an unrelated project on the registry; the plugin's module name staysdsh-github). -
issue_commenttool: comment on an issue or pull request (approval-gated, actionissue.comment). -
issue_closetool: close an issue with an optionalstate_reason(completed/not_planned; approval-gated, actionissue.close). -
gh_searchtool: search issues and pull requests across repositories with the separate search quota surfaced. -
review_postmode: "inline": posts line-anchored review comments against the PR head commit viaPOST /pulls/{n}/reviews(the background job now captures the head-commit SHA).mode: "summary"remains the default. -
review_postbodyparameter: override the drafted comment before posting; approval reasons preview the override. -
Background review jobs now fetch PR metadata, CI check runs, and existing review comments; the completion output carries the CI summary and comment count.
/reviewaccepts--max-diff <n>,--no-ci,--no-comments. -
gh_reviewreturns the full capped diff asdiff.text(the render excerpt is bounded by the newrenderExcerptCharsconfig) and reports per-section fetch failures viadiff.error/comments.error/ci.errorinstead of swallowing them. -
gh_issueitems carrykind: issue | pr | comment; pull requests in listings are marked, and real comment payloads map correctly. -
Rate-limit facts now ride every result: write successes and all structured errors.
-
Analyzer rules:
google-api-keyandhardcoded-credentialdetection. -
Review-job record map is capped by the new
maxReviewRecordsconfig (oldest settled records evict first). -
GitHub Enterprise support in origin parsing:
repoFromRemoteUrlmatches the configuredapiBaseUrlhost. -
reviewMode: "model"config: background review jobs can delegate the capped diff to a one-shot subagent through the host'ssubagentsseam (the owning agent is the parent; the child's Markdown output becomes the postable report). The defaultstaticmode stays deterministic and token-free;modelReviewProviderselects the provider, and a missing seam or provider fails loud. -
Opt-in real-API smoke tests (
test/e2e.test.ts) that self-skip withoutGITHUB_TOKEN. -
CI workflow (
.github/workflows/ci.yml) and acheck:readmesscript wired intopackage.json.
- Approval reasons preview titles, body sizes, and review-body overrides.
- Posted review comments escape diff-derived file names (backticks + HTML) so hostile PRs cannot inject Markdown.
/review stopand/review <pr>return clean command errors for unknown jobs and missing job controllers (withdsh-tool-jobsguidance).maxDiffCharsis documented as a character cap (matching the implementation).
- Removed the unused
@deepseek-ai/dsh-scopepeer/dev dependency. - Removed the duplicated
RepoStateViewinterface declaration insrc/state.ts. - Fixed the module JSDoc reference to a README section that did not exist.
- Read-tool presenters now render structured error cards instead of assuming success values.
- README token wording corrected: the token is read per operation from the configured source and sent only in the Authorization header (it was never exclusively "in the credential layer").
- Documented the prompt-injection surface of GitHub content (issue/PR bodies, comments, search results) in the README security section.
- Initial release:
pr_create,gh_review,review_post,gh_issue,issue_opentools;/pr,/review,/issuecommand families; approval-gated writes; credential-seam token resolution; 429 retry with rate-limit surfacing; deterministic background review jobs.