#!/usr/bin/env bash # Purpose: State machine for prompt-optimizer agent with integrated template processing # Inputs: XML input via stdin containing user_task, template, execution_mode # Outputs: Instructions with template content and variable extraction guidance # Architecture: Incorporates template-processor.sh logic directly (no external bash calls needed) set -euo pipefail # Source common functions SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" . "$SCRIPT_DIR/../../scripts/common.sh" # Setup plugin root setup_plugin_root TEMPLATE_DIR="${CLAUDE_PLUGIN_ROOT}/templates" # Parse XML input from command-line argument (supports multiline content) read_xml_input() { local xml_input="$1" # Validate input provided if [ -z "$xml_input" ]; then echo "Error: No input provided. Usage: $0 ''" >&2 exit 1 fi # Extract required fields using common functions local user_task user_task=$(require_xml_field "$xml_input" "user_task" "auto") || exit 1 local template template=$(require_xml_field "$xml_input" "template") || { echo "Error: Template selection must be done before calling prompt-optimizer." >&2 exit 1 } # Extract optional field with default local execution_mode execution_mode=$(optional_xml_field "$xml_input" "execution_mode" "direct") # Validate execution_mode if [ "$execution_mode" != "plan" ] && [ "$execution_mode" != "direct" ]; then echo "Error: Invalid execution_mode: $execution_mode (must be 'plan' or 'direct')" >&2 exit 1 fi # Export safely (without sanitization for USER_TASK to preserve content, but sanitize for output) export USER_TASK="$user_task" export TEMPLATE="$template" export EXECUTION_MODE="$execution_mode" } # Map template name to skill name get_skill_for_template() { local template=$1 case "$template" in code-refactoring) echo "meta-prompt:code-refactoring" ;; code-review) echo "meta-prompt:code-review" ;; test-generation) echo "meta-prompt:test-generation" ;; documentation-generator) echo "meta-prompt:documentation-generator" ;; data-extraction) echo "meta-prompt:data-extraction" ;; code-comparison) echo "meta-prompt:code-comparison" ;; custom) echo "none" ;; *) echo "none" ;; esac } # Load template file with improved error messages load_template() { local template_name="$1" local template_path="$TEMPLATE_DIR/${template_name}.md" # Check if file exists if [ ! -f "$template_path" ]; then echo "Error: Template file not found: $template_path" >&2 echo "" >&2 echo "Available templates in $TEMPLATE_DIR:" >&2 if [ -d "$TEMPLATE_DIR" ]; then ls -1 "$TEMPLATE_DIR"/*.md 2>/dev/null | sed 's/.*\// - /' | sed 's/\.md$//' >&2 || echo " (none found)" >&2 else echo " Error: Template directory does not exist: $TEMPLATE_DIR" >&2 fi echo "" >&2 echo "Requested template: $template_name" >&2 return 1 fi # Check if file is readable if [ ! -r "$template_path" ]; then echo "Error: Template file not readable: $template_path" >&2 echo "Check file permissions." >&2 return 1 fi # Check if file is non-empty if [ ! -s "$template_path" ]; then echo "Error: Template file is empty: $template_path" >&2 echo "Template files must contain prompt content." >&2 return 1 fi cat "$template_path" } # Extract variables from template content extract_variables() { local template="$1" # Find all {$VARIABLE} and {$VARIABLE:default} patterns # Output format: VARIABLE (required) or VARIABLE:default (optional) echo "$template" | grep -oE '\{\$[A-Z_][A-Z0-9_]*(:[^}]*)?\}' 2>/dev/null | sort -u | sed 's/[{}$]//g' || true } # Extract variable descriptions from template YAML frontmatter # Returns formatted variable descriptions for agent guidance extract_variable_descriptions() { local template_content="$1" # Extract YAML frontmatter (between first two ---) local frontmatter frontmatter=$(echo "$template_content" | awk '/^---$/{if(++n==1){next}else{exit}} n==1{print}') # Check for variable_descriptions section if ! echo "$frontmatter" | grep -q "variable_descriptions:"; then return 0 # No descriptions available fi # Extract variable_descriptions block - get lines after variable_descriptions: until next top-level key # Pipe through sanitize_input to escape potential shell metacharacters echo "$frontmatter" | awk ' /^variable_descriptions:/ { in_block=1; next } in_block && /^[a-z_]+:/ { exit } in_block && /^ [A-Z_]+:/ { # Remove leading spaces and print sub(/^ /, ""); print } ' | while IFS= read -r line; do sanitize_input "$line" done } # Format variable descriptions for display # Input: raw variable descriptions (one per line in "NAME: description" format) # Output: formatted markdown list format_variable_descriptions() { local var_descriptions="$1" # Skip if empty or only whitespace [ -z "$var_descriptions" ] && return 0 echo "$var_descriptions" | grep -q '[^[:space:]]' || return 0 echo "## Variable Descriptions" echo "" echo "$var_descriptions" | while IFS= read -r line; do # Skip empty lines [ -z "$line" ] && continue # Split on first colon only to handle descriptions containing colons name="${line%%:*}" desc="${line#*:}" # Trim whitespace from name name="$(echo "$name" | sed 's/^[[:space:]]*//; s/[[:space:]]*$//')" # Skip if name is empty [ -z "$name" ] && continue # Clean up the description (remove surrounding quotes and whitespace) desc=$(echo "$desc" | sed 's/^[[:space:]]*"//; s/"[[:space:]]*$//; s/^[[:space:]]*//; s/[[:space:]]*$//') echo "- **$name**: $desc" done } # Escape special characters for output escape_for_output() { local value="$1" # Escape for safe display in instructions (including $ for heredoc safety) printf '%s\n' "$value" | sed 's/\\/\\\\/g; s/\$/\\$/g; s/`/\\`/g; s/"/\\"/g' } # Generate instructions for processing the template generate_instructions() { local sanitized_task=$(sanitize_input "$USER_TASK") local skill=$(get_skill_for_template "$TEMPLATE") # Load template content local template_content template_content=$(load_template "$TEMPLATE") || { echo "Error: Failed to load template '$TEMPLATE'" >&2 exit 1 } # Extract variables from template local variables variables=$(extract_variables "$template_content") # Extract variable descriptions from frontmatter local var_descriptions var_descriptions=$(extract_variable_descriptions "$template_content") # Escape template content for safe output local escaped_template=$(escape_for_output "$template_content") # Parse variables into required and optional lists local required_vars="" local optional_vars="" while IFS= read -r var; do if [ -z "$var" ]; then continue fi if [[ "$var" == *:* ]]; then # Variable has default value (optional) local var_name="${var%%:*}" # Note: default value is preserved in template, not extracted here if [ -n "$optional_vars" ]; then optional_vars="$optional_vars, $var_name" else optional_vars="$var_name" fi else # No default value (required) if [ -n "$required_vars" ]; then required_vars="$required_vars, $var" else required_vars="$var" fi fi done <<< "$variables" cat < $skill $EXECUTION_MODE [Insert the complete processed template here with all variables substituted] \`\`\` ## Template Content \`\`\` $escaped_template \`\`\` ## Validation Checklist Before returning your result, verify: - [ ] No {\\\$VARIABLE} patterns remain in - [ ] No {\\\$VARIABLE:default} patterns remain in - [ ] YAML frontmatter (lines between ---) is NOT included - [ ] All required variables have meaningful values from user task - [ ] Optional variables either have extracted values or use their defaults EOF } # Main function main() { # Check for command-line argument if [ $# -eq 0 ]; then echo "Error: No input provided. Usage: $0 ''" >&2 exit 1 fi # Read and parse XML input from first argument read_xml_input "$1" # Generate and output instructions generate_instructions } # Run main function main "$@"