Files
Jyotisha/tools/agent-loop/nightly-review.sh
T

60 lines
2.8 KiB
Bash
Executable File

#!/usr/bin/env bash
# Nightly review: today's staging commits and agent comments → docs/agents/LESSONS.md.
# Pushes docs-only changes to staging (enforced by the pre-push hook via AGENT_LOOP_DOCS_ONLY=1).
set -euo pipefail
HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
GITEA="$HERE/gitea.sh"
REPO_DIR="${JYOTISHA_REPO_DIR:-/workspace/Jyotisha}"
LOOP_WT="${AGENT_LOOP_WORKTREE:-$REPO_DIR/.worktrees/agent-loop}"
CONF_DIR="$HOME/.config/jyotisha-agent"
STATE_DIR="${AGENT_LOOP_STATE_DIR:-$HOME/.local/state/jyotisha-agent}"
MAX_BUDGET="${AGENT_LOOP_NIGHTLY_BUDGET_USD:-10}"
LOG="$STATE_DIR/nightly.log"
mkdir -p "$STATE_DIR/runs"
log() { printf '%s %s\n' "$(date -u +%FT%TZ)" "$*" >> "$LOG"; }
[[ -e "$CONF_DIR/STOP" ]] && { log "STOP file present, skipping"; exit 0; }
# Shares the tick lock so a review never runs beside a tick in the same worktree.
exec 9> "$STATE_DIR/claude.lock"
flock -w 3600 9 || { log "tick lock busy for an hour, skipping tonight"; exit 0; }
RUN_DIR="$STATE_DIR/runs/nightly-$(date -u +%Y%m%dT%H%M%SZ)"
mkdir -p "$RUN_DIR"
today="$(TZ=Asia/Shanghai date +%F)"
git -C "$REPO_DIR" fetch -q origin --prune
[[ -d "$LOOP_WT" ]] || git -C "$REPO_DIR" worktree add -q --detach "$LOOP_WT" origin/staging
git -C "$LOOP_WT" checkout -q --detach -f origin/staging
git -C "$LOOP_WT" clean -q -fd
# Today's agent comments on loop issues (owner account only).
since="$(TZ=Asia/Shanghai date -d "$today 00:00" -u +%FT%TZ)"
"$GITEA" issues "agent/试点" > "$RUN_DIR/open.json" || echo '[]' > "$RUN_DIR/open.json"
jq -n '[]' > "$RUN_DIR/comments.json"
for n in $(jq -r '.[].number' "$RUN_DIR/open.json"); do
"$GITEA" comments "$n" | jq --arg s "$since" --argjson n "$n" \
'[.[] | select(.created_at >= $s and (.body | startswith("<!-- agent:"))) | {issue: $n, created_at, body}]' \
> "$RUN_DIR/c.json"
jq -s 'add' "$RUN_DIR/comments.json" "$RUN_DIR/c.json" > "$RUN_DIR/m.json" && mv "$RUN_DIR/m.json" "$RUN_DIR/comments.json"
done
jq -n --arg day "$today" --arg staging "$(git -C "$REPO_DIR" rev-parse origin/staging)" \
--slurpfile c "$RUN_DIR/comments.json" '{date: $day, origin_staging: $staging, agent_comments: $c[0]}' > "$RUN_DIR/context.json"
(
cd "$LOOP_WT"
export GIT_CONFIG_COUNT=1 GIT_CONFIG_KEY_0=core.hooksPath GIT_CONFIG_VALUE_0="$HERE/hooks/claude"
export AGENT_LOOP_DOCS_ONLY=1
timeout --kill-after=60 30m claude -p \
"$(cat "$HERE/prompts/claude-nightly.md")
本轮上下文文件(JSON):$RUN_DIR/context.json" \
--output-format json \
--json-schema "$(cat "$HERE/prompts/claude-nightly.schema.json")" \
--settings "$HERE/claude-settings.json" \
--permission-mode dontAsk \
--max-budget-usd "$MAX_BUDGET"
) > "$RUN_DIR/out.json" 2>> "$LOG" || { log "nightly run failed ($RUN_DIR)"; exit 0; }
log "nightly: $(jq -c '.structured_output // {error: "no structured output"}' "$RUN_DIR/out.json")"