diff --git a/mkfile b/mkfile index c71cd31..1f10e60 100644 --- a/mkfile +++ b/mkfile @@ -8,6 +8,8 @@ all:V: chmod +x $BIN/Kmpl cp scripts/Oi $BIN/Oi chmod +x $BIN/Oi + cp scripts/Theo $BIN/Theo + chmod +x $BIN/Theo clean:V: - rm -f $TARG $BIN/Kmpl $BIN/Oi + rm -f $TARG $BIN/Kmpl $BIN/Oi $BIN/Theo diff --git a/scripts/Theo b/scripts/Theo new file mode 100755 index 0000000..31c8d8f --- /dev/null +++ b/scripts/Theo @@ -0,0 +1,86 @@ +#!/usr/bin/env bash +# Theo: single-click adversarial security review trigger for acme. +# Spawns or reuses a persistent Theo session scoped to cwd, +# sends current file context for security review. +set -euo pipefail + +cmd=${0##*/} +die() { printf '%s: %s\n' "$cmd" "$*" >&2; exit 1; } + +OLLIE=${OLLIE:-$HOME/mnt/ollie} +try() { "$@" 2>/dev/null || { ollie-remount && "$@"; }; } + +# Workspace detection: find workspace root (indicator file) or git/worktree root. +WSPACE_INDICATORS=".mdworkspace" + +detect_workspace() { + local dir="$1" + local git_root="" + + while [ "$dir" != "/" ]; do + local IFS=, + for ind in $WSPACE_INDICATORS; do + [ -e "$dir/$ind" ] && { printf '%s\n' "$dir"; return; } + done + unset IFS + if [ -z "$git_root" ] && [ -e "$dir/.git" ]; then + if [ -d "$dir/.git" ]; then + git_root="$dir" + elif ! grep -q '/modules/' "$dir/.git" 2>/dev/null; then + git_root="$dir" + fi + fi + dir=$(dirname "$dir") + done + + [ -n "$git_root" ] && { printf '%s\n' "$git_root"; return; } + return 1 +} + +cwd=$(detect_workspace "$(pwd)") || die "not in a git repo or workspace" +hash=$(printf '%s' "$cwd" | cksum | awk '{print $1}') +THEO_NAME="theo-${hash}" +THEO_PATH="$OLLIE/s/$THEO_NAME" + +# Spawn session if it doesn't exist +if ! [ -d "$THEO_PATH" ] 2>/dev/null; then + printf 'name=%s\ncwd=%s\nagent=theo\n' "$THEO_NAME" "$cwd" | try tee "$OLLIE/s/new" >/dev/null + sleep 0.5 +fi + +# Gather context from acme window +filename=$(9p read acme/$winid/tag | awk '{print $1; exit}') + +context="" +if [ -d "$filename" ]; then + # Directory window: review uncommitted changes + branch commits + base=$(cd "$cwd" && git merge-base HEAD main 2>/dev/null || git merge-base HEAD master 2>/dev/null || echo HEAD) + uncommitted=$(cd "$cwd" && git diff 2>/dev/null || true) + staged=$(cd "$cwd" && git diff --cached 2>/dev/null || true) + branch_diff=$(cd "$cwd" && git diff "$base"..HEAD 2>/dev/null || true) + + context="Working directory: $cwd" + [ -n "$uncommitted" ] && context="$context\n\nUncommitted changes:\n$uncommitted" + [ -n "$staged" ] && context="$context\n\nStaged changes:\n$staged" + [ -n "$branch_diff" ] && context="$context\n\nBranch commits (since merge base):\n$branch_diff" +else + # File window: review that file + sel=$(9p read acme/$winid/rdsel 2>/dev/null || true) + diff=$(cd "$cwd" && git diff -- "$filename" 2>/dev/null | head -100 || true) + + context="File: $filename" + [ -n "$sel" ] && context="$context\n\nSelection:\n$sel" + [ -n "$diff" ] && context="$context\n\nRecent changes (git diff):\n$diff" +fi + +# Send prompt +printf '%b' "$context\n\nSecurity review. Tear it apart." | try tee "$THEO_PATH/prompt" >/dev/null + +# Wait for agent to finish, then print response +waitstate() { cat "$THEO_PATH/statewait" 2>/dev/null || { ollie-remount && cat "$THEO_PATH/statewait"; } || die "mount lost"; } +while [ "$(waitstate)" = "idle" ]; do :; done +while [ "$(waitstate)" != "idle" ]; do :; done +sleep 0.1 +offset=$(cat "$THEO_PATH/offset") +tail -c +$((offset + 1)) "$THEO_PATH/chat" +printf '\n%s\n' "$THEO_NAME"