Files
roboco/docker/scripts/fable-bash-discipline-hook.sh
Renn F 7716830322 feat(fleet): opus-fable adoption — doctrine + discipline hooks (v0.18.0 A)
Fleet behaves more like Fable 5 on existing model tiers, behind
ROBOCO_FABLE_MODE_ENABLED (config default off; armed :-true on the NAS compose,
absent from the registry compose).

- Doctrine: vendored agents/prompts/doctrine/fable.md composed into every
  agent's system prompt via fable_doctrine_layer() after base.md.
- Hooks (Claude Code): 4 non-overlapping hooks (stop-gate/bash-discipline/
  honesty-nudge/precompact) appended per-agent via _fable_hook_groups(). The
  make-quality + lint-suppression duplicates are deliberately NOT added (already
  gate-enforced); session-start skipped.
- Hooks (grok): conservative V1 — only the non-denying honesty-nudge, since a
  grok hook deny cancels the whole run.
- Flag on the feature-flags card; hook scripts shipped into the agent image.

Flag-off spawn path proven byte-identical (worktree diff, sha256 match); full
suite green (2074 unit + e2e-smoke + hook harness), mypy/xenon/ruff clean.
Fixed a real stdin bug in the vendored stop-gate hook (heredoc + pipe both
claimed stdin). Distilled from rennf93/opus-fable-playbook (MIT).
2026-07-04 06:44:40 +02:00

28 lines
1.3 KiB
Bash
Executable File

#!/usr/bin/env bash
# Fable tool discipline: deny pure shell file-reads; dedicated tools exist.
# Ported from opus-fable-playbook hooks/bash-discipline.sh (v0.1.3) — see
# docs/superpowers/plans/2026-07-04-v0.18.0-A-opus-fable-plan.md.
# PreToolUse[Bash], Claude-only (see the plan's grok risk section — a grok
# hook deny cancels the whole run, so this is not shipped to grok in V1).
# Fail-open: any internal error => exit 0 (allow).
set -u
INPUT="$(cat)" || exit 0
CMD="$(printf '%s' "$INPUT" | python3 -c \
'import json,sys; print(json.load(sys.stdin).get("tool_input",{}).get("command",""))' \
2>/dev/null || true)"
[ -z "$CMD" ] && exit 0
# Pipelines, compounds, redirects, heredocs are legitimate — allow.
printf '%s' "$CMD" | grep -qE '\||&&|;|>|<<' && exit 0
DENY=0
printf '%s' "$CMD" | grep -qE '^[[:space:]]*(cat|head|tail|less|more)[[:space:]]' && DENY=1
printf '%s' "$CMD" | grep -qE '^[[:space:]]*sed[[:space:]]+-n[[:space:]]' && DENY=1
[ "$DENY" -eq 0 ] && exit 0
cat <<'JSON'
{"hookSpecificOutput": {"hookEventName": "PreToolUse", "permissionDecision": "deny", "permissionDecisionReason": "Fable tool discipline: use the dedicated Read/Grep tools instead of shell file-reads (cat/head/tail/less/sed -n). Read is paginated and line-numbered; Grep searches without loading whole files."}}
JSON
exit 0