Skip to content

dl --herdr-setup installs the whole Herdr kit - #656

Open
blooop wants to merge 23 commits into
mainfrom
feat/herdr-setup-kit
Open

blooop wants to merge 23 commits into
mainfrom
feat/herdr-setup-kit

Conversation

@blooop

@blooop blooop commented Sep 29, 2026 •

Copy link
Copy Markdown
Owner

What

dl --herdr-setup now installs the whole Herdr kit, so a new machine gets a working Herdr + devlaunch setup from devlaunch alone, with no dotfiles:

  • the pane shell (dl-herdr-shell), as before
  • status.sh (CPU, memory, disk for the tab bar) and the agent-queue plugin under $XDG_DATA_HOME/devlaunch/herdr/, then herdr plugin link and the plugin's startup hook (linking does not fire it)
  • a merge into Herdr's config ($HERDR_CONFIG_PATH, else $XDG_CONFIG_HOME/herdr/config.toml)
  • herdr integration install claude, only when herdr integration status says missing or outdated
  • a tab-title Stop hook (~/.claude/hooks/devlaunch-herdr-tab-title.sh) and its settings.json entry
  • herdr's skill at ~/.claude/skills/herdr/SKILL.md, from herdr --skill
  • kitty's F2/F3/F4 fix in devlaunch-herdr.conf, plus an include line in kitty.conf
  • ~/.local/bin/herdr -> ~/.pixi/bin/herdr, only when the first is absent and the second exists

New modifiers: --dry-run (print every planned change, write nothing), --no-claude, --no-kitty.

Why

devlaunch ships as a conda package and a wheel that hold only binaries, and the glue lived only in one user's chezmoi source. Every asset is now include_str!'d into dl and written at setup time.

How it behaves

  • Each step prints changed / current / skipped / failed with a reason. Files are written only when their content (or a script's mode) differs, by rename from a temp file in the same directory, through a symlink when the path is one. A second run writes nothing.
  • Config merge: managed keys are set on every run: terminal.default_shell, the prefix+a -> local.agent-queue.toggle binding, the status.sh segment in ui.tab_bar_right, the · herdr end of ui.window_title. Each stops at a value that is plainly the user's (custom default_shell fails the step as before; a user's own status.sh keeps its segment; prefix+a bound elsewhere stays bound). Default keys (the rest of the packaged keymap, theme, sidebar, toasts) are added only when absent. An empty or missing config gets the packaged file verbatim.
  • settings.json: only devlaunch's Stop hook is added or corrected, and duplicates of it and of herdr's herdr-agent-state.sh SessionStart hook are removed. Every other key and hook stays, in order. Backed up once to settings.json.devlaunch-backup. If a dotfiles herdr-tab-title.sh hook is registered, devlaunch adds neither its entry nor its script.
  • chezmoi: Herdr's config, settings.json and kitty.conf are probed with the existing chezmoi source-path check. A managed file is skipped with the line to add; the other steps still run (before, the whole command refused). A chezmoi that cannot answer still fails.
  • It never starts or reloads a Herdr server; it prints herdr server reload-config when the config changed, and names DEVLAUNCH_HERDR=1 as an opt-in (its default is unchanged).
  • The config merge moved from devlaunch_core::flows::herdr_environment::configure into dl's new herdr_kit module, so public-api.rest.txt loses that one row.
  • clap's requires = "herdr_setup" is met by any member of the what group, so dl --prune --dry-run parsed. The three modifiers are refused explicitly on other commands, as a usage error with exit 2. (--prune --json has the same pre-existing gap; not touched here.)

Try it

T=$(mktemp -d); mkdir -p $T/.claude $T/.config/kitty
env -i HOME=$T PATH=/usr/bin:/bin:$HOME/.pixi/bin ./rust/target/release/dl --herdr-setup --dry-run

Use a scratch HOME as above, and unset CLAUDE_CONFIG_DIR, or it writes your real Claude settings.

Tests

  • Rust unit tests for the merge (managed vs default keys, user values kept, table order, idempotency, custom shell refused) and for settings.json (foreign hooks kept in order, our and herdr's duplicates removed, dotfiles hook respected).
  • rust/dl/tests/herdr_environment.rs: end-to-end runs of the binary in an isolated HOME with a herdr stub that records its argv (full install then no-op second run, dry run writes nothing, --no-claude/--no-kitty, backup once, failing --skill keeps the old skill, plugin linked elsewhere, chezmoi-managed files, herdr missing).
  • test/unit/test_herdr_setup_kit.py: the same, from the pytest harness against the release binary.

🤖 Generated with Claude Code

Summary by Sourcery

Make dl --herdr-setup install and safely maintain the complete Herdr integration kit without requiring dotfiles.

New Features:

  • Expand dl --herdr-setup into a complete, self-contained Herdr and devlaunch installation kit, including configuration, plugins, Claude Code integration, skills, and kitty support.
  • Add --dry-run, --no-claude, and --no-kitty modifiers for controlling setup behavior.

Bug Fixes:

  • Make Herdr setup idempotent and preserve user-owned configuration, hooks, symlinks, file modes, and chezmoi-managed files while reporting skipped or failed steps clearly.

Enhancements:

  • Move Herdr configuration merging into the dl setup flow with managed-key updates, default-key preservation, atomic writes, and reload guidance.
  • Add robust Claude settings merging with duplicate-hook cleanup, one-time backups, and respect for existing dotfiles tab-title hooks.
  • Embed all Herdr kit assets in the dl binary so packaged installations no longer depend on dotfiles.

Build:

  • Move TOML editing and temporary-file dependencies to the dl package for Herdr kit installation.

Documentation:

  • Document the expanded Herdr setup workflow, configuration merge behavior, Claude and kitty integration, chezmoi handling, and dry-run options.

Tests:

  • Add unit, Rust end-to-end, and pytest coverage for full installation, idempotency, dry runs, selective installation, configuration preservation, hook handling, symlinks, chezmoi integration, and missing dependencies.

Chores:

  • Update CLI completions and public API metadata for the new setup options and relocated configuration logic.

dl --herdr-setup now writes, besides the pane shell, the status segment and
the agent-queue plugin (then links it and runs its startup hook), merges the
packaged keymap/theme into Herdr's config (managed keys always set, default
keys only when absent), installs herdr's Claude integration when it is
missing or outdated, merges a tab-title Stop hook into settings.json (foreign
hooks kept, only our entries and herdr's duplicates deduped, backed up once),
writes herdr's skill (old one kept when --skill fails), writes kitty's F-key
fix with an include line, and links ~/.local/bin/herdr to ~/.pixi/bin/herdr.

Every asset is include_str!'d so the conda package and the wheel carry it.
--dry-run, --no-claude and --no-kitty modify it. A chezmoi-managed file is
skipped with the line to add; the other steps still run.

The config merge moves from devlaunch-core (flows::herdr_environment::configure)
into dl's herdr_kit, so the rest snapshot loses that one row.
Isolated HOME and XDG dirs, a fake herdr that records its argv, and the
release binary: one run installs everything, a second writes nothing, and
a dry run leaves the disk as it was.

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry @blooop, you've used your own review budget of 250,000 diff characters for the last 7 days.

You can request another review in 2 days and 22 hours by commenting @sourcery-ai review. Upgrade to get a review now.

@sourcery-ai

sourcery-ai Bot commented Sep 29, 2026

Copy link
Copy Markdown

Reviewer's Guide

dl --herdr-setup is reworked into an idempotent, atomic installer for the full Herdr kit, including embedded assets, config merging, plugin/Claude/kitty integration, and setup-only modifiers, with extensive documentation and isolated end-to-end coverage.

Sequence diagram for an idempotent Herdr kit installation

sequenceDiagram
    participant User
    participant DL as dl
    participant FS as Filesystem
    participant Herdr
    participant Chezmoi
    User->>DL: --herdr-setup
    DL->>FS: Install embedded assets when content or mode differs
    DL->>Herdr: plugin list --json
    alt Plugin is not linked
        DL->>Herdr: plugin link
        DL->>FS: Run agent-queue view.sh set
    else Plugin already linked elsewhere
        DL-->>User: Skip plugin link
    end
    DL->>Chezmoi: source-path Herdr config
    alt Config unmanaged
        DL->>FS: Merge managed and default config keys
    else Config managed
        DL-->>User: Report skipped with source path
    end
    opt Claude enabled and config directory exists
        DL->>Herdr: integration status
        alt Missing or outdated
            DL->>Herdr: integration install claude
        end
        DL->>FS: Merge settings.json and write skill from --skill
    end
    opt Kitty enabled and config directory exists
        DL->>FS: Write devlaunch-herdr.conf and include line
    end
    DL-->>User: Report outcomes and reload hint
Loading

Flow diagram for the Herdr kit setup command

flowchart TD
    A["dl --herdr-setup"] --> B["Parse setup options"]
    B --> C["Install pane shell and embedded kit assets"]
    C --> D["Link agent-queue plugin and run startup hook"]
    D --> E["Probe chezmoi and merge Herdr config"]
    E --> F["Optionally install Claude integration, hooks and skill"]
    F --> G["Optionally install kitty F-key fix"]
    G --> H["Report changed, current, skipped or failed"]
    H --> I["Print reload-config hint when config changed"]
Loading

File-Level Changes

Change Details Files
Expanded Herdr setup from a pane-shell installer into an idempotent, multi-step machine bootstrap that embeds and installs the complete Herdr/devlaunch kit.
  • Added embedded status, plugin, config, Claude hook, and kitty assets.
  • Added atomic, mode-aware, symlink-preserving writes with dry-run reporting.
  • Added plugin linking and startup-hook execution, Claude integration/skill setup, kitty include handling, and optional local Herdr symlink.
  • Added chezmoi ownership checks so managed files are skipped while independent steps continue.
rust/dl/src/herdr_kit/mod.rs
rust/dl/src/herdr_kit/assets/agent-queue/herdr-plugin.toml
rust/dl/src/herdr_kit/assets/agent-queue/view.sh
rust/dl/src/herdr_kit/assets/status.sh
rust/dl/src/herdr_kit/assets/devlaunch-herdr-tab-title.sh
rust/dl/src/herdr_kit/assets/kitty.conf
rust/dl/src/herdr_kit/assets/config.toml
rust/dl/Cargo.toml
rust/Cargo.lock
Introduced a TOML-preserving Herdr configuration merge with managed and default-key policies.
  • Moved config editing out of devlaunch-core into the dl Herdr kit module.
  • Preserved user values, comments, table order, file modes, and symlinks while adding missing packaged defaults.
  • Managed the pane shell, agent-queue binding, status segment, and window-title suffix with safeguards for custom user settings.
  • Added tests for idempotency, ordering, custom-shell refusal, user bindings, and custom status segments.
rust/dl/src/herdr_kit/config.rs
rust/devlaunch-core/src/flows/herdr_environment.rs
rust/devlaunch-core/Cargo.toml
rust/devlaunch-core/public-api.rest.txt
Added narrowly scoped Claude Code settings integration that preserves unrelated configuration.
  • Added or corrected devlaunch's Stop hook and removed duplicate devlaunch/herdr hook entries.
  • Detected existing dotfiles tab-title hooks and avoided installing a competing hook.
  • Created a one-time settings backup and protected an existing skill when herdr --skill fails.
  • Added JSON merge unit and end-to-end coverage.
rust/dl/src/herdr_kit/claude.rs
rust/dl/src/herdr_kit/assets/devlaunch-herdr-tab-title.sh
rust/dl/tests/herdr_environment.rs
test/unit/test_herdr_setup_kit.py
Added setup-specific CLI modifiers and validation.
  • Added --dry-run, --no-claude, and --no-kitty options to setup.
  • Explicitly rejected those modifiers on unrelated commands despite clap group behavior.
  • Updated command dispatch, shell completions, and completion-table tests.
rust/dl/src/cli.rs
rust/dl/src/commands.rs
rust/dl/src/lib.rs
rust/devlaunch-core/completions/dl.bash
rust/dl/tests/completion_tables.rs
Updated user-facing documentation and regression coverage for the expanded setup contract.
  • Documented installed files, merge semantics, Claude/kitty behavior, chezmoi handling, dry-run behavior, and non-actions.
  • Added full-machine, idempotency, dry-run, opt-out, failure, symlink, and missing-dependency integration scenarios.
  • Recorded the feature in the changelog and README.
CHANGELOG.md
README.md
docs/workspace-tools.md
rust/dl/tests/herdr_environment.rs
test/unit/test_herdr_setup_kit.py

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@codecov

codecov Bot commented Sep 29, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 93.32049% with 104 lines in your changes missing coverage. Please review.
✅ Project coverage is 95.10%. Comparing base (7012aae) to head (c7c2c6c).

Files with missing lines Patch % Lines
rust/dl/src/herdr_kit/mod.rs 92.25% 56 Missing ⚠️
rust/dl/src/herdr_kit/config.rs 91.88% 46 Missing ⚠️
rust/dl/src/herdr_kit/claude.rs 99.11% 2 Missing ⚠️
Additional details and impacted files
Flag Coverage Δ
python 42.98% <ø> (ø)
rust 95.32% <93.32%> (+0.18%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Components Coverage Δ
shipped code (rust) 95.32% <93.32%> (+0.18%) ⬆️
harness and tooling (python) 42.98% <ø> (ø)
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

herdr appends its current SessionStart entry, so the last copy is the one to keep.
herdr_config built terminal.default_shell and the status.sh segment from the paths it meant to install, not from what this run installed. When the pane shell (or status.sh) could not be written, the config still pointed every new pane at a missing file. The install steps now hand back the installed path, and the config step is skipped when either is absent.
…ings.json

`herdr integration install claude` rewrote ~/.claude/settings.json even when
chezmoi managed it and the settings step had skipped it, so the next
`chezmoi apply` reverted the change. The integration step now asks chezmoi
first and reports skipped, with the hooks to add to the dotfiles source.
With [keys] zoom = "f6", setup added rename_tab = ["prefix+shift+t", "f6"];
with a [[keys.command]] on f7, it added f7 to next_agent. A filled default
action or command now drops every key string the user's [keys] already binds,
and an action left with no key is not added.
…action

With [keys] zoom = "prefix+a", setup still added a [[keys.command]] on
prefix+a, because it checked the key only against other [[keys.command]]
entries. It now checks every key the user's [keys] binds, and reports the
toggle skipped as it does for a command.
…stered tab-title hook

settings() returned false both for "no other tab-title hook" and for
"could not read settings.json", so a settings.json with invalid JSON
failed the settings step and still got ~/.claude/hooks/devlaunch-herdr-tab-title.sh,
which nothing runs. settings() now says Ours, Other or Unknown, and the
script is installed only on Ours.
… is no longer reported as needing no change

The chezmoi branch said "it needs no change" whenever another
herdr-tab-title.sh hook was registered, using that as a stand-in for the
merge changing nothing. A file with that hook beside devlaunch's own, or
with two herdr SessionStart copies, still needs entries removed. The
report now follows the merge: no change only when it would write nothing,
otherwise the steps it would take.
…er run refuses, or re-ask for a hook already added

report() spelled the home directory ~ across the whole line, so the
herdr config instruction told the user to paste
default_shell = ~/.local/bin/dl-herdr-shell, which the next run's
set_default_shell refuses as a custom shell; the same ~ in the Stop hook
command would sit in single quotes and never expand. Text to paste is now
in backticks and kept verbatim.

The chezmoi settings report also asked to add the Stop hook on every run,
even after the user had added it with devlaunch's exact command, because
the merge counts a hook without the timeout as needing a correction. It
now asks only when no Stop hook runs that command.
The packaged herdr config predates [session] resume_agents_on_restore and
startup_per_agent_delay_ms, so dl --herdr-setup never turned auto-resume on.
Ship both as default keys: a new config gets them, and a user's value wins.
The suite clears the child's environment, which dropped LLVM_PROFILE_FILE, so
every counter the 37 tests produced went to a stray default_*.profraw that
cargo llvm-cov never reads. Forward it through KeepingCoverage, as the other
binary-boundary suites do.
It runs in the rust job but was missing from the coverage suite list, so
herdr_kit and herdr_environment.rs reported only what the unit tests reach.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant