- AGENTS.md: design principles, enforcement hierarchy, deferred loading - agents/: brainstorm, build, orchestrator, research (auto-discovered by MCP server) - skills/: research methodology (auto-discovered by MCP server) - hooks/: pre-tool-use, post-tool-use (BFF block removed), session-start, stop, pre-compact, user-prompt-submit - frameworks/: opencode/plugin.ts (resolves hooks via import.meta.url — works as project-local or global plugin), github/hooks.json - mcp/index.ts: auto-discovers agents/*.md and skills/*.md from frontmatter (replaces hand-maintained registry); server renamed all-agents - docs/: agent-infrastructure.md (generalized), research docs (7 files), ai_architectures.md, llama-server-cuda-wsl2.md - install.sh: idempotent setup — Copilot global hooks, OpenCode global plugin + AGENTS.md + MCP entry, VS Code global MCP config
65 lines
4.0 KiB
Bash
Executable File
65 lines
4.0 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# UserPromptSubmit hook:
|
|
# 1. Writes the typed prompt text to a temp file as a fallback.
|
|
# 2. Injects an additionalContext instruction telling the agent to overwrite
|
|
# that file with a complete capture (including attachments / selections)
|
|
# before doing anything else. The Stop hook reads the file to remind the
|
|
# agent to verify every question was answered.
|
|
# 3. Injects agent nudges when trigger words are detected in the prompt.
|
|
# Nudges fire here (context tail) rather than in AGENTS.md (context start)
|
|
# so they survive long sessions — see "lost in the middle" in .agents/AGENTS.md.
|
|
set -euo pipefail
|
|
|
|
INPUT=$(cat)
|
|
|
|
PROMPT=$(echo "$INPUT" | node -e "
|
|
const d = JSON.parse(require('fs').readFileSync('/dev/stdin','utf8'));
|
|
process.stdout.write(d.prompt || '');
|
|
" 2>/dev/null || true)
|
|
|
|
if [[ -n "$PROMPT" ]]; then
|
|
echo "$PROMPT" > /tmp/.last-user-prompt.txt
|
|
fi
|
|
|
|
# ── Agent nudges ─────────────────────────────────────────────────────────────
|
|
# Check prompt for trigger words and append a one-line nudge to additionalContext.
|
|
# Only one nudge fires per turn (brainstorm takes priority over research).
|
|
NUDGE=""
|
|
|
|
if echo "$PROMPT" | grep -qiE \
|
|
'\bwait\b|actually|hmm+|\breconsider\b|overthink|overcomplicat|too complicated|simpler way|going in circles|second thoughts|what if we|maybe i should|step back|rethink|hold on|i.m not sure|analysis paralysis|stuck'; then
|
|
NUDGE="NUDGE: The user may be showing signs of analysis paralysis or complexity spiral. Consider suggesting they switch to @brainstorm — one sentence, non-intrusive. Don't force it; if they decline, don't repeat it for the same topic."
|
|
elif echo "$PROMPT" | grep -qiE \
|
|
'why is this broken|something.s wrong|not working|what changed|it used to work|\bregression\b|\btrace\b|dig into|root cause|figure out why|\bdebug\b|\bdiagnose\b|investigate|repeated.*fail|why.*code|how does this work|i don.t understand'; then
|
|
NUDGE="NUDGE: The user may need systematic investigation. Consider suggesting they switch to @research — one sentence, non-intrusive. Don't force it; if they decline, don't repeat it for the same topic."
|
|
fi
|
|
|
|
TASK_CAPTURE="TASK CAPTURE (do this first, before any other action): Write a concise bullet list of every distinct question and task in this prompt — including content from any attached files or selected text — to /tmp/.last-user-prompt.txt. Read the file and choose to overwrite or merge, depending on if there are still unanswered questions remaining. This file is read at session end to verify every question was answered. If there is nothing to capture (e.g. a one-word test message), write a single dash to the file."
|
|
|
|
# CURRENT QUESTION marker — placed last (context tail = highest recency bias).
|
|
# If the current prompt resembles an earlier one, this prevents the model from
|
|
# answering the earlier version. Mechanism validated by S2A (Weston & Sukhbaatar,
|
|
# arXiv:2311.11829): explicitly isolating the current query from prior context
|
|
# reduces sycophancy and improves factuality without a second LLM call.
|
|
CURRENT_Q="CURRENT QUESTION: Answer the user's most recent message (above). If a similar question appeared earlier in this session, answer THIS version — do not conflate it with the prior one."
|
|
|
|
if [[ -n "$NUDGE" ]]; then
|
|
ADDITIONAL_CONTEXT="${NUDGE}\n\n${TASK_CAPTURE}\n\n${CURRENT_Q}"
|
|
else
|
|
ADDITIONAL_CONTEXT="${TASK_CAPTURE}\n\n${CURRENT_Q}"
|
|
fi
|
|
|
|
# Prefix with a self-identifying marker so the model cannot confuse the
|
|
# injection with the user's own message.
|
|
ADDITIONAL_CONTEXT="[HOOK INJECTION: user-prompt-submit] System reminder — NOT part of the user's message:\n\n${ADDITIONAL_CONTEXT}"
|
|
|
|
json_context=$(printf '%b' "$ADDITIONAL_CONTEXT" | node -e 'process.stdout.write(JSON.stringify(require("fs").readFileSync("/dev/stdin","utf8")))')
|
|
cat <<EOF
|
|
{
|
|
"hookSpecificOutput": {
|
|
"hookEventName": "UserPromptSubmit",
|
|
"additionalContext": ${json_context}
|
|
}
|
|
}
|
|
EOF
|