7 Commits
Author SHA1 Message Date
gitea-actions 3d60f03220 Release v0.1.2 [skip release] [skip actions] 2026-09-26 06:14:58 +00:00
gitea-actions 48e5238877 Release v0.1.1 [skip release] [skip actions] 2026-09-26 06:06:57 +00:00
dsh-slopo agent 1b2ab3613f fix!: port the settings page and shell seam to dsh 0.1.7-rc.2
Release / release (push) Successful in 6s
0.1.7-rc.2 moved four things this bundle was built on, and every one of them
failed quietly rather than loudly, so the plugin looked installed and did
nothing:

  * `ctx.settings.installSection(...)` is gone. The call threw into a
    `try/catch` that only logged a warning, so the settings namespace was never
    registered and `configure` had nowhere to persist. An entry's exported
    Config IS the form now; the namespace is the Loader entry id the bundle's
    patch declares, and `ctx.settings.configure({ auto: false })` is what claims
    that entry's own page.

  * `settingsScope` and the `settings.plugin.item` slot are gone. The card
    registered into both, so it never rendered — the tab draws the intersection
    of served namespaces and registered cards, and that intersection was empty.
    It now registers into `plugins.bundle.config` keyed by the bundle's package
    name, over the `configForms` service, and uses the framework's own form
    chrome (`SettingsFormModel` / `SettingsForm` / `SettingsValueField` /
    `SettingsSecretField`) instead of the hand-rolled equivalent. A save is one
    revision-fenced write.

  * `ctx.shell.run(spec)` is gone. Every action that runs a command threw
    `shell.run is not a function`; the seam is now
    `await ctx.shell.execute(ctx.shell.resolve(request)).result()`.

  * A job's `owner` is a session id, not the agent object. The wrong owner
    registered a job no session could collect or cancel, and the producer's
    `done` resolved `output` where the registry reads `result`, so a finished
    job handed back nothing.

Two conformance gaps went with them: a Config field that is not marked
`volatile()` is invisible to every settings UI — the form projection keeps only
volatile fields, and an entry with no form is dropped from `settings.describe`
entirely — and `embeddingApiKey` was a plain field, so the key rode every
settings read back to the browser. It is `role('secret')` now: the browser is
told whether a key is stored and never receives the key.

Also declares the `@deepseek-ai/dsh-*` peerDependencies the compatibility gate
reads, so an incompatible runtime refuses this plugin at startup instead of
half-loading it.

Verified against a real 0.1.7-rc.2 boot: `settings.describe` serves the entry
with `autoGenerate: false`, the key redacted and reported only as `set: true`,
and no startup diagnostics. The host mock in the tests now has no `run` and no
`installSection`, so those seams are contract-tested rather than assumed, and
the browser tests read the installed harness's own slot catalog and package
exports — a rename fails a test instead of quietly emptying a page.
2026-09-26 06:38:16 +01:00
James Mackay e0813bcbea Release on every push to master, and make updating possible
A dsh plugin installed as a bare git URL is pinned to a commit in the profile
lockfile. With no tags in the repository there was nothing for an update to move
to, which is why updating appeared to do nothing and a reinstall was the only way
through.

Gitea action on push to master: work out the range since the last tag, decide
whether the push releases at all, bump package.json, prepend a CHANGELOG section,
commit, tag vX.Y.Z, and publish the release with those notes as the body.

The bump is a patch unless a commit says [minor] or [major]; [skip release]
publishes nothing. With no tags yet the declared version is what gets tagged, so
the first release matches the code. Notes come from commit subjects, grouped under
headings only when the range actually uses conventional prefixes.

The logic is a script rather than inline YAML so it runs without a runner:
npm run test:release covers 31 cases, including an empty range, a repository with
no tags, and a patch number already at 9. The empty-range guard is load-bearing:
the release commit is itself a push to master.

Two bugs the tests caught: an empty note section made '[ -n x ] && printf' return
non-zero, which under set -e aborted the whole release, and a bullet beginning
with '-' was read by grep as an option.

README documents the #semver: spec, which tracks releases rather than every
commit, and the two things that look like a failed update: an unfetchable remote,
and the profile restart client bundle bytes require.
2026-09-25 19:03:50 +01:00
dsh-slopo agent 4bb19af2ac Add the settings card — the browser half
The settings namespace was registered but nothing drew a form for it, because
the Plugin configuration tab renders the INTERSECTION of the namespaces the Host
serves and the cards a browser half claims. A served namespace with no card
renders nothing at all. This is the missing card.

Plain ESM in the module-loader wrapper, no build step, requiring only `react` —
which is in the platform seed table. Anything outside that table would have to be
bundled into this file or declared in `dsh.client.external`, and the tests fail
loudly on any other specifier.

It stages edits and writes them on Save; a cleared field is an `unset` rather
than a write of "", so the value re-inherits the composition layer instead of
becoming empty, and the user layer's presence is what marks a field overridden.
Reset clears every override. A namespace this client cannot reach renders
nothing, so a deployment without the Host half shows no trace.

package.json gains an `exports` map (the loader reads `exports["./client"]`; a
bare `main` would not be enough) and the `dsh.client` declaration.
2026-09-22 07:22:03 +01:00
dsh-slopo agent 3373e0b4f6 Test the plugin against the real harness seams and the real CLI
The integration tests load the real plugin module against a thin mock Cordis
context and drive the tool through a real shell into the real slopo binary. The
mock implements only the seams the plugin touches, so a wrong assumption about a
dsh service fails a test instead of producing a plugin that silently does
nothing at runtime.

Two real bugs were caught this way and are now pinned:

  * renderSlopoConfig would write the embedding API key to disk if it were
    handed one. It cannot now.
  * --config-version was placed before the subcommand, which slopo rejects with
    exit code 2. It is declared on the command, not the callback.

The end-to-end test is opt-in (DSH_SLOPO_E2E=1) because it needs a reachable
embedding backend; it indexes a real repository, embeds it, and asserts a real
duplicate cluster comes back.
2026-09-22 06:11:17 +01:00
dsh-slopo agent 3490e5aba0 Scaffold the dsh-slopo bundle and port slopo's three agent skills
The three SKILL.md files are ports of the configs slopo 0.8.0 ships for
Claude Code, not new prose. What changed is the mechanics:

  * name + description frontmatter, both required by dsh
  * disable-model-invocation: true kept verbatim -- dsh has the same key,
    and slopo makes that choice for a reason (every one of these commands
    can spend minutes embedding)
  * allowed-tools / argument-hint / arguments dropped, not dsh keys
  * the !`slopo ...` shell-injection directive replaced with an
    instruction to call the slopo tool plus an explicit failure check,
    because dsh skill bodies do not execute commands

The guidance prose is quoted from slopo; NOTICE.md records the mapping and
the licence obligation that follows from it (AGPL-3.0-or-later, matching
upstream and the LICENSE file fetched from it).
2026-09-22 06:05:28 +01:00