Compare commits

...
2 Commits
Author SHA1 Message Date
Matt PocockandClaude Opus 4.8 085d886fca feat(spawn): default to the running agent; herdr-first management, no nohup
Six agents (claude, codex, pi, cursor, opencode, copilot). claude keeps
its native --bg/agents management. Everything else spawns interactively
inside herdr when its server is up (list/read/send/attach); otherwise a
transient systemd --user unit gives status/logs/stop. Research confirmed
none of the five non-claude CLIs has a native background job manager.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-23 10:47:09 +01:00
Matt PocockandClaude Opus 4.8 fc73c04a34 feat(spawn): generalise claude-handoff into agent-agnostic spawn skill
SKILL.md now only defines the handoff; per-agent launch mechanics
(claude --bg vs backgrounded codex exec) live in a bundled spawn.sh
the agent runs but never reads. Prompt arrives via heredoc/stdin so
long summaries with quotes survive.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-23 10:37:53 +01:00
5 changed files with 119 additions and 23 deletions
@@ -1,18 +0,0 @@
---
name: claude-handoff
description: Hand the current conversation off to a fresh background agent that picks up the work immediately.
argument-hint: "What will the next session be used for?"
disable-model-invocation: true
---
Write a handoff summary of the current conversation so a fresh agent can continue the work. Instead of saving it, launch a background agent seeded with the summary as its prompt: `claude --bg --name "<descriptive name>" "<handoff summary>"`. It starts in the current working directory and returns immediately; the user manages it with `claude agents`.
Always pass `-n`/`--name` with a descriptive name (e.g. `--name "Fix login bug"`) — it sets the display name shown in the job list, session picker, and terminal title.
Include a "suggested skills" section in the summary, which suggests skills that the agent should invoke.
Do not duplicate content already captured in other artifacts (PRDs, plans, ADRs, issues, commits, diffs). Reference them by path or URL instead.
Redact any sensitive information, such as API keys, passwords, or personally identifiable information — the summary becomes the agent's prompt.
If the user passed arguments, treat them as a description of what the next session will focus on and tailor the summary accordingly.
@@ -1,5 +0,0 @@
interface:
display_name: "Claude Handoff"
short_description: "Hand off to a background agent"
policy:
allow_implicit_invocation: false
+23
View File
@@ -0,0 +1,23 @@
---
name: spawn
description: Hand the current conversation off to a fresh background agent that picks up the work immediately.
argument-hint: "[agent] What will the next session be used for?"
disable-model-invocation: true
---
Write a handoff summary of the current conversation so a fresh agent can continue the work, then spawn the agent with it via the bundled [spawn.sh](spawn.sh) — run it, never read it:
```bash
bash <this skill's base directory>/spawn.sh <agent> "<descriptive session name>" - <<'PROMPT'
<handoff summary>
PROMPT
```
`<agent>` is one of `claude | codex | pi | cursor | opencode | copilot`. Spawn the agent you are running as, unless the user named another. The agent starts in the current working directory; the script prints how the user monitors and manages the session — relay that.
The handoff summary:
- Include a "suggested skills" section naming skills the agent should invoke.
- Reference artifacts (PRDs, plans, ADRs, issues, commits, diffs) by path or URL instead of duplicating them.
- Redact sensitive information (API keys, passwords, PII) — the summary becomes the agent's prompt.
- If the user passed arguments beyond the agent choice, treat them as what the next session will focus on and tailor the summary accordingly.
@@ -0,0 +1,5 @@
interface:
display_name: "Spawn"
short_description: "Hand off to a background agent (claude or codex)"
policy:
allow_implicit_invocation: false
+91
View File
@@ -0,0 +1,91 @@
#!/usr/bin/env bash
# spawn.sh <agent> <name> [prompt|-]
# Launches a background agent session seeded with the prompt.
# Prompt is read from stdin when the third argument is omitted or '-'.
#
# claude has native managed background jobs (claude --bg / claude agents).
# Every other agent is spawned interactively inside herdr when its server is
# running (list/read/send/attach management); otherwise as a supervised
# one-shot under the user's systemd session (status/logs/stop management).
set -euo pipefail
usage() {
echo "usage: spawn.sh <claude|codex|pi|cursor|opencode|copilot> <name> [prompt|-]" >&2
echo " prompt is read from stdin when omitted or '-'" >&2
exit 2
}
agent="${1:-}"
name="${2:-}"
prompt="${3:--}"
[ -n "$agent" ] && [ -n "$name" ] || usage
if [ "$prompt" = "-" ]; then
prompt="$(cat)"
fi
[ -n "$prompt" ] || { echo "spawn.sh: empty prompt" >&2; exit 2; }
if [ "$agent" = "claude" ]; then
claude --bg --name "$name" "$prompt"
echo "Spawned claude session '$name'. Manage it with: claude agents"
exit 0
fi
# Interactive invocation, seeded with the prompt — used inside herdr panes.
case "$agent" in
codex) interactive=(codex "$prompt") ;;
pi) interactive=(pi "$prompt") ;;
cursor) interactive=(cursor-agent "$prompt") ;;
opencode) interactive=(opencode --prompt "$prompt") ;;
copilot) interactive=(copilot -i "$prompt") ;;
*) usage ;;
esac
if herdr agent list >/dev/null 2>&1; then
herdr agent start "$name" --cwd "$PWD" --no-focus -- "${interactive[@]}"
echo "Spawned $agent session '$name' in herdr."
echo " list: herdr agent list"
echo " read: herdr agent read '$name'"
echo " steer: herdr agent send '$name' '<text>'"
echo " attach: herdr agent attach '$name'"
exit 0
fi
command -v systemd-run >/dev/null 2>&1 || {
echo "spawn.sh: no herdr server running and systemd-run unavailable — launch herdr and retry" >&2
exit 1
}
# Non-interactive one-shot — flags keep the run from blocking on approvals.
case "$agent" in
codex) oneshot=(codex exec --sandbox workspace-write --skip-git-repo-check "$prompt") ;;
pi) oneshot=(pi -p "$prompt") ;;
cursor) oneshot=(cursor-agent -p --force --output-format text "$prompt") ;;
opencode) oneshot=(opencode run --title "$name" "$prompt") ;;
copilot) oneshot=(copilot -p "$prompt" --allow-all-tools) ;;
esac
# The systemd user manager doesn't inherit this shell's PATH; resolve the
# binary now and pass PATH through for any children it spawns.
bin="$(command -v "${oneshot[0]}")" || {
echo "spawn.sh: ${oneshot[0]} is not installed" >&2
exit 1
}
oneshot[0]="$bin"
slug="$(printf '%s' "$name" | tr '[:upper:] ' '[:lower:]-' | tr -cd 'a-z0-9-')"
unit="spawn-${slug:-session}-$(date +%H%M%S)"
systemd-run --user --unit "$unit" --working-directory="$PWD" \
--setenv=PATH="$PATH" --setenv=HOME="$HOME" "${oneshot[@]}" >/dev/null 2>&1
echo "Spawned $agent session '$name' as user unit $unit (no herdr server running)."
echo " status: systemctl --user status $unit"
echo " logs: journalctl --user -u $unit -f"
echo " stop: systemctl --user stop $unit"
case "$agent" in
codex) echo " resume: codex exec resume <session id from logs>" ;;
pi) echo " resume: pi -c (from this directory)" ;;
cursor) echo " resume: cursor-agent resume" ;;
opencode) echo " resume: opencode run -c (or: opencode session list)" ;;
copilot) echo " resume: copilot --resume" ;;
esac