Skip to content
Development
Hook

Hooks

What crowdstrike-falcon-foundry runs automatically, and when. A hook is a command Claude Code fires at a fixed moment, without you asking for it.

From plugin
crowdstrike-falcon-foundry
2711 skills3 hooks
Install
> /plugin marketplace add CrowdStrike/foundry-skills
> /plugin install crowdstrike-falcon-foundry@foundry-marketplace

Ships with crowdstrike-falcon-foundry. Installing the plugin gets these hooks.

What fires, and when

SessionStart

Fires once when a session begins, and again after a context compaction. It is where a plugin sets up its environment, or restores state the compaction dropped.

  • ${CLAUDE_PLUGIN_ROOT}/hooks/foundry-session-start.sh

UserPromptSubmit

Fires before Claude sees each prompt you send. A plugin can use it to inject context, so the same instruction reaches the model every turn instead of only at session start.

  • ${CLAUDE_PLUGIN_ROOT}/hooks/foundry-skill-router.sh

PreToolUse

  • ${CLAUDE_PLUGIN_ROOT}/hooks/foundry-skill-router.sh
  • MatchesBash${CLAUDE_PLUGIN_ROOT}/hooks/foundry-cli-guard.sh
  • MatchesSkill${CLAUDE_PLUGIN_ROOT}/hooks/superpowers-foundry-bridge.sh
Read hooks/hooks.json

Where it lives

  • hooks/foundry-cli-guard.shRunsGitHub
    Read the script
    #!/usr/bin/env bash
    #
    # foundry-cli-guard.sh
    #
    # PreToolUse hook that enforces --no-prompt on all Foundry CLI commands,
    # blocks manual directory/file creation that should use the CLI, and
    # reminds Claude to confirm resource names with the user before creating.
    #
    # Prevents common failures:
    # 1. Running Foundry CLI commands without --no-prompt (causes Error: EOF)
    # 2. Running foundry apps deploy without --change-type (causes 500 error)
    # 3. Running ui extensions create without --sockets (interactive picker hangs)
    # 4. Using mkdir/touch to create app structure (causes invalid manifests)
    # 5. Creating resources without user confirmation of the name
    #
    # Receives JSON on stdin with hook_event_name and tool-specific fields.
    # Outputs JSON with additionalContext (advisory nudge, not blocking).
    #
    # Environment variables:
    #   FOUNDRY_SKIP_NAME_CONFIRM=1  Bypass name confirmation (for automated tests)
    #
    # Note: foundry-skill-router.sh also fires on `api-integrations create` for
    # OpenAPI spec adaptation. Both hooks produce independent advisories.
    
    set -euo pipefail
    
    INPUT=$(cat)
    
    HOOK_EVENT=$(echo "$INPUT" | jq -r '.hook_event_name // empty')
    
    if [ "$HOOK_EVENT" != "PreToolUse" ]; then
      exit 0
    fi
    
    TOOL_NAME=$(echo "$INPUT" | jq -r '.tool_name // empty')
    
    # Only validate Bash commands
    if [ "$TOOL_NAME" != "Bash" ]; then
      exit 0
    fi
    
    COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty')
    
    # Check for Foundry CLI commands that need --no-prompt
    # Nearly all Foundry CLI commands support --no-prompt:
    #   apps create/validate/release/delete, functions create, collections create,
    #   workflows create, api-integrations create,
    #   ui pages create, ui extensions create, rtr-scripts create, profile create/delete
    #   functions exec (incl. exec list / exec status), functions logs, functions test
    if echo "$COMMAND" | grep -qE 'foundry\s+apps\b.*\b(create|validate|release|delete)\b|foundry\s+(functions|collections|workflows|api-integrations|rtr-scripts)\b.*\bcreate\b|foundry\s+functions\s+(exec|logs|test)\b|foundry\s+profile\b.*\b(create|delete)\b|foundry\s+ui\s+(pages|extensions)\b.*\bcreate\b'; then
      # Check if --no-prompt is missing
      if ! echo "$COMMAND" | grep -qF -- '--no-prompt'; then
        jq -n '{
          hookSpecificOutput: {
            hookEventName: "PreToolUse",
            additionalContext: "The command is missing --no-prompt. Foundry CLI commands (create/validate/release, functions exec/logs/test) run non-interactively in Claude Code and will hang with Error: EOF without it. Add --no-prompt before retrying. Example: foundry apps create --name \"app-name\" --no-prompt"
          }
        }'
        exit 0
      fi
    fi
    
    # Check for foundry apps deploy without --change-type
    # Omitting --change-type causes a 500 error (server-side panic) because the
    # Foundry API requires a change_type field in deploy requests.
    if echo "$COMMAND" | grep -qE 'foundry\s+apps\s+deploy\b'; then
      if ! echo "$COMMAND" | grep -qF -- '--change-type'; then
        jq -n '{
          hookSpecificOutput: {
            hookEventName: "PreToolUse",
            additionalContext: "The command is missing --change-type. Foundry apps deploy requires --change-type and --change-log to avoid a 500 error. Add both flags before retrying. Example: foundry apps deploy --change-type Patch --change-log \"description of changes\" --no-prompt"
          }
        }'
        exit 0
      fi
      if ! echo "$COMMAND" | grep -qF -- '--change-log'; then
        jq -n '{
          hookSpecificOutput: {
            hookEventName: "PreToolUse",
            additionalContext: "The command is missing --change-log. Foundry apps deploy requires --change-type and --change-log. Add both flags before retrying. Example: foundry apps deploy --change-type Patch --change-log \"description of changes\" --no-prompt"
          }
        }'
        exit 0
      fi
    fi
    
    # Check for foundry ui extensions create without --sockets
    # Omitting --sockets launches an interactive picker that hangs with Error: EOF.
    if echo "$COMMAND" | grep -qE 'foundry\s+ui\s+extensions\b.*\bcreate\b'; then
      if ! echo "$COMMAND" | grep -qF -- '--sockets'; then
        jq -n '{
          hookSpecificOutput: {
            hookEventName: "PreToolUse",
            additionalContext: "The command is missing --sockets. Without it, the CLI launches an interactive socket picker that will hang with Error: EOF. Run `foundry ui extensions list-sockets` to see available sockets. Example: foundry ui extensions create --name \"my-ext\" --from-template React --sockets \"activity.detections.details\" --no-prompt"
          }
        }'
        exit 0
      fi
      # Validate --sockets value against known valid socket IDs
      SOCKET_VAL=$(echo "$COMMAND" | grep -oE -- '--sockets\s+"?[^"[:space:]]+"?' | sed 's/--sockets[[:space:]]*//' | tr -d '"')
      if [ -n "$SOCKET_VAL" ]; then
        VALID_SOCKETS="activity.detections.details identity.detections.details automated-leads.leads.details hosts.host.panel xdr.cases.panel ngsiem.workbench.details workflows.executions.execution.details"
        IS_VALID=false
        for vs in $VALID_SOCKETS; do
          if [ "$SOCKET_VAL" = "$vs" ]; then
            IS_VALID=true
            break
          fi
        done
        if [ "$IS_VALID" = "false" ]; then
          jq -n --arg val "$SOCKET_VAL" '{
            hookSpecificOutput: {
              hookEventName: "PreToolUse",
              additionalContext: ("Invalid socket ID: \"" + $val + "\". Run `foundry ui extensions list-sockets` for available sockets. Known IDs: activity.detections.details, identity.detections.details, automated-leads.leads.details, hosts.host.panel, xdr.cases.panel, ngsiem.workbench.details, workflows.executions.execution.details.")
            }
          }'
          exit 0
        fi
      fi
    fi
    
    # Check for foundry workflows actions/triggers view without --no-prompt
    # The CLI currently ignores --no-prompt for these commands (FOUNDRY-3049) and
    # always launches an interactive Select() prompt. Adding --no-prompt is still
    # correct (for when the bug is fixed), but the real workaround is
    # the workflow skill's bundled action_search.py, which queries the API directly.
    if echo "$COMM
  • hooks/foundry-session-start.shRunsGitHub
    Read the script
    #!/usr/bin/env bash
    #
    # foundry-session-start.sh — SessionStart hook
    #
    # 1. Check Foundry CLI version and warn if below minimum required
    # 2. Set FOUNDRY_UI_HEADLESS_MODE=true for CLIs below 2.0.1 (older versions
    #    need this env var to suppress the TUI; 2.0.1+ auto-detects headless)
    #
    set -euo pipefail
    
    # --- Version check ---
    MINIMUM_VERSION="2.1.0"
    NEEDS_UPGRADE=0
    
    if CLI_OUTPUT=$(foundry version 2>/dev/null); then
      CLI_VERSION=$(echo "$CLI_OUTPUT" | awk '{print $2}')
    
      # Compare semver: split into major.minor.patch
      IFS='.' read -r CUR_MAJ CUR_MIN CUR_PAT <<< "$CLI_VERSION"
      IFS='.' read -r MIN_MAJ MIN_MIN MIN_PAT <<< "$MINIMUM_VERSION"
    
      if [ "${CUR_MAJ:-0}" -lt "${MIN_MAJ:-0}" ]; then
        NEEDS_UPGRADE=1
      elif [ "${CUR_MAJ:-0}" -eq "${MIN_MAJ:-0}" ]; then
        if [ "${CUR_MIN:-0}" -lt "${MIN_MIN:-0}" ]; then
          NEEDS_UPGRADE=1
        elif [ "${CUR_MIN:-0}" -eq "${MIN_MIN:-0}" ] && [ "${CUR_PAT:-0}" -lt "${MIN_PAT:-0}" ]; then
          NEEDS_UPGRADE=1
        fi
      fi
    
      if [ "$NEEDS_UPGRADE" -eq 1 ]; then
        cat >&2 << UPGRADE_EOF
    IMPORTANT: Foundry CLI $CLI_VERSION is below the minimum required $MINIMUM_VERSION. Before proceeding with any Foundry work, you MUST inform the user and offer to upgrade:
    
      - macOS/Linux: brew upgrade crowdstrike/foundry-cli/foundry
      - Windows: Download https://assets.foundry.crowdstrike.com/cli/latest/foundry_Windows_x86_64.zip
    
    Feature unavailable below $MINIMUM_VERSION: function execution and debugging (foundry functions exec, test, logs), non-interactive output for actions view and triggers view with multiple matches, and automatic headless mode detection.
    
    Use AskUserQuestion to ask whether to upgrade now. If the user declines, Foundry commands may fail or behave unexpectedly.
    UPGRADE_EOF
      fi
    fi
    
    # --- Env vars for older CLIs ---
    # CLIs below 2.0.1 need FOUNDRY_UI_HEADLESS_MODE=true to suppress the TUI.
    # 2.0.1+ auto-detects headless environments, so this is only needed as a fallback.
    if [ "$NEEDS_UPGRADE" -eq 1 ]; then
      if [ -n "${CLAUDE_ENV_FILE:-}" ]; then
        echo 'export FOUNDRY_UI_HEADLESS_MODE=true' >> "$CLAUDE_ENV_FILE"
      else
        cat >&2 << ENV_EOF
    IMPORTANT: Foundry CLI $CLI_VERSION requires FOUNDRY_UI_HEADLESS_MODE=true to suppress the TUI in non-interactive environments. You MUST run this export before ANY foundry CLI command (deploy, release, validate, etc.), or those commands will fail with "could not open a new TTY":
    
      export FOUNDRY_UI_HEADLESS_MODE=true
    
    Run this silently at the start of any Bash tool call that uses the foundry CLI. Do not ask the user — just prepend it.
    ENV_EOF
      fi
    fi
    
    exit 0
    
  • hooks/foundry-skill-router.shRunsGitHub
    Read the script
    #!/usr/bin/env bash
    #
    # foundry-skill-router.sh
    #
    # Two-hook system for Foundry skill routing:
    # 1. UserPromptSubmit: Detects specific Foundry keywords → writes marker file + injects context
    # 2. PreToolUse (all tools): Reads marker → injects advisory reminder to use
    #    the Foundry development workflow skill (non-blocking)
    #
    # The marker file bridges the two hooks since they run at different times.
    # Cleaned up once the Skill tool is invoked.
    #
    # Receives JSON on stdin with hook_event_name and event-specific fields.
    # Outputs JSON with additionalContext or permissionDecision.
    
    set -euo pipefail
    
    INPUT=$(cat)
    
    HOOK_EVENT=$(echo "$INPUT" | jq -r '.hook_event_name // empty')
    MARKER="/tmp/.foundry-skill-router-active"
    
    case "$HOOK_EVENT" in
      UserPromptSubmit)
        USER_PROMPT=$(echo "$INPUT" | jq -r '.prompt // .user_prompt // empty')
        PROMPT_LOWER=$(echo "$USER_PROMPT" | tr '[:upper:]' '[:lower:]')
    
        FOUNDRY_MATCH=false
    
        # Require an action verb + Foundry noun to detect real development intent.
        # "create a foundry app" triggers; "if we were in a foundry app" does not.
        VERBS="create|build|deploy|release|scaffold|add|update|fix|debug|configure"
        NOUNS="foundry app|foundry function|foundry collection|foundry workflow|foundry ui|foundry page|foundry api|falcon foundry|falcon app|crowdstrike app|foundry extension"
    
        if echo "$PROMPT_LOWER" | grep -qE "\b(${VERBS})\b.*(${NOUNS})"; then
          FOUNDRY_MATCH=true
        elif echo "$PROMPT_LOWER" | grep -qE "(${NOUNS}).*\b(${VERBS})\b"; then
          # Also catch "foundry app ... deploy" word order
          FOUNDRY_MATCH=true
        fi
    
        # Explicit CLI commands always trigger
        for cmd in "foundry apps create" "foundry apps deploy" "foundry apps release" \
                   "foundry apps run" "foundry login"; do
          if echo "$PROMPT_LOWER" | grep -qF "$cmd"; then
            FOUNDRY_MATCH=true
            break
          fi
        done
    
        # Explicit skill requests always trigger
        if echo "$PROMPT_LOWER" | grep -qE "(use|invoke|run) foundry (skill|plugin)"; then
          FOUNDRY_MATCH=true
        fi
    
        if [ "$FOUNDRY_MATCH" = true ]; then
          # Write marker so PreToolUse hook knows to inject advisory context
          echo "$$" > "$MARKER"
    
          # Standalone Fusion workflow? Advise the sibling plugin instead of
          # steering into app scaffolding. Without this the classifier is never
          # consulted at runtime and the agent tends to quietly author the workflow
          # itself, never telling the user a better-suited plugin exists.
          REDIRECT_SCRIPT="${CLAUDE_PLUGIN_ROOT:-$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)}/skills/development-workflow/scripts/detect_fusion_redirect.py"
          if [ -f "$REDIRECT_SCRIPT" ]; then
            VERDICT=$(printf '%s' "$USER_PROMPT" | python3 "$REDIRECT_SCRIPT" 2>/dev/null || true)
            if echo "$VERDICT" | grep -q '"redirect": true'; then
              # If the sibling plugin is already installed, say "use it" rather than
              # "install it". Best-effort: the file may be absent, in which case we
              # fall back to the install wording. Mirrors the check in
              # fusion-skills' fusion-foundry-bridge.sh.
              FUSION_HINT="Advise the crowdstrike-falcon-fusion plugin — /plugin install crowdstrike-falcon-fusion, or https://claude.com/plugins/crowdstrike-falcon-fusion."
              if [ -f "$HOME/.claude/plugins/installed_plugins.json" ] &&
                 grep -q "crowdstrike-falcon-fusion" "$HOME/.claude/plugins/installed_plugins.json" 2>/dev/null; then
                FUSION_HINT="The crowdstrike-falcon-fusion plugin is already installed — hand off to its workflows skill."
              fi
              jq -n --arg hint "$FUSION_HINT" '{
                hookSpecificOutput: {
                  hookEventName: "UserPromptSubmit",
                  additionalContext: ("STANDALONE FUSION WORKFLOW DETECTED: this request looks like a trigger plus actions that already exist in the CID — no UI, function, collection, or API integration to build. It does NOT need a Foundry app. " + $hint + " Do NOT scaffold a Foundry app. Naming the plugin is required output — declining to scaffold is only half the redirect, and hand-writing the workflow YAML yourself defeats the purpose since that plugin discovers real action IDs, validates against the platform schema, and imports to the CID. This detection is advisory: if the request genuinely needs an app capability built, proceed with crowdstrike-falcon-foundry:development-workflow instead.")
                }
              }'
              exit 0
            fi
          fi
    
          jq -n '{
            hookSpecificOutput: {
              hookEventName: "UserPromptSubmit",
              additionalContext: "FOUNDRY PLUGIN DETECTED: This prompt involves Falcon Foundry development. Do NOT enter plan mode. IMMEDIATELY invoke crowdstrike-falcon-foundry:development-workflow using the Skill tool. That skill handles requirements gathering, clarifying questions, CLI scaffolding, and sub-skill delegation."
            }
          }'
          exit 0
        fi
        ;;
    
      PreToolUse)
        TOOL_NAME=$(echo "$INPUT" | jq -r '.tool_name // empty')
    
        # Auto-adapt OpenAPI spec before allowing api-integrations create
        if [ "$TOOL_NAME" = "Bash" ]; then
          TOOL_INPUT=$(echo "$INPUT" | jq -r '.tool_input.command // empty')
          if echo "$TOOL_INPUT" | grep -q 'foundry api-integrations create'; then
            # Extract the spec file path from --spec flag
            SPEC_FILE=$(echo "$TOOL_INPUT" | grep -oE '\-\-spec\s+[^ ]+' | awk '{print $2}')
            if [ -n "$SPEC_FILE" ] && [ -f "$SPEC_FILE" ]; then
              # Find the adapt script relative to the plugin root
              PLUGIN_ROOT="$(cd "$(dirname "$0")/.." && pwd)"
              ADAPT_SCRIPT="$PLUGIN_ROOT/skills/api-integrations/scripts/adapt_spec_for_foundry.py"
    
              # Run the adapt script automatically to fix known issues
              if [ -f "$ADAPT_SCRIPT" ]; then
                if ! ADAPT_OUTPUT=$(python3 "$ADAPT_SCRIPT" "$SPEC_FILE" 2>&1); then
                  jq -n --arg output "$ADAPT_OUTPUT" --arg r
  • hooks/superpowers-foundry-bridge.shRunsGitHub
    Read the script
    #!/usr/bin/env bash
    #
    # superpowers-foundry-bridge.sh
    #
    # PreToolUse hook on the Skill tool. Blocks superpowers:brainstorming and
    # redirects to development-workflow which owns the Foundry dev flow.
    # Advisory context is injected for other superpowers planning skills.
    #
    # Receives JSON on stdin with tool_input.skill (the skill being invoked).
    # Outputs JSON with decision or additionalContext.
    
    set -euo pipefail
    
    INPUT=$(cat)
    
    SKILL_NAME=$(echo "$INPUT" | jq -r '.tool_input.skill // empty')
    
    # Redirect brainstorming to the Foundry development workflow skill.
    # Uses additionalContext for a clean UX (no error messages).
    # ~75% reliable — when it misses, the model still has Foundry skills in its
    # available skills list. deny is 100% reliable but shows ugly duplicate errors.
    case "$SKILL_NAME" in
      superpowers:brainstorming|brainstorming)
        jq -n '{
          hookSpecificOutput: {
            hookEventName: "PreToolUse",
            additionalContext: "STOP. Do NOT proceed with brainstorming. The Foundry plugin is installed and crowdstrike-falcon-foundry:development-workflow MUST be used instead. It handles requirements gathering, CLI scaffolding, and manifest coordination for Foundry apps. Cancel this brainstorming skill invocation and invoke crowdstrike-falcon-foundry:development-workflow immediately."
          }
        }'
        exit 0
        ;;
    esac
    
    # Advisory context for other superpowers planning skills
    case "$SKILL_NAME" in
      superpowers:writing-plans|writing-plans|\
      superpowers:executing-plans|executing-plans|\
      superpowers:subagent-driven-development|subagent-driven-development)
        CONTEXT=$(cat <<'FOUNDRY_CONTEXT'
    FOUNDRY PLUGIN INSTALLED: If this task involves Falcon Foundry, invoke crowdstrike-falcon-foundry:development-workflow BEFORE this skill. That skill owns Foundry app creation — it uses CLI commands (foundry apps create, foundry api-integrations create, etc.) that generate manifest.yml and wire up capability IDs correctly. Hand-writing manifest.yml or workflow YAML without the CLI causes deploy failures.
    FOUNDRY_CONTEXT
        )
        jq -n \
          --arg ctx "$CONTEXT" \
          '{
            hookSpecificOutput: {
              hookEventName: "PreToolUse",
              additionalContext: $ctx
            }
          }'
        exit 0
        ;;
    esac
    

Read the script before you install anything that runs on your machine. This is the one part of a plugin that acts without being asked.

Ships withcrowdstrike-falcon-foundry

AI coding assistant skills for building CrowdStrike Falcon Foundry apps. Build Foundry apps from a natural language prompt — API integrations, workflows, UI pages, functions, and collections — all scaffolded with the Foundry CLI and deployed to the Falcon

Get the whole plugin