Read configured plugin market indexes on the Host, search them with fuzzy
multilingual matching, and install a chosen version from the Plugins page.
- dsh-plugin-market-index validates format-1 indexes and builds install
arguments; inline icons are capped at 32 KiB.
- The pluginMarket service fetches configured http(s) URLs, parses bodies in
a Worker Thread, caches documents, and pages filtered, ordered results with
installation status and engines.dsh compatibility.
- Discover is an opt-in experimental bundle; the Plugins page gains a
plugins.tab slot and confirmed installs.
- The plugin_market tool lists indexes, plugins, and versions for Creator mode.
- Plugin compatibility reads engines.dsh before DSH peer dependencies.
docs/subsystems/claude-code-mods.md lists every way a mod behaves
differently through the bridge than under Claude Code 2.1.287 — packaging,
events, the chain, $ members, drawing, testing, and the example mods,
including the four official mods assessed by reading. The bridge README
describes defineMod, the band, and the test kit; the band package gets its
README; the group index, release families, config and client catalogs,
module and seam graphs, and the Cordis API catalog are regenerated.
The reminder stack no longer hides behind an optional experimental bundle.
`packages/bundle/web-app/cordis.patch.yml` inserts `schedule` and
`ui-schedule`, so a shipped profile mounts the task store, the delivery
runtime, and the Automation tasks page; the
`@deepseek-ai/dsh-experimental-schedule-bundle` package and its wiring are
removed.
Preset ownership is unchanged: `standard`, `cordis`, and `ptc` declare the
clock and the four reminder tools in their own scope, and `minimal` declares
neither, so a minimal session carries no reading and no reminder tool even
though the service is mounted.
`apps/cli` depends on `@deepseek-ai/dsh-tool-schedule`, and
`packages/bundle/web-app` declares `@deepseek-ai/dsh-schedule` and
`@deepseek-ai/dsh-client-ui-schedule`: preset and profile rows resolve plugin
names from the installation anchor, so a missing declaration leaves the row
without a fiber and every session creation fails with
`agent-preset/invalid`.
Documentation, Agent Notes, and the recorded Web fixtures follow the shipped
composition.
The shipped Web composition carries `time-context`, `schedule`, and
`ui-schedule` disabled. This adds
`@deepseek-ai/dsh-experimental-schedule-bundle`, whose patch sets
`disabled: false` on those three rows and inserts none, and names it in
`OPTIONAL_BUNDLES` with a runtime dependency of `apps/cli`, so every
installation ships it switched off and the Web Plugins page offers it in
the Official group.
The two Web suites and the built-Web Schedule Remote scenario enable the
rows through that bundle instead of a scenario-local fixture.
Three comments in the same review round went unread and still applied.
A failed tag push left the local tag in place, so the printed recovery
`git tag … && git push …` short-circuited on "already exists" and never
pushed. The result now carries the commands still outstanding, and the
tests cover a failed push and a failed tag separately.
Automatic numbering caught every error from resolving the upload
destination, which turned a misconfigured destination into silent local
numbering that can reuse a published index. It now checks whether a
destination is configured at all and lets an invalid one fail, and the
remaining catch names the error it reports.
`pnpmCommand` fell back to a bare `pnpm`, which on Windows is the batch
shim spawnSync cannot start — the failure this branch exists to fix. It
says so instead, and keeps the fallback where a shim is executable.
The afterPack regression this branch introduced earlier reached review
because nothing exercised that hook: one spec calls it directly for an
ordinary build, a prepared qualification runtime, and a build version,
and it fails when the expected version collapses back to one value.
Review found that `apps/desktop/README.md#release-versions` and the
2026-09-16 version-derivation note already specify this convention, and
that three of their requirements were reversed without saying so. The
rules themselves are right; what was wrong was leaving them undocumented
and rejecting one of the forms they prescribe.
A stable base takes `-test.YYYYMMDD.index`, which validation refused
because such a version ranks below its base. That ordering is the point:
the form belongs to a test feed that never serves the production
release. Both READMEs, the older note, and a new note now record which
requirements this change reverses and why.
Three defects the review found, each reproducible:
- `afterPack` compared the bundled runtime against the product version,
which broke installed-update qualification: that flow passes a runtime
whose version was deliberately rewritten. The expected version is now
a parameter the preparer supplies, and `verifyDesktopRuntime` says
"bundled X is not the expected Y" rather than naming Electron.
- Upload could only learn the published version from a shell variable,
conflicting with "release fields come only from the target file". It
reads the completion record packaging wrote, and a mismatch now prints
the version that record holds.
- Automatic numbering read one listing page and one fixed directory. It
now follows every page, discards a listing it could not finish rather
than reusing an index that would overwrite a published installer,
bounds the request through the client's own timeout, and reads the
directory an unsigned build actually writes.
Also: `npm run` and `yarn run` set `npm_execpath` too, so the pnpm
resolution checks the path names pnpm; the argument separator can arrive
after a run script's preset arguments, not only first; and the toolchain
probe, parser, build commit, and tarball readers now have specs.
spawnSync cannot start the pnpm .cmd shim without a shell, so the bump
step's lockfile refresh failed with ENOENT on Windows after rewriting
every manifest. Resolve pnpm to a JavaScript entry executed by the
current Node: the run-script's npm_execpath when present, otherwise the
workspace's own pnpm dependency (located on disk because pnpm's manifest
does not export its bin entry).
GNU tar parses the colon in an absolute Windows path as a remote-host
separator and aborts, so the tarball readers now run tar in the
tarball's directory and pass only its file name.
OPTIONAL_BUNDLES in app-boot lists the bundles the installation ships
switched off; the plugin manager, the default-product isolation gate,
the workspace constraints, and the packed-install check read that one
list instead of a dsh.optionalBundles field in apps/cli/package.json.
The dsh installation lists under `dsh.optionalBundles` the bundles it
ships for a person to switch on. The plugin manager reports them as
`optional`, and the Web plugin page shows them first, in a built-in
group with an official tag, off until selected and never removable.
Agent Teams ships this way, so a blank profile offers both of its
layers. Default-product isolation, the workspace constraints, and the
packed-install check accept that one dependency edge and nothing else
experimental.
A bundle's title and one-liner come from `dsh.title` and the new
`dsh.description`, else from the package name and `description`. A
dependency without a bundle patch is no longer listed unless the
profile selects it. The install dialog asks for a package name or
address; a guide under the field shows the three accepted forms with
an example each and ends in a security warning, and the copy no longer
speaks of a plugin ID.