feat: update kimi-code skill with session persistence, file arch refactor issues #164-#170
This commit is contained in:
@@ -1,7 +1,12 @@
|
||||
#!/usr/bin/env bash
|
||||
# ── Timmy Development Loop v2 ──────────────────────────────────────────
|
||||
# Runs forever. Each cycle: Timmy triages → Hermes picks work → execute
|
||||
# → Timmy reviews → merge. State in .loop/state.json.
|
||||
# ── Timmy Development Loop v3 ──────────────────────────────────────────
|
||||
# Runs forever. Each cycle: triage → Hermes executes → retro → repeat.
|
||||
#
|
||||
# TRIAGE (two tiers):
|
||||
# Fast (every 5 cycles): mechanical scoring → .loop/queue.json
|
||||
# Deep (every 20 cycles): Hermes reads code + consults Timmy → refined queue
|
||||
#
|
||||
# RETRO (every cycle): structured log → .loop/retro/cycles.jsonl
|
||||
# ───────────────────────────────────────────────────────────────────────
|
||||
|
||||
set -uo pipefail
|
||||
@@ -17,6 +22,12 @@ LOCKFILE="/tmp/timmy-loop.lock"
|
||||
COOLDOWN=3
|
||||
MAX_CYCLE_TIME=1200 # 20 min — enough for complex issues
|
||||
CLAIM_TTL_SECONDS=3600 # 1 hour — stale claims auto-expire
|
||||
FAST_TRIAGE_INTERVAL=5 # mechanical scoring every N cycles
|
||||
DEEP_TRIAGE_INTERVAL=20 # Hermes+Timmy deep triage every N cycles
|
||||
TRIAGE_SCRIPT="$REPO/scripts/triage_score.py"
|
||||
RETRO_SCRIPT="$REPO/scripts/cycle_retro.py"
|
||||
DEEP_TRIAGE_SCRIPT="$REPO/scripts/deep_triage.sh"
|
||||
QUEUE_FILE="$REPO/.loop/queue.json"
|
||||
# macOS doesn't have timeout; use perl fallback
|
||||
if ! command -v timeout &>/dev/null; then
|
||||
timeout() {
|
||||
@@ -125,11 +136,69 @@ except Exception as e:
|
||||
print(f'[cleanup] Claim cleanup failed: {e}')
|
||||
" 2>&1
|
||||
}
|
||||
# ── Fast triage (mechanical scoring) ──────────────────────────────────
|
||||
run_fast_triage() {
|
||||
if [ -f "$TRIAGE_SCRIPT" ]; then
|
||||
log "Running fast triage..."
|
||||
python3 "$TRIAGE_SCRIPT" 2>&1
|
||||
else
|
||||
log "WARNING: triage script not found at $TRIAGE_SCRIPT"
|
||||
fi
|
||||
}
|
||||
|
||||
# ── Deep triage (Hermes + Timmy) ─────────────────────────────────────
|
||||
run_deep_triage() {
|
||||
if [ -f "$DEEP_TRIAGE_SCRIPT" ]; then
|
||||
log "Running deep triage (Hermes + Timmy)..."
|
||||
timeout 600 bash "$DEEP_TRIAGE_SCRIPT" 2>&1
|
||||
else
|
||||
log "WARNING: deep triage script not found at $DEEP_TRIAGE_SCRIPT"
|
||||
fi
|
||||
}
|
||||
|
||||
# ── Cycle retrospective ─────────────────────────────────────────────
|
||||
log_retro() {
|
||||
# Usage: log_retro <success|failure> [extra args for cycle_retro.py]
|
||||
local outcome="$1"; shift
|
||||
if [ -f "$RETRO_SCRIPT" ]; then
|
||||
local duration=$(( $(date +%s) - CYCLE_START ))
|
||||
if [ "$outcome" = "success" ]; then
|
||||
python3 "$RETRO_SCRIPT" --cycle "$CYCLE" --success \
|
||||
--duration "$duration" "$@" 2>&1
|
||||
else
|
||||
python3 "$RETRO_SCRIPT" --cycle "$CYCLE" --failure \
|
||||
--duration "$duration" "$@" 2>&1
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
# ── Inject queue context into prompt ─────────────────────────────────
|
||||
get_queue_context() {
|
||||
if [ -f "$QUEUE_FILE" ]; then
|
||||
python3 -c "
|
||||
import json
|
||||
with open('$QUEUE_FILE') as f: q = json.load(f)
|
||||
if not q:
|
||||
print('Queue is empty. Check open issues on Gitea and pick the highest-priority one.')
|
||||
else:
|
||||
print(f'PRIORITIZED QUEUE ({len(q)} ready issues):')
|
||||
for i, item in enumerate(q[:8]):
|
||||
flag = 'BUG' if item.get('type') == 'bug' else item.get('type', '?').upper()
|
||||
print(f' {i+1}. #{item[\"issue\"]} [{flag}] score={item[\"score\"]} — {item.get(\"title\",\"?\")[:60]}')
|
||||
if item.get('files'):
|
||||
print(f' files: {\", \".join(item[\"files\"][:3])}')
|
||||
if len(q) > 8:
|
||||
print(f' ... +{len(q)-8} more')
|
||||
print()
|
||||
print(f'Pick from the TOP of this queue. Issue #{q[0][\"issue\"]} is highest priority.')
|
||||
" 2>/dev/null
|
||||
else
|
||||
echo "No queue file. Check open issues on Gitea and pick the highest-priority one."
|
||||
fi
|
||||
}
|
||||
|
||||
# ── Main Loop ─────────────────────────────────────────────────────────
|
||||
log "Timmy development loop v2 starting. PID $$"
|
||||
log "Timmy development loop v3 starting. PID $$"
|
||||
log "Timeout: ${MAX_CYCLE_TIME}s | Cooldown: ${COOLDOWN}s | Claim TTL: ${CLAIM_TTL_SECONDS}s"
|
||||
log "Repo: $REPO"
|
||||
update_state "started_at" "\"$(date -u +%Y-%m-%dT%H:%M:%SZ)\""
|
||||
@@ -152,13 +221,25 @@ while true; do
|
||||
update_state "cycle" "$CYCLE"
|
||||
update_state "status" '"working"'
|
||||
|
||||
CYCLE_START=$(date +%s)
|
||||
|
||||
# ── Pre-cycle housekeeping ────────────────────────────────────────
|
||||
expire_claims
|
||||
|
||||
# ── Build the prompt with time budget ──────────────────────────────
|
||||
# ── Triage (fast every 5, deep every 20) ─────────────────────────
|
||||
if (( CYCLE % DEEP_TRIAGE_INTERVAL == 0 )); then
|
||||
run_deep_triage
|
||||
elif (( CYCLE % FAST_TRIAGE_INTERVAL == 0 )); then
|
||||
run_fast_triage
|
||||
fi
|
||||
|
||||
# ── Build the prompt with time budget + queue ────────────────────
|
||||
QUEUE_CONTEXT=$(get_queue_context)
|
||||
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.
|
||||
|
||||
$QUEUE_CONTEXT
|
||||
|
||||
$PROMPT"
|
||||
|
||||
log "Spawning hermes for cycle $CYCLE..."
|
||||
@@ -169,6 +250,8 @@ $PROMPT"
|
||||
update_state "status" '"idle"'
|
||||
update_state "last_completed" "\"$(date -u +%Y-%m-%dT%H:%M:%SZ)\""
|
||||
|
||||
# ── Cycle retro (success) ────────────────────────────────────
|
||||
log_retro success
|
||||
|
||||
else
|
||||
EXIT_CODE=$?
|
||||
@@ -182,7 +265,10 @@ errs.append({'cycle': $CYCLE, 'code': $EXIT_CODE, 'time': '$(date -u +%Y-%m-%dT%
|
||||
s['errors'] = errs[-10:]
|
||||
with open('$STATE', 'w') as f: json.dump(s, f, indent=2)
|
||||
"
|
||||
# ── Cleanup on failure ────────────────────────────────────────
|
||||
# ── Cycle retro (failure) ────────────────────────────────────
|
||||
log_retro failure --reason "exit code $EXIT_CODE"
|
||||
|
||||
# ── Cleanup on failure ───────────────────────────────────────
|
||||
cleanup_cycle "$CYCLE"
|
||||
fi
|
||||
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
#!/usr/bin/env bash
|
||||
# ── Timmy Loop tmux Session ────────────────────────────────────────────
|
||||
# Creates session with 3 panes using standard tmux splits.
|
||||
# Creates session with 4 panes.
|
||||
#
|
||||
# Layout:
|
||||
# ┌──────────────────────┬──────────────────────┐
|
||||
# │ LOOP (small) │ │
|
||||
# ├──────────────────────┤ HERMES CHAT │
|
||||
# │ STATUS DASHBOARD │ (full height) │
|
||||
# │ (live refresh) │ │
|
||||
# └──────────────────────┴──────────────────────┘
|
||||
# Layout (245×62 terminal):
|
||||
# ┌────────────────────────────────────────────┬──────────────────────┐
|
||||
# │ LOOP (10 rows) │ │
|
||||
# ├──────────────────────┬─────────────────────┤ CHAT (full height) │
|
||||
# │ STATUS (81 cols) │ LOOPSTAT (40 cols) │ │
|
||||
# │ (50 rows) │ (50 rows) │ │
|
||||
# └──────────────────────┴─────────────────────┴──────────────────────┘
|
||||
# ───────────────────────────────────────────────────────────────────────
|
||||
|
||||
SESSION="timmy-loop"
|
||||
@@ -21,22 +21,26 @@ sleep 1
|
||||
# Create session — pane 0 starts as shell
|
||||
tmux new-session -d -s "$SESSION" -x 245 -y 62
|
||||
|
||||
# Vertical split: left (50%) | right (50%)
|
||||
# Vertical split: left (~50%) | right Chat (~50%)
|
||||
tmux split-window -h -p 50 -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
|
||||
# Horizontal split on left: Loop (small top ~16%) | bottom (~84%)
|
||||
tmux split-window -v -p 83 -t "$SESSION:0.0"
|
||||
|
||||
# Vertical split on bottom-left: Status (wide ~67%) | LOOPSTAT (~33%)
|
||||
tmux split-window -h -p 33 -t "$SESSION:0.1"
|
||||
|
||||
# Pane map after splits:
|
||||
# 0 = top-left (small) → Loop output
|
||||
# 1 = bottom-left (big) → Status dashboard
|
||||
# 2 = right (full height) → Hermes chat
|
||||
# 0 = top-left (full width) → Loop output
|
||||
# 1 = bottom-left (wide) → Status dashboard
|
||||
# 2 = bottom-mid (narrow) → LOOPSTAT (strategy)
|
||||
# 3 = right (full height) → Hermes chat
|
||||
|
||||
# Set titles
|
||||
tmux select-pane -t "$SESSION:0.0" -T "Loop"
|
||||
tmux select-pane -t "$SESSION:0.1" -T "Status"
|
||||
tmux select-pane -t "$SESSION:0.2" -T "Chat"
|
||||
tmux select-pane -t "$SESSION:0.2" -T "LOOPSTAT"
|
||||
tmux select-pane -t "$SESSION:0.3" -T "Chat"
|
||||
|
||||
# Pane border styling
|
||||
tmux set-option -t "$SESSION" pane-border-status top
|
||||
@@ -47,17 +51,18 @@ 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.1" "$HOME/.hermes/bin/timmy-status.sh" Enter
|
||||
tmux send-keys -t "$SESSION:0.2" "cd ~/Timmy-Time-dashboard && hermes" Enter
|
||||
tmux send-keys -t "$SESSION:0.2" "$HOME/.hermes/bin/timmy-loopstat.sh" Enter
|
||||
tmux send-keys -t "$SESSION:0.3" "cd ~/Timmy-Time-dashboard && hermes" Enter
|
||||
|
||||
# Focus chat pane
|
||||
tmux select-pane -t "$SESSION:0.2"
|
||||
tmux select-pane -t "$SESSION:0.3"
|
||||
|
||||
echo ""
|
||||
echo " ┌──────────────────┬──────────────────┐"
|
||||
echo " ┌──────────────────────────────────┬──────────────────┐"
|
||||
echo " │ Loop (pane 0) │ │"
|
||||
echo " ├──────────────────┤ Chat (pane 2) │"
|
||||
echo " │ Status (pane 1) │ │"
|
||||
echo " └──────────────────┴──────────────────┘"
|
||||
echo " ├────────────────────┬─────────────┤ Chat (pane 3) │"
|
||||
echo " │ Status (pane 1) │ LOOPSTAT(2) │ │"
|
||||
echo " └────────────────────┴─────────────┴──────────────────┘"
|
||||
echo ""
|
||||
echo " Attach: tmux attach -t timmy-loop"
|
||||
echo " Stop: touch ~/Timmy-Time-dashboard/.loop/STOP"
|
||||
|
||||
@@ -35,12 +35,12 @@
|
||||
"schedule_display": "every 8m",
|
||||
"repeat": {
|
||||
"times": null,
|
||||
"completed": 20
|
||||
"completed": 28
|
||||
},
|
||||
"enabled": true,
|
||||
"created_at": "2026-03-14T21:25:31.831983-04:00",
|
||||
"next_run_at": "2026-03-15T10:17:06.096722-04:00",
|
||||
"last_run_at": "2026-03-15T10:09:06.096722-04:00",
|
||||
"next_run_at": "2026-03-15T11:35:42.310507-04:00",
|
||||
"last_run_at": "2026-03-15T11:27:42.310507-04:00",
|
||||
"last_status": "ok",
|
||||
"last_error": null,
|
||||
"deliver": "local",
|
||||
@@ -58,12 +58,12 @@
|
||||
"schedule_display": "every 8m",
|
||||
"repeat": {
|
||||
"times": null,
|
||||
"completed": 18
|
||||
"completed": 26
|
||||
},
|
||||
"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",
|
||||
"next_run_at": "2026-03-15T11:35:59.953250-04:00",
|
||||
"last_run_at": "2026-03-15T11:27:59.953250-04:00",
|
||||
"last_status": "ok",
|
||||
"last_error": null,
|
||||
"deliver": "local",
|
||||
@@ -81,17 +81,17 @@
|
||||
"schedule_display": "every 10m",
|
||||
"repeat": {
|
||||
"times": null,
|
||||
"completed": 3
|
||||
"completed": 8
|
||||
},
|
||||
"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",
|
||||
"next_run_at": "2026-03-15T11:32:58.861581-04:00",
|
||||
"last_run_at": "2026-03-15T11:22:58.861581-04:00",
|
||||
"last_status": "ok",
|
||||
"last_error": null,
|
||||
"deliver": "local",
|
||||
"origin": null
|
||||
}
|
||||
],
|
||||
"updated_at": "2026-03-15T10:09:32.633259-04:00"
|
||||
"updated_at": "2026-03-15T11:27:59.953394-04:00"
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
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.
|
||||
Gitea (localhost:3000): Users: rockachopa(admin), hermes(id=4), manus(id=3), kimi(id=5). Repos: rockachopa/Timmy-time-dashboard, hermes/hermes-config (private, rockachopa=admin collab). Hermes token: ~/.hermes/gitea_token. DO NOT use Alex's token — ever. Sync tool: ~/.hermes/bin/hermes-config-sync — run after modifying config/scripts/memories/skills. 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.
|
||||
Kimi delegation: 1-3 files max, pre-extract context (scans full codebase otherwise), ~/worktrees/ not /tmp/, verify commits, two-attempt rule. Worktrees: ~/worktrees/kimi-*.
|
||||
§
|
||||
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.
|
||||
§
|
||||
|
||||
@@ -21,7 +21,22 @@ Delegate coding tasks to Kimi Code CLI via the Hermes terminal. Powered by kimi-
|
||||
- Logged in: `kimi login`
|
||||
- Config at `~/.kimi/config.toml`
|
||||
|
||||
## One-Shot Tasks (Print Mode)
|
||||
## Session Persistence (ALWAYS USE)
|
||||
|
||||
Kimi scans the entire codebase on first run. Reuse sessions to keep context warm:
|
||||
|
||||
```
|
||||
# First task in a worktree — creates session, caches codebase
|
||||
terminal(command="kimi --print --session worktree-148 -p 'Fix the XSS vulnerability'", workdir="~/worktrees/kimi-148", timeout=300)
|
||||
|
||||
# Subsequent tasks — context is cached, much faster
|
||||
terminal(command="kimi --print --continue --session worktree-148 -p 'Now fix the CSRF issue'", workdir="~/worktrees/kimi-148", timeout=180)
|
||||
```
|
||||
|
||||
**NEVER run Kimi without --session on large codebases.** First run takes 2-5 min.
|
||||
Subsequent runs in same session take 30-90s.
|
||||
|
||||
## One-Shot Tasks (only for tiny repos or quick questions)
|
||||
|
||||
```
|
||||
terminal(command="kimi --print -p 'Fix the XSS vulnerability in swarm_live.html by sanitizing innerHTML'", workdir="~/project", timeout=120)
|
||||
|
||||
Reference in New Issue
Block a user