Skip to content
dsh.fish
Bundle

dsh-workspace-switch

Per-workspace activation control for DeepSeek Harness: turn individual skills, MCP servers, and plugins on or off for each workspace, from a dialog on the workspace row.

Source
liaoyuqing
License
MIT
Updated
Updated 13 days ago

Readme

# dsh-workspace-switch

Per-workspace activation control for [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness):
each workspace decides which **plugins**, **MCP servers**, and **skills** its
sessions may use, and a dialog on the workspace row edits that decision.

English | [中文](README.zh.md)

## What it does

Two halves, one plugin:

- **Host half** (`index.js`, built to `lib/index.js`) reads the per-workspace
  configuration when an Agent is created and applies it to that Agent's own
  Cordis scope, so two Agents in two workspaces inside one harness process see
  different capabilities:
  - a disabled **skill** is shadowed in that scope by a same-name
    non-invocable registration, which removes it from the model's skill
    catalog;
  - a disabled **MCP server** has every `mcp__<serverName>__*` tool denied in
    that scope;
  - a disabled **plugin** has the tool names its entry declares denied in that
    scope.
  The same half serves the configuration routes the dialog uses.
- **Browser half** (`src/client/`, built to `lib/client.js`) adds one row to a
  workspace's `...` menu. It opens a dialog listing what the running
  composition offers under each category, with a switch per capability.

A workspace with no entry inherits the deployment's composition unchanged, so
the plugin changes nothing until a workspace is configured.

## Configuration

Two sources describe one workspace, in increasing precedence:

1. `<DSH_HOME>/workspace-switch.json`, whose `workspaces` map is keyed by
   workspace directory;
2. `<workspace>/.dsh/workspace-switch.json`, which replaces the shared entry
   for that workspace when it exists.

```json
{
  "workspaces": {
    "E:\\my_project\\godot-game": {
      "plugins": { "mcp-godot-ai": { "enabled": false, "tools": ["mcp__godot-ai__run"] } },
      "mcp": { "godot-ai": { "enabled": false } },
      "skills": { "godot-scene-authoring": { "enabled": false } }
    }
  }
}
```

`enabled: false` switches a capability off for that workspace; an absent entry
means enabled. A plugin entry also declares the **tool names** the plugin
registers — the gate denies tools by name, so a plugin without `tools` has
nothing to deny.

The dialog writes the document that currently answers for the workspace: the
project file while one exists, the shared store otherwise. Output is published
through a temp-file rename, so a reader never sees a half-written document.

## Install

The plugin is a DSH bundle: add it to a profile and select it.

```sh
dsh plugin add link:/path/to/dsh_workspace_switch
```

then add `dsh-workspace-switch` to the profile's `dsh.profile.bundles`. A new
bundle row loads at the next harness start.

## Build

The browser half is a DSH client bundle and must be built; the repository
toolchain is required (the plugin vendors its copy of the client build preset at
`scripts/tsdown.client.ts`, which resolves the harness sources from the checkout
it sits in).

```sh
# from a deepseek-harness checkout
pnpm exec tsdown --config dsh_workspace_switch/tsdown.config.ts   # or, inside the plugin: pnpm build
pnpm exec tsc -p dsh_workspace_switch/tsconfig.typecheck.json     # typecheck the browser half
node --test "tests/**/*.test.js"                                  # unit tests
```

**`lib/` is committed.** The package's `main` and `./client` both point into it,
and a clone has neither the harness sources nor a build toolchain, so a
build-from-source install (`dsh plugin add github:…`) would otherwise install a
package with no entry points. Commit the rebuilt files with any change that
alters them.

A standalone clone cannot run the build: the vendored preset imports the harness
sources (`../../packages/…`, `../../scripts/…`) that only exist inside a
checkout. Build inside the checkout, then commit `lib/` here.

## Known limitations

- **Skills are gated, not unloaded** — a disabled skill keeps its files on
  disk; only its catalog entry and loading are removed for that workspace.
- **A plugin needs declared tools** — the gate denies tools by name, so
  switching a plugin off only removes what its entry names in `tools`.
- **The dialog lists what the Host can address** — plugin rows come from the
  profile's plugin manager (or the read-only inventory); a capability the
  composition does not expose cannot be configured.

## Licence

MIT.

Install

dsh plugin --profile web add github:liaoyuqing/dsh_workspace_switch

Profile: web

  • This package builds from source on install. pnpm will ask you to allow its build script — that is permission to run the package’s code on your machine, outside the agent sandbox. Only allow sources you trust.
  • This source has no pinned commit, so a later push upstream changes what installs. Prefer pinning a commit.
Source