#!/usr/bin/env bash # ============================================================================= # adapters/lib.sh — Shared helpers for framework adapters # ============================================================================= # Sourced by scripts/build.sh BEFORE the framework-specific adapter.sh. # Provides vocabulary tables, parsing helpers, filtering, and JSON utilities. # Do NOT execute directly. # ============================================================================= # ── Vocabulary constants ───────────────────────────────────────────────────── # The closed set of capabilities the source frontmatter may declare. # Adapters consult this list to validate frontmatter before translating. CAPABILITY_VOCAB="read write edit bash webfetch websearch notebook task todo" # The closed set of hook event names the source .hook.yaml may declare. EVENT_VOCAB="before-tool-use after-tool-use on-notification on-session-start on-prompt-submit" # ── Parsing helpers ────────────────────────────────────────────────────────── # Defined in subsequent tasks: parse_frontmatter, parse_capabilities, # parse_hook_yaml, agent_body, should_include, enumerate_agents, # enumerate_hooks, json_object. # parse_frontmatter # Echoes the value of a top-level YAML key from the file's --- ... --- block. # Returns nothing (empty) if the key is not found. # Supports scalar values and flat list values; preserves the raw value as-written. parse_frontmatter() { local file="$1" key="$2" awk -v key="$key" ' /^---$/ { fm++; next } fm == 1 { # Match "key: value" — strip leading whitespace, capture value after first ":" sub(/^[[:space:]]+/, "") if (match($0, "^" key ":[[:space:]]*")) { value = substr($0, RLENGTH + 1) sub(/[[:space:]]+$/, "", value) print value exit } } fm >= 2 { exit } ' "$file" } # parse_capabilities # Echoes the agent's capabilities as space-separated tokens. # Empty output if capabilities is missing or []. parse_capabilities() { local file="$1" local raw; raw="$(parse_frontmatter "$file" capabilities)" # Strip [ and ] and commas, leaving space-separated tokens echo "$raw" | tr -d '[]' | tr ',' ' ' | xargs } # should_include # Exit 0 if the component should be included in the given framework's build. # Exit 1 if the component's exclude: list contains the framework. should_include() { local file="$1" framework="$2" local raw; raw="$(parse_frontmatter "$file" exclude)" # Treat missing or empty exclude as "include" [[ -z "$raw" || "$raw" == "[]" ]] && return 0 # Tokenize the list and check membership local tokens; tokens="$(echo "$raw" | tr -d '[]' | tr ',' ' ' | xargs)" for t in $tokens; do [[ "$t" == "$framework" ]] && return 1 done return 0 } # parse_hook_yaml # Emits key=value lines for each top-level scalar AND for trigger fields. # Output keys: name, script, event, match-tool (space-separated tokens), exclude. parse_hook_yaml() { local file="$1" awk ' /^name:/ { sub(/^name:[[:space:]]*/, ""); print "name=" $0 } /^script:/ { sub(/^script:[[:space:]]*/, ""); print "script=" $0 } /^[[:space:]]*-[[:space:]]*event:/ { sub(/^[[:space:]]*-[[:space:]]*event:[[:space:]]*/, "") print "event=" $0 } /^[[:space:]]*match-tool:/ { sub(/^[[:space:]]*match-tool:[[:space:]]*/, "") gsub(/[\[\],]/, " ") gsub(/^[[:space:]]+|[[:space:]]+$/, "") gsub(/[[:space:]]+/, " ") print "match-tool=" $0 } /^exclude:/ { sub(/^exclude:[[:space:]]*/, ""); print "exclude=" $0 } ' "$file" } # agent_body # Echoes everything after the closing --- of the frontmatter block. agent_body() { local file="$1" awk ' /^---$/ { fm++; next } fm >= 2 { print } ' "$file" } # enumerate_agents # Echoes one agent file path per line. enumerate_agents() { local dir="$1" [[ -d "$dir" ]] || return 0 for f in "$dir"/*.md; do [[ -f "$f" ]] && echo "$f" done } # enumerate_hooks # Echoes one .hook.yaml file path per line. enumerate_hooks() { local dir="$1" [[ -d "$dir" ]] || return 0 for f in "$dir"/*.hook.yaml; do [[ -f "$f" ]] && echo "$f" done }