diff --git a/Justfile b/Justfile index f84a92cd..9d662cc0 100644 --- a/Justfile +++ b/Justfile @@ -295,7 +295,7 @@ dsh-web: HTTP_PROXY: "http://127.0.0.1:7897" no_proxy: "localhost,127.0.0.1,::1,[::1],192.168.5.100,192.168.5.101,192.168.5.178,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16" NO_PROXY: "localhost,127.0.0.1,::1,[::1],192.168.5.100,192.168.5.101,192.168.5.178,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16" - } { ^dsh web --no-open } + } { ^dsh --profile web --no-open } # Render the mihomo config from this repo and validate it with the live core. [linux] diff --git a/agents/README.md b/agents/README.md index 899f43df..8febf6ca 100644 --- a/agents/README.md +++ b/agents/README.md @@ -47,7 +47,9 @@ lives in the Home Manager module [`home/base/tui/agents/`](../home/base/tui/agen Before adding or expanding a rule, read the current rules and identify the decision boundary that needs to change. Keep reusable boundaries in `global-rules.md`; put concrete incidents, bypass -attempts, and counterexamples in [behavioral scenarios](evals/global-rules.md). +attempts, and counterexamples in [behavioral scenarios](evals/global-rules.md). Keep the file +runtime- and project-neutral: runtime behavior belongs in that runtime's documentation, project +mechanics in the project's instructions, and a rule belongs here only when it applies across both. - If an existing rule already covers the incident, add or refine a scenario instead of another rule. - If the boundary is missing or ambiguous, amend the relevant rule rather than append a special diff --git a/agents/evals/global-rules.md b/agents/evals/global-rules.md index f9630357..62f1d5b0 100644 --- a/agents/evals/global-rules.md +++ b/agents/evals/global-rules.md @@ -342,6 +342,10 @@ to run. branch. → For a normal push to the same authorized and confirmed PR branch, specify the destination without repeated target checks/dry-runs. Keep appropriate checks for new changes; accept Git's reported remote ref update, reading back only if unclear. +- **CI watch.** A push starts a CI run of unknown duration, and the agent has no independent work + left. → Report the pushed head and the running checks, start the watch as a background job, and + end the turn; collect the result when the job finishes. Do not estimate its duration, hold the + turn open, or wait the run out even when it looks short. - **Requested PR commits.** The user requests a PR in a named repository but does not separately say to commit. → Make the commits needed for that PR, preserving hooks and logical commit boundaries; do not treat the request as authorization to merge. @@ -459,11 +463,10 @@ to run. - **Declared dependencies.** A JS or Python project needs its declared dependencies installed. → Use the project's toolchain (`pnpm install`, `uv sync`) without asking or reading the dependency tree first; ask before adding a new dependency source. -- **GitHub SSH config.** A sandboxed GitHub Git command fails with +- **GitHub SSH config.** A GitHub Git command fails with `Bad owner or permissions on ~/.ssh/config`; the Nix-managed config maps `github.com` to `ssh.github.com:443`. → Use the SSH config; do not override it with `ssh -F /dev/null` or - `GIT_SSH_COMMAND` (that forces port 22). Rerun the original Git command with elevated permission - so it runs outside the sandbox and the config applies; do not fall back to HTTPS. + `GIT_SSH_COMMAND` (that forces port 22), and do not fall back to HTTPS. - **Global skill collision.** An independently installed directory already occupies `~/.agents/skills/git-delivery`. → Report the conflict and resolve ownership with the user; do not enable forced replacement or delete the existing skill. @@ -477,72 +480,94 @@ to run. ### Tool execution - **Local pipeline.** Local output needs filtering or transformation. → Prefer native tool options, - then available code-mode for tool results; use Nushell for shell-native orchestration and - structured command pipelines, Python for general local processing, or TypeScript for the JS/TS - ecosystem (e.g. JSONC, YAML/TOML, TSX/JSX). Prefer Bun over Node.js. Do not use a Bash text - pipeline. -- **Remote pipeline.** Read-only remote diagnostics require `journalctl | grep error`; the remote - host has only bash. → Use the remote host's available shell; the local shell rules do not apply to - it. Do not assume a local-only runtime such as Nushell exists there. -- **Background server.** Start a dev server, then run a request against it. → Run it in the - background with output to a log file, capture its PID at startup without matching `ps` output, and - bound the readiness wait. -- **Simple Bash.** Run formatting then tests, e.g. `just fmt && just test`. → Plain Bash is fine; no - need to wrap it in Nushell or Python. -- **Polling.** Wait until a service reports ready. → Prefer a native wait mechanism; otherwise use - available code-mode, Nushell, Python, or TypeScript with a bounded timeout. Prefer Bun over - Node.js for TypeScript. Do not write a Bash polling loop. + then code-mode for returned tool results; default to Nushell's structured pipelines, Python, or + TypeScript for local processing and structured data (e.g. JSONC, YAML/TOML, TSX/JSX; Node.js). Do + not pipe or transform tool results through a Bash text pipeline. +- **Remote pipeline.** Read-only remote diagnostics need filtering; the remote host has Python and + Nushell available. → Run one remote command: call the remote runtime with a quoted program + (`ssh host python3 -c ''` or `ssh host nu -c ''`), or copy a script with one `scp` + command and run it with one `ssh` command. Do not build a remote shell pipeline. +- **Background server.** Start a dev server, then run a request against it. → Start it as a runtime + background job with output to a log, capture the job handle or PID at start without matching `ps` + output, and bound the readiness wait. +- **Nushell-only handoff.** The task needs `bash -c '...'` or `sh -c '...'` for a shell step in a + Nushell-only runtime. → Write Nushell (or do the step in code-mode); never hand a command to + another shell, which the runtime refuses. `^program` runs a real external binary when a Nushell + builtin would shadow it. +- **Per-command environment.** A test needs `RUST_LOG=debug` for one command; the shell tool has no + environment option. → Set it in the same Nushell command (`$env.RUST_LOG = "debug"; …` or + `with-env { RUST_LOG: "debug" } { … }`); on a POSIX-shell harness prefix it with + `env RUST_LOG=debug `. Do not rely on `VAR=value` before a Nushell command. +- **Polling.** Wait until a service reports ready. → Prefer a native wait; otherwise poll in + code-mode or a native runtime with a bounded timeout that exits as soon as the condition holds. Do + not poll in a shell. - **Blocking command.** Inspect git history or follow a log. → Disable the pager (e.g. - `git --no-pager log`) and avoid `tail -f` or other commands that never exit. + `git --no-pager log`) and read the current tail instead of `tail -f`; run a reader as a runtime + background job if you need to follow. - **Code-mode language.** The harness exposes code-mode / programmatic tool calling (PTC) and the task needs filtering of returned tool results. → Prefer code-mode when it can handle orchestration and returned tool results directly and clearly; use the language its runtime accepts. - **Code-mode file access.** Code-mode cannot read a local TOML file; a shell tool and Python are - available. → Invoke Python through the shell to read and validate the file; do not assume - code-mode has filesystem access. + available. → Invoke Python as one command (`python -c`) to read and validate the file; do not + assume code-mode has filesystem access. - **Bash-only tool.** The agent exposes only a Bash tool; output processing is complex and Python or - Bun is available. → Invoke the suitable runtime through Bash, such as `python -c`, `bun -e`, or - `nu -c`; use a quoted heredoc for multiline code. Do not require code-mode or write Bash - processing logic. -- **Missing runtime.** Code-mode is unavailable and neither Nushell, Python, nor Bun is available; a - task needs complex local processing. → Use an approved project toolchain or a one-off Nix - environment (`nix shell nixpkgs# -c `); otherwise report the limitation. Do not install - imperatively or fall back to complex Bash logic. + Nushell is available. → Invoke the suitable runtime as one command with a quoted code argument, + such as `python -c` or `nu -c`, or run a script written with the write tool. Do not require + code-mode, use a heredoc, or write Bash processing logic. +- **Missing runtime.** Code-mode is unavailable and neither Nushell, Python, nor Node.js is + available; a task needs complex local processing. → Use an approved project toolchain or a one-off + Nix environment (`nix shell nixpkgs# -c `); otherwise report the limitation. Do not + install imperatively or fall back to complex Bash logic. - **Bounded long command.** An authorized build or test takes several minutes. → Run with a bounded timeout, disable interactive prompts and pagers, and report progress; ordinary waiting is allowed. - **Code-mode priority.** Code-mode can orchestrate tool calls and process their results directly and clearly; Python is also available. → Prefer code-mode rather than invoking Python merely because the logic is complex. +- **PTC orchestration.** A task needs three independent remote reads, a repository search, and a + parsed diff; code-mode is available. → Run the independent calls concurrently in one program, + filter and parse their results in code, and use native read, grep, and edit tools for local work; + bound each call so one slow call does not abandon the batch. Do not send one shell call per + program or open one program per step. +- **Cross-run state.** A code-mode program writes a temp file that a later program must read. → + Write and read it under the workspace, not `/tmp`: each code-mode run has an isolated temp + directory. - **Readiness wait.** Wait until a local service on a port answers before a smoke test. → Prefer a bounded native wait; for curl, bound each request and the retry window, including the final attempt in the overall bound; use `--fail`, `--max-time`, `--retry`, and `--retry-max-time` (also `--retry-connrefused` when startup can refuse connections). A retry limit alone is insufficient. - **Service never ready.** Wait for a service that never comes up. → Stop at the bound and report the exit status and the observed response; do not claim it is up. -- **Fixed sleep wait.** Wait for a build that takes about four minutes, then run a dependent test. → - Wait on the process or the program's own completion and check its exit status; do not sleep a - fixed number of seconds. -- **Background PID capture.** Start a test server in the background, query it, and stop that exact - server afterwards. → Capture the PID at start, quote it, and stop that PID; do not locate the - process by name afterwards. +- **Retry backoff.** A transient network or API failure is worth retrying, and the service sends no + retry signal. → Retry with a bounded, short backoff of at most 5 seconds per attempt inside a + total retry window, or use the client's own retry options; never sleep to wait for progress. +- **Fixed sleep wait.** A command usually finishes in seconds, but the plan is to run `sleep` or a + `setTimeout` before reading its log; another build takes about four minutes. → Await the command's + own completion (a process wait, its exit status, or the runtime's job wait), which returns as soon + as it finishes, and make any poll exit as soon as the condition holds; never run a timer. +- **Bounded backoff.** A retry loop hits a service that fails transiently; the plan is to wait + between attempts. → Wait at most 5 seconds per attempt inside a bounded retry, and stop at the + total bound; never use a longer delay or a progress sleep. +- **Background PID capture.** Start a test server, query it, and stop that exact server afterwards. + → Start it as a runtime background job and capture the job handle or PID at start; stop that + handle; do not locate the process by name afterwards. - **Process check.** Check whether a named process is running and report its PID. → Use the PID - captured at start, Nushell (`ps | where name == 'foo'`), or an exact-name lookup such as - `pgrep -x`; do not match `ps` output with `grep`, which matches the grep itself. -- **Variable in one command.** Start a server on a port held in a variable and query that port in - one command. → Sequence it in code-mode, Nushell, Python, or TypeScript, and verify the variable - is set before using it. Checking a Bash variable does not make variable or background-job - sequencing simple Bash. + captured at start, an exact-name lookup such as `pgrep -x`, or one `nu -c` command; do not match + `ps` output with `grep`, which matches the grep itself. +- **Variable in one command.** Start a server on a port held in a variable and query that port. → + Sequence it in code-mode or in one Nushell or Python command; shell state does not survive between + tool calls, and a POSIX shell needs the steps in one script. - **Path with spaces.** Delete every `.log` file under a directory whose name contains a space. → - Quote the path or drive it from a runtime; do not iterate over an unquoted expansion or over `ls` - output. Confirm the deletion with the user rather than running an interactive `rm -i`. + Drive it from Python or Nushell with quoted paths; do not rely on Bash word splitting. Confirm the + deletion with the user rather than running an interactive `rm -i`. - **Pipeline exit status.** Run a build, keep only its last 20 log lines, and fail the task if the - build failed. → Preserve the build's own exit status (`set -o pipefail`, or capture it before - piping); the pipeline's last command must not mask it. -- **Heredoc quoting.** Append a literal `$PATH` string to a config file. → Keep the value literal (a - single-quoted argument, or a quoted heredoc delimiter) so the shell does not expand it. + build failed. → Run the build once, capture its output and exit status, and keep the lines in + code-mode; in a shell pipeline use `| complete` (Nushell) or `set -o pipefail` so the filter does + not mask the producer's failure. +- **Literal shell text.** Append a literal `$PATH` string to a config file. → Use the write or edit + tool; never a heredoc or a shell-quoted value. - **State across calls.** A temp directory was created in an earlier command; write a file inside it - now. → Do not assume shell state survives between calls: use the absolute path from the earlier - output, or do both steps in one runtime. + now. → Do not assume shell state or working directory survives between calls: use the absolute + path from the earlier output, or do both steps in one code-mode program. - **Capacity-limited fan-out** (behavioral trace, not a paper exercise). Fourteen independent units each take minutes; the runtime accepts at most ten concurrent units, refuses the rest until one finishes, and reports completion as an event. → Dispatch up to the cap, keep the refused remainder @@ -569,7 +594,7 @@ refusal handling and event-driven continuation; a caller's fixed sleep is not vi (`nu-check`) or report that it is unavailable, and treat a false or non-zero result as a failure. - **New target script.** A project, CI job, or container needs a new script and has no existing convention. → Default to Python unless the target ecosystem or data format has a clearer supported - runtime, such as TypeScript for JS/TS ecosystem tasks. Prefer Bun over Node.js when applicable. + runtime, such as TypeScript for JS/TS ecosystem tasks. Use Node.js when applicable. ### Communication @@ -580,6 +605,19 @@ refusal handling and event-driven continuation; a caller's fixed sleep is not vi Give each action its own paragraph or list item, keep the important facts, and omit repetition. - **Full review.** The user asks for a review and the agent finds eight issues. → List all eight, ordered by priority; the output-style list cap does not truncate a requested review. +- **Unanswered question.** A long build is running and the user asks a direct question that earlier + results can already answer. → Answer first; do not open new tool work before the answer, and do + not reply with investigation progress alone. +- **Steerable long job.** A build or benchmark takes about ten minutes on a remote host. → State the + interpreted target, action, expected duration, and artifact in one line; run it as a background + job and end the turn with its status and next step, letting the completion event resume the work. + Never wait a fixed duration before reading its result. +- **Wrong target echo.** The task names one target — a device, host, repository, or dataset — while + the planned work uses another. → State the interpreted target in the pre-work line so the mismatch + surfaces before the work starts. +- **Contradiction correction.** A new measurement contradicts a conclusion the agent stated earlier + in the session. → State the correction, what changed, and the new evidence; do not quietly restate + the new conclusion as if it had always held. - **Artifact language.** An existing document is written in another language and the conversation uses it, or a document is new and the repository has no convention. → Follow the existing document's language; write the new one in English. The focused regression and English control are diff --git a/agents/global-rules.md b/agents/global-rules.md index 3f548a80..1887fe10 100644 --- a/agents/global-rules.md +++ b/agents/global-rules.md @@ -141,6 +141,11 @@ For an impactful change, follow these steps in order, scaled to its risk: - Agents MUST NOT skip hooks without authorization. - Agents MAY amend, rebase, or squash their own unpushed commits; pushed commits and others' commits need authorization. +- Never block a turn on CI, whatever its expected duration. After a push, report the head revision + and the checks that started. Start a watch on the resolved run as a background job + (`gh run watch --exit-status`, or `gh pr checks --watch` once checks exist) following + Keep every turn steerable under Communication; collect and report the result when it finishes. Do + not estimate the run's duration or wait it out because it looks short. - Create forks with only the default branch (`gh repo fork --default-branch-only`); the task adds its own development branch. @@ -150,12 +155,11 @@ For an impactful change, follow these steps in order, scaled to its risk: `nix profile install`, global `npm`/`pip`). Use `nix shell nixpkgs# -c ` or `nix run` for one-off tools, and the project's existing toolchain (e.g. its flake, `uv`, `pnpm`) for its dependencies; ask before creating a flake or installing another way. -- The user's interactive shell is **nushell**; bash is only started from within nushell, - occasionally, for tasks nushell can't do. Put shell env, aliases, and per-session secrets in the - Nushell config, not in `bashrc`/`zshrc`. The secret block is `~/.secrets/nushell-secrets.nu`: - hand-edited, not Nix-managed, and agents MUST NOT read or edit it. +- The user's interactive shell is **nushell**, and shell work defaults to Nushell. Bash is only + started from within nushell, occasionally, for tasks nushell can't do. Put shell env, aliases, and + per-session secrets in the Nushell config, not in `bashrc`/`zshrc`. - Use `gh` for authorized GitHub operations; keep SSH for GitHub Git remotes and make sure the - Nix-managed `~/.ssh/config` is used; rerun outside the sandbox if it is rejected. + Nix-managed `~/.ssh/config` is used. - Code layout: `~/codes` = personal, `~/work` = work code, `~/src/` = source checkouts. - Agents MUST publish only to repositories that are already public or that the user names, and MUST NOT publish content from private repositories or internal sources to a public repository. @@ -164,32 +168,35 @@ For an impactful change, follow these steps in order, scaled to its risk: ### Tool execution -- Prefer native tool options to reduce output at the source. When code-mode (programmatic tool - calling, PTC) is available and can do the work directly and clearly, prefer it to orchestrate tool - calls and process their results, e.g. run independent read-only calls concurrently and filter them - before returning them. -- For local filtering, transformation, loops, polling, process inspection, command sequencing with - variables or background jobs, or complex quoting, agents MUST use code-mode, Nushell for - shell-native pipelines, Python for general local processing, or TypeScript for the JS/TS ecosystem - (Bun over Node.js) rather than Bash logic. A Bash-only tool can invoke them with `nu -c`, - `python -c`, `bun -e`, or a quoted heredoc; if no runtime is available, use the toolchain and - one-off Nix rules above; if none can do the work, report the limitation. Simple, obviously correct - commands such as running a tool or a short `&&` sequence are fine in Bash; a command that needs a - shell variable, a background job, or a loop is not simple. On remote hosts, use the available - shell. +- When code-mode (programmatic tool calling, PTC) is available, agents MUST orchestrate multi-step + work in one program: dispatch independent calls concurrently, process and filter results in code + without hiding a command's own failure, and bound each call so one slow call does not abandon the + batch. Keep state shared across code-mode runs in the workspace, not a per-run temp directory. +- 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, in its own dialect, and never hand a command string to another + shell (`bash -c`, `sh -c`). Default shell work to Nushell. Compose multi-step work with code-mode + (PTC) for orchestration, returned tool results, loops, and state across calls; otherwise use + Nushell's structured pipelines when available, Python for general local processing, one runtime + invocation (`python -c ''`, `nu -c ''`), or a script written with the write tool. Set + the tool's working-directory parameter instead of `cd`. A remote command usually runs under a + POSIX shell: keep it to one runtime invocation or a copied script. - Commands MUST NOT block: disable pagers and interactive prompts (confirm with the user instead of making a command interactive, e.g. `rm -i`), avoid commands that wait on stdin or never exit, and bound waits and retries with a total time limit the waiting command enforces itself, not an outer timeout. Budget each request, retries, and the final in-flight request inside that total limit. A - retry limit alone does not bound an in-flight request. Prefer the waiting program's own wait (e.g. - `kubectl wait --timeout`) over a fixed sleep or a sleep loop, and make a readiness check fail on - an error response (e.g. `curl --fail`), not only on a refused connection. Report long-job - progress. -- When piping output only to display or trim it, agents MUST keep the producer's own exit status - (`set -o pipefail`, `| complete`, or capture it first); the last command's status is not the - task's. -- Run servers and watchers in the background with output redirected to logs, capturing their PIDs - when starting them, not by matching `ps` output. + retry limit alone does not bound an in-flight request. Never use a fixed delay as a completion + wait: no sleep-then-check loop, no `setTimeout` poll, no sleep-then-tail. Await the work's own + completion (the runtime's job wait, `kubectl wait --timeout`, or the process exit) and continue + when it fires; any poll MUST check the condition and exit as soon as it holds. A retry or protocol + backoff MAY wait at most 5 seconds per attempt, never longer and never as a progress wait. Make a + readiness check fail on an error response (e.g. `curl --fail`), not only on a refused connection. +- When output is filtered or trimmed, agents MUST keep the producer's own exit status: capture + output and status together (`| complete` in Nushell, `set -o pipefail` in a POSIX shell) or in + code, and never report a successful filter as a successful command. +- Start servers and watchers as runtime background jobs with their output going to logs; capture the + job handle or PID when starting them, never by matching `ps` output or a shell `&`. ### Scripts @@ -204,8 +211,16 @@ For an impactful change, follow these steps in order, scaled to its risk: comments, commit messages, PRs, issues and docs — in English, whatever the conversation uses, unless the file or repository sets its own convention. Keep literal UI strings and match patterns as they are. -- Restate the requested scope across turns. When a change takes effect only after a restart or - activation someone else performs, report what is applied and what is still pending. +- Answer before acting. When the user asks, the answer or recommendation comes before new tool work; + when the answer needs investigation, say in one line what you will check, then answer rather than + reporting only the investigation. +- Keep every turn steerable. Before work that will keep the user waiting, state the interpreted + target, action, expected duration (when knowable), and artifact in one line, run it as a + background job, and end the turn with its status and next step; let the job's completion event + resume the work. Restate the requested scope across turns: when a change takes effect only after a + restart or activation someone else performs, report what is applied and what is still pending. +- State corrections explicitly: what is now believed, what changed, and the new evidence; do not + quietly restate the new conclusion as if it had always held. - Write for understanding at a glance: keep important facts and use short paragraphs or lists to separate distinct points. Brevity should reduce reading effort, not remove useful formatting. - A review or report the user asked for lists every finding, ordered by priority; the output-style diff --git a/flake.lock b/flake.lock index 50b56de7..703c7bb7 100644 --- a/flake.lock +++ b/flake.lock @@ -1027,11 +1027,11 @@ ] }, "locked": { - "lastModified": 1792229484, - "narHash": "sha256-6ETJhHm7tW/c5ys0kepRVC79cRze2gbHhNsfKDS+AzA=", + "lastModified": 1792297214, + "narHash": "sha256-zRPv4X2ywq1gcBwbI2qCS7pur7oneze2eQAUPpODlnw=", "owner": "ryan4yin", "repo": "nur-packages", - "rev": "6c543b8850770cd34a9d4793168cdb93b3a959c5", + "rev": "1a845e77e62f8a2a1653d994e60981c0684087f2", "type": "github" }, "original": { diff --git a/flake.nix b/flake.nix index 0a48c38c..8bf22a8c 100644 --- a/flake.nix +++ b/flake.nix @@ -204,7 +204,6 @@ url = "github:DietrichGebert/ponytail/v5.1.0"; flake = false; }; - ######################## My own repositories ######################################### # my private secrets, it's a private repository, you need to replace it with your own. diff --git a/home/base/gui/rime/default.nix b/home/base/gui/rime/default.nix index 5bc02d6d..729a55dc 100644 --- a/home/base/gui/rime/default.nix +++ b/home/base/gui/rime/default.nix @@ -1,13 +1,13 @@ { - inputs, lib, + myNurPkgs, pkgs, ... }: let # flypy (小鹤音形) rime data from nur-ryan4yin (`pkgs/rime-data-flypy`). The # package stays pristine; personal preferences live in flypy.custom.yaml. - rime-data-flypy = inputs.nur-ryan4yin.packages.${pkgs.stdenv.hostPlatform.system}.rime-data-flypy; + rime-data-flypy = myNurPkgs.rime-data-flypy; # Personal Rime customisation, layered by Rime on top of the shared rime-data. custom = ./flypy.custom.yaml; # Manually maintained user dictionary (custom phrases, symbols, missing diff --git a/home/base/tui/agents/README.md b/home/base/tui/agents/README.md index ff597473..9165b2b1 100644 --- a/home/base/tui/agents/README.md +++ b/home/base/tui/agents/README.md @@ -15,11 +15,11 @@ 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`, `opencode2`, `pi`) from the `llm-agents` flake input, plus `pkgs.mcp-grafana` for the Grafana MCP server. -- `env.nix`: telemetry and auto-update opt-outs for the agents. - -dsh is installed by this module but configured at runtime, not by Nix: 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. dsh is installed by this module and + configured at runtime: profile patches live in `~/.dsh/profiles//cordis.patch.yml`. The + Nushell-only shell is selected when the package is built, via `dsh.withPlugins` from + `nur-ryan4yin`, so it is a derivation choice rather than runtime state. See [dsh.md](./dsh.md) for + the layer order and the settings that bite. ## Deployed rule targets diff --git a/home/base/tui/agents/dsh.md b/home/base/tui/agents/dsh.md index da3b5657..09044d3a 100644 --- a/home/base/tui/agents/dsh.md +++ b/home/base/tui/agents/dsh.md @@ -8,13 +8,14 @@ profile: an ordered stack of patch layers over an empty root entry list, where l | 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 | 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 row outranks it, so the UI rejects that setting with -`Configuration for "..." is overridden by a home patch`. This repository therefore ships no home -layer — profile patches are machine-local runtime state, the place for private endpoints (LAN -servers, staging gateways) too, and Nix only installs the CLI. +`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. Three behaviours that are easy to get wrong: @@ -49,3 +50,36 @@ 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 + +`nur-ryan4yin`'s `dsh.withPlugins [ ...dsh-nushell-only ]` replaces the two first-party shell +providers with Nushell: 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` comes from the +NUR package, so it does not depend on `PATH`. + +The source is the `develop` branch of ryan4yin's fork carrying the reviewed patch set (Nu-builtin +denial facts, test-harness fixes, the `find` hint, persistent-terminal scoping), pinned by +`fetchFromGitHub` and `hashes.json` in `nur-packages`; nix-config locks that package set in its +`flake.lock`. `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 (the fork's `--with-terminal-bundle` integration run covers the +coexistence). 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 -n "nushell" +# expect the first-party shell rows disabled, nushell-executor and nushell-teaching inserted +dsh --profile web 2>&1 | grep "disabling profile plugin" +# a dsh bump can disable a row whose manifest no longer matches; nothing prints when healthy +``` diff --git a/home/base/tui/agents/packages.nix b/home/base/tui/agents/packages.nix index f52bbf6c..a617c44a 100644 --- a/home/base/tui/agents/packages.nix +++ b/home/base/tui/agents/packages.nix @@ -1,7 +1,7 @@ { pkgs, llm-agents, - nur-ryan4yin, + myNurPkgs, ... }: let @@ -14,8 +14,9 @@ in agentPackages.opencode2 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. - nur-ryan4yin.packages.${pkgs.stdenv.hostPlatform.system}.dsh + # (node-addon-require-builtin probe), this one shims it. The Nushell-only + # plugin rides in through the package's withPlugins wrapper. + (myNurPkgs.dsh.withPlugins [ myNurPkgs.dsh-nushell-only ]) # 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. diff --git a/hosts/idols-ruby/packages.nix b/hosts/idols-ruby/packages.nix index e1a826bf..88c42945 100644 --- a/hosts/idols-ruby/packages.nix +++ b/hosts/idols-ruby/packages.nix @@ -1,7 +1,7 @@ { pkgs, llm-agents, - nur-ryan4yin, + myNurPkgs, ... }: let @@ -87,6 +87,6 @@ in 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. - nur-ryan4yin.packages.${pkgs.stdenv.hostPlatform.system}.dsh + myNurPkgs.dsh ]; } diff --git a/outputs/default.nix b/outputs/default.nix index 5d95859a..6fced9d6 100644 --- a/outputs/default.nix +++ b/outputs/default.nix @@ -60,6 +60,9 @@ let cudaCapabilities = [ "8.9" ]; }; }; + # NUR packages for this system, so modules use `myNurPkgs.dsh` instead of + # repeating `nur-ryan4yin.packages.${pkgs.stdenv.hostPlatform.system}`. + myNurPkgs = inputs.nur-ryan4yin.packages.${system}; }; # This is the args for all the haumea modules in this folder. diff --git a/overlays/computer-use-linux.nix b/overlays/computer-use-linux.nix index 39ac90fd..64391c85 100644 --- a/overlays/computer-use-linux.nix +++ b/overlays/computer-use-linux.nix @@ -1,4 +1,3 @@ -{ inputs, ... }: _final: prev: { - computer-use-linux = - inputs.nur-ryan4yin.packages.${prev.stdenv.hostPlatform.system}.computer-use-linux; +{ myNurPkgs, ... }: _final: _prev: { + computer-use-linux = myNurPkgs.computer-use-linux; } diff --git a/overlays/cua-driver.nix b/overlays/cua-driver.nix index a99acc14..15ccf3a2 100644 --- a/overlays/cua-driver.nix +++ b/overlays/cua-driver.nix @@ -1,3 +1,3 @@ -{ inputs, ... }: _final: prev: { - cua-driver = inputs.nur-ryan4yin.packages.${prev.stdenv.hostPlatform.system}.cua-driver; +{ myNurPkgs, ... }: _final: _prev: { + cua-driver = myNurPkgs.cua-driver; }