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

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
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.

View File

@@ -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"

View File

@@ -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

View File

@@ -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"

View File

@@ -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"