Make all Cognitive Sandwich hooks opt-in

This commit is contained in:
Sam Valladares 2026-05-01 05:12:54 -05:00
parent cb7ee2dcb5
commit 1d95347b88
8 changed files with 137 additions and 95 deletions

View file

@ -9,7 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
The Sanhedrin Executioner — Vestige's veto layer for Claude Code responses — can run against a local MLX model (`mlx-community/Qwen3.6-35B-A3B-4bit`) when explicitly enabled. Combined with four pre-cognitive UserPromptSubmit hooks (synthesis-preflight, cwd-state-injector, vestige-pulse-daemon, preflight-swarm), Vestige now ships a complete "Cognitive Sandwich" — Vestige memories injected before the model thinks, optional Sanhedrin veto after the model speaks.
> 2026-05-01 hotfix: Sanhedrin and all Vestige Stop hooks are optional by default. The default installer wires UserPromptSubmit preflight hooks only, removes old Vestige Stop hooks from previous v2.1.0 installs, no longer starts MLX, and removes the old v2.1.0 MLX launchd job on reinstall. Users who want Sanhedrin can opt in with `--enable-sanhedrin`; Apple Silicon local MLX autostart is a separate `--with-launchd` flag, and x86 users can point `--sanhedrin-endpoint` at any OpenAI-compatible `/v1/chat/completions` endpoint.
> 2026-05-01 hotfix: Sanhedrin, preflight, and all Vestige Claude Code hooks are optional by default. The default installer activates no hooks, makes no automatic model calls, removes old Vestige hook wiring from previous v2.1.0 installs, no longer starts MLX, and removes the old v2.1.0 MLX launchd job on reinstall. Users who want preflight can opt in with `--enable-preflight`; users who want Sanhedrin can opt in with `--enable-sanhedrin`; Apple Silicon local MLX autostart is a separate `--with-launchd` flag, and x86 users can point `--sanhedrin-endpoint` at any OpenAI-compatible `/v1/chat/completions` endpoint.
### Added
@ -23,19 +23,20 @@ The Sanhedrin Executioner — Vestige's veto layer for Claude Code responses —
- `synthesis-stop-validator.sh` — Stop hook regex against forbidden hedging patterns.
- `veto-detector.sh` — fast 50ms regex pre-screen against `veto`-tagged Vestige memories.
- `synthesis-gate.sh` — legacy v1 trigger (kept for backward compat).
- `settings.fragment.json` — default UserPromptSubmit-only JSON snippet merged into `~/.claude/settings.json` by the installer.
- `settings.fragment.json` — empty default fragment used to remove old Vestige hook wiring without enabling new hooks.
- `settings.preflight.fragment.json` — opt-in UserPromptSubmit hooks used only with `--enable-preflight`.
- `settings.sanhedrin.fragment.json` — opt-in JSON snippet used only with `--enable-sanhedrin`.
- **Dashboard `/api/changelog` endpoint** — bounded REST event feed for recent `DreamCompleted` and `ConnectionDiscovered` events, used by the Pulse hook to inject fresh synthesis into Claude Code context.
- **`agents/`** — `executioner.md` (legacy/fallback Haiku 4.5 path), `lateral-thinker.md`, `synthesis-composer.md`.
- **`launchd/com.vestige.mlx-server.plist.template`** — optional Apple Silicon helper that auto-starts `mlx_lm.server` with the Qwen3.6-35B-A3B-4bit model on login. Templated with `__HOME__` and `__MODEL__` placeholders.
- **`scripts/install-sandwich.sh`** — one-command installer that stages hooks, agents, jq-merges the settings fragment, and backs up `settings.json` to `.bak.pre-sandwich`. Supports `--force`, `--enable-sanhedrin`, `--with-launchd`, `--sanhedrin-endpoint`, `--sanhedrin-model`, `--include-memory-loader`, `--src=PATH`.
- **`scripts/check-sandwich-prereqs.sh`** — prereq verifier for lightweight hooks by default, with `--sanhedrin` for the optional endpoint / MLX checks.
- **`scripts/check-sandwich-prereqs.sh`** — verifier that default installs have no Vestige hooks wired, with `--preflight` and `--sanhedrin` checks for optional layers.
- **`docs/COGNITIVE_SANDWICH.md`** — architecture diagram, install guide, performance notes (82 tok/s on M3 Max), uninstall, configuration env vars.
- **PR #48**`VESTIGE_DATA_DIR` env-var support + tilde expansion + secure unix perms (thanks @Jelloeater) — directly addresses the ghost env-vars exposed by v2.0.9 cleanup.
### Changed
- **Sanhedrin and all Vestige Stop hooks are optional by default.** Default installs run on x86 and low-memory machines without wiring any Vestige Stop hook, downloading the 19 GB MLX model, or starting MLX. Reinstalling the default v2.1.0 hotfix removes the old Vestige Stop hooks and the old mandatory `com.vestige.mlx-server` launchd job if they exist.
- **Sanhedrin, preflight, and all Vestige Claude Code hooks are optional by default.** Default installs run on x86 and low-memory machines without wiring any Vestige hook, calling Claude, downloading the 19 GB MLX model, or starting MLX. Reinstalling the default v2.1.0 hotfix removes the old Vestige hook wiring and the old mandatory `com.vestige.mlx-server` launchd job if they exist.
- **Sanhedrin Executioner backend swapped from Anthropic Haiku 4.5 → OpenAI-compatible endpoint, with local `mlx_lm.server` + Qwen3.6-35B-A3B-4bit as the Apple Silicon opt-in path.** Anthropic API key no longer required for the post-cognitive layer. The `executioner.md` agent definition is retained as manual/fallback only when invoked explicitly via `Task(subagent_type='executioner')`.
- **All hooks sanitized for public release** — replaced hardcoded personal absolute paths with `$HOME` / `$VESTIGE_*` env vars; removed personal regex tokens.
- **NPM binary installer now follows package version**`vestige-mcp-server@2.1.0` downloads release assets from `v2.1.0` instead of a stale hardcoded binary tag, while local workspace installs skip the release-asset download before the tag exists.
@ -45,7 +46,7 @@ The Sanhedrin Executioner — Vestige's veto layer for Claude Code responses —
- `cargo test --workspace --release --no-fail-fast`: **1,229 passing, 0 failed** (366 vestige-core + 358 vestige-mcp lib + 4 vestige-mcp bin + 497 e2e + 4 doctests).
- Sanhedrin bridge smoke checks: Python bytecode compilation passes, fail-open bridge invocation returns `yes`, and public hook settings validate as JSON.
- 8-day Sandwich dogfood: **84% pass rate, 16% legitimate vetoes** caught real hallucinations.
- 2026-05-01 hotfix checks: `cargo test --workspace --no-fail-fast`, `cargo build --release --workspace`, shell/Python/JSON validation, and default/opt-in installer dry-runs all pass.
- 2026-05-01 hotfix checks: `cargo test --workspace --no-fail-fast`, `cargo build --release --workspace`, shell/Python/JSON validation, and default/preflight/Sanhedrin installer dry-runs all pass.
### Closes
@ -66,7 +67,7 @@ Optional local MLX Sanhedrin backend:
### Migration
None required for existing Vestige users. The Cognitive Sandwich is opt-in via `scripts/install-sandwich.sh`. The MCP server, schema, and tool surface are bit-identical to v2.0.9.
None required for existing Vestige users. The default installer now removes old v2.1.0 Claude Code hook wiring. The Cognitive Sandwich hook layers are opt-in via `--enable-preflight`, `--enable-sanhedrin`, or `--enable-sandwich`. The MCP server, schema, and tool surface are bit-identical to v2.0.9.
---

View file

@ -22,10 +22,10 @@ Built on 130 years of memory research — FSRS-6 spaced repetition, prediction e
## What's New in v2.1.0 "Cognitive Sandwich Goes Local"
v2.1.0 adds an opt-in Claude Code hook harness around the existing Vestige MCP server. The MCP tool surface and database schema stay backward compatible, while the new local Sanhedrin verifier and preflight hooks can inject trusted memory context before Claude answers and check drafts against high-trust Vestige evidence before delivery.
v2.1.0 adds an opt-in Claude Code hook harness around the existing Vestige MCP server. The MCP tool surface and database schema stay backward compatible, while optional preflight hooks can inject trusted memory context before Claude answers and optional Sanhedrin hooks can check drafts against high-trust Vestige evidence before delivery.
- **Local Sanhedrin Executioner.** The post-response verifier now runs through `mlx_lm.server` with `mlx-community/Qwen3.6-35B-A3B-4bit` by default, so the veto layer can run offline on Apple Silicon without Anthropic API calls.
- **One-command Cognitive Sandwich installer.** `scripts/install-sandwich.sh` stages hooks, agents, and a launchd plist, merges the Claude Code hooks block, and prints real verification commands.
- **Optional Sanhedrin Executioner.** The post-response verifier can run through `mlx_lm.server` with `mlx-community/Qwen3.6-35B-A3B-4bit` on Apple Silicon, or through any OpenAI-compatible endpoint on x86, but it is never enabled by default.
- **One-command Cognitive Sandwich installer.** `scripts/install-sandwich.sh` stages hook files and agents, removes old v2.1.0 hook wiring by default, and only activates Claude Code hooks with explicit `--enable-preflight`, `--enable-sanhedrin`, or `--enable-sandwich`.
- **Pulse hook backed by `/api/changelog`.** Fresh dream and connection events can be injected into the next Claude Code prompt context without blocking the prompt.
- **`VESTIGE_DATA_DIR` support.** `--data-dir` now has an env-var fallback, tilde expansion, secure directory creation, and clear precedence docs.
- **NPM release wrapper fixed.** `vestige-mcp-server@2.1.0` now downloads binaries from the matching `v2.1.0` GitHub release tag instead of an old hardcoded release.

View file

@ -2,7 +2,7 @@
**Vestige's defense-in-depth safety architecture for Claude Code.**
The default Cognitive Sandwich installs the preflight layer only. The Stop-hook layer is explicit opt-in:
The default Cognitive Sandwich installer only stages files and removes old v2.1.0 hook wiring. It activates no Claude Code hooks and makes no automatic model calls. Both the preflight layer and the Stop-hook layer are explicit opt-ins:
```
┌────────────────────────────────────────────────┐
@ -21,14 +21,14 @@ The default Cognitive Sandwich installs the preflight layer only. The Stop-hook
└────────────────────────────────────────────────┘
```
Sanhedrin and all Vestige Stop hooks are optional. The default installer wires UserPromptSubmit preflight hooks only; it does not install any Vestige Stop hook, start MLX, require a 19 GB model download, or require 20+ GB of RAM. Users who want the post-response verifier can opt in and point it at any OpenAI-compatible `/v1/chat/completions` endpoint. On Apple Silicon, an additional `--with-launchd` flag can auto-start the local MLX Qwen backend.
Sanhedrin, preflight, and all Vestige Claude Code hooks are optional. The default installer wires none of them; it does not call Claude, start MLX, require a 19 GB model download, or require 20+ GB of RAM. Users who want preflight context can opt in with `--enable-preflight`. Users who want the post-response verifier can opt in with `--enable-sanhedrin` and point it at any OpenAI-compatible `/v1/chat/completions` endpoint. On Apple Silicon, an additional `--with-launchd` flag can auto-start the local MLX Qwen backend.
---
## How a single response flows through the Sandwich
1. **You type a prompt in Claude Code.**
2. **UserPromptSubmit hooks fire in parallel** (none can block — all fail-open):
2. **If explicitly enabled, UserPromptSubmit hooks fire in parallel** (none can block — all fail-open):
- `load-all-memory.sh` (opt-in) — dumps every memory MD into context
- `synthesis-preflight.sh` — POSTs your prompt to `vestige-mcp` `/api/deep_reference`, injects the trust-scored reasoning chain
- `cwd-state-injector.sh` — captures git status, branch, open PRs/issues, modified files
@ -81,7 +81,18 @@ curl -fsSL https://raw.githubusercontent.com/samvallad33/vestige/v2.1.0/scripts/
git clone https://github.com/samvallad33/vestige
cd vestige
./scripts/install-sandwich.sh # add --force to overwrite existing hooks
./scripts/check-sandwich-prereqs.sh # verify everything's wired
./scripts/check-sandwich-prereqs.sh # verify no Vestige hooks are wired by default
```
The default command does not activate any Claude Code hook. It removes old v2.1.0 Vestige hook wiring from `~/.claude/settings.json` while preserving unrelated user hooks.
### Optional Preflight
Preflight is a separate opt-in layer. It includes `preflight-swarm.sh`, which uses `claude -p --model claude-haiku-4-5-20251001`; it is not wired by default.
```bash
./scripts/install-sandwich.sh --enable-preflight
./scripts/check-sandwich-prereqs.sh --preflight
```
### Optional Sanhedrin
@ -125,9 +136,10 @@ Optional Apple Silicon local Sanhedrin backend:
1. Verifies prereqs (warnings for missing tools, fatal only on jq/python3).
2. Copies hooks to `~/.claude/hooks/`, agents to `~/.claude/agents/`.
3. Backs up existing `~/.claude/settings.json` to `.bak.pre-sandwich`, then `jq`-merges the default UserPromptSubmit hooks block and removes old Vestige Stop hooks from previous v2.1.0 installs.
4. With `--enable-sanhedrin`, writes `~/.claude/hooks/vestige-sanhedrin.env` and merges a Sanhedrin-enabled hooks block.
5. With `--enable-sanhedrin --with-launchd` on Apple Silicon, renders and loads `launchd/com.vestige.mlx-server.plist.template`.
3. Backs up existing `~/.claude/settings.json` to `.bak.pre-sandwich`, then removes old Vestige hook wiring from previous v2.1.0 installs.
4. With `--enable-preflight`, merges the UserPromptSubmit hooks block.
5. With `--enable-sanhedrin`, writes `~/.claude/hooks/vestige-sanhedrin.env` and merges a Sanhedrin-enabled Stop hooks block.
6. With `--enable-sanhedrin --with-launchd` on Apple Silicon, renders and loads `launchd/com.vestige.mlx-server.plist.template`.
### Uninstall
@ -179,7 +191,7 @@ Full architecture memory: search Vestige for `god-tier-plan` or `cognitive-sandw
The base hook harness runs on x86. The launchd MLX helper is macOS-arm64-only.
On Linux, Windows under WSL, or Intel Mac:
- Run `scripts/install-sandwich.sh` normally for default preflight hooks with no Vestige Stop hooks.
- Run `scripts/install-sandwich.sh` normally to stage files and remove old Vestige hook wiring. No hooks are activated.
- If you want Sanhedrin, run an OpenAI-compatible endpoint such as vLLM, Ollama, llama.cpp server, or a remote MLX/vLLM box.
- Install with `--enable-sanhedrin --sanhedrin-endpoint=<url> --sanhedrin-model=<model>`.
- If the endpoint is unreachable, Sanhedrin fails open and does not block Claude Code.

View file

@ -1,14 +1 @@
{
"hooks": {
"UserPromptSubmit": [
{
"hooks": [
{ "type": "command", "command": "$HOME/.claude/hooks/synthesis-preflight.sh", "timeout": 8 },
{ "type": "command", "command": "$HOME/.claude/hooks/cwd-state-injector.sh", "timeout": 8 },
{ "type": "command", "command": "$HOME/.claude/hooks/vestige-pulse-daemon.sh", "timeout": 6 },
{ "type": "command", "command": "$HOME/.claude/hooks/preflight-swarm.sh", "timeout": 45 }
]
}
]
}
}
{}

View file

@ -0,0 +1,14 @@
{
"hooks": {
"UserPromptSubmit": [
{
"hooks": [
{ "type": "command", "command": "$HOME/.claude/hooks/synthesis-preflight.sh", "timeout": 8 },
{ "type": "command", "command": "$HOME/.claude/hooks/cwd-state-injector.sh", "timeout": 8 },
{ "type": "command", "command": "$HOME/.claude/hooks/vestige-pulse-daemon.sh", "timeout": 6 },
{ "type": "command", "command": "$HOME/.claude/hooks/preflight-swarm.sh", "timeout": 45 }
]
}
]
}
}

View file

@ -1,15 +1,5 @@
{
"hooks": {
"UserPromptSubmit": [
{
"hooks": [
{ "type": "command", "command": "$HOME/.claude/hooks/synthesis-preflight.sh", "timeout": 8 },
{ "type": "command", "command": "$HOME/.claude/hooks/cwd-state-injector.sh", "timeout": 8 },
{ "type": "command", "command": "$HOME/.claude/hooks/vestige-pulse-daemon.sh", "timeout": 6 },
{ "type": "command", "command": "$HOME/.claude/hooks/preflight-swarm.sh", "timeout": 45 }
]
}
],
"Stop": [
{
"hooks": [

View file

@ -8,19 +8,22 @@ miss() { printf ' \033[1;31m[MISS]\033[0m %s\n' "$*"; FAIL=1; }
info() { printf ' \033[1;36m[INFO]\033[0m %s\n' "$*"; }
FAIL=0
CHECK_PREFLIGHT=0
CHECK_SANHEDRIN=0
DASHBOARD_PORT="${VESTIGE_DASHBOARD_PORT:-3927}"
SANHEDRIN_ENV="${VESTIGE_SANHEDRIN_ENV:-$HOME/.claude/hooks/vestige-sanhedrin.env}"
for arg in "$@"; do
case "$arg" in
--preflight|--enable-preflight) CHECK_PREFLIGHT=1 ;;
--sanhedrin|--enable-sanhedrin) CHECK_SANHEDRIN=1 ;;
-h|--help)
cat <<'EOF'
Usage: scripts/check-sandwich-prereqs.sh [--sanhedrin]
Usage: scripts/check-sandwich-prereqs.sh [--preflight] [--sanhedrin]
Without flags, checks the default Cognitive Sandwich preflight hooks.
With --sanhedrin, also checks the optional OpenAI-compatible verifier endpoint.
Without flags, verifies that the default install has no Vestige hooks wired.
With --preflight, checks the optional UserPromptSubmit hook layer.
With --sanhedrin, checks the optional OpenAI-compatible verifier endpoint.
EOF
exit 0
;;
@ -64,31 +67,40 @@ fi
# CLI tools
command -v jq >/dev/null && ok "jq" || miss "jq missing — brew install jq"
command -v claude >/dev/null && ok "claude CLI" || miss "claude CLI — install Claude Code"
command -v vestige-mcp >/dev/null && ok "vestige-mcp" || miss "vestige-mcp — cargo install vestige-mcp"
if [ "$CHECK_PREFLIGHT" -eq 1 ]; then
command -v claude >/dev/null && ok "claude CLI" || miss "claude CLI — install Claude Code"
command -v vestige-mcp >/dev/null && ok "vestige-mcp" || miss "vestige-mcp — cargo install vestige-mcp"
# Vestige MCP HTTP API
if curl -fsS -m 2 "http://127.0.0.1:${DASHBOARD_PORT}/api/health" >/dev/null 2>&1; then
ok "vestige-mcp dashboard responding on :$DASHBOARD_PORT"
else
warn "vestige-mcp dashboard not responding on :$DASHBOARD_PORT"
# Vestige MCP HTTP API
if curl -fsS -m 2 "http://127.0.0.1:${DASHBOARD_PORT}/api/health" >/dev/null 2>&1; then
ok "vestige-mcp dashboard responding on :$DASHBOARD_PORT"
else
warn "vestige-mcp dashboard not responding on :$DASHBOARD_PORT"
fi
fi
# Settings hook wiring
if [ -f "$HOME/.claude/settings.json" ] && \
jq -e '.hooks.UserPromptSubmit' "$HOME/.claude/settings.json" >/dev/null 2>&1; then
ok "settings.json UserPromptSubmit hooks present"
else
warn "settings.json missing UserPromptSubmit hooks — run: install-sandwich.sh"
if [ "$CHECK_PREFLIGHT" -eq 0 ] && [ "$CHECK_SANHEDRIN" -eq 0 ]; then
if [ -f "$HOME/.claude/settings.json" ] && \
jq -e 'any((.hooks.UserPromptSubmit[]?.hooks[]?, .hooks.Stop[]?.hooks[]?); ((.command? // "") | test("synthesis-preflight\\.sh|cwd-state-injector\\.sh|vestige-pulse-daemon\\.sh|preflight-swarm\\.sh|load-all-memory\\.sh|veto-detector\\.sh|sanhedrin\\.sh|synthesis-stop-validator\\.sh|synthesis-gate\\.sh")))' "$HOME/.claude/settings.json" >/dev/null 2>&1; then
warn "Vestige hooks are still wired; run: install-sandwich.sh --force"
else
ok "no Vestige Claude Code hooks wired by default"
fi
fi
if [ "$CHECK_SANHEDRIN" -eq 0 ]; then
if [ "$CHECK_PREFLIGHT" -eq 1 ]; then
echo
echo "Optional Preflight"
if [ -f "$HOME/.claude/settings.json" ] && \
jq -e 'any(.hooks.Stop[]?.hooks[]?; ((.command? // "") | contains("/.claude/hooks/veto-detector.sh") or contains("/.claude/hooks/sanhedrin.sh") or contains("/.claude/hooks/synthesis-stop-validator.sh")))' "$HOME/.claude/settings.json" >/dev/null 2>&1; then
warn "Vestige Stop hooks are still wired; run: install-sandwich.sh --force"
jq -e 'any(.hooks.UserPromptSubmit[]?.hooks[]?; ((.command? // "") | contains("synthesis-preflight.sh"))) and any(.hooks.UserPromptSubmit[]?.hooks[]?; ((.command? // "") | contains("preflight-swarm.sh")))' "$HOME/.claude/settings.json" >/dev/null 2>&1; then
ok "preflight UserPromptSubmit hooks wired"
else
ok "no Vestige Stop hooks wired by default"
warn "preflight hooks not wired — run: install-sandwich.sh --enable-preflight"
fi
info "preflight-swarm.sh uses claude -p with Haiku when enabled; default installs do not wire it."
fi
if [ "$CHECK_SANHEDRIN" -eq 1 ]; then
@ -143,7 +155,7 @@ fi
echo
if [ $FAIL -eq 0 ]; then
echo " Ready. Default preflight hooks will fire on next Claude Code prompt; no Vestige Stop hooks are wired."
echo " Ready. Default install has no Vestige Claude Code hooks wired and makes no automatic model calls."
exit 0
else
echo " Fix the items above, then re-run."

View file

@ -4,15 +4,15 @@
# Usage:
# curl -fsSL https://raw.githubusercontent.com/samvallad33/vestige/v2.1.0/scripts/install-sandwich.sh | sh
# # or, from a checkout:
# ./scripts/install-sandwich.sh [--force] [--enable-sanhedrin] [--with-launchd] [--include-memory-loader]
# ./scripts/install-sandwich.sh [--force] [--enable-preflight] [--enable-sanhedrin] [--with-launchd] [--include-memory-loader]
# ./scripts/install-sandwich.sh --enable-sanhedrin --sanhedrin-endpoint=http://127.0.0.1:11434/v1/chat/completions --sanhedrin-model=qwen2.5:14b
#
# What it does:
# 1. Verifies required local tools
# 2. Stages ~/.claude/hooks/ and ~/.claude/agents/
# 3. Copies sanitized hooks + agents
# 4. Merges the default UserPromptSubmit hooks into ~/.claude/settings.json
# 5. Optionally enables Sanhedrin and, only with --with-launchd on Apple Silicon,
# 4. Removes old Vestige hook wiring from ~/.claude/settings.json by default
# 5. Optionally enables preflight hooks and/or Sanhedrin. Only with --with-launchd on Apple Silicon,
# auto-starts mlx_lm.server with Qwen3.6-35B-A3B
set -euo pipefail
@ -31,6 +31,7 @@ LAUNCHD_DIR="$HOME/Library/LaunchAgents"
SETTINGS="$HOME/.claude/settings.json"
FORCE=0
ENABLE_PREFLIGHT=0
ENABLE_SANHEDRIN=0
WITH_LAUNCHD=0
INCLUDE_MEMORY_LOADER=0
@ -39,6 +40,8 @@ SRC=""
for arg in "$@"; do
case "$arg" in
--force) FORCE=1 ;;
--enable-preflight) ENABLE_PREFLIGHT=1 ;;
--enable-sandwich) ENABLE_PREFLIGHT=1; ENABLE_SANHEDRIN=1 ;;
--enable-sanhedrin) ENABLE_SANHEDRIN=1 ;;
--with-launchd) WITH_LAUNCHD=1 ;;
--no-launchd) WITH_LAUNCHD=0 ;;
@ -83,8 +86,10 @@ fi
# --- Prereqs (warnings only, install proceeds) ---
command -v jq >/dev/null || die "jq required: brew install jq"
command -v python3 >/dev/null || die "python3 required (3.10+)"
command -v claude >/dev/null || warn "'claude' CLI not found — install Claude Code first."
command -v vestige-mcp >/dev/null || warn "'vestige-mcp' not found — install with: cargo install vestige-mcp"
if [ "$ENABLE_PREFLIGHT" -eq 1 ]; then
command -v claude >/dev/null || warn "'claude' CLI not found — preflight-swarm.sh will fail open."
command -v vestige-mcp >/dev/null || warn "'vestige-mcp' not found — Vestige preflight hooks will fail open."
fi
if [ "$WITH_LAUNCHD" -eq 1 ]; then
command -v uv >/dev/null || warn "'uv' not found — install with: brew install uv"
command -v mlx_lm.server >/dev/null || warn "mlx-lm not installed — run: uv tool install mlx-lm"
@ -191,51 +196,72 @@ else
cp "$SETTINGS" "$HOME/.claude/settings.json.bak.pre-sandwich"
fi
TMP_MERGE="$(mktemp)"
SETTINGS_FRAGMENT="$SCRIPT_DIR/hooks/settings.fragment.json"
if [ "$ENABLE_SANHEDRIN" -eq 1 ]; then
SETTINGS_FRAGMENT="$SCRIPT_DIR/hooks/settings.sanhedrin.fragment.json"
PREFLIGHT_FRAGMENT="$SCRIPT_DIR/hooks/settings.fragment.json"
SANHEDRIN_FRAGMENT="$SCRIPT_DIR/hooks/settings.fragment.json"
if [ "$ENABLE_PREFLIGHT" -eq 1 ]; then
PREFLIGHT_FRAGMENT="$SCRIPT_DIR/hooks/settings.preflight.fragment.json"
fi
jq -s --arg enable_sanhedrin "$ENABLE_SANHEDRIN" '
def is_vestige_stop:
(.command? // "") as $cmd
| ($cmd | contains("/.claude/hooks/veto-detector.sh"))
or ($cmd | contains("/.claude/hooks/sanhedrin.sh"))
or ($cmd | contains("/.claude/hooks/synthesis-stop-validator.sh"));
.[0] * .[1]
| if $enable_sanhedrin == "1" then
.
else
.hooks.Stop = (
(.hooks.Stop // [])
| map(.hooks = ((.hooks // []) | map(select((is_vestige_stop | not)))))
| map(select(((.hooks // []) | length) > 0))
)
| if ((.hooks.Stop // []) | length) == 0 then del(.hooks.Stop) else . end
end
' "$SETTINGS" "$SETTINGS_FRAGMENT" > "$TMP_MERGE"
mv "$TMP_MERGE" "$SETTINGS"
if [ "$ENABLE_SANHEDRIN" -eq 1 ]; then
say "merged hooks block into $SETTINGS with Sanhedrin Stop hook enabled (backup at .bak.pre-sandwich)"
SANHEDRIN_FRAGMENT="$SCRIPT_DIR/hooks/settings.sanhedrin.fragment.json"
fi
jq -s '
def is_vestige_hook:
(.command? // "") as $cmd
| [
"synthesis-preflight.sh",
"cwd-state-injector.sh",
"vestige-pulse-daemon.sh",
"preflight-swarm.sh",
"load-all-memory.sh",
"veto-detector.sh",
"sanhedrin.sh",
"synthesis-stop-validator.sh",
"synthesis-gate.sh"
] | any(. as $needle | $cmd | contains($needle));
def scrub_vestige_hooks:
.hooks.UserPromptSubmit = (
(.hooks.UserPromptSubmit // [])
| map(.hooks = ((.hooks // []) | map(select((is_vestige_hook | not)))))
| map(select(((.hooks // []) | length) > 0))
)
| if ((.hooks.UserPromptSubmit // []) | length) == 0 then del(.hooks.UserPromptSubmit) else . end
| .hooks.Stop = (
(.hooks.Stop // [])
| map(.hooks = ((.hooks // []) | map(select((is_vestige_hook | not)))))
| map(select(((.hooks // []) | length) > 0))
)
| if ((.hooks.Stop // []) | length) == 0 then del(.hooks.Stop) else . end
| if ((.hooks // {}) | length) == 0 then del(.hooks) else . end;
(.[0] | scrub_vestige_hooks) * .[1] * .[2]
' "$SETTINGS" "$PREFLIGHT_FRAGMENT" "$SANHEDRIN_FRAGMENT" > "$TMP_MERGE"
mv "$TMP_MERGE" "$SETTINGS"
if [ "$ENABLE_PREFLIGHT" -eq 1 ] || [ "$ENABLE_SANHEDRIN" -eq 1 ]; then
enabled_layers=""
[ "$ENABLE_PREFLIGHT" -eq 1 ] && enabled_layers="${enabled_layers} preflight"
[ "$ENABLE_SANHEDRIN" -eq 1 ] && enabled_layers="${enabled_layers} sanhedrin"
say "merged optional hook layer(s) into $SETTINGS:${enabled_layers} (backup at .bak.pre-sandwich)"
else
say "merged default preflight hooks into $SETTINGS; no Vestige Stop hooks are installed (backup at .bak.pre-sandwich)"
say "removed Vestige hook wiring from $SETTINGS; default install activates no Claude Code hooks (backup at .bak.pre-sandwich)"
fi
# --- Next steps ---
cat <<EOF
┌──────────────────────────────────────────────────────────────┐
│ Cognitive Sandwich preflight hooks installed. │
│ Cognitive Sandwich files installed. No hooks enabled by default.
└──────────────────────────────────────────────────────────────┘
Next steps:
1. Restart Claude Code so it picks up the new hooks.
Default installs include no Vestige Stop hooks.
1. Restart Claude Code if you enabled optional hooks.
Default installs activate no Vestige Claude Code hooks and make no model calls.
2. Verify the install:
vestige health # if vestige CLI installed
curl http://127.0.0.1:$DASHBOARD_PORT/api/health
scripts/check-sandwich-prereqs.sh # from a checkout
3. Optional Sanhedrin verifier:
3. Optional hook layers:
./scripts/install-sandwich.sh --enable-preflight
./scripts/install-sandwich.sh --enable-sanhedrin --sanhedrin-endpoint=$SANHEDRIN_ENDPOINT --sanhedrin-model=$MODEL_ID
On Apple Silicon with >20 GB free RAM, add --with-launchd to auto-start
the local MLX Qwen server. On x86, point --sanhedrin-endpoint at vLLM,