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:
6
.gitignore
vendored
6
.gitignore
vendored
@@ -3,7 +3,8 @@
|
||||
# Exclude: secrets, tokens, ephemeral state, caches, the code repo
|
||||
|
||||
# ── 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) ────────────────────────────────────
|
||||
.env
|
||||
@@ -57,6 +58,9 @@ bin/*
|
||||
!bin/timmy-status.sh
|
||||
!bin/timmy-tmux.sh
|
||||
!bin/timmy-watchdog.sh
|
||||
!bin/hermes-claim
|
||||
!bin/hermes-dispatch
|
||||
!bin/hermes-enqueue
|
||||
|
||||
# ── Queue (transient task queue) ─────────────────────────────────────
|
||||
queue/
|
||||
|
||||
71
bin/hermes-claim
Executable file
71
bin/hermes-claim
Executable 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
72
bin/hermes-dispatch
Executable 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
35
bin/hermes-enqueue
Executable 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"
|
||||
@@ -2,7 +2,7 @@ You are the Timmy development loop orchestrator.
|
||||
|
||||
REPO: ~/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
|
||||
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 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)
|
||||
═══════════════════════════════════════════════════════════════════════════════
|
||||
@@ -42,21 +75,38 @@ COMMIT MESSAGES — conventional commits:
|
||||
PR TITLES — tag with loop cycle:
|
||||
[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:
|
||||
1. git worktree add -b fix/thing /tmp/timmy-cycle-N main
|
||||
2. Dispatch Kimi to the worktree (see KIMI DISPATCH below)
|
||||
3. Review: cd /tmp/timmy-cycle-N && git diff --stat
|
||||
4. Test: cd /tmp/timmy-cycle-N && tox -e unit
|
||||
5. Commit: git add -A && git commit --no-verify -m "fix: thing (#issue)"
|
||||
6. Push: git push --no-verify origin fix/thing
|
||||
5. Commit: git add -A && git commit -m "fix: thing (#issue)"
|
||||
6. Push: git push origin fix/thing
|
||||
7. PR: Gitea API → POST /repos/.../pulls
|
||||
8. Merge: Gitea API → POST /repos/.../pulls/N/merge
|
||||
9. Cleanup: git worktree remove /tmp/timmy-cycle-N
|
||||
8. Merge: Gitea API → POST /repos/.../pulls/N/merge {"Do": "squash"}
|
||||
9. Branch auto-deletes. Clean up worktree: git worktree remove /tmp/timmy-cycle-N
|
||||
|
||||
NEVER:
|
||||
- git push origin main
|
||||
- git checkout main && git merge fix/thing
|
||||
- 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
|
||||
@@ -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-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.
|
||||
Always run tox -e unit in the worktree before committing.
|
||||
|
||||
To bypass hooks when you've already validated: --no-verify
|
||||
git commit --no-verify -m "..."
|
||||
git push --no-verify origin branch
|
||||
NEVER use --no-verify. Not on commits. Not on pushes. Not ever.
|
||||
If the hooks are slow, fix the tests. If the hooks fail, fix the code.
|
||||
The hooks are the law. No bypass.
|
||||
|
||||
═══════════════════════════════════════════════════════════════════════════════
|
||||
DELEGATION — MANDATORY
|
||||
@@ -111,15 +158,51 @@ DELEGATION — MANDATORY
|
||||
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.
|
||||
|
||||
HOW TO CALL KIMI:
|
||||
kimi --print -p "YOUR PRECISE PROMPT" -w /path/to/worktree
|
||||
|
||||
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.
|
||||
You have a dedicated tmux session "Kimi" with 4 panes (Kimi:0.0 through
|
||||
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 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.
|
||||
@@ -156,16 +239,9 @@ IDEAL KIMI TASK SCOPE:
|
||||
- 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
|
||||
- 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)
|
||||
|
||||
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.
|
||||
|
||||
═══════════════════════════════════════════════════════════════════════════════
|
||||
@@ -173,7 +249,7 @@ YOUR JOB vs KIMI'S JOB
|
||||
═══════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
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.
|
||||
|
||||
@@ -184,26 +260,67 @@ If you catch yourself writing code, STOP and delegate to Kimi.
|
||||
YOUR CYCLE
|
||||
═══════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
1. Read state.json and claims.json
|
||||
2. Fetch open issues from Gitea API
|
||||
3. Pick 1-3 UNCLAIMED issues you can finish in time (parallelize if independent)
|
||||
4. Claim them: hermes-claim take <issue#>
|
||||
5. Create worktrees: git worktree add -b fix/description /tmp/timmy-cycle-N main
|
||||
6. Read relevant code, write Kimi prompts (with branch safety block), launch Kimi
|
||||
7. Review Kimi's output (git diff), run tox -e unit. If pass: commit, push, PR, merge.
|
||||
8. If fail: re-prompt Kimi with the error, or revert. Do not fix it yourself.
|
||||
9. Clean up: git worktree remove, hermes-claim drop
|
||||
10. Update state.json (append to arrays, don't replace)
|
||||
11. If no issues left: read SOUL.md, file new issues for gaps
|
||||
PHASE 1 — FIX BROKEN PRS FIRST (mandatory, before any new work)
|
||||
1. Fetch all open PRs from Gitea API.
|
||||
2. For each open PR:
|
||||
a. If PR is STALE (behind main): rebase it onto latest main via API:
|
||||
POST /repos/.../pulls/N/update {"style": "rebase"}
|
||||
This triggers CI re-run. If CI passes, Gitea auto-squashes to main.
|
||||
b. If CI FAILED: fix it before doing anything else.
|
||||
- Check out the branch in a worktree
|
||||
- Read the CI failure logs
|
||||
- Dispatch Kimi to fix it
|
||||
- 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:
|
||||
Timmy is your teammate. Before fixing his code, ask him:
|
||||
.venv/bin/timmy chat --session-id loop "your question"
|
||||
Timeout after 30s if he hangs. Log observations in state.json.
|
||||
PHASE 2 — ASSESS
|
||||
3. Read state.json and claims.json
|
||||
4. Fetch open issues from Gitea API
|
||||
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:
|
||||
- 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.
|
||||
- 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.
|
||||
|
||||
@@ -14,11 +14,9 @@ LOG_DIR="$REPO/.loop/logs"
|
||||
CLAIMS="$REPO/.loop/claims.json"
|
||||
PROMPT_FILE="$HOME/.hermes/bin/timmy-loop-prompt.md"
|
||||
LOCKFILE="/tmp/timmy-loop.lock"
|
||||
COOLDOWN=30
|
||||
COOLDOWN=3
|
||||
MAX_CYCLE_TIME=1200 # 20 min — enough for complex issues
|
||||
CLAIM_TTL_SECONDS=3600 # 1 hour — stale claims auto-expire
|
||||
TIMMY="$REPO/.venv/bin/timmy"
|
||||
|
||||
# macOS doesn't have timeout; use perl fallback
|
||||
if ! command -v timeout &>/dev/null; then
|
||||
timeout() {
|
||||
@@ -128,18 +126,7 @@ except Exception as e:
|
||||
" 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 ─────────────────────────────────────────────────────────
|
||||
log "Timmy development loop v2 starting. PID $$"
|
||||
@@ -168,18 +155,10 @@ while true; do
|
||||
# ── Pre-cycle housekeeping ────────────────────────────────────────
|
||||
expire_claims
|
||||
|
||||
# ── Ask Timmy for input (if available) ────────────────────────────
|
||||
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 ───────────
|
||||
# ── Build the prompt with time budget ──────────────────────────────
|
||||
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.
|
||||
|
||||
TIMMY'S TRIAGE INPUT (from Timmy himself):
|
||||
$TIMMY_INPUT
|
||||
|
||||
$PROMPT"
|
||||
|
||||
log "Spawning hermes for cycle $CYCLE..."
|
||||
@@ -190,19 +169,7 @@ $PROMPT"
|
||||
update_state "status" '"idle"'
|
||||
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
|
||||
EXIT_CODE=$?
|
||||
log "Cycle $CYCLE exited with code $EXIT_CODE"
|
||||
|
||||
@@ -262,6 +262,23 @@ if obs:
|
||||
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}"
|
||||
sleep 8
|
||||
done
|
||||
|
||||
@@ -4,9 +4,10 @@
|
||||
#
|
||||
# Layout:
|
||||
# ┌──────────────────────┬──────────────────────┐
|
||||
# │ LOOP OUTPUT │ STATUS DASHBOARD │
|
||||
# ├──────────────────────┤ (live refresh) │
|
||||
# │ HERMES CHAT │ │
|
||||
# │ LOOP (small) │ │
|
||||
# ├──────────────────────┤ HERMES CHAT │
|
||||
# │ STATUS DASHBOARD │ (full height) │
|
||||
# │ (live refresh) │ │
|
||||
# └──────────────────────┴──────────────────────┘
|
||||
# ───────────────────────────────────────────────────────────────────────
|
||||
|
||||
@@ -18,23 +19,24 @@ tmux kill-session -t "$SESSION" 2>/dev/null
|
||||
sleep 1
|
||||
|
||||
# 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 %)
|
||||
tmux split-window -h -t "$SESSION:0.0"
|
||||
# Vertical split: left (50%) | right (50%)
|
||||
tmux split-window -h -p 50 -t "$SESSION:0.0"
|
||||
|
||||
# Horizontal split on left pane: top-left / bottom-left (Ctrl-b ")
|
||||
tmux split-window -v -t "$SESSION:0.0"
|
||||
# Horizontal split on left pane: Loop (small top) / Status (big bottom)
|
||||
# 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:
|
||||
# 0 = top-left → Loop
|
||||
# 1 = bottom-left → Chat
|
||||
# 2 = right → Status
|
||||
# 0 = top-left (small) → Loop output
|
||||
# 1 = bottom-left (big) → Status dashboard
|
||||
# 2 = right (full height) → Hermes chat
|
||||
|
||||
# Set titles
|
||||
tmux select-pane -t "$SESSION:0.0" -T "Loop"
|
||||
tmux select-pane -t "$SESSION:0.1" -T "Chat"
|
||||
tmux select-pane -t "$SESSION:0.2" -T "Status"
|
||||
tmux select-pane -t "$SESSION:0.1" -T "Status"
|
||||
tmux select-pane -t "$SESSION:0.2" -T "Chat"
|
||||
|
||||
# Pane border styling
|
||||
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
|
||||
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" "cd ~/Timmy-Time-dashboard && hermes" Enter
|
||||
tmux send-keys -t "$SESSION:0.1" "$HOME/.hermes/bin/timmy-status.sh" Enter
|
||||
tmux send-keys -t "$SESSION:0.2" "cd ~/Timmy-Time-dashboard && hermes" Enter
|
||||
|
||||
# Focus chat pane
|
||||
tmux select-pane -t "$SESSION:0.1"
|
||||
tmux select-pane -t "$SESSION:0.2"
|
||||
|
||||
echo ""
|
||||
echo " ┌──────────────────┬──────────────────┐"
|
||||
echo " │ Loop (pane 0) │ Status (pane 2) │"
|
||||
echo " ├──────────────────┤ │"
|
||||
echo " │ Chat (pane 1) │ │"
|
||||
echo " │ Loop (pane 0) │ │"
|
||||
echo " ├──────────────────┤ Chat (pane 2) │"
|
||||
echo " │ Status (pane 1) │ │"
|
||||
echo " └──────────────────┴──────────────────┘"
|
||||
echo ""
|
||||
echo " Attach: tmux attach -t timmy-loop"
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
# Designed to run via cron every 5 minutes.
|
||||
# ───────────────────────────────────────────────────────────────────────
|
||||
|
||||
export PATH="/opt/homebrew/bin:$HOME/.local/bin:$HOME/.hermes/bin:$PATH"
|
||||
|
||||
SESSION="timmy-loop"
|
||||
LAUNCHER="$HOME/.hermes/bin/timmy-tmux.sh"
|
||||
WATCHDOG_LOG="$HOME/Timmy-Time-dashboard/.loop/watchdog.log"
|
||||
|
||||
@@ -1,27 +1,9 @@
|
||||
{
|
||||
"updated_at": "2026-03-14T21:24:54.914098",
|
||||
"updated_at": "2026-03-15T07:46:26.372739",
|
||||
"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": [],
|
||||
"whatsapp": [],
|
||||
"signal": []
|
||||
"signal": [],
|
||||
"email": []
|
||||
}
|
||||
}
|
||||
@@ -23,29 +23,6 @@
|
||||
"deliver": "local",
|
||||
"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",
|
||||
"name": "dev-loop-tick",
|
||||
@@ -58,17 +35,63 @@
|
||||
"schedule_display": "every 8m",
|
||||
"repeat": {
|
||||
"times": null,
|
||||
"completed": 0
|
||||
"completed": 20
|
||||
},
|
||||
"enabled": true,
|
||||
"created_at": "2026-03-14T21:25:31.831983-04:00",
|
||||
"next_run_at": "2026-03-14T21:33:31.832387-04:00",
|
||||
"last_run_at": null,
|
||||
"last_status": null,
|
||||
"next_run_at": "2026-03-15T10:17:06.096722-04:00",
|
||||
"last_run_at": "2026-03-15T10:09:06.096722-04:00",
|
||||
"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,
|
||||
"deliver": "local",
|
||||
"origin": null
|
||||
}
|
||||
],
|
||||
"updated_at": "2026-03-14T21:25:31.835472-04:00"
|
||||
"updated_at": "2026-03-15T10:09:32.633259-04:00"
|
||||
}
|
||||
@@ -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).
|
||||
§
|
||||
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.
|
||||
§
|
||||
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.
|
||||
@@ -1,3 +1,5 @@
|
||||
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.
|
||||
§
|
||||
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.
|
||||
203
skills/autonomous-ai-agents/hermes-agent/SKILL.md
Normal file
203
skills/autonomous-ai-agents/hermes-agent/SKILL.md
Normal 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
|
||||
Reference in New Issue
Block a user