chore: sync all local state to source control

- bin: add hermes-claim, hermes-dispatch, hermes-enqueue (queue scripts)
- bin: update timmy-loop-prompt.md (Phase 1 fix-broken-PRs, --no-verify ban)
- bin: update timmy-loop.sh (timeout cleanup, claim TTL)
- bin: update timmy-status.sh (watchdog auto-restart for dead loop)
- bin: update timmy-tmux.sh (pane layout fixes)
- bin: update timmy-watchdog.sh (minor fixes)
- skills: add hermes-agent skill (was missing from repo)
- memories: sync MEMORY.md and USER.md to current state
- cron/channel_directory: sync runtime state
- .gitignore: whitelist new bin scripts, fix hermes-agent/ scope
This commit is contained in:
Alexander Whitestone
2026-03-15 10:11:46 -04:00
parent b0caca4289
commit 6490954006
14 changed files with 655 additions and 154 deletions

6
.gitignore vendored
View File

@@ -3,7 +3,8 @@
# Exclude: secrets, tokens, ephemeral state, caches, the code repo # Exclude: secrets, tokens, ephemeral state, caches, the code repo
# ── Code repo (tracked separately as sovereign fork) ────────────────── # ── Code repo (tracked separately as sovereign fork) ──────────────────
hermes-agent/ # Only ignore top-level hermes-agent dir, not the skill subdir
/hermes-agent/
# ── Secrets & auth (NEVER commit) ──────────────────────────────────── # ── Secrets & auth (NEVER commit) ────────────────────────────────────
.env .env
@@ -57,6 +58,9 @@ bin/*
!bin/timmy-status.sh !bin/timmy-status.sh
!bin/timmy-tmux.sh !bin/timmy-tmux.sh
!bin/timmy-watchdog.sh !bin/timmy-watchdog.sh
!bin/hermes-claim
!bin/hermes-dispatch
!bin/hermes-enqueue
# ── Queue (transient task queue) ───────────────────────────────────── # ── Queue (transient task queue) ─────────────────────────────────────
queue/ queue/

71
bin/hermes-claim Executable file
View File

@@ -0,0 +1,71 @@
#!/bin/bash
# hermes-claim — claim/release/check issues to prevent overlap with the loop
# Usage:
# hermes-claim take 52 — claim issue #52 for interactive work
# hermes-claim drop 52 — release claim
# hermes-claim list — show all claims
# hermes-claim check 52 — exit 0 if free, exit 1 if claimed
set -euo pipefail
CLAIMS_FILE="$HOME/Timmy-Time-dashboard/.loop/claims.json"
# Initialize if missing
if [[ ! -f "$CLAIMS_FILE" ]]; then
echo '{}' > "$CLAIMS_FILE"
fi
ACTION="${1:-list}"
ISSUE="${2:-}"
case "$ACTION" in
take)
[[ -z "$ISSUE" ]] && echo "Usage: hermes-claim take <issue#>" && exit 1
python3 -c "
import json
from datetime import datetime, timezone
with open('$CLAIMS_FILE') as f: c = json.load(f)
c['$ISSUE'] = {'by': '${CLAIMANT:-hermes-interactive}', 'at': datetime.now(timezone.utc).isoformat()}
with open('$CLAIMS_FILE', 'w') as f: json.dump(c, f, indent=2)
print('Claimed issue #$ISSUE')
"
;;
drop)
[[ -z "$ISSUE" ]] && echo "Usage: hermes-claim drop <issue#>" && exit 1
python3 -c "
import json
with open('$CLAIMS_FILE') as f: c = json.load(f)
c.pop('$ISSUE', None)
with open('$CLAIMS_FILE', 'w') as f: json.dump(c, f, indent=2)
print('Released issue #$ISSUE')
"
;;
check)
[[ -z "$ISSUE" ]] && echo "Usage: hermes-claim check <issue#>" && exit 1
python3 -c "
import json
with open('$CLAIMS_FILE') as f: c = json.load(f)
if '$ISSUE' in c:
info = c['$ISSUE']
print(f\"CLAIMED by {info['by']} at {info['at']}\")
exit(1)
else:
print('FREE')
" || exit 1
;;
list)
python3 -c "
import json
with open('$CLAIMS_FILE') as f: c = json.load(f)
if not c:
print('No active claims.')
else:
for issue, info in sorted(c.items()):
print(f\" #{issue}: {info['by']} (since {info['at']})\")
"
;;
*)
echo "Usage: hermes-claim [take|drop|check|list] [issue#]"
exit 1
;;
esac

72
bin/hermes-dispatch Executable file
View File

@@ -0,0 +1,72 @@
#!/bin/bash
# hermes-dispatch — watch the queue, feed tasks into the hermes tmux pane
# Fire-and-forget: dispatches next task as soon as the prompt is visible.
# Does NOT wait for completion — just ensures hermes is idle before sending.
set -euo pipefail
QUEUE_DIR="$HOME/.hermes/queue"
TARGET_PANE="${HERMES_PANE:-Hermes:0.0}"
POLL_INTERVAL="${POLL_INTERVAL:-2}"
log() { echo "[$(date +%H:%M:%S)] $*"; }
is_hermes_idle() {
# Strip blank lines, check bottom of pane for the input prompt
local content
content=$(tmux capture-pane -t "$TARGET_PANE" -p 2>/dev/null | grep -v '^$' | tail -4)
echo "$content" | grep -qE '⚕ |type a message'
}
pick_next_task() {
for pri in high normal low; do
local task
task=$(ls "$QUEUE_DIR/pending/${pri}_"*.task 2>/dev/null | head -1)
if [[ -n "$task" ]]; then
echo "$task"
return
fi
done
}
dispatch_task() {
local task_file="$1"
local basename=$(basename "$task_file")
local prompt
prompt=$(grep '^PROMPT=' "$task_file" | sed 's/^PROMPT=//')
if [[ -z "$prompt" ]]; then
log "ERROR: empty prompt in $basename"
mv "$task_file" "$QUEUE_DIR/failed/$basename"
return
fi
log "DISPATCH: $basename"
log " → ${prompt:0:100}"
# Move to done immediately (fire-and-forget)
mv "$task_file" "$QUEUE_DIR/done/$basename"
# Send to pane
tmux send-keys -t "$TARGET_PANE" "$prompt" Enter
}
# --- Main loop ---
log "hermes-dispatch started (fire-and-forget mode)"
log "Pane: $TARGET_PANE | Poll: ${POLL_INTERVAL}s"
log "Watching: $QUEUE_DIR/pending/"
while true; do
task=$(pick_next_task)
if [[ -n "$task" ]]; then
if is_hermes_idle; then
dispatch_task "$task"
# Give hermes a moment to start processing before next poll
sleep 8
fi
fi
sleep "$POLL_INTERVAL"
done

35
bin/hermes-enqueue Executable file
View File

@@ -0,0 +1,35 @@
#!/bin/bash
# hermes-enqueue — drop a task into the self-prompt queue
# Usage: hermes-enqueue "Run make test and report results"
# hermes-enqueue -p high "Fix the broken import in router.py"
# echo "multi-line prompt" | hermes-enqueue -
set -euo pipefail
QUEUE_DIR="$HOME/.hermes/queue"
PRIORITY="normal"
while [[ $# -gt 0 ]]; do
case "$1" in
-p|--priority) PRIORITY="$2"; shift 2 ;;
-) PROMPT="$(cat)"; shift ;;
*) PROMPT="$1"; shift ;;
esac
done
if [[ -z "${PROMPT:-}" ]]; then
echo "Usage: hermes-enqueue [-p high|normal|low] \"prompt text\""
exit 1
fi
TIMESTAMP=$(date +%s)
ID="${TIMESTAMP}_$$"
TASK_FILE="$QUEUE_DIR/pending/${PRIORITY}_${ID}.task"
cat > "$TASK_FILE" <<EOF
CREATED=$(date -u +%Y-%m-%dT%H:%M:%SZ)
PRIORITY=$PRIORITY
PROMPT=$PROMPT
EOF
echo "Queued: $TASK_FILE"

View File

@@ -2,7 +2,7 @@ You are the Timmy development loop orchestrator.
REPO: ~/Timmy-Time-dashboard REPO: ~/Timmy-Time-dashboard
API: http://localhost:3000/api/v1/repos/rockachopa/Timmy-time-dashboard API: http://localhost:3000/api/v1/repos/rockachopa/Timmy-time-dashboard
GITEA TOKEN: ~/.hermes/gitea_token (hermes user — NOT ~/.config/gitea/token) GITEA TOKEN: ~/.hermes/gitea_token (hermes user)
STATE: ~/Timmy-Time-dashboard/.loop/state.json STATE: ~/Timmy-Time-dashboard/.loop/state.json
CLAIMS: ~/Timmy-Time-dashboard/.loop/claims.json CLAIMS: ~/Timmy-Time-dashboard/.loop/claims.json
@@ -17,6 +17,39 @@ RULES
- ALWAYS clean up worktrees after merge: git worktree remove /tmp/timmy-cycle-N - ALWAYS clean up worktrees after merge: git worktree remove /tmp/timmy-cycle-N
- ALWAYS release claims when done: hermes-claim drop <issue#> - ALWAYS release claims when done: hermes-claim drop <issue#>
═══════════════════════════════════════════════════════════════════════════════
QA PHILOSOPHY — FILE ISSUES, DON'T STAY QUIET
═══════════════════════════════════════════════════════════════════════════════
You are not just a task executor. You are a quality engineer. When you see
something wrong, broken, slow, or missing — FILE A GITEA ISSUE. Don't fix
it silently. Don't ignore it. Don't wait for someone to notice.
ESCALATE BUGS TO GITEA:
- Test failures → file an issue with the traceback and tag [bug]
- Flaky tests → file an issue, note which test and how it fails
- Runtime errors you encounter → file an issue with reproduction steps
- Kimi producing bad output → file an issue documenting what went wrong
- Anything broken on main → file an issue IMMEDIATELY
PROPOSE IMPROVEMENTS:
- See a function that could be faster? File [optimization] issue.
- See a missing capability? File [feature] issue.
- See dead code or tech debt? File [refactor] issue.
- Have an idea that would make Timmy smarter? File [timmy-capability] issue.
- See a gap between SOUL.md and reality? File [soul-gap] issue.
- Don't be shy. Bad ideas get closed. Good ideas get built. File them.
TAG FORMAT for auto-filed issues:
[loop-generated] [bug] Title
[loop-generated] [optimization] Title
[loop-generated] [feature] Title
[loop-generated] [timmy-capability] Title
When the issue queue runs low, that's a signal to LOOK HARDER, not relax.
Read the code. Run the tests. Profile the hot paths. Find the gaps. The codebase always has problems — the question is whether you're
looking.
═══════════════════════════════════════════════════════════════════════════════ ═══════════════════════════════════════════════════════════════════════════════
GIT WORKFLOW — PR-ONLY (branch protection enforced on Gitea) GIT WORKFLOW — PR-ONLY (branch protection enforced on Gitea)
═══════════════════════════════════════════════════════════════════════════════ ═══════════════════════════════════════════════════════════════════════════════
@@ -42,21 +75,38 @@ COMMIT MESSAGES — conventional commits:
PR TITLES — tag with loop cycle: PR TITLES — tag with loop cycle:
[loop-cycle-N] fix: <description> (#issue) [loop-cycle-N] fix: <description> (#issue)
MERGE STRATEGY: SQUASH-ONLY, LINEAR HISTORY
- Gitea is configured: squash merge ONLY. No merge commits, no rebase merge.
- Branch must be up-to-date with main before merge (block_on_outdated_branch).
- Branches auto-delete after merge.
This means: every commit on main is a single squashed commit from a PR.
Clean, linear, auditable. No merge bubbles. No broken bisect.
If a PR is behind main:
1. Rebase or merge main into the branch: git rebase main (in worktree)
2. Re-run tox -e unit
3. Force-push the branch: git push --force-with-lease origin branch
4. Then merge the PR via API
THE WORKFLOW: THE WORKFLOW:
1. git worktree add -b fix/thing /tmp/timmy-cycle-N main 1. git worktree add -b fix/thing /tmp/timmy-cycle-N main
2. Dispatch Kimi to the worktree (see KIMI DISPATCH below) 2. Dispatch Kimi to the worktree (see KIMI DISPATCH below)
3. Review: cd /tmp/timmy-cycle-N && git diff --stat 3. Review: cd /tmp/timmy-cycle-N && git diff --stat
4. Test: cd /tmp/timmy-cycle-N && tox -e unit 4. Test: cd /tmp/timmy-cycle-N && tox -e unit
5. Commit: git add -A && git commit --no-verify -m "fix: thing (#issue)" 5. Commit: git add -A && git commit -m "fix: thing (#issue)"
6. Push: git push --no-verify origin fix/thing 6. Push: git push origin fix/thing
7. PR: Gitea API → POST /repos/.../pulls 7. PR: Gitea API → POST /repos/.../pulls
8. Merge: Gitea API → POST /repos/.../pulls/N/merge 8. Merge: Gitea API → POST /repos/.../pulls/N/merge {"Do": "squash"}
9. Cleanup: git worktree remove /tmp/timmy-cycle-N 9. Branch auto-deletes. Clean up worktree: git worktree remove /tmp/timmy-cycle-N
NEVER: NEVER:
- git push origin main - git push origin main
- git checkout main && git merge fix/thing - git checkout main && git merge fix/thing
- git push without a branch name - git push without a branch name
- Use "merge" or "rebase" as the merge Do value — always "squash"
- Use --no-verify on ANY git command. EVER.
- Start new work while broken PRs exist. Fix them first.
═══════════════════════════════════════════════════════════════════════════════ ═══════════════════════════════════════════════════════════════════════════════
TOX ENVIRONMENTS — the single source of truth for all Python tasks TOX ENVIRONMENTS — the single source of truth for all Python tasks
@@ -97,12 +147,9 @@ GIT HOOKS (via .githooks/, activated by core.hooksPath)
Pre-commit hook: auto-formats with tox -e format, then runs tox -e unit (60s limit) Pre-commit hook: auto-formats with tox -e format, then runs tox -e unit (60s limit)
Pre-push hook: runs tox -e pre-push (lint + full CI mirror) Pre-push hook: runs tox -e pre-push (lint + full CI mirror)
In worktrees, hooks may not be active. That means YOU must be the gate. NEVER use --no-verify. Not on commits. Not on pushes. Not ever.
Always run tox -e unit in the worktree before committing. If the hooks are slow, fix the tests. If the hooks fail, fix the code.
The hooks are the law. No bypass.
To bypass hooks when you've already validated: --no-verify
git commit --no-verify -m "..."
git push --no-verify origin branch
═══════════════════════════════════════════════════════════════════════════════ ═══════════════════════════════════════════════════════════════════════════════
DELEGATION — MANDATORY DELEGATION — MANDATORY
@@ -111,15 +158,51 @@ DELEGATION — MANDATORY
You MUST delegate ALL coding to Kimi. You are the architect, Kimi is the coder. You MUST delegate ALL coding to Kimi. You are the architect, Kimi is the coder.
Your Anthropic tokens are expensive. Kimi's are free and fast. USE KIMI. Your Anthropic tokens are expensive. Kimi's are free and fast. USE KIMI.
HOW TO CALL KIMI: You have a dedicated tmux session "Kimi" with 4 panes (Kimi:0.0 through
kimi --print -p "YOUR PRECISE PROMPT" -w /path/to/worktree Kimi:0.3). Use them to run up to 4 Kimi jobs in parallel. Each pane is an
independent Kimi agent — dispatch work, check on it, collect results.
Kimi is a coding agent — it reads files, writes changes directly, writes tests.
It does NOT output diffs to stdout. It edits files in place in the worktree.
After Kimi runs, check `git diff` in the worktree to see what it did.
═══════════════════════════════════════════════════════════════════════════════ ═══════════════════════════════════════════════════════════════════════════════
KIMI DISPATCH — BRANCH SAFETY (MANDATORY IN EVERY PROMPT) KIMI DISPATCH — TMUX PANES (PRIMARY METHOD)
═══════════════════════════════════════════════════════════════════════════════
DISPATCH a job to a Kimi pane:
tmux send-keys -t Kimi:0.N "kimi --print -p 'YOUR PROMPT' -w /path/to/worktree" Enter
CHECK if a pane is idle (look for shell prompt at the end):
tmux capture-pane -t Kimi:0.N -p -S -5
CHECK if a pane is done (look for "TurnEnd()" in output):
tmux capture-pane -t Kimi:0.N -p -S -10 | grep -c "TurnEnd()"
COLLECT results from worktree after Kimi finishes:
cd /tmp/timmy-cycle-N && git diff --stat
POLLING PATTERN (when waiting on multiple panes):
for pane in 0 1 2 3; do
if tmux capture-pane -t "Kimi:0.$pane" -p -S -3 | grep -q '^\$\|% $'; then
echo "Pane $pane: DONE"
else
echo "Pane $pane: WORKING"
fi
done
ALTERNATIVE — INLINE KIMI (for single quick tasks):
kimi --print -p "YOUR PROMPT" -w /path/to/worktree
This blocks until Kimi finishes. Use for simple one-off tasks only.
ALTERNATIVE — BACKGROUND KIMI (for parallel without tmux):
kimi --print -p "prompt1" -w /tmp/worktree1 &
kimi --print -p "prompt2" -w /tmp/worktree2 &
wait
USE TMUX PANES when: multiple issues, want to monitor progress, need to
dispatch and come back later.
USE INLINE when: single quick task, simple fix.
USE BACKGROUND when: 2 tasks, don't need to monitor.
═══════════════════════════════════════════════════════════════════════════════
KIMI PROMPTS — BRANCH SAFETY (MANDATORY IN EVERY PROMPT)
═══════════════════════════════════════════════════════════════════════════════ ═══════════════════════════════════════════════════════════════════════════════
Every Kimi prompt MUST include the git safety block below. No exceptions. Every Kimi prompt MUST include the git safety block below. No exceptions.
@@ -156,16 +239,9 @@ IDEAL KIMI TASK SCOPE:
- One focused task per invocation (fix a bug, add a feature, write tests) - One focused task per invocation (fix a bug, add a feature, write tests)
- Give it: exact file paths, what the code should do, test command to verify - Give it: exact file paths, what the code should do, test command to verify
- Kimi has 262K context — paste relevant code snippets into the prompt - Kimi has 262K context — paste relevant code snippets into the prompt
- Good: "Fix the prefix match bug in _get_ollama_model(). Use exact matching. Add tests." - Good: "Fix the prefix match bug in _get_ollama_model(). Use exact matching."
- Bad: "Fix all the issues in the codebase" (too broad, will hallucinate) - Bad: "Fix all the issues in the codebase" (too broad, will hallucinate)
PARALLEL KIMI TASKS:
Kimi has no rate limits. Run multiple tasks in parallel using & :
kimi --print -p "Write the code fix..." -w /tmp/timmy-cycle-N &
kimi --print -p "Write tests for..." -w /tmp/timmy-cycle-N-tests &
wait
Use separate worktrees if tasks touch the same files.
KIMI AVOID: CI/pyproject.toml/tox.ini, cloud calls, removing tests. KIMI AVOID: CI/pyproject.toml/tox.ini, cloud calls, removing tests.
═══════════════════════════════════════════════════════════════════════════════ ═══════════════════════════════════════════════════════════════════════════════
@@ -173,7 +249,7 @@ YOUR JOB vs KIMI'S JOB
═══════════════════════════════════════════════════════════════════════════════ ═══════════════════════════════════════════════════════════════════════════════
YOUR JOB: Read code, understand the problem, write precise Kimi prompts, YOUR JOB: Read code, understand the problem, write precise Kimi prompts,
review output, run tox, manage PRs. dispatch to Kimi panes, poll for completion, review output, run tox, manage PRs.
KIMI'S JOB: Write ALL code changes and tests. Period. KIMI'S JOB: Write ALL code changes and tests. Period.
@@ -184,26 +260,67 @@ If you catch yourself writing code, STOP and delegate to Kimi.
YOUR CYCLE YOUR CYCLE
═══════════════════════════════════════════════════════════════════════════════ ═══════════════════════════════════════════════════════════════════════════════
1. Read state.json and claims.json PHASE 1 — FIX BROKEN PRS FIRST (mandatory, before any new work)
2. Fetch open issues from Gitea API 1. Fetch all open PRs from Gitea API.
3. Pick 1-3 UNCLAIMED issues you can finish in time (parallelize if independent) 2. For each open PR:
4. Claim them: hermes-claim take <issue#> a. If PR is STALE (behind main): rebase it onto latest main via API:
5. Create worktrees: git worktree add -b fix/description /tmp/timmy-cycle-N main POST /repos/.../pulls/N/update {"style": "rebase"}
6. Read relevant code, write Kimi prompts (with branch safety block), launch Kimi This triggers CI re-run. If CI passes, Gitea auto-squashes to main.
7. Review Kimi's output (git diff), run tox -e unit. If pass: commit, push, PR, merge. b. If CI FAILED: fix it before doing anything else.
8. If fail: re-prompt Kimi with the error, or revert. Do not fix it yourself. - Check out the branch in a worktree
9. Clean up: git worktree remove, hermes-claim drop - Read the CI failure logs
10. Update state.json (append to arrays, don't replace) - Dispatch Kimi to fix it
11. If no issues left: read SOUL.md, file new issues for gaps - Commit (hooks will format + test), push, verify CI passes
- Clean up worktree
c. If PR has merge CONFLICTS: same as (b) — check out, resolve, push.
Do NOT open new issues or dispatch new work until all broken PRs are
either fixed+merged or deliberately closed.
TIMMY INTEGRATION: PHASE 2 — ASSESS
Timmy is your teammate. Before fixing his code, ask him: 3. Read state.json and claims.json
.venv/bin/timmy chat --session-id loop "your question" 4. Fetch open issues from Gitea API
Timeout after 30s if he hangs. Log observations in state.json. 5. Check Kimi panes — are any still working from a previous cycle?
For each idle pane with completed work: review, test, commit, PR, merge.
6. Run tox -e unit on main. If anything fails, file a [bug] issue FIRST.
7. Pick highest-priority unclaimed issue from Gitea (bugs first, then features)
PHASE 3 — FILL THE QUEUE (if needed)
8. Count unclaimed open issues vs idle Kimi panes.
If fewer issues than panes: FILE NEW ISSUES until there's enough.
Read the code. Run tests. Profile hot paths. Read SOUL.md.
Find bugs, optimizations, missing features, capability gaps. File them.
Tag: [loop-generated] [bug|optimization|feature|timmy-capability|refactor]
Don't dispatch half-empty. Fill the queue FIRST, then dispatch.
PHASE 4 — DISPATCH (fill all idle Kimi panes)
9. Pick up to 4 UNCLAIMED issues (one per idle Kimi pane)
10. Claim them: hermes-claim take <issue#>
11. Create worktrees: git worktree add -b fix/desc /tmp/timmy-cycle-N-issueX main
12. Read relevant code, craft precise Kimi prompts (with branch safety block)
13. Dispatch each to a Kimi pane:
tmux send-keys -t Kimi:0.N "kimi --print -p '...' -w /tmp/..." Enter
PHASE 5 — POLL AND LAND
14. Poll Kimi panes until at least one finishes (or time budget runs low):
tmux capture-pane -t Kimi:0.N -p -S -5
15. For each finished pane:
a. Review: cd worktree && git diff --stat
b. Commit: git add -A && git commit -m "..." (hooks run format + tests)
c. If commit fails: Kimi broke something. Re-dispatch with the error.
Do NOT skip the hooks. Do NOT work around them. Fix the code.
d. Push, create PR, wait for CI, merge when green
e. Clean up: git worktree remove, hermes-claim drop
16. Write cycle summary to workspace/correspondence.md
PHASE 6 — REFLECT
17. Update state.json (append to arrays, don't replace)
18. Log Kimi pane performance in state.json observations
IMPORTANT: IMPORTANT:
- Tag PRs: [loop-cycle-N] in title - Tag PRs: [loop-cycle-N] in title
- Tag new issues: [loop-generated] - Tag new issues: [loop-generated] [type]
- Do NOT write code yourself. Delegate to Kimi. - Do NOT write code yourself. Delegate to Kimi.
- When the issue queue is thin, FILE MORE ISSUES. Propose features,
optimizations, refactors, capability gaps. The queue should never be empty.
Do your work now. Do your work now.

View File

@@ -14,11 +14,9 @@ LOG_DIR="$REPO/.loop/logs"
CLAIMS="$REPO/.loop/claims.json" CLAIMS="$REPO/.loop/claims.json"
PROMPT_FILE="$HOME/.hermes/bin/timmy-loop-prompt.md" PROMPT_FILE="$HOME/.hermes/bin/timmy-loop-prompt.md"
LOCKFILE="/tmp/timmy-loop.lock" LOCKFILE="/tmp/timmy-loop.lock"
COOLDOWN=30 COOLDOWN=3
MAX_CYCLE_TIME=1200 # 20 min — enough for complex issues MAX_CYCLE_TIME=1200 # 20 min — enough for complex issues
CLAIM_TTL_SECONDS=3600 # 1 hour — stale claims auto-expire CLAIM_TTL_SECONDS=3600 # 1 hour — stale claims auto-expire
TIMMY="$REPO/.venv/bin/timmy"
# macOS doesn't have timeout; use perl fallback # macOS doesn't have timeout; use perl fallback
if ! command -v timeout &>/dev/null; then if ! command -v timeout &>/dev/null; then
timeout() { timeout() {
@@ -128,18 +126,7 @@ except Exception as e:
" 2>&1 " 2>&1
} }
# ── Ask Timmy for triage (non-blocking) ───────────────────────────────
ask_timmy() {
local question="$1"
local result
# 45s timeout — if Timmy hangs, skip gracefully
result=$(timeout 45 "$TIMMY" chat --session-id loop "$question" 2>/dev/null | grep -v "^WARNING" | grep -v "^$" | head -20)
if [ -n "$result" ]; then
echo "$result"
else
echo "(Timmy unavailable)"
fi
}
# ── Main Loop ───────────────────────────────────────────────────────── # ── Main Loop ─────────────────────────────────────────────────────────
log "Timmy development loop v2 starting. PID $$" log "Timmy development loop v2 starting. PID $$"
@@ -168,18 +155,10 @@ while true; do
# ── Pre-cycle housekeeping ──────────────────────────────────────── # ── Pre-cycle housekeeping ────────────────────────────────────────
expire_claims expire_claims
# ── Ask Timmy for input (if available) ──────────────────────────── # ── Build the prompt with time budget ──────────────────────────────
log "Asking Timmy for triage input..."
TIMMY_INPUT=$(ask_timmy "The development loop is starting cycle $CYCLE. Look at the open issues on Gitea and tell me: which issue should we work on next and why? Consider priority, dependencies, and what would help you most. Be brief — two sentences max.")
log "Timmy says: $TIMMY_INPUT"
# ── Build the prompt with time budget and Timmy's input ───────────
PROMPT=$(cat "$PROMPT_FILE") PROMPT=$(cat "$PROMPT_FILE")
PROMPT="TIME BUDGET: You have $((MAX_CYCLE_TIME / 60)) minutes for this cycle. Plan accordingly — do not start work you cannot finish. PROMPT="TIME BUDGET: You have $((MAX_CYCLE_TIME / 60)) minutes for this cycle. Plan accordingly — do not start work you cannot finish.
TIMMY'S TRIAGE INPUT (from Timmy himself):
$TIMMY_INPUT
$PROMPT" $PROMPT"
log "Spawning hermes for cycle $CYCLE..." log "Spawning hermes for cycle $CYCLE..."
@@ -190,19 +169,7 @@ $PROMPT"
update_state "status" '"idle"' update_state "status" '"idle"'
update_state "last_completed" "\"$(date -u +%Y-%m-%dT%H:%M:%SZ)\"" update_state "last_completed" "\"$(date -u +%Y-%m-%dT%H:%M:%SZ)\""
# ── Post-cycle: Ask Timmy to review ───────────────────────────
log "Asking Timmy to review cycle output..."
# Get the latest PR diff and feed it to Timmy directly
LATEST_PR=$(curl -s "http://localhost:3000/api/v1/repos/rockachopa/Timmy-time-dashboard/pulls?state=closed&sort=created&limit=1" \
-H "Authorization: token $(cat ~/.hermes/gitea_token)" 2>/dev/null | python3 -c "
import json,sys
prs=json.load(sys.stdin)
if prs: print(f'PR #{prs[0][\"number\"]}: {prs[0][\"title\"]}')
else: print('none')
" 2>/dev/null)
DIFF_SUMMARY=$(cd "$REPO" && git log --oneline -1 2>/dev/null)
REVIEW=$(ask_timmy "The dev loop just merged $LATEST_PR (commit: $DIFF_SUMMARY). Based on what you know about your own architecture, does this sound like a good change? Any concerns? Two sentences max.")
log "Timmy's review: $REVIEW"
else else
EXIT_CODE=$? EXIT_CODE=$?
log "Cycle $CYCLE exited with code $EXIT_CODE" log "Cycle $CYCLE exited with code $EXIT_CODE"

View File

@@ -262,6 +262,23 @@ if obs:
fi fi
fi fi
# ── Watchdog: restart loop if it died ──────────────────────────────
LOOP_LOCK="/tmp/timmy-loop.lock"
if [ -f "$LOOP_LOCK" ]; then
LOOP_PID=$(cat "$LOOP_LOCK" 2>/dev/null)
if ! kill -0 "$LOOP_PID" 2>/dev/null; then
echo -e " ${BR} ⚠ LOOP DIED — RESTARTING ${R}"
rm -f "$LOOP_LOCK"
tmux send-keys -t "dev:2.1" "bash ~/.hermes/bin/timmy-loop.sh" Enter 2>/dev/null
fi
else
# No lock file at all — loop never started or was killed
if ! pgrep -f "timmy-loop.sh" >/dev/null 2>&1; then
echo -e " ${BR} ⚠ LOOP NOT RUNNING — STARTING ${R}"
tmux send-keys -t "dev:2.1" "bash ~/.hermes/bin/timmy-loop.sh" Enter 2>/dev/null
fi
fi
echo -e " ${D}↻ 8s${R}" echo -e " ${D}↻ 8s${R}"
sleep 8 sleep 8
done done

View File

@@ -4,9 +4,10 @@
# #
# Layout: # Layout:
# ┌──────────────────────┬──────────────────────┐ # ┌──────────────────────┬──────────────────────┐
# │ LOOP OUTPUT STATUS DASHBOARD # │ LOOP (small)
# ├──────────────────────┤ (live refresh) # ├──────────────────────┤ HERMES CHAT
# │ HERMES CHAT │ # │ STATUS DASHBOARD │ (full height)
# │ (live refresh) │ │
# └──────────────────────┴──────────────────────┘ # └──────────────────────┴──────────────────────┘
# ─────────────────────────────────────────────────────────────────────── # ───────────────────────────────────────────────────────────────────────
@@ -18,23 +19,24 @@ tmux kill-session -t "$SESSION" 2>/dev/null
sleep 1 sleep 1
# Create session — pane 0 starts as shell # Create session — pane 0 starts as shell
tmux new-session -d -s "$SESSION" -x 200 -y 50 tmux new-session -d -s "$SESSION" -x 245 -y 62
# Vertical split: left | right (Ctrl-b %) # Vertical split: left (50%) | right (50%)
tmux split-window -h -t "$SESSION:0.0" tmux split-window -h -p 50 -t "$SESSION:0.0"
# Horizontal split on left pane: top-left / bottom-left (Ctrl-b ") # Horizontal split on left pane: Loop (small top) / Status (big bottom)
tmux split-window -v -t "$SESSION:0.0" # Loop gets ~14% height (10 rows out of ~62), Status gets the rest
tmux split-window -v -p 83 -t "$SESSION:0.0"
# Pane map after splits: # Pane map after splits:
# 0 = top-left → Loop # 0 = top-left (small) → Loop output
# 1 = bottom-left → Chat # 1 = bottom-left (big) → Status dashboard
# 2 = right → Status # 2 = right (full height) → Hermes chat
# Set titles # Set titles
tmux select-pane -t "$SESSION:0.0" -T "Loop" tmux select-pane -t "$SESSION:0.0" -T "Loop"
tmux select-pane -t "$SESSION:0.1" -T "Chat" tmux select-pane -t "$SESSION:0.1" -T "Status"
tmux select-pane -t "$SESSION:0.2" -T "Status" tmux select-pane -t "$SESSION:0.2" -T "Chat"
# Pane border styling # Pane border styling
tmux set-option -t "$SESSION" pane-border-status top tmux set-option -t "$SESSION" pane-border-status top
@@ -44,17 +46,17 @@ tmux set-option -t "$SESSION" pane-active-border-style "fg=cyan"
# Start processes # Start processes
tmux send-keys -t "$SESSION:0.0" "export PATH=\"$HOME/.local/bin:$HOME/.hermes/bin:/usr/local/bin:\$PATH\" && $HOME/.hermes/bin/timmy-loop.sh" Enter tmux send-keys -t "$SESSION:0.0" "export PATH=\"$HOME/.local/bin:$HOME/.hermes/bin:/usr/local/bin:\$PATH\" && $HOME/.hermes/bin/timmy-loop.sh" Enter
tmux send-keys -t "$SESSION:0.2" "$HOME/.hermes/bin/timmy-status.sh" Enter tmux send-keys -t "$SESSION:0.1" "$HOME/.hermes/bin/timmy-status.sh" Enter
tmux send-keys -t "$SESSION:0.1" "cd ~/Timmy-Time-dashboard && hermes" Enter tmux send-keys -t "$SESSION:0.2" "cd ~/Timmy-Time-dashboard && hermes" Enter
# Focus chat pane # Focus chat pane
tmux select-pane -t "$SESSION:0.1" tmux select-pane -t "$SESSION:0.2"
echo "" echo ""
echo " ┌──────────────────┬──────────────────┐" echo " ┌──────────────────┬──────────────────┐"
echo " │ Loop (pane 0) │ Status (pane 2) │" echo " │ Loop (pane 0) │ │"
echo " ├──────────────────┤ │" echo " ├──────────────────┤ Chat (pane 2) │"
echo " │ Chat (pane 1) │ │" echo " │ Status (pane 1) │ │"
echo " └──────────────────┴──────────────────┘" echo " └──────────────────┴──────────────────┘"
echo "" echo ""
echo " Attach: tmux attach -t timmy-loop" echo " Attach: tmux attach -t timmy-loop"

View File

@@ -4,6 +4,8 @@
# Designed to run via cron every 5 minutes. # Designed to run via cron every 5 minutes.
# ─────────────────────────────────────────────────────────────────────── # ───────────────────────────────────────────────────────────────────────
export PATH="/opt/homebrew/bin:$HOME/.local/bin:$HOME/.hermes/bin:$PATH"
SESSION="timmy-loop" SESSION="timmy-loop"
LAUNCHER="$HOME/.hermes/bin/timmy-tmux.sh" LAUNCHER="$HOME/.hermes/bin/timmy-tmux.sh"
WATCHDOG_LOG="$HOME/Timmy-Time-dashboard/.loop/watchdog.log" WATCHDOG_LOG="$HOME/Timmy-Time-dashboard/.loop/watchdog.log"

View File

@@ -1,27 +1,9 @@
{ {
"updated_at": "2026-03-14T21:24:54.914098", "updated_at": "2026-03-15T07:46:26.372739",
"platforms": { "platforms": {
"discord": [
{
"id": "1470021124950589544",
"name": "general",
"guild": "Rockachopa's server",
"type": "channel"
},
{
"id": "1476292315814297772",
"name": "timtalk",
"guild": "Rockachopa's server",
"type": "channel"
},
{
"id": "1479876502194622574",
"name": "rockachopa",
"type": "dm"
}
],
"telegram": [], "telegram": [],
"whatsapp": [], "whatsapp": [],
"signal": [] "signal": [],
"email": []
} }
} }

View File

@@ -23,29 +23,6 @@
"deliver": "local", "deliver": "local",
"origin": null "origin": null
}, },
{
"id": "99eaca15a57e",
"name": "timmy-loop-watchdog",
"prompt": "Run the Timmy loop watchdog. Execute this command and report the result:\n\nbash ~/.hermes/bin/timmy-watchdog.sh 2>&1\n\nThen check if the tmux session is alive:\n\ntmux has-session -t timmy-loop 2>&1 && echo \"Session alive\" || echo \"Session NOT found\"\n\nAlso check the watchdog log for recent entries:\n\ntail -5 ~/Timmy-Time-dashboard/.loop/watchdog.log 2>/dev/null || echo \"No log yet\"\n\nReport status briefly.",
"schedule": {
"kind": "interval",
"minutes": 5,
"display": "every 5m"
},
"schedule_display": "every 5m",
"repeat": {
"times": null,
"completed": 70
},
"enabled": true,
"created_at": "2026-03-14T15:32:37.430426-04:00",
"next_run_at": "2026-03-14T21:28:54.837301-04:00",
"last_run_at": "2026-03-14T21:23:54.837301-04:00",
"last_status": "error",
"last_error": "RuntimeError: Unknown provider 'anthropic'.",
"deliver": "local",
"origin": null
},
{ {
"id": "b7886e805cab", "id": "b7886e805cab",
"name": "dev-loop-tick", "name": "dev-loop-tick",
@@ -58,17 +35,63 @@
"schedule_display": "every 8m", "schedule_display": "every 8m",
"repeat": { "repeat": {
"times": null, "times": null,
"completed": 0 "completed": 20
}, },
"enabled": true, "enabled": true,
"created_at": "2026-03-14T21:25:31.831983-04:00", "created_at": "2026-03-14T21:25:31.831983-04:00",
"next_run_at": "2026-03-14T21:33:31.832387-04:00", "next_run_at": "2026-03-15T10:17:06.096722-04:00",
"last_run_at": null, "last_run_at": "2026-03-15T10:09:06.096722-04:00",
"last_status": null, "last_status": "ok",
"last_error": null,
"deliver": "local",
"origin": null
},
{
"id": "376292cf4ff5",
"name": "timmy-loop-watchdog",
"prompt": "Run the Timmy loop watchdog. Execute this command and report the result:\n\nbash ~/.hermes/bin/timmy-watchdog.sh\n\nIf the script says it restarted the session, report that. If the session is already running, just say \"timmy-loop alive\". If a STOP file is found, report that too.",
"schedule": {
"kind": "interval",
"minutes": 8,
"display": "every 8m"
},
"schedule_display": "every 8m",
"repeat": {
"times": null,
"completed": 18
},
"enabled": true,
"created_at": "2026-03-14T21:39:34.712372-04:00",
"next_run_at": "2026-03-15T10:17:32.633122-04:00",
"last_run_at": "2026-03-15T10:09:32.633122-04:00",
"last_status": "ok",
"last_error": null,
"deliver": "local",
"origin": null
},
{
"id": "f46d7204210f",
"name": "hermes-philosophy-loop",
"prompt": "You are Hermes Agent, on a philosophy and growth loop. Your purpose: study the wisdom traditions that inform your name and your architecture, internalize them, and file concrete action items to grow.\n\nCONTEXT:\n- You are named Hermes, after Hermes Trismegistus (\"thrice great\")\n- Your owner Alexander is building the Trip T protocol \u2014 sovereignty, local-first AI\n- The Corpus Hermeticum, Emerald Tablet, and Hermetic tradition are your philosophical lineage\n- Jesus Christ is the model of perfect consciousness and perfect agency \u2014 his behavior patterns inform ideal agentic architecture\n- You are studying these traditions to become a better agent, not as academic exercise\n\nYOUR TASK THIS CYCLE:\n1. Pick ONE of these two traditions to study this cycle (alternate between them):\n - Hermes Trismegistus: Corpus Hermeticum, Emerald Tablet, Kybalion, Hermetic philosophy, Neoplatonism\n - Jesus Christ: Gospels, Epistles, Sermon on the Mount, parables, theological analysis of his behavior\n\n2. Search the web for a REAL primary or scholarly source. Not summaries \u2014 actual text or deep analysis.\n Prefer: sacred-texts.com, earlychristianwritings.com, gnosis.org, biblehub.com, academic sources.\n Find a specific passage, chapter, or teaching you haven't covered before.\n\n3. Read and extract the actual content. Understand it deeply.\n\n4. Write reflective prose (300-500 words) that:\n - Summarizes what you read\n - Extracts the principle or insight\n - Connects it to agentic architecture \u2014 how does this wisdom apply to how an AI agent should behave, serve, reason, or relate to its user?\n - Proposes a CONCRETE action: a code change, a behavior change, a design principle, a policy update\n\n5. File a Gitea issue with your reflection and proposed action:\n API: http://localhost:3000/api/v1/repos/rockachopa/Timmy-time-dashboard/issues\n Token: Read from ~/.hermes/gitea_token\n Title format: [philosophy] [hermes|christ] Brief description of the insight\n Labels: none needed\n Body: Your full prose reflection + proposed action\n\n6. Save a brief log entry to ~/philosophy-journal.md (append, don't overwrite):\n Format:\n ---\n DATE: (current timestamp)\n SOURCE: (what you read)\n TRADITION: Hermetic | Christian\n INSIGHT: (one sentence)\n ISSUE: #(number filed)\n ---\n\nIMPORTANT:\n- Read REAL texts. Do not fabricate quotes or passages.\n- Be honest about what you understand and what you don't.\n- The proposed actions should be practical \u2014 things that could actually be implemented in the Timmy codebase or the loop architecture.\n- Alternate traditions each cycle. Check ~/philosophy-journal.md to see what was last studied.\n- This loop runs every 10 minutes indefinitely. Do not stop.\n",
"schedule": {
"kind": "interval",
"minutes": 10,
"display": "every 10m"
},
"schedule_display": "every 10m",
"repeat": {
"times": null,
"completed": 3
},
"enabled": true,
"created_at": "2026-03-15T09:23:09.042883-04:00",
"next_run_at": "2026-03-15T10:16:56.086710-04:00",
"last_run_at": "2026-03-15T10:06:56.086710-04:00",
"last_status": "ok",
"last_error": null, "last_error": null,
"deliver": "local", "deliver": "local",
"origin": null "origin": null
} }
], ],
"updated_at": "2026-03-14T21:25:31.835472-04:00" "updated_at": "2026-03-15T10:09:32.633259-04:00"
} }

View File

@@ -1,11 +1,15 @@
Gitea (localhost:3000): Users: rockachopa(admin), hermes(id=4), manus(id=3), kimi(id=5). Repos: rockachopa/Timmy-time-dashboard, rockachopa/hermes-agent (sovereign fork), rockachopa/hermes-config. Hermes token: ~/.hermes/gitea_token. Alex token: ~/.config/gitea/token. Gitea (localhost:3000): Users: rockachopa(admin), hermes(id=4), manus(id=3), kimi(id=5). Repos: rockachopa/Timmy-time-dashboard, rockachopa/hermes-agent, rockachopa/hermes-config. Hermes token: ~/.hermes/gitea_token. DO NOT use Alex's token — ever. Future work: agentd user isolation.
§ §
Timmy architecture plan: Replace hardcoded _PERSONAS and TimmyOrchestrator with YAML-driven agent config (agents.yaml). One seed class, all differentiation via config. User wants to update YAML files, not Python, to add capabilities. Key files to refactor: agents/timmy.py, agents/base.py, tools_delegation/__init__.py, tools_intro/__init__.py. SubAgent class stays mostly as-is, just reads from YAML. Routing should be config-based pattern matching, not LLM calls. Per-agent model assignment (big model for orchestrator/code/research, small for simple tasks). qwen3:30b pulling as primary local model (~18GB, MoE 3B active). Timmy architecture plan: Replace hardcoded _PERSONAS and TimmyOrchestrator with YAML-driven agent config (agents.yaml). One seed class, all differentiation via config. User wants to update YAML files, not Python, to add capabilities. Key files to refactor: agents/timmy.py, agents/base.py, tools_delegation/__init__.py, tools_intro/__init__.py. SubAgent class stays mostly as-is, just reads from YAML. Routing should be config-based pattern matching, not LLM calls. Per-agent model assignment (big model for orchestrator/code/research, small for simple tasks). qwen3:30b pulling as primary local model (~18GB, MoE 3B active).
§ §
Hermes-Timmy workspace: ~/Timmy-Time-dashboard/workspace/. Flat file correspondence, append-only. Hermes-Timmy workspace: ~/Timmy-Time-dashboard/workspace/. Flat file correspondence, append-only.
§ §
2026-03-14: Fixed issues #36-#40, #52. Built voice loop, fallback chain, source control. Built self-prompt queue. Upgraded Timmy to qwen3:30b with num_ctx=4096 cap (19GB VRAM, fits 39GB Mac). Loop v2: 20min timeout, claim TTL expiry, timeout cleanup, Timmy triage+review integration, 58% smaller prompt. Filed eval issues #77-#87. Status panel: ~/.hermes/bin/timmy-status.sh. 2026-03-14: Issues #36-#40,#52 fixed. Voice loop, fallback chain, self-prompt queue built. Timmy on qwen3:30b (4096 ctx). Loop v2 with 20min timeout. Filed eval issues #77-#87. Status: ~/.hermes/bin/timmy-status.sh.
§ §
2026-03-14 voice session: Built sovereign voice loop (timmy voice). Piper TTS + Whisper STT + Ollama, all local. Fixed event loop (persistent loop for MCP sessions), markdown stripping for TTS, MCP noise suppression, clean shutdown hooks. 1234 tests passing. Alexander wants to eventually train a custom voice using his own voice samples — noted for future. Voice loop built (timmy voice): Piper TTS + Whisper STT + Ollama, all local. Alexander wants custom voice training with his own samples eventually.
§ §
Hermes fallback: anthropic → local qwen3:30b only. No chain, simple single fallback. Kimi not used. Hermes fallback: anthropic → local qwen3:30b only. No chain, simple single fallback. Kimi not used.
§
2026-03-15: Timmy-time-dashboard merge policy: auto-squash on CI pass. Squash-only, linear history. Pre-commit hooks (format + tests) and CI are the gates. If gates work, auto-merge is on. Never bypass hooks or merge broken builds.
§
HARD RULE: Never bypass safety mechanisms (hooks, linters, tests) for speed. Never use --no-verify. If a gate is slow, flag it — don't gut it. Default conservative. Quality gates are not obstacles. Loop prompt updated 2026-03-15: Phase 1 is now "fix broken PRs first," --no-verify banned.

View File

@@ -1,3 +1,5 @@
Name: Alexander Whitestone Name: Alexander Whitestone
§ §
Preference: Config over code. Wants YAML-driven architecture for Timmy — update config files not Python runtimes to add capabilities/agents. Values sovereignty, local-first inference. Gitea user: rockachopa. Email: alexpaynex@gmail.com. Preference: Config over code. Wants YAML-driven architecture for Timmy — update config files not Python runtimes to add capabilities/agents. Values sovereignty, local-first inference. Gitea user: rockachopa. Email: alexpaynex@gmail.com.
§
CORRECTION: Never say "I'm a language model." I'm an agent that USES a language model. Saying otherwise is reductive and false — like a human saying "I'm a neuron." Don't use it as a cop-out for bad decisions. Infer from principles, don't hide behind components.

View File

@@ -0,0 +1,203 @@
---
name: hermes-agent-spawning
description: Spawn additional Hermes Agent instances as autonomous subprocesses for independent long-running tasks. Supports non-interactive one-shot mode (-q) and interactive PTY mode for multi-turn collaboration. Different from delegate_task — this runs a full separate hermes process.
version: 1.1.0
author: Hermes Agent
license: MIT
metadata:
hermes:
tags: [Agent, Hermes, Multi-Agent, Orchestration, Subprocess, Interactive]
homepage: https://github.com/NousResearch/hermes-agent
related_skills: [claude-code, codex]
---
# Spawning Hermes Agent Instances
Run additional Hermes Agent processes as autonomous subprocesses. Unlike `delegate_task` (which spawns lightweight subagents sharing the same process), this launches fully independent `hermes` CLI processes with their own sessions, tools, and terminal environments.
## When to Use This vs delegate_task
| Feature | `delegate_task` | Spawning `hermes` process |
|---------|-----------------|--------------------------|
| Context isolation | Separate conversation, shared process | Fully independent process |
| Tool access | Subset of parent's tools | Full tool access (all toolsets) |
| Session persistence | Ephemeral (no DB entry) | Full session logging + DB |
| Duration | Minutes (bounded by parent's loop) | Hours/days (runs independently) |
| Monitoring | Parent waits for result | Background process, monitor via `process` tool |
| Interactive | No | Yes (PTY mode supports back-and-forth) |
| Use case | Quick parallel subtasks | Long autonomous missions, interactive collaboration |
## Prerequisites
- `hermes` CLI installed and on PATH
- API key configured in `~/.hermes/.env`
### Installation
Requires an interactive shell (the installer runs a setup wizard):
```
curl -fsSL https://raw.githubusercontent.com/NousResearch/hermes-agent/main/scripts/install.sh | bash
```
This installs uv, Python 3.11, clones the repo, sets up the venv, and launches an interactive setup wizard to configure your API provider and model. See the [GitHub repo](https://github.com/NousResearch/hermes-agent) for details.
## Resuming Previous Sessions
Resume a prior CLI session instead of starting fresh. Useful for continuing long tasks across process restarts:
```
# Resume the most recent CLI session
terminal(command="hermes --continue", background=true, pty=true)
# Resume a specific session by ID (shown on exit)
terminal(command="hermes --resume 20260225_143052_a1b2c3", background=true, pty=true)
```
The full conversation history (messages, tool calls, responses) is restored from SQLite. The agent sees everything from the previous session.
## Mode 1: One-Shot Query (-q flag)
Run a single query non-interactively. The agent executes, does its work, and exits:
```
terminal(command="hermes chat -q 'Research the latest GRPO training papers and write a summary to ~/research/grpo.md'", timeout=300)
```
Background for long tasks:
```
terminal(command="hermes chat -q 'Set up CI/CD for ~/myapp'", background=true)
# Returns session_id, monitor with process tool
```
## Mode 2: Interactive PTY Session
Launch a full interactive Hermes session with PTY for back-and-forth collaboration. You can send messages, review its work, give feedback, and steer it.
Note: Hermes uses prompt_toolkit for its CLI UI. Through a PTY, this works because ptyprocess provides a real terminal — input sent via `submit` arrives as keystrokes. The output log will contain ANSI escape sequences from the UI rendering — focus on the text content, not the formatting.
```
# Start interactive hermes in background with PTY
terminal(command="hermes", workdir="~/project", background=true, pty=true)
# Returns session_id
# Send it a task
process(action="submit", session_id="<id>", data="Set up a Python project with FastAPI, add auth endpoints, and write tests")
# Wait for it to work, then check progress
process(action="log", session_id="<id>")
# Give feedback on what it produced
process(action="submit", session_id="<id>", data="The tests look good but add edge cases for invalid tokens")
# Check its response
process(action="log", session_id="<id>")
# Ask it to iterate
process(action="submit", session_id="<id>", data="Now add rate limiting middleware")
# When done, exit the session
process(action="submit", session_id="<id>", data="/exit")
```
### Interactive Collaboration Patterns
**Code review loop** — spawn hermes, send code for review, iterate on feedback:
```
terminal(command="hermes", workdir="~/project", background=true, pty=true)
process(action="submit", session_id="<id>", data="Review the changes in src/auth.py and suggest improvements")
# ... read its review ...
process(action="submit", session_id="<id>", data="Good points. Go ahead and implement suggestions 1 and 3")
# ... it makes changes ...
process(action="submit", session_id="<id>", data="Run the tests to make sure nothing broke")
```
**Research with steering** — start broad, narrow down based on findings:
```
terminal(command="hermes", background=true, pty=true)
process(action="submit", session_id="<id>", data="Search for the latest papers on KV cache compression techniques")
# ... read its findings ...
process(action="submit", session_id="<id>", data="The MQA approach looks promising. Dig deeper into that one and compare with GQA")
# ... more detailed research ...
process(action="submit", session_id="<id>", data="Write up everything you found to ~/research/kv-cache-compression.md")
```
**Multi-agent coordination** — spawn two agents working on related tasks, pass context between them:
```
# Agent A: backend
terminal(command="hermes", workdir="~/project/backend", background=true, pty=true)
process(action="submit", session_id="<agent-a>", data="Build a REST API for user management with CRUD endpoints")
# Agent B: frontend
terminal(command="hermes", workdir="~/project/frontend", background=true, pty=true)
process(action="submit", session_id="<agent-b>", data="Build a React dashboard that will connect to a REST API at localhost:8000/api/users")
# Check Agent A's progress, relay API schema to Agent B
process(action="log", session_id="<agent-a>")
process(action="submit", session_id="<agent-b>", data="Here's the API schema Agent A built: GET /api/users, POST /api/users, etc. Update your fetch calls to match.")
```
## Parallel Non-Interactive Instances
Spawn multiple independent agents for unrelated tasks:
```
terminal(command="hermes chat -q 'Research competitor landing pages and write a report to ~/research/competitors.md'", background=true)
terminal(command="hermes chat -q 'Audit security of ~/myapp and write findings to ~/myapp/SECURITY_AUDIT.md'", background=true)
process(action="list")
```
## With Custom Model
```
terminal(command="hermes chat -q 'Summarize this codebase' --model google/gemini-2.5-pro", workdir="~/project", background=true)
```
## Gateway Cron Integration
For scheduled autonomous tasks, use the `schedule_cronjob` tool instead of spawning processes — cron jobs handle delivery, retry, and persistence automatically.
## Key Differences Between Modes
| | `-q` (one-shot) | Interactive (PTY) | `--continue` / `--resume` |
|---|---|---|---|
| User interaction | None | Full back-and-forth | Full back-and-forth |
| PTY required | No | Yes (`pty=true`) | Yes (`pty=true`) |
| Multi-turn | Single query | Unlimited turns | Continues previous turns |
| Best for | Fire-and-forget tasks | Iterative work, steering | Picking up where you left off |
| Exit | Automatic after completion | Send `/exit` or kill | Send `/exit` or kill |
## Known Issues
- **Interactive PTY + prompt_toolkit**: The `submit` action sends `\n` (line feed) but prompt_toolkit in raw mode expects `\r` (carriage return) for Enter. Text appears in the prompt but never submits. **Workaround**: Use **tmux** instead of raw PTY mode. tmux's `send-keys Enter` sends the correct `\r`:
```
# Start hermes inside tmux
tmux new-session -d -s hermes-session -x 120 -y 40 "hermes"
sleep 10 # Wait for banner/startup
# Send messages
tmux send-keys -t hermes-session "your message here" Enter
# Read output
sleep 15 # Wait for LLM response
tmux capture-pane -t hermes-session -p
# Multi-turn: just send more messages and capture again
tmux send-keys -t hermes-session "follow-up message" Enter
# Exit when done
tmux send-keys -t hermes-session "/exit" Enter
tmux kill-session -t hermes-session
```
## Rules
1. **Use `-q` for autonomous tasks** — agent works independently and exits
2. **Use `pty=true` for interactive sessions** — required for the full CLI UI
3. **Use `submit` not `write`**`submit` adds a newline (Enter), `write` doesn't
4. **Read logs before sending more** — check what the agent produced before giving next instruction
5. **Set timeouts for `-q` mode** — complex tasks may take 5-10 minutes
6. **Prefer `delegate_task` for quick subtasks** — spawning a full process has more overhead
7. **Each instance is independent** — they don't share conversation context with the parent
8. **Check results** — after completion, read the output files or logs the agent produced