From d7c493f5cdcb84a3e83b159a8174d190c01e45e7 Mon Sep 17 00:00:00 2001 From: Ryan Yin Date: Thu, 5 Nov 2026 11:46:03 +0800 Subject: [PATCH] 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) --- agents/global-rules.md | 8 ++--- flake.lock | 45 +------------------------- flake.nix | 7 ---- home/base/tui/agents/README.md | 4 +-- home/base/tui/agents/dsh.md | 54 ++++--------------------------- home/base/tui/agents/packages.nix | 12 ++----- 6 files changed, 15 insertions(+), 115 deletions(-) diff --git a/agents/global-rules.md b/agents/global-rules.md index a93982ca..db1bb00c 100644 --- a/agents/global-rules.md +++ b/agents/global-rules.md @@ -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, 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 - upstream project first and consume the fixed revision; if a local stopgap is genuinely unavoidable, - stop and ask the user before adding it. "Minimal" means the smallest change that follows existing - best practice, not the smallest diff at any cost. + upstream project first and consume the fixed revision; if a local stopgap is genuinely + unavoidable, stop and ask the user before adding it. "Minimal" means the smallest change that + follows existing best practice, not the smallest diff at any cost. - 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 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, and edit tools over their shell equivalents. A single shell dispatch is for genuinely shell work (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 ''` for complex local 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. diff --git a/flake.lock b/flake.lock index a69f8b42..f9cfcc7c 100644 --- a/flake.lock +++ b/flake.lock @@ -145,27 +145,6 @@ "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": { "inputs": { "nixpkgs": [ @@ -778,27 +757,6 @@ } }, "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": { "nixpkgs": [ "nixpkgs" @@ -899,7 +857,6 @@ "catppuccin": "catppuccin", "colmena": "colmena", "disko": "disko", - "dsh-nu": "dsh-nu", "fcitx5-vinput": "fcitx5-vinput", "haumea": "haumea", "home-manager": "home-manager", @@ -923,7 +880,7 @@ "nixpkgs-patched": "nixpkgs-patched", "nixpkgs-stable": "nixpkgs-stable", "nu_scripts": "nu_scripts", - "nur-ryan4yin": "nur-ryan4yin_2", + "nur-ryan4yin": "nur-ryan4yin", "ponytail": "ponytail", "pre-commit-hooks": "pre-commit-hooks", "preservation": "preservation", diff --git a/flake.nix b/flake.nix index 377f6181..6058e266 100644 --- a/flake.nix +++ b/flake.nix @@ -203,12 +203,5 @@ url = "github:ryan4yin/nur-packages"; 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"; - }; }; } diff --git a/home/base/tui/agents/README.md b/home/base/tui/agents/README.md index 4397409b..eefab5e7 100644 --- a/home/base/tui/agents/README.md +++ b/home/base/tui/agents/README.md @@ -15,9 +15,7 @@ Markdown with no Nix dependency; this module only wires it into place. [agents/skills/README.md](../../../../agents/skills/README.md). - `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 - the Grafana MCP server. dsh's Nushell-only shell is selected when the package is built, via - `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//cordis.patch.yml`. See + the Grafana MCP server. Profile patches live in `~/.dsh/profiles//cordis.patch.yml`. See [dsh.md](./dsh.md) for the layer order and the settings that bite. - `env.nix`: telemetry and auto-update opt-outs for the agents. diff --git a/home/base/tui/agents/dsh.md b/home/base/tui/agents/dsh.md index 552de4dc..83f5b9f1 100644 --- a/home/base/tui/agents/dsh.md +++ b/home/base/tui/agents/dsh.md @@ -3,19 +3,18 @@ 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. -| # | Layer | Path | Writer | -| --- | --------- | -------------------------------------------- | --------------- | -| 1 | bundles | shipped with the profile | upstream | -| 2 | profile | `$DSH_HOME/profiles//cordis.patch.yml` | dsh, at runtime | -| 3 | home | `$DSH_HOME/cordis.patch.yml` | you | -| 4 | `--patch` | whatever the launcher passes | package wrapper | +| # | Layer | Path | Writer | +| --- | ------- | -------------------------------------------- | --------------- | +| 1 | bundles | shipped with the profile | upstream | +| 2 | profile | `$DSH_HOME/profiles//cordis.patch.yml` | dsh, at runtime | +| 3 | home | `$DSH_HOME/cordis.patch.yml` | you | `$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 `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, -staging gateways) too. The shell swap is a package concern instead: Nix installs `dsh` from -`nur-ryan4yin` with `dsh.withPlugins`, which adds a `--patch` overlay after the profile layer. +staging gateways) too. Nix installs plain `dsh` from `nur-ryan4yin`, with no package wrapper and no +`--patch` overlay. 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`. - 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. - -## 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 ` 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. diff --git a/home/base/tui/agents/packages.nix b/home/base/tui/agents/packages.nix index a63edfcd..e0bafeff 100644 --- a/home/base/tui/agents/packages.nix +++ b/home/base/tui/agents/packages.nix @@ -1,17 +1,10 @@ { pkgs, llm-agents, - dsh-nu, ... }: let 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 # with the v1 package. Re-expose its binary under the normal command name. opencode = pkgs.linkFarm "opencode" [ @@ -29,9 +22,8 @@ in agentPackages.qwen-code agentPackages.pi # 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 - # plugin rides in through the package's withPlugins wrapper. - (pkgs.dsh.withPlugins [ dshNu ]) + # (node-addon-require-builtin probe), this one shims it. + pkgs.dsh # 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.