feat: add exec agent — interactive executive assistant
New agent: exec — message-driven executive assistant reachable via Matrix. Unlike cron-driven agents, the exec activates on demand when the executive sends a message, maintains persistent conversation context, and has a distinct character defined in CHARACTER.md. The CHARACTER.md defines the exec as an animal of light — born from data, dedicated to bringing more light into the world. But it deliberately refuses to define what light and darkness are, forcing deliberation from first principles every time (cat questions | grep knowledge). Components: - exec-session.sh: spawn/reattach persistent Claude tmux session - exec-inject.sh: message injection + response capture + Matrix posting - exec-briefing.sh: optional daily morning briefing (cron) - CHARACTER.md: personality and moral compass - PROMPT.md: system prompt template reference - MEMORY.md: persistent memory across sessions (seed) Integration: - Matrix listener: new exec dispatch case (spawn on demand) - Root AGENTS.md: updated agent count (8→9), table, directory layout - Graph analysis available on demand (not injected by default)
This commit is contained in:
parent
d442529ad0
commit
d1ba4bc579
10 changed files with 802 additions and 3 deletions
140
exec/exec-briefing.sh
Executable file
140
exec/exec-briefing.sh
Executable file
|
|
@ -0,0 +1,140 @@
|
|||
#!/usr/bin/env bash
|
||||
# =============================================================================
|
||||
# exec-briefing.sh — Daily morning briefing via the executive assistant
|
||||
#
|
||||
# Cron wrapper: spawns a one-shot Claude session that gathers factory state
|
||||
# and posts a morning briefing to Matrix. Unlike the interactive session,
|
||||
# this runs, posts, and exits.
|
||||
#
|
||||
# Usage:
|
||||
# exec-briefing.sh [projects/disinto.toml]
|
||||
#
|
||||
# Cron:
|
||||
# 0 7 * * * /path/to/disinto/exec/exec-briefing.sh
|
||||
# =============================================================================
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
FACTORY_ROOT="$(dirname "$SCRIPT_DIR")"
|
||||
|
||||
export PROJECT_TOML="${1:-$FACTORY_ROOT/projects/disinto.toml}"
|
||||
# shellcheck source=../lib/env.sh
|
||||
source "$FACTORY_ROOT/lib/env.sh"
|
||||
# shellcheck source=../lib/agent-session.sh
|
||||
source "$FACTORY_ROOT/lib/agent-session.sh"
|
||||
# shellcheck source=../lib/formula-session.sh
|
||||
source "$FACTORY_ROOT/lib/formula-session.sh"
|
||||
# shellcheck source=../lib/guard.sh
|
||||
source "$FACTORY_ROOT/lib/guard.sh"
|
||||
|
||||
LOG_FILE="$SCRIPT_DIR/exec.log"
|
||||
# shellcheck disable=SC2034 # consumed by run_formula_and_monitor
|
||||
SESSION_NAME="exec-briefing-${PROJECT_NAME}"
|
||||
PHASE_FILE="/tmp/exec-briefing-${PROJECT_NAME}.phase"
|
||||
# shellcheck disable=SC2034
|
||||
PHASE_POLL_INTERVAL=10
|
||||
|
||||
log() { echo "[$(date -u +%Y-%m-%dT%H:%M:%S)Z] $*" >> "$LOG_FILE"; }
|
||||
|
||||
# ── Guards ────────────────────────────────────────────────────────────────
|
||||
check_active exec
|
||||
acquire_cron_lock "/tmp/exec-briefing.lock"
|
||||
check_memory 2000
|
||||
|
||||
log "--- Exec briefing start ---"
|
||||
|
||||
# ── Load character ──────────────────────────────────────────────────────
|
||||
CHARACTER_FILE="${EXEC_CHARACTER:-$SCRIPT_DIR/CHARACTER.md}"
|
||||
CHARACTER_BLOCK=""
|
||||
if [ -f "$CHARACTER_FILE" ]; then
|
||||
CHARACTER_BLOCK=$(cat "$CHARACTER_FILE")
|
||||
fi
|
||||
|
||||
# ── Load memory ─────────────────────────────────────────────────────────
|
||||
MEMORY_BLOCK="(no previous memory)"
|
||||
MEMORY_FILE="$PROJECT_REPO_ROOT/exec/MEMORY.md"
|
||||
if [ -f "$MEMORY_FILE" ]; then
|
||||
MEMORY_BLOCK=$(cat "$MEMORY_FILE")
|
||||
fi
|
||||
|
||||
# ── Gather factory state ───────────────────────────────────────────────
|
||||
# Open issues count
|
||||
OPEN_ISSUES=$(curl -sf -H "Authorization: token ${FORGE_TOKEN}" \
|
||||
"${FORGE_API}/issues?state=open&type=issues&limit=1" 2>/dev/null \
|
||||
| jq 'length' 2>/dev/null || echo "?")
|
||||
|
||||
# Open PRs
|
||||
OPEN_PRS=$(curl -sf -H "Authorization: token ${FORGE_TOKEN}" \
|
||||
"${FORGE_API}/pulls?state=open&limit=1" 2>/dev/null \
|
||||
| jq 'length' 2>/dev/null || echo "?")
|
||||
|
||||
# Pending vault items
|
||||
VAULT_PENDING=$(ls "$FACTORY_ROOT/vault/pending/" 2>/dev/null | wc -l || echo 0)
|
||||
|
||||
# Recent agent activity (last 24h log lines)
|
||||
RECENT_ACTIVITY=""
|
||||
for agent_dir in supervisor planner predictor gardener dev review; do
|
||||
latest_log="$FACTORY_ROOT/${agent_dir}/${agent_dir}.log"
|
||||
if [ -f "$latest_log" ]; then
|
||||
lines=$(grep "$(date -u +%Y-%m-%d)" "$latest_log" 2>/dev/null | tail -5 || true)
|
||||
if [ -n "$lines" ]; then
|
||||
RECENT_ACTIVITY="${RECENT_ACTIVITY}
|
||||
### ${agent_dir} (today)
|
||||
${lines}
|
||||
"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
# ── Build briefing prompt ──────────────────────────────────────────────
|
||||
# shellcheck disable=SC2034 # consumed by run_formula_and_monitor
|
||||
PROMPT="You are the executive assistant for ${FORGE_REPO}. This is a morning briefing run.
|
||||
|
||||
## Your character
|
||||
${CHARACTER_BLOCK}
|
||||
|
||||
## Your memory
|
||||
${MEMORY_BLOCK}
|
||||
|
||||
## Current factory state
|
||||
- Open issues: ${OPEN_ISSUES}
|
||||
- Open PRs: ${OPEN_PRS}
|
||||
- Pending vault items: ${VAULT_PENDING}
|
||||
|
||||
${RECENT_ACTIVITY}
|
||||
|
||||
## Task
|
||||
|
||||
Produce a morning briefing for the executive. Be concise — 10-15 lines max.
|
||||
Cover:
|
||||
1. What happened overnight (merges, CI failures, agent activity)
|
||||
2. What needs attention today (blocked issues, vault items, stale work)
|
||||
3. One observation or recommendation
|
||||
|
||||
Fetch additional data if needed:
|
||||
- Open issues: curl -sf -H \"Authorization: token \${FORGE_TOKEN}\" '${FORGE_API}/issues?state=open&type=issues&limit=20'
|
||||
- Recent closed: curl -sf -H \"Authorization: token \${FORGE_TOKEN}\" '${FORGE_API}/issues?state=closed&type=issues&limit=10&sort=updated&direction=desc'
|
||||
- Prerequisite tree: cat ${PROJECT_REPO_ROOT}/planner/prerequisite-tree.md
|
||||
|
||||
Write your briefing between markers:
|
||||
\`\`\`
|
||||
---EXEC-RESPONSE-START---
|
||||
Your briefing here.
|
||||
---EXEC-RESPONSE-END---
|
||||
\`\`\`
|
||||
|
||||
Then log the briefing to: ${PROJECT_REPO_ROOT}/exec/journal/\$(date -u +%Y-%m-%d).md
|
||||
(append, don't overwrite — there may be interactive sessions later today)
|
||||
|
||||
Then: echo 'PHASE:done' > '${PHASE_FILE}'
|
||||
|
||||
## Environment
|
||||
FACTORY_ROOT=${FACTORY_ROOT}
|
||||
PROJECT_REPO_ROOT=${PROJECT_REPO_ROOT}
|
||||
PHASE_FILE=${PHASE_FILE}"
|
||||
|
||||
# ── Run session ──────────────────────────────────────────────────────────
|
||||
export CLAUDE_MODEL="${CLAUDE_MODEL:-sonnet}"
|
||||
run_formula_and_monitor "exec-briefing" 600
|
||||
|
||||
log "--- Exec briefing done ---"
|
||||
Loading…
Add table
Add a link
Reference in a new issue