chore(agents): drop the dsh-nu plugin

dsh-nu replaced every shell command with Nushell and refused bash/pwsh
syntax before it spawned; in practice the refusals were too noisy. Drop
the flake input, install plain dsh, and remove the docs describing the
Nushell-only shell.

Assisted-by: DeepSeek Harness (deepseek-flash)
This commit is contained in:
Ryan Yin committed 2026-11-05 11:46:03 +08:00
1 parent dfa53c71d7
commit d7c493f5cd
6 files changed
+15 -115

No files matched your search

+4 -4
View File
@@ -119,9 +119,9 @@ For an impactful change, follow these steps in order, scaled to its risk:
Agents MUST NOT delete config the task did not ask about; when config merely appears unused, Agents MUST NOT delete config the task did not ask about; when config merely appears unused,
report it instead. Ask before a breaking change. report it instead. Ask before a breaking change.
- Minimality is not a reason to encode a local workaround for an upstream defect. Fix the owning - Minimality is not a reason to encode a local workaround for an upstream defect. Fix the owning
upstream project first and consume the fixed revision; if a local stopgap is genuinely unavoidable, upstream project first and consume the fixed revision; if a local stopgap is genuinely
stop and ask the user before adding it. "Minimal" means the smallest change that follows existing unavoidable, stop and ask the user before adding it. "Minimal" means the smallest change that
best practice, not the smallest diff at any cost. follows existing best practice, not the smallest diff at any cost.
- Documentation should describe current behavior for readers without the conversation history; give - Documentation should describe current behavior for readers without the conversation history; give
each fact one home per audience and link instead of repeating. Comments explain contracts or each fact one home per audience and link instead of repeating. Comments explain contracts or
non-obvious constraints, not obvious code, and preserve safety, license, and workaround non-obvious constraints, not obvious code, and preserve safety, license, and workaround
@@ -182,7 +182,7 @@ For an impactful change, follow these steps in order, scaled to its risk:
- Prefer native tool options to reduce output at the source, and the native read, grep, glob, write, - Prefer native tool options to reduce output at the source, and the native read, grep, glob, write,
and edit tools over their shell equivalents. A single shell dispatch is for genuinely shell work and edit tools over their shell equivalents. A single shell dispatch is for genuinely shell work
(git, ssh, nix, remote commands). (git, ssh, nix, remote commands).
- Use the shell the runtime provides; dsh-nu fails closed rather than substituting another shell. - Use the shell the runtime provides; never hand work to another shell (`bash -c`, `sh -c`, `pwsh`).
For genuinely shell work use the available runtime, `python -c '<code>'` for complex local For genuinely shell work use the available runtime, `python -c '<code>'` for complex local
processing, or a script written with the write tool. A remote command usually runs under a POSIX processing, or a script written with the write tool. A remote command usually runs under a POSIX
shell: keep it to one invocation or a copied script. shell: keep it to one invocation or a copied script.
Generated
+1 -44
View File
@@ -145,27 +145,6 @@
"type": "github" "type": "github"
} }
}, },
"dsh-nu": {
"inputs": {
"nixpkgs": [
"nixpkgs"
],
"nur-ryan4yin": "nur-ryan4yin"
},
"locked": {
"lastModified": 1793290908,
"narHash": "sha256-EXQpNeSoiLhk3NsfETZxuWbMwZT/01l/uP5maVziiCI=",
"owner": "ryan4yin",
"repo": "dsh-nu",
"rev": "b3a7c6eb7383aea56fe1ebbfbf06e5592952713a",
"type": "github"
},
"original": {
"owner": "ryan4yin",
"repo": "dsh-nu",
"type": "github"
}
},
"fcitx5-vinput": { "fcitx5-vinput": {
"inputs": { "inputs": {
"nixpkgs": [ "nixpkgs": [
@@ -778,27 +757,6 @@
} }
}, },
"nur-ryan4yin": { "nur-ryan4yin": {
"inputs": {
"nixpkgs": [
"dsh-nu",
"nixpkgs"
]
},
"locked": {
"lastModified": 1793285532,
"narHash": "sha256-srlNGg3/PBb3DfaC9ROlYl68VjxJfKPJoddFSU89oOQ=",
"owner": "ryan4yin",
"repo": "nur-packages",
"rev": "4687255272d66973ee4a8c1b025ca9f84371b9a6",
"type": "github"
},
"original": {
"owner": "ryan4yin",
"repo": "nur-packages",
"type": "github"
}
},
"nur-ryan4yin_2": {
"inputs": { "inputs": {
"nixpkgs": [ "nixpkgs": [
"nixpkgs" "nixpkgs"
@@ -899,7 +857,6 @@
"catppuccin": "catppuccin", "catppuccin": "catppuccin",
"colmena": "colmena", "colmena": "colmena",
"disko": "disko", "disko": "disko",
"dsh-nu": "dsh-nu",
"fcitx5-vinput": "fcitx5-vinput", "fcitx5-vinput": "fcitx5-vinput",
"haumea": "haumea", "haumea": "haumea",
"home-manager": "home-manager", "home-manager": "home-manager",
@@ -923,7 +880,7 @@
"nixpkgs-patched": "nixpkgs-patched", "nixpkgs-patched": "nixpkgs-patched",
"nixpkgs-stable": "nixpkgs-stable", "nixpkgs-stable": "nixpkgs-stable",
"nu_scripts": "nu_scripts", "nu_scripts": "nu_scripts",
"nur-ryan4yin": "nur-ryan4yin_2", "nur-ryan4yin": "nur-ryan4yin",
"ponytail": "ponytail", "ponytail": "ponytail",
"pre-commit-hooks": "pre-commit-hooks", "pre-commit-hooks": "pre-commit-hooks",
"preservation": "preservation", "preservation": "preservation",
-7
View File
@@ -203,12 +203,5 @@
url = "github:ryan4yin/nur-packages"; url = "github:ryan4yin/nur-packages";
inputs.nixpkgs.follows = "nixpkgs"; inputs.nixpkgs.follows = "nixpkgs";
}; };
# The Nushell-only shell bundle. Its flake builds the plugin from the
# repository, so a release needs only `nix flake update dsh-nu`.
dsh-nu = {
url = "github:ryan4yin/dsh-nu";
inputs.nixpkgs.follows = "nixpkgs";
};
}; };
} }
+1 -3
View File
@@ -15,9 +15,7 @@ Markdown with no Nix dependency; this module only wires it into place.
[agents/skills/README.md](../../../../agents/skills/README.md). [agents/skills/README.md](../../../../agents/skills/README.md).
- `packages.nix`: agent CLIs (`codex`, `opencode`, `qwen-code`, `pi`) from the `llm-agents` flake - `packages.nix`: agent CLIs (`codex`, `opencode`, `qwen-code`, `pi`) from the `llm-agents` flake
input, dsh from `nur-ryan4yin`, `pkgs.ctx7` for the `find-docs` skill, and `pkgs.mcp-grafana` for input, dsh from `nur-ryan4yin`, `pkgs.ctx7` for the `find-docs` skill, and `pkgs.mcp-grafana` for
the Grafana MCP server. dsh's Nushell-only shell is selected when the package is built, via the Grafana MCP server. Profile patches live in `~/.dsh/profiles/<name>/cordis.patch.yml`. See
`dsh.withPlugins` with the plugin from the `dsh-nu` flake input, so it is a derivation choice
rather than runtime state; profile patches live in `~/.dsh/profiles/<name>/cordis.patch.yml`. See
[dsh.md](./dsh.md) for the layer order and the settings that bite. [dsh.md](./dsh.md) for the layer order and the settings that bite.
- `env.nix`: telemetry and auto-update opt-outs for the agents. - `env.nix`: telemetry and auto-update opt-outs for the agents.
+7 -47
View File
@@ -3,19 +3,18 @@
dsh is installed by this module (`packages.nix`) and configured at runtime, not by Nix. It boots a dsh is installed by this module (`packages.nix`) and configured at runtime, not by Nix. It boots a
profile: an ordered stack of patch layers over an empty root entry list, where later layers win. profile: an ordered stack of patch layers over an empty root entry list, where later layers win.
| # | Layer | Path | Writer | | # | Layer | Path | Writer |
| --- | --------- | -------------------------------------------- | --------------- | | --- | ------- | -------------------------------------------- | --------------- |
| 1 | bundles | shipped with the profile | upstream | | 1 | bundles | shipped with the profile | upstream |
| 2 | profile | `$DSH_HOME/profiles/<name>/cordis.patch.yml` | dsh, at runtime | | 2 | profile | `$DSH_HOME/profiles/<name>/cordis.patch.yml` | dsh, at runtime |
| 3 | home | `$DSH_HOME/cordis.patch.yml` | you | | 3 | home | `$DSH_HOME/cordis.patch.yml` | you |
| 4 | `--patch` | whatever the launcher passes | package wrapper |
`$DSH_HOME` defaults to `~/.dsh`. Settings UI changes are written to the profile patch; a home-layer `$DSH_HOME` defaults to `~/.dsh`. Settings UI changes are written to the profile patch; a home-layer
row outranks it, so the UI rejects that setting with row outranks it, so the UI rejects that setting with
`Configuration for "..." is overridden by a home patch`. This repository ships no home layer: `Configuration for "..." is overridden by a home patch`. This repository ships no home layer:
profile patches remain machine-local runtime state, the place for private endpoints (LAN servers, profile patches remain machine-local runtime state, the place for private endpoints (LAN servers,
staging gateways) too. The shell swap is a package concern instead: Nix installs `dsh` from staging gateways) too. Nix installs plain `dsh` from `nur-ryan4yin`, with no package wrapper and no
`nur-ryan4yin` with `dsh.withPlugins`, which adds a `--patch` overlay after the profile layer. `--patch` overlay.
Three behaviours that are easy to get wrong: Three behaviours that are easy to get wrong:
@@ -50,42 +49,3 @@ on, `$DSH_AGENTS_HOME/AGENTS.md` — so no patch row is needed; identical conten
- The scopes come from the environment: `0.2.1-alpha.2` removed the row's `dshHome` / `agentsHome`. - The scopes come from the environment: `0.2.1-alpha.2` removed the row's `dshHome` / `agentsHome`.
- Nested `AGENTS.md` files are discovered by `read` / `write` / `edit`, not by `cd` inside `bash`, - Nested `AGENTS.md` files are discovered by `read` / `write` / `edit`, not by `cd` inside `bash`,
and nothing is watched: an external edit lands on the next such call, or on resume. and nothing is watched: an external edit lands on the next such call, or on resume.
## Nushell-only shell
`dsh-nu`'s flake builds the bundle and `nur-ryan4yin`'s `dsh.withPlugins` applies it: every
`ctx.shell` consumer (model shell tools, background jobs, hook bridges, in-process callers) runs
`nu --no-config-file -c`, and a command that hands execution to another shell is refused before it
spawns. The plugin adds Nushell rules and a syntax guide, and rewrites the shell-tool descriptions,
so the model is told what actually runs. `nu` is the Nushell `dsh-nu`'s flake resolves — Nixpkgs',
through `inputs.nixpkgs.follows` — substituted into the rendered patch at build time, so it does not
depend on `PATH`. The bundle still records Nushell 0.115.1 as its tested release; nix-config follows
the main nixpkgs and rewrites that manifest field to the resolved Nushell in `outputs/default.nix`
until dsh-nu retests, rather than pinning an older nixpkgs for the whole plugin. A command that
hands a script file to a shell (`bash scripts/build.sh`) is refused, and the refusal names the
direct `./scripts/build.sh` spelling; the guard itself is unchanged.
`home/base/tui/agents/packages.nix` calls `dsh-nu.lib.mkDshNuPackage { inherit pkgs dsh; }` and
passes the result to `withPlugins`; `flake.lock` pins both inputs. `dsh.withPlugins` wraps the
binary: each invocation gets a generated `--patch <file>` overlay after the profile layer, while
`dsh plugin` stays untouched. No `$DSH_HOME/cordis.patch.yml` is written, so there is no home-layer
state to resolve or roll back. Boundaries: preset rows that spawn a shell themselves
(`custom-bash.mjs`, `persistent-shell`) bypass `ctx.shell`. So does
`@deepseek-ai/dsh-experimental-terminal-bundle`: its `terminal_*` tools use a separate
`ctx.terminals` seam and run the platform shell (bash on the POSIX hosts this repository manages),
so they coexist with this plugin but stay outside the Nushell-only guarantee — keep the bundle off
for a strict Nu-only machine. Bash hooks now run under Nushell and need rewriting; remote hosts
still run their own POSIX shell. Rollback: install plain `dsh` or pass an empty plugin list; there
is no home file to delete. Tested on Linux; the same package also builds for Darwin, where it has
not been boot-tested.
Verify after a switch:
```sh
dsh --profile web --dump-config | grep -nE 'nu-executor|nu-teaching'
# expect the first-party shell rows disabled, nu-executor and nu-teaching inserted
```
A dsh bump can disable a row whose manifest no longer matches. That compatibility check runs only
when a profile boots, not under the boot-free `--dump-config`, so read the stderr of the next boot
(for example `just dsh-web`) for `disabling profile plugin`; nothing prints when healthy.
+2 -10
View File
@@ -1,17 +1,10 @@
{ {
pkgs, pkgs,
llm-agents, llm-agents,
dsh-nu,
... ...
}: }:
let let
agentPackages = llm-agents.packages.${pkgs.stdenv.hostPlatform.system}; agentPackages = llm-agents.packages.${pkgs.stdenv.hostPlatform.system};
# dsh-nu's flake builds the plugin from its own checkout; this repository
# supplies the dsh installation it plugs into.
dshNu = dsh-nu.lib.mkDshNuPackage {
inherit pkgs;
inherit (pkgs) dsh;
};
# llm-agents.nix still exposes OpenCode V2 as `opencode2` so it can coexist # llm-agents.nix still exposes OpenCode V2 as `opencode2` so it can coexist
# with the v1 package. Re-expose its binary under the normal command name. # with the v1 package. Re-expose its binary under the normal command name.
opencode = pkgs.linkFarm "opencode" [ opencode = pkgs.linkFarm "opencode" [
@@ -29,9 +22,8 @@ in
agentPackages.qwen-code agentPackages.qwen-code
agentPackages.pi agentPackages.pi
# dsh from nur-packages: llm-agents.nix's dsh cannot boot on nixpkgs node # dsh from nur-packages: llm-agents.nix's dsh cannot boot on nixpkgs node
# (node-addon-require-builtin probe), this one shims it. The Nushell-only # (node-addon-require-builtin probe), this one shims it.
# plugin rides in through the package's withPlugins wrapper. pkgs.dsh
(pkgs.dsh.withPlugins [ dshNu ])
# Docs lookup CLI behind the find-docs skill (agents/skills/find-docs). The skill's flags are # Docs lookup CLI behind the find-docs skill (agents/skills/find-docs). The skill's flags are
# written against this package, not against upstream's npx instructions. # written against this package, not against upstream's npx instructions.