#!/usr/bin/env bash # CASAN — lightweight CLI wrapper (Level 1 Core Harness). # # Locates the harness package relative to this script (works both in the full # source hub and in an extracted casan-core / casan-devkit bundle) and dispatches # to the harness bash entrypoints. No dependencies beyond bash + python3. set -uo pipefail # --- locate the harness root (dir containing scripts/bash/casan-harness.sh) ---- _self="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" _find_project_root() { local d="${CASAN_APP_ROOT:-$PWD}" while [[ "$d" != "/" && -n "$d" ]]; do [[ -f "$d/.casan/config.json" ]] && { echo "$d"; return 0; } d="$(dirname "$d")" done return 1 } PROJECT_ROOT="$(_find_project_root || true)" PROJECT_RUNTIME_MODE="managed" if [[ -n "$PROJECT_ROOT" ]]; then PROJECT_RUNTIME_MODE="$(python3 -c \ 'import json,sys; print(json.load(open(sys.argv[1], encoding="utf-8")).get("runtime_mode", "managed"))' \ "$PROJECT_ROOT/.casan/config.json" 2>/dev/null || echo managed)" fi PROJECT_VENDORED_HARNESS="${PROJECT_ROOT:+$PROJECT_ROOT/.casan/runtime/casan-core/packages/casan-harness}" if [[ "$PROJECT_RUNTIME_MODE" == "vendored" && ! -f "$PROJECT_VENDORED_HARNESS/scripts/bash/casan-harness.sh" ]]; then echo "casan: project is pinned to vendored Core but runtime is missing: $PROJECT_VENDORED_HARNESS" >&2 echo "casan: restore it with 'casan init --runtime vendored' from an approved DevKit install" >&2 exit 1 fi _find_harness() { # 1) project-vendored Core, when explicitly present. local c if [[ "$PROJECT_RUNTIME_MODE" == "vendored" ]]; then c="$PROJECT_VENDORED_HARNESS" [[ -f "$c/scripts/bash/casan-harness.sh" ]] && { (cd "$c" && pwd); return 0; } fi # 2) managed/global Core passed by the launcher. c="${CASAN_GLOBAL_HARNESS_ROOT:-}" [[ -n "$c" && -f "$c/scripts/bash/casan-harness.sh" ]] && { (cd "$c" && pwd); return 0; } # 3) sibling packages/casan-harness (source hub or bundle root layout). for c in "$_self/../packages/casan-harness" "$_self/../casan-harness" "$_self/packages/casan-harness"; do [[ -f "$c/scripts/bash/casan-harness.sh" ]] && { (cd "$c" && pwd); return 0; } done # 4) walk up looking for it. local d="$_self" while [[ "$d" != "/" ]]; do [[ -f "$d/packages/casan-harness/scripts/bash/casan-harness.sh" ]] && { echo "$d/packages/casan-harness"; return 0; } d="$(dirname "$d")" done return 1 } HARNESS="${CASAN_HARNESS_ROOT:-$(_find_harness)}" if [[ -z "${HARNESS:-}" || ! -d "$HARNESS" ]]; then echo "casan: cannot locate packages/casan-harness (set CASAN_HARNESS_ROOT)" >&2 exit 1 fi BASH_DIR="$HARNESS/scripts/bash" CASAN_APP_ROOT="${CASAN_APP_ROOT:-${PROJECT_ROOT:-$(cd "$HARNESS/../.." && pwd)}}" export CASAN_APP_ROOT VERSION_FILE="$_self/../VERSION" [[ -f "$VERSION_FILE" ]] || VERSION_FILE="$HARNESS/../../VERSION" version() { printf 'casan %s\n' "$( [[ -f "$VERSION_FILE" ]] && cat "$VERSION_FILE" || echo 'unknown')"; } usage() { cat < [args] Commands: init [--runtime managed|vendored] Adopt/reconfigure CASAN (interactive wizard by default) uninstall [--purge] Remove CASAN from this project (preserves user config) doctor [--client ...] Verify configured hooks, pin, adapters, and VS Code route readiness [--refresh] [--json] Assess Core, Domain Pipeline, and Provider Telemetry domain Manage optional project-owned Domain Pack selection edition Show / change the product edition level Deprecated alias for edition verify-harness Verify the resolved harness matches the project pin run [action] [-- cmd...] Run a step through the harness (H4→H5→H6→exec→H4-out) gate Run production checks from the project manifest verify Verify audit chain + tool audit + policy bundle reuse Verify multi-project harness reuse (registry) project validate [--manifest path] Validate a project manifest and quality profile project init Create an idempotent NestJS/React project shell prompt verify Verify the adopted prompt-enforcement contract prompt trace Verify that a prompt trace is H1-H7 certified report latest [--json] Show the latest prompt assurance receipt report export [trace] [--format] Export a run/H6 dossier on demand view [trace-id] Open the Core Local Assurance Viewer pipeline [--manifest path] Run an optional project-owned SRS→test pipeline dashboard Run local assurance (Core) or Control Plane version Print version help This help Env: CASAN_HARNESS_ROOT overrides harness location. Harness: $HARNESS EOF } cmd="${1:-help}"; shift || true DEVKIT_ROOT="${CASAN_DEVKIT_ROOT:-$CASAN_APP_ROOT/packages/casan-devkit}" [[ -f "$DEVKIT_ROOT/casan-init.py" ]] || DEVKIT_ROOT="$HARNESS/../casan-devkit" case "$cmd" in init) [[ -f "$DEVKIT_ROOT/casan-init.py" ]] || { echo "casan: init requires the casan-devkit package" >&2; exit 1; } exec python3 "$DEVKIT_ROOT/casan-init.py" init "$@" ;; uninstall) [[ -f "$DEVKIT_ROOT/casan-init.py" ]] || { echo "casan: uninstall requires the casan-devkit package" >&2; exit 1; } exec python3 "$DEVKIT_ROOT/casan-init.py" uninstall "$@" ;; verify-harness) [[ -f "$DEVKIT_ROOT/casan-init.py" ]] || { echo "casan: verify-harness requires the casan-devkit package" >&2; exit 1; } exec python3 "$DEVKIT_ROOT/casan-init.py" verify "$@" ;; doctor) [[ -f "$DEVKIT_ROOT/casan-init.py" ]] || { echo "casan: doctor requires the casan-devkit package" >&2; exit 1; } exec python3 "$DEVKIT_ROOT/casan-init.py" doctor "$@" ;; readiness) exec python3 "$HARNESS/scripts/python/report_cli.py" --root "$CASAN_APP_ROOT" readiness "$@" ;; domain) exec python3 "$HARNESS/scripts/python/report_cli.py" --root "$CASAN_APP_ROOT" domain "$@" ;; level) [[ -f "$DEVKIT_ROOT/casan-init.py" ]] || { echo "casan: level requires the casan-devkit package" >&2; exit 1; } sub="${1:-show}"; shift || true case "$sub" in show) exec python3 "$DEVKIT_ROOT/casan-init.py" level --show "$@" ;; set) n="${1:-devkit}"; shift || true exec python3 "$DEVKIT_ROOT/casan-init.py" init --level "$n" "$@" ;; *) echo "casan: usage: casan level >" >&2; exit 64 ;; esac ;; edition) [[ -f "$DEVKIT_ROOT/casan-init.py" ]] || { echo "casan: edition requires the casan-devkit package" >&2; exit 1; } sub="${1:-show}"; shift || true case "$sub" in show) exec python3 "$DEVKIT_ROOT/casan-init.py" edition --show "$@" ;; set) name="${1:-core}"; shift || true exec python3 "$DEVKIT_ROOT/casan-init.py" init --edition "$name" "$@" ;; *) echo "casan: usage: casan edition >" >&2; exit 64 ;; esac ;; run) exec bash "$BASH_DIR/casan-harness.sh" "$@" ;; gate) if [[ -z "${CASAN_PROJECT_MANIFEST:-}${CASAN_PROJECT_ID:-}" \ && ! -f "$CASAN_APP_ROOT/apps/okr/domain/project.manifest.json" ]]; then echo "casan: gate requires CASAN_PROJECT_MANIFEST or CASAN_PROJECT_ID" >&2 exit 64 fi exec bash "$BASH_DIR/project-gate.sh" "$@" ;; verify) rc=0 bash "$BASH_DIR/verify-audit-chain.sh" "$@" || rc=$? bash "$BASH_DIR/verify-tool-audit.sh" || rc=$? bash "$BASH_DIR/sign-policy-bundle.sh" verify || rc=$? exit $rc ;; reuse) exec bash "$BASH_DIR/verify-harness-reuse.sh" "$@" ;; project) sub="${1:-help}"; shift || true case "$sub" in validate) exec python3 "$BASH_DIR/project_manifest.py" validate --root "$CASAN_APP_ROOT" "$@" ;; init) DEVKIT="${CASAN_DEVKIT_ROOT:-$CASAN_APP_ROOT/packages/casan-devkit}" [[ -f "$DEVKIT/project-scaffold.py" ]] || { echo "casan: project init requires the casan-devkit bundle" >&2; exit 1; } exec python3 "$DEVKIT/project-scaffold.py" "$@" ;; *) echo "casan: usage: casan project " >&2; exit 64 ;; esac ;; prompt) sub="${1:-help}"; shift || true case "$sub" in verify) exec bash "$BASH_DIR/prompt-enforcement-verify.sh" --root "$CASAN_APP_ROOT" "$@" ;; trace) trace_id="${1:-}"; [[ -n "$trace_id" ]] || { echo "casan: usage: casan prompt trace " >&2; exit 64; } shift exec bash "$BASH_DIR/prompt-enforcement-verify.sh" --root "$CASAN_APP_ROOT" --trace-id "$trace_id" "$@" ;; *) echo "casan: usage: casan prompt " >&2; exit 64 ;; esac ;; report) sub="${1:-latest}"; shift || true case "$sub" in latest) exec python3 "$HARNESS/scripts/python/report_cli.py" --root "$CASAN_APP_ROOT" latest "$@" ;; export) exec python3 "$HARNESS/scripts/python/report_cli.py" --root "$CASAN_APP_ROOT" export "$@" ;; *) echo "casan: usage: casan report " >&2; exit 64 ;; esac ;; view) exec python3 "$HARNESS/scripts/python/report_cli.py" --root "$CASAN_APP_ROOT" view "$@" ;; pipeline) RUNNER="$CASAN_APP_ROOT/scripts/run-casan-pipeline.mjs" if [[ "${1:-}" == "-h" || "${1:-}" == "--help" ]]; then cat <<'EOF' Usage: casan pipeline [project runner arguments] Runs a project-owned manifest-driven SRS→test pipeline when scripts/run-casan-pipeline.mjs exists. The pipeline is an optional Domain Pack capability; CASAN Core prompt assurance and visual reports do not depend on it. EOF exit 0 fi if [[ ! -f "$RUNNER" ]]; then echo "CASAN_PIPELINE_NOT_CONFIGURED — no project-owned Domain Pipeline runner was found." >&2 echo "Core remains available: use 'casan view' and 'casan readiness'." >&2 exit 2 fi exec node "$RUNNER" "$@" ;; dashboard) CONTROL_PLANE_SCRIPT="$HARNESS/../casan-control-panel/scripts/control-plane-local.sh" if [[ -f "$CONTROL_PLANE_SCRIPT" ]]; then exec bash "$CONTROL_PLANE_SCRIPT" "$@" fi exec python3 "$HARNESS/scripts/python/report_cli.py" --root "$CASAN_APP_ROOT" viewer "$@" ;; version|-v|--version) version ;; help|-h|--help) usage ;; *) echo "casan: unknown command '$cmd'" >&2; usage >&2; exit 64 ;; esac