Symlink safety for copy and conversion, Windsurf --path, OpenClaw registration reporting; maintainer follow-ups so a stray link or one failed registration doesn't abort the rest. Tested with the destructive matrix (25 + 6 scenarios), real OpenClaw 2026.9.6, and all 19 suites on macOS and Linux.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
#915 made registration failures visible (main swallowed them with `|| true` and
reported success). But it returned at the first failure, so every agent after it
went unregistered. In real OpenClaw 2026.9.6 with one registration forced to fail,
batch 2 registered only 1 of the 2 agents that could have succeeded.
Keep registering the rest, name every failure in one message at the end, and
still exit non-zero. Verified against real OpenClaw (node:24, official npm
package): the failure case now registers both good agents and exits 1; the
normal install and the re-run are unchanged. #915's own test still passes.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
#916 stops cp from writing through a symlink at an agent's destination, which on
main overwrites whatever the link points at (verified: a user's file was replaced
by an agent). But it made one stray link fatal: under `set -e` the refusal aborted
the install at the first link, leaving a partial roster (57 of 279 in testing).
It also refused our own links, so re-installing without --link after a --link
install failed for every file (main fails there too, with "are the same file").
- A link into this clone ($REPO_ROOT) is ours: remove it and copy. That's the
intended --link -> copy switch, and the clone's sources stay untouched.
- Any other link is skipped with a warning, recorded, and the install continues.
The final summary lists what was not installed. A temp file carries the list so
it survives the parallel installer's subshells.
- test-install.sh: #916's case now asserts skip-and-continue (sentinel intact,
link left in place, the rest of the selection installs, summary reports it);
new case for --link then copy (link replaced by a real file, source unchanged).
65 passed, 0 failed.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Installer, converter and CI hardening from @rudycelekli (15 PRs, each with a regression test that fails before its fix), plus a maintainer fix so the Hermes installer never follows a symlink when replacing its plugin. Tested with a 25-scenario destructive-path matrix (25/25), signal tests, real Hermes (install, upgrade, live delegation), and all 17 suites on macOS and Linux.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
`rm -rf "$dest/"` follows a symlink and empties its target. basename ignores a
trailing slash, so HERMES_PLUGIN_DIR=".../agency-agents-router/" passed every
check, and a symlinked destination (our own --link install, or a user's folder)
had its target's contents deleted. #903's ownership check didn't cover it: the
check reads through the link and the rm still follows it.
- Strip all trailing slashes from the destination before any check.
- A symlinked destination is removed with `rm -f` (the link itself), never
`rm -rf`.
- test-install-hermes-destination.sh: two new cases (trailing slash on a
symlink to a folder with a matching plugin.yaml; re-install over a --link
install). Both fail before this commit and pass after.
Found by a destructive-path matrix (25 scenarios, throwaway container, fake HOME
with sentinel files): main fails 10 (deletes unrelated dirs, files and symlink
targets at the plugin path); the series without this commit fails 2 (H8/H9);
with it, 25/25.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The Hermes installer printed "[OK] enabled plugin" and "Done!" while leaving agency-agents-router disabled or absent. On current Hermes this hit every user: the default plugins: block contains a column-0 "# ====" banner, and `hermes plugins enable/disable` writes enabled:/disabled: below it.
- Bound the enabled: sub-block at the first sibling key; sweep stale entries out of disabled: (Hermes: "an explicit disable wins"); handle inline [], [a,b], disabled: above enabled:, trailing comments, corrupted-scalar recovery; bail on shapes that can't be edited line-wise. (@guozi-lab)
- A column-0 comment no longer ends the plugins: block, and the rewrite's exit status propagates (`|| return 1`) so a bail warns instead of reporting success. (maintainer follow-up)
- check-hermes-config-rewrite.py: 16 cases, including Hermes' real shape.
Verified in real Hermes (official installer, 9a0a162) on configs Hermes wrote itself, with a live turn on local qwen3.8-27b: main -> "agency_agents_delegate does not exist"; this change -> delegated: true, real subagent, real result.
Closes#879
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Adds DeepSeek Harness as an integration target: 279 agency-* SKILL.md skills installed to ${DSH_HOME:-~/.dsh}/skills (or DSH_SKILLS_DIR for project scope).
Verified in DeepSeek Harness itself (@deepseek-ai/dsh 0.1.7-rc.2, installed via the documented npx path on Ubuntu 26.04 arm64): the repo installer placed 279 skills, and dsh's own skill-filesystem parser accepted all 279 with zero warnings (a planted invalid name and a missing description were both rejected with dsh's own messages).
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The section held one entry, describing awesome-openclaw-agents as "derived
from this repo". That claim does not hold up: its README never mentions this
project, and only 2 of our 265 agent names appear anywhere in its list —
"incident responder" and "ux researcher", both generic role names that are
coincidence rather than lineage. It was last pushed 2026-05-25.
Asserting a derivation the other project does not claim, and that the content
does not support, is not something to leave in the README.
The section was also generating work it could not pay for. #876 asked to add a
third-party workbench to it — a reasonable thing to attempt precisely because
the section existed — and declining that meant explaining a curation standard
the one existing entry did not meet.
Removing it rather than correcting the line: a list of one, kept honest by
hand, invites requests to grow it and gives no way to judge them.
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
#871 shrank the aider integration from 3.8M characters of concatenated agents
to a 97K roster index, because aider keeps a conventions file in context for a
whole session. The fix only reached new installs.
install_aider refuses to overwrite an existing CONVENTIONS.md, which is right —
it is aider's own user-authored file and the one on disk may be the reader's.
But "already exists (remove to reinstall)" says nothing about which file it is,
so anyone holding the pre-index roster re-ran the installer, read that, and
kept the broken file. The people the fix was written for were the ones it could
not reach.
Our generated file has always opened with "# The Agency — AI Agent
Conventions", so the installer can now tell its own stale copy from someone
else's conventions and say which it is. Neither branch writes anything.
Verified in a container, all three cases:
stale Agency roster -> names it, reports 3800039 bytes, says to delete and re-run
user's own file -> "leaving your file alone", file intact
no file -> installs, 97065 bytes
Windsurf has the same guard and the same gap; #870 already handles it there, so
this leaves that path alone rather than colliding with it.
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>