Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,8 @@ script links are managed:
- `hv-hermes-vikunja-task-updates` — polls Vikunja task updates
- `hv-hermes-dev-team-manager` — runs the Dev Team Mode sidecar for board
provisioning, worker dispatch, and worker process status
- `hv-hermes-dev-team-worker` — runs one Hermes worker agent inside the
sidecar-created isolated worktree
- `hv-hermes-github-cricket-issues` — polls GitHub open issues labeled
`cricket`

Expand Down
9 changes: 6 additions & 3 deletions docs/hermes-dev-team-mode.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,11 @@ Workers are per-task runs. For each dispatched issue, the manager:
7. Moves the main-board issue to `Review` for human QA.

The worker command is configured locally with `HV_HERMES_WORKER_COMMAND` or the
equivalent Hermes config value. Workers must not edit the integration worktree
directly.
equivalent Hermes config value. `hv-hermes-dev-team-worker` is the default
worker wrapper supplied by have-config; it launches a non-interactive Hermes run
inside the sidecar-created worktree and passes the task/worktree context through
the `HERMES_DEV_TEAM_*` environment variables. Workers must not edit the
integration worktree directly.

## Manager Sidecar

Expand Down Expand Up @@ -116,7 +119,7 @@ dev_team:
- ~/Work/anytown/repos
worktree_root: ~/.hermes/dev-team/worktrees
integration_root: ~/.hermes/dev-team/integration
worker_command: "${HV_HERMES_WORKER_COMMAND}"
worker_command: "hv-hermes-dev-team-worker"
main_buckets: [To-Do, Blocked, Doing, Review, Done]
manager_buckets: [Queued, Working, Integrating, Blocked, Closed]
status_updates: on_request
Expand Down
7 changes: 7 additions & 0 deletions hv/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,13 @@
"executable": true,
"description": "Run the Hermes Dev Team Mode sidecar for Vikunja board provisioning, worker dispatch, and worker process status."
},
{
"agent": "no-agent",
"name": "hv-hermes-dev-team-worker",
"path": "reusable-scripts/hermes/no-agent/hv-hermes-dev-team-worker",
"executable": true,
"description": "Run one Hermes Dev Team worker agent inside the sidecar-created isolated worktree."
},
{
"agent": "no-agent",
"name": "hv-hermes-github-cricket-issues",
Expand Down
86 changes: 86 additions & 0 deletions reusable-scripts/hermes/no-agent/hv-hermes-dev-team-worker
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
#!/usr/bin/env bash
# Run a single Hermes Dev Team worker task inside the sidecar-created worktree.
#
# The manager sidecar sets HERMES_DEV_TEAM_* environment variables and invokes
# this script from the isolated worker worktree. The worker must leave reviewable
# changes in that worktree and exit 0 only when ready for manager review.
set -euo pipefail

if ! command -v hermes >/dev/null 2>&1; then
echo "hv-hermes-dev-team-worker: hermes CLI not found on PATH" >&2
exit 127
fi

required_vars=(
HERMES_DEV_TEAM_MAIN_TASK_ID
HERMES_DEV_TEAM_MAIN_TASK_URL
HERMES_DEV_TEAM_WORKER_TASK_ID
HERMES_DEV_TEAM_WORKER_TASK_URL
HERMES_DEV_TEAM_REPO_PATH
HERMES_DEV_TEAM_WORKTREE_PATH
HERMES_DEV_TEAM_BRANCH
HERMES_DEV_TEAM_INTEGRATION_ROOT
HERMES_DEV_TEAM_MANAGER_IDENTITY
)
for var in "${required_vars[@]}"; do
if [[ -z "${!var:-}" ]]; then
echo "hv-hermes-dev-team-worker: missing required environment variable $var" >&2
exit 64
fi
done

if [[ ! -d "$HERMES_DEV_TEAM_WORKTREE_PATH" ]]; then
echo "hv-hermes-dev-team-worker: worktree does not exist: $HERMES_DEV_TEAM_WORKTREE_PATH" >&2
exit 66
fi

cd "$HERMES_DEV_TEAM_WORKTREE_PATH"

prompt_file="$(mktemp)"
cleanup() {
rm -f "$prompt_file"
}
trap cleanup EXIT

cat >"$prompt_file" <<'PROMPT'
You are a Hermes Dev Team worker agent running in an isolated git worktree.

Task context is provided in environment variables:
- HERMES_DEV_TEAM_MAIN_TASK_ID / HERMES_DEV_TEAM_MAIN_TASK_URL: canonical user-visible Vikunja issue.
- HERMES_DEV_TEAM_WORKER_TASK_ID / HERMES_DEV_TEAM_WORKER_TASK_URL: internal worker task.
- HERMES_DEV_TEAM_REPO_PATH: source repository path.
- HERMES_DEV_TEAM_WORKTREE_PATH: this isolated worktree; make changes here only.
- HERMES_DEV_TEAM_BRANCH: branch for this work.
- HERMES_DEV_TEAM_INTEGRATION_ROOT: manager-owned integration root; do not edit it.
- HERMES_DEV_TEAM_MANAGER_IDENTITY: manager identity.
- HERMES_DEV_TEAM_DEV_SERVER_URL: optional dev server URL.

Follow this contract:
1. Inspect the worker task and linked main task if tools are available; otherwise use the URLs and repository context in this prompt.
2. Make only scoped changes for the linked main task in HERMES_DEV_TEAM_WORKTREE_PATH.
3. Do not edit the integration root and do not move main-board cards.
4. Run the smallest useful verification for the change.
5. Leave reviewable output in the worktree and summarize what changed, tests run, and blockers.
6. Exit non-zero if you cannot produce reviewable output.
PROMPT

{
printf '\nConcrete values for this run:\n'
printf -- '- Main task: #%s %s\n' "$HERMES_DEV_TEAM_MAIN_TASK_ID" "$HERMES_DEV_TEAM_MAIN_TASK_URL"
printf -- '- Worker task: #%s %s\n' "$HERMES_DEV_TEAM_WORKER_TASK_ID" "$HERMES_DEV_TEAM_WORKER_TASK_URL"
printf -- '- Repository: %s\n' "$HERMES_DEV_TEAM_REPO_PATH"
printf -- '- Worktree: %s\n' "$HERMES_DEV_TEAM_WORKTREE_PATH"
printf -- '- Branch: %s\n' "$HERMES_DEV_TEAM_BRANCH"
printf -- '- Integration root: %s\n' "$HERMES_DEV_TEAM_INTEGRATION_ROOT"
printf -- '- Manager: %s\n' "$HERMES_DEV_TEAM_MANAGER_IDENTITY"
if [[ -n "${HERMES_DEV_TEAM_DEV_SERVER_URL:-}" ]]; then
printf -- '- Dev server: %s\n' "$HERMES_DEV_TEAM_DEV_SERVER_URL"
fi
} >>"$prompt_file"

# Use non-interactive Hermes. The sidecar already isolates each worker in a git
# worktree, so do not ask Hermes to create another one.
exec hermes chat \
--query "$(cat "$prompt_file")" \
--source "hermes-dev-team-worker" \
--quiet
16 changes: 16 additions & 0 deletions scripts/test-hv-agent-resolver.sh
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,13 @@ cat > "$HAVE_CONFIG_DIR/hv/manifest.json" <<'JSON'
"executable": true,
"description": "fixture dev team manager script"
},
{
"agent": "no-agent",
"name": "hv-hermes-dev-team-worker",
"path": "reusable-scripts/hermes/no-agent/hv-hermes-dev-team-worker",
"executable": true,
"description": "fixture dev team worker script"
},
{
"agent": "no-agent",
"name": "inline-notify",
Expand Down Expand Up @@ -135,6 +142,11 @@ cat > "$HAVE_CONFIG_DIR/reusable-scripts/hermes/no-agent/hv-hermes-dev-team-mana
echo fixture dev team manager
EOF

cat > "$HAVE_CONFIG_DIR/reusable-scripts/hermes/no-agent/hv-hermes-dev-team-worker" <<'EOF'
#!/usr/bin/env bash
echo fixture dev team worker
EOF

cat > "$HAVE_CONFIG_DIR/profiles/hermes/manifest.json" <<'JSON'
{
"schema": "https://happyvertical.com/hv-agent-manifest/v1",
Expand Down Expand Up @@ -258,6 +270,9 @@ test -x "$HOME_DIR/.local/bin/fixture-notify"
grep -q "fixture dev team manager" "$OUTPUT_DIR/scripts/hv-hermes-dev-team-manager"
grep -q "fixture dev team manager" "$HOME_DIR/.local/bin/hv-hermes-dev-team-manager"
test -x "$HOME_DIR/.local/bin/hv-hermes-dev-team-manager"
grep -q "fixture dev team worker" "$OUTPUT_DIR/scripts/hv-hermes-dev-team-worker"
grep -q "fixture dev team worker" "$HOME_DIR/.local/bin/hv-hermes-dev-team-worker"
test -x "$HOME_DIR/.local/bin/hv-hermes-dev-team-worker"
grep -q "inline notify" "$OUTPUT_DIR/scripts/inline-notify"
grep -q "inline notify" "$HOME_DIR/.local/bin/inline-notify"
test -x "$HOME_DIR/.local/bin/inline-notify"
Expand All @@ -269,6 +284,7 @@ grep -q '"key": "codex:command:review-cycle"' "$LOCK_PATH"
grep -q '"key": "codex:skill:hermes-manager"' "$LOCK_PATH"
grep -q '"key": "no-agent:script:fixture-notify"' "$LOCK_PATH"
grep -q '"key": "no-agent:script:hv-hermes-dev-team-manager"' "$LOCK_PATH"
grep -q '"key": "no-agent:script:hv-hermes-dev-team-worker"' "$LOCK_PATH"
grep -q '"key": "no-agent:script:inline-notify"' "$LOCK_PATH"
grep -q '`dotfiles` priority 10: available' "$REPORT_PATH"
grep -q "invalid declared priority 'dynamic' ignored; using fixed 30" "$REPORT_PATH"
Expand Down