#!/usr/bin/env bash # Oi: single-click Navigator trigger for acme. # Spawns or reuses a persistent Navigator session scoped to cwd, # sends current file context, and opens the chat window. 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. # Indicators: comma-delimited list of filenames that mark a workspace. WORKSPACE_INDICATORS=".mdworkspace" detect_workspace() { local dir="$1" local git_root="" while [ "$dir" != "/" ]; do # Check for workspace indicator local IFS=, for ind in $WORKSPACE_INDICATORS; do [ -e "$dir/$ind" ] && { printf '%s\n' "$dir"; return; } done unset IFS # Check for .git if [ -z "$git_root" ] && [ -e "$dir/.git" ]; then if [ -d "$dir/.git" ]; then # Real repo — this is a root git_root="$dir" elif ! grep -q '/modules/' "$dir/.git" 2>/dev/null; then # Worktree (.git file pointing to .git/worktrees/) — treat as root git_root="$dir" fi # Submodule (.git file pointing to .git/modules/) — skip 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}') NAV_NAME="nav-${hash}" NAV_PATH="$OLLIE/s/$NAV_NAME" # Spawn session if it doesn't exist new_session=false if ! [ -d "$NAV_PATH" ] 2>/dev/null; then printf 'name=%s\ncwd=%s\nagent=partner\n' "$NAV_NAME" "$cwd" | try tee "$OLLIE/s/new" >/dev/null sleep 0.5 new_session=true fi # Gather context from acme window (if running inside one) context="" if [ -n "${winid:-}" ] && [ "$winid" != "0" ]; then filename=$(9p read acme/$winid/tag | awk '{print $1; exit}') # Read selection (dot) sel=$(9p read acme/$winid/rdsel 2>/dev/null || true) # Git diff for this file (unstaged changes) 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" else # No acme window context; just send cwd info diff=$(cd "$cwd" && git diff --stat HEAD 2>/dev/null | head -30 || true) context="Working directory: $cwd" [ -n "$diff" ] && context="$context\n\nRecent changes:\n$diff" fi # Send prompt to Navigator if [ "$new_session" = "true" ]; then printf '%b' "$context\n\nAcknowledge context. What are we working on?" | try tee "$NAV_PATH/prompt" >/dev/null else printf '%b' "$context\n\nCheck in." | try tee "$NAV_PATH/prompt" >/dev/null fi # Wait for agent to finish, then print response waitstate() { cat "$NAV_PATH/statewait" 2>/dev/null || { ollie-remount && cat "$NAV_PATH/statewait"; } || die "mount lost"; } while [ "$(waitstate)" = "idle" ]; do :; done while [ "$(waitstate)" != "idle" ]; do :; done sleep 0.1 offset=$(try cat "$NAV_PATH/offset") || die "cannot read offset" try tail -c +$((offset + 1)) "$NAV_PATH/chat" printf '\n%s\n' "$NAV_NAME"