| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110 |
- #!/bin/sh
- # planning-with-files: lock the current task_plan.md content with a SHA-256 attestation.
- #
- # Use after you finalise (or intentionally edit) a plan. The hooks then refuse
- # to inject plan content into the model context if the file diverges from the
- # attested hash, surfacing a "[PLAN TAMPERED]" warning instead.
- #
- # Resolution:
- # 1. $PLAN_ID env var → ./.planning/$PLAN_ID/
- # 2. ./.planning/.active_plan
- # 3. Newest ./.planning/<dir>/ by mtime
- # 4. Legacy ./task_plan.md at project root
- #
- # Usage:
- # sh scripts/attest-plan.sh # attest the active plan
- # sh scripts/attest-plan.sh --show # print the stored hash
- # sh scripts/attest-plan.sh --clear # remove the attestation (re-open the plan)
- set -u
- SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
- RESOLVER="${SCRIPT_DIR}/resolve-plan-dir.sh"
- resolve_plan_file() {
- plan_dir=""
- if [ -f "${RESOLVER}" ]; then
- plan_dir="$(sh "${RESOLVER}" 2>/dev/null)"
- fi
- if [ -n "${plan_dir}" ] && [ -f "${plan_dir}/task_plan.md" ]; then
- printf "%s\n" "${plan_dir}/task_plan.md"
- return 0
- fi
- if [ -f "./task_plan.md" ]; then
- printf "%s\n" "./task_plan.md"
- return 0
- fi
- return 1
- }
- attestation_path_for() {
- plan_file="$1"
- plan_dir="$(dirname "${plan_file}")"
- if [ "${plan_dir}" = "." ]; then
- # Legacy mode: store at project root.
- printf "%s\n" "./.plan-attestation"
- else
- printf "%s\n" "${plan_dir}/.attestation"
- fi
- }
- compute_hash() {
- target="$1"
- if command -v sha256sum >/dev/null 2>&1; then
- sha256sum "${target}" | awk '{print $1}'
- elif command -v shasum >/dev/null 2>&1; then
- shasum -a 256 "${target}" | awk '{print $1}'
- else
- printf "ERROR: no sha256 utility available\n" >&2
- return 1
- fi
- }
- mode="attest"
- case "${1:-}" in
- --show) mode="show" ;;
- --clear) mode="clear" ;;
- "") mode="attest" ;;
- *)
- printf "Usage: %s [--show|--clear]\n" "$0" >&2
- exit 2
- ;;
- esac
- plan_file="$(resolve_plan_file)" || {
- printf "[plan-attest] No task_plan.md found. Create a plan first.\n" >&2
- exit 1
- }
- attestation_file="$(attestation_path_for "${plan_file}")"
- case "${mode}" in
- show)
- if [ -f "${attestation_file}" ]; then
- printf "Plan: %s\n" "${plan_file}"
- printf "Attestation: %s\n" "${attestation_file}"
- printf "SHA-256: %s\n" "$(cat "${attestation_file}")"
- else
- printf "[plan-attest] No attestation set for %s.\n" "${plan_file}"
- exit 1
- fi
- ;;
- clear)
- if [ -f "${attestation_file}" ]; then
- rm -f "${attestation_file}"
- printf "[plan-attest] Cleared attestation for %s.\n" "${plan_file}"
- else
- printf "[plan-attest] No attestation to clear.\n"
- fi
- ;;
- attest)
- hash_val="$(compute_hash "${plan_file}")" || exit 1
- printf "%s\n" "${hash_val}" > "${attestation_file}"
- short_hash="$(printf "%s" "${hash_val}" | cut -c1-12)"
- printf "[plan-attest] Locked %s\n" "${plan_file}"
- printf "[plan-attest] SHA-256: %s... (stored in %s)\n" "${short_hash}" "${attestation_file}"
- printf "[plan-attest] Hooks will block injection if the file is modified without re-running this command.\n"
- ;;
- esac
- exit 0
|