feat: rename rails/ to tools/ and add service tool suites
Rename the `rails/` directory to `tools/` for agent discoverability — agents frequently failed to locate helper scripts due to the non-intuitive directory name. Add backward-compat symlink `rails/ → tools/`. New tool suites: - Authentik: auth-token, user-list, user-create, group-list, app-list, flow-list, admin-status (8 scripts) - Coolify: team-list, project-list, service-list, service-status, deploy, env-set (7 scripts) - Woodpecker: pipeline-list, pipeline-status, pipeline-trigger (3 stubs) - GLPI: session-init, computer-list, ticket-list, ticket-create, user-list (6 scripts) - Health: stack-health.sh — stack-wide connectivity check Infrastructure: - Shared credential loader at tools/_lib/credentials.sh - install.sh creates symlink + chmod on tool scripts - All ~253 rails/ path references updated across 68+ files Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
15
tools/qa/debug-hook.sh
Executable file
15
tools/qa/debug-hook.sh
Executable file
@@ -0,0 +1,15 @@
|
||||
#!/bin/bash
|
||||
# Debug hook to identify available variables
|
||||
|
||||
echo "=== Hook Debug ===" >> /tmp/hook-debug.log
|
||||
echo "Date: $(date)" >> /tmp/hook-debug.log
|
||||
echo "All args: $@" >> /tmp/hook-debug.log
|
||||
echo "Arg count: $#" >> /tmp/hook-debug.log
|
||||
echo "Arg 1: ${1:-EMPTY}" >> /tmp/hook-debug.log
|
||||
echo "Arg 2: ${2:-EMPTY}" >> /tmp/hook-debug.log
|
||||
echo "Arg 3: ${3:-EMPTY}" >> /tmp/hook-debug.log
|
||||
echo "Environment:" >> /tmp/hook-debug.log
|
||||
env | grep -i file >> /tmp/hook-debug.log 2>/dev/null || true
|
||||
env | grep -i path >> /tmp/hook-debug.log 2>/dev/null || true
|
||||
env | grep -i tool >> /tmp/hook-debug.log 2>/dev/null || true
|
||||
echo "==================" >> /tmp/hook-debug.log
|
||||
197
tools/qa/qa-hook-handler.sh
Executable file
197
tools/qa/qa-hook-handler.sh
Executable file
@@ -0,0 +1,197 @@
|
||||
#!/bin/bash
|
||||
# Universal QA hook handler with robust error handling
|
||||
# Location: ~/.config/mosaic/tools/qa/qa-hook-handler.sh
|
||||
|
||||
# Don't exit on unset variables initially to handle missing params gracefully
|
||||
set -eo pipefail
|
||||
|
||||
PROJECT_ROOT=$(git rev-parse --show-toplevel 2>/dev/null || pwd)
|
||||
TOOL_NAME="${1:-}"
|
||||
FILE_PATH="${2:-}"
|
||||
|
||||
# Debug logging
|
||||
echo "[DEBUG] Script called with args: \$1='$1' \$2='$2'" >> "$PROJECT_ROOT/logs/qa-automation.log" 2>/dev/null || true
|
||||
|
||||
# Validate inputs
|
||||
if [ -z "$FILE_PATH" ] || [ -z "$TOOL_NAME" ]; then
|
||||
echo "[ERROR] Missing required parameters: tool='$TOOL_NAME' file='$FILE_PATH'" >&2
|
||||
echo "[ERROR] Usage: $0 <tool> <file_path>" >&2
|
||||
# Log to file if possible
|
||||
echo "[$(date '+%Y-%m-%d %H:%M:%S')] [ERROR] Missing parameters - tool='$TOOL_NAME' file='$FILE_PATH'" >> "$PROJECT_ROOT/logs/qa-automation.log" 2>/dev/null || true
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Now enable strict mode after parameter handling
|
||||
set -u
|
||||
|
||||
# Skip non-JS/TS files
|
||||
if ! [[ "$FILE_PATH" =~ \.(ts|tsx|js|jsx|mjs|cjs)$ ]]; then
|
||||
echo "[INFO] Skipping non-JS/TS file: $FILE_PATH"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Generate naming components
|
||||
TIMESTAMP=$(date '+%Y%m%d-%H%M')
|
||||
SANITIZED_NAME=$(echo "$FILE_PATH" | sed 's/\//-/g' | sed 's/^-//' | sed 's/\.\./\./g')
|
||||
ITERATION=1
|
||||
|
||||
# Log file for debugging
|
||||
LOG_FILE="$PROJECT_ROOT/logs/qa-automation.log"
|
||||
mkdir -p "$(dirname "$LOG_FILE")"
|
||||
|
||||
# Function to detect Epic with fallback
|
||||
detect_epic() {
|
||||
local file_path="$1"
|
||||
local epic=""
|
||||
|
||||
# Try to detect Epic from path patterns
|
||||
case "$file_path" in
|
||||
*/apps/frontend/src/components/*adapter*|*/apps/frontend/src/views/*adapter*)
|
||||
epic="E.3001-ADAPTER-CONFIG-SYSTEM"
|
||||
;;
|
||||
*/services/backend/src/adapters/*)
|
||||
epic="E.3001-ADAPTER-CONFIG-SYSTEM"
|
||||
;;
|
||||
*/services/backend/src/*)
|
||||
epic="E.2004-enterprise-data-synchronization-engine"
|
||||
;;
|
||||
*/services/syncagent-debezium/*|*/services/syncagent-n8n/*)
|
||||
epic="E.2004-enterprise-data-synchronization-engine"
|
||||
;;
|
||||
*)
|
||||
epic="" # General QA
|
||||
;;
|
||||
esac
|
||||
|
||||
echo "$epic"
|
||||
}
|
||||
|
||||
# Detect Epic association
|
||||
EPIC_FOLDER=$(detect_epic "$FILE_PATH")
|
||||
|
||||
# Function to setup report directory with creation if needed
|
||||
setup_report_dir() {
|
||||
local epic="$1"
|
||||
local project_root="$2"
|
||||
local report_dir=""
|
||||
|
||||
if [ -n "$epic" ]; then
|
||||
# Check if Epic directory exists
|
||||
local epic_dir="$project_root/docs/task-management/epics/active/$epic"
|
||||
|
||||
if [ -d "$epic_dir" ]; then
|
||||
# Epic exists, use it
|
||||
report_dir="$epic_dir/reports/qa-automation/pending"
|
||||
echo "[INFO] Using existing Epic: $epic" | tee -a "$LOG_FILE"
|
||||
else
|
||||
# Epic doesn't exist, check if we should create it
|
||||
local epic_parent="$project_root/docs/task-management/epics/active"
|
||||
|
||||
if [ -d "$epic_parent" ]; then
|
||||
# Parent exists, create Epic structure
|
||||
echo "[WARN] Epic $epic not found, creating structure..." | tee -a "$LOG_FILE"
|
||||
mkdir -p "$epic_dir/reports/qa-automation/pending"
|
||||
mkdir -p "$epic_dir/reports/qa-automation/in-progress"
|
||||
mkdir -p "$epic_dir/reports/qa-automation/done"
|
||||
mkdir -p "$epic_dir/reports/qa-automation/escalated"
|
||||
|
||||
# Create Epic README
|
||||
cat > "$epic_dir/README.md" << EOF
|
||||
# Epic: $epic
|
||||
|
||||
**Status**: Active
|
||||
**Created**: $(date '+%Y-%m-%d')
|
||||
**Purpose**: Auto-created by QA automation system
|
||||
|
||||
## Description
|
||||
This Epic was automatically created to organize QA remediation reports.
|
||||
|
||||
## QA Automation
|
||||
- Reports are stored in \`reports/qa-automation/\`
|
||||
- Pending issues: \`reports/qa-automation/pending/\`
|
||||
- Escalated issues: \`reports/qa-automation/escalated/\`
|
||||
EOF
|
||||
report_dir="$epic_dir/reports/qa-automation/pending"
|
||||
echo "[INFO] Created Epic structure: $epic" | tee -a "$LOG_FILE"
|
||||
else
|
||||
# Epic structure doesn't exist, fall back to general
|
||||
echo "[WARN] Epic structure not found, using general QA" | tee -a "$LOG_FILE"
|
||||
report_dir="$project_root/docs/reports/qa-automation/pending"
|
||||
fi
|
||||
fi
|
||||
else
|
||||
# No Epic association, use general
|
||||
report_dir="$project_root/docs/reports/qa-automation/pending"
|
||||
echo "[INFO] No Epic association, using general QA" | tee -a "$LOG_FILE"
|
||||
fi
|
||||
|
||||
# Ensure directory exists
|
||||
mkdir -p "$report_dir"
|
||||
echo "$report_dir"
|
||||
}
|
||||
|
||||
# Setup report directory (capture only the last line which is the path)
|
||||
REPORT_DIR=$(setup_report_dir "$EPIC_FOLDER" "$PROJECT_ROOT" | tail -1)
|
||||
|
||||
# Check for existing reports from same timestamp
|
||||
check_existing_iteration() {
|
||||
local dir="$1"
|
||||
local name="$2"
|
||||
local timestamp="$3"
|
||||
local max_iter=0
|
||||
|
||||
for file in "$dir"/${name}_${timestamp}_*_remediation_needed.md; do
|
||||
if [ -f "$file" ]; then
|
||||
# Extract iteration number
|
||||
local iter=$(echo "$file" | sed 's/.*_\([0-9]\+\)_remediation_needed\.md$/\1/')
|
||||
if [ "$iter" -gt "$max_iter" ]; then
|
||||
max_iter=$iter
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
echo $((max_iter + 1))
|
||||
}
|
||||
|
||||
ITERATION=$(check_existing_iteration "$REPORT_DIR" "$SANITIZED_NAME" "$TIMESTAMP")
|
||||
|
||||
# Check if we're at max iterations
|
||||
if [ "$ITERATION" -gt 5 ]; then
|
||||
echo "[ERROR] Max iterations (5) reached for $FILE_PATH" | tee -a "$LOG_FILE"
|
||||
# Move to escalated immediately
|
||||
REPORT_DIR="${REPORT_DIR/pending/escalated}"
|
||||
mkdir -p "$REPORT_DIR"
|
||||
ITERATION=5 # Cap at 5
|
||||
fi
|
||||
|
||||
# Create report filename
|
||||
REPORT_FILE="${SANITIZED_NAME}_${TIMESTAMP}_${ITERATION}_remediation_needed.md"
|
||||
REPORT_PATH="$REPORT_DIR/$REPORT_FILE"
|
||||
|
||||
# Log the action
|
||||
echo "[$(date '+%Y-%m-%d %H:%M:%S')] QA Hook: $TOOL_NAME on $FILE_PATH" | tee -a "$LOG_FILE"
|
||||
echo "[$(date '+%Y-%m-%d %H:%M:%S')] Creating report: $REPORT_PATH" | tee -a "$LOG_FILE"
|
||||
echo "[$(date '+%Y-%m-%d %H:%M:%S')] Epic: ${EPIC_FOLDER:-general}, Iteration: $ITERATION" | tee -a "$LOG_FILE"
|
||||
|
||||
# Create a task file for the QA agent instead of calling Claude directly
|
||||
cat > "$REPORT_PATH" << EOF
|
||||
# QA Remediation Report
|
||||
|
||||
**File:** $FILE_PATH
|
||||
**Tool Used:** $TOOL_NAME
|
||||
**Epic:** ${EPIC_FOLDER:-general}
|
||||
**Iteration:** $ITERATION
|
||||
**Generated:** $(date '+%Y-%m-%d %H:%M:%S')
|
||||
|
||||
## Status
|
||||
Pending QA validation
|
||||
|
||||
## Next Steps
|
||||
This report was created by the QA automation hook.
|
||||
To process this report, run:
|
||||
\`\`\`bash
|
||||
claude -p "Use Task tool to launch universal-qa-agent for report: $REPORT_PATH"
|
||||
\`\`\`
|
||||
EOF
|
||||
|
||||
echo "[$(date '+%Y-%m-%d %H:%M:%S')] Created report template at: $REPORT_PATH" | tee -a "$LOG_FILE"
|
||||
59
tools/qa/qa-hook-stdin.sh
Executable file
59
tools/qa/qa-hook-stdin.sh
Executable file
@@ -0,0 +1,59 @@
|
||||
#!/bin/bash
|
||||
# QA Hook handler that reads from stdin
|
||||
# Location: ~/.config/mosaic/tools/qa/qa-hook-stdin.sh
|
||||
|
||||
set -eo pipefail
|
||||
|
||||
PROJECT_ROOT=$(git rev-parse --show-toplevel 2>/dev/null || pwd)
|
||||
LOG_FILE="$PROJECT_ROOT/logs/qa-automation.log"
|
||||
mkdir -p "$(dirname "$LOG_FILE")"
|
||||
|
||||
# Read JSON from stdin
|
||||
JSON_INPUT=$(cat)
|
||||
|
||||
# Log raw input for debugging
|
||||
echo "[$(date '+%Y-%m-%d %H:%M:%S')] Hook triggered with JSON:" >> "$LOG_FILE"
|
||||
echo "$JSON_INPUT" >> "$LOG_FILE"
|
||||
|
||||
# Extract file path using jq if available, otherwise use grep/sed
|
||||
if command -v jq &> /dev/null; then
|
||||
# Try multiple paths - tool_input.file_path is the actual structure from Claude Code
|
||||
FILE_PATH=$(echo "$JSON_INPUT" | jq -r '.tool_input.file_path // .tool_response.filePath // .file_path // .path // .file // empty' 2>/dev/null || echo "")
|
||||
TOOL_NAME=$(echo "$JSON_INPUT" | jq -r '.tool_name // .tool // .matcher // "Edit"' 2>/dev/null || echo "Edit")
|
||||
else
|
||||
# Fallback parsing without jq - search in tool_input first
|
||||
FILE_PATH=$(echo "$JSON_INPUT" | grep -o '"tool_input"[^}]*}' | grep -o '"file_path"[[:space:]]*:[[:space:]]*"[^"]*"' | sed 's/.*"file_path"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/' | head -1)
|
||||
if [ -z "$FILE_PATH" ]; then
|
||||
FILE_PATH=$(echo "$JSON_INPUT" | grep -o '"file_path"[[:space:]]*:[[:space:]]*"[^"]*"' | sed 's/.*"file_path"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/' | head -1)
|
||||
fi
|
||||
if [ -z "$FILE_PATH" ]; then
|
||||
FILE_PATH=$(echo "$JSON_INPUT" | grep -o '"filePath"[[:space:]]*:[[:space:]]*"[^"]*"' | sed 's/.*"filePath"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/' | head -1)
|
||||
fi
|
||||
TOOL_NAME=$(echo "$JSON_INPUT" | grep -o '"tool_name"[[:space:]]*:[[:space:]]*"[^"]*"' | sed 's/.*"tool_name"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/' | head -1)
|
||||
if [ -z "$TOOL_NAME" ]; then
|
||||
TOOL_NAME=$(echo "$JSON_INPUT" | grep -o '"tool"[[:space:]]*:[[:space:]]*"[^"]*"' | sed 's/.*"tool"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/' | head -1)
|
||||
fi
|
||||
[ -z "$TOOL_NAME" ] && TOOL_NAME="Edit"
|
||||
fi
|
||||
|
||||
echo "[$(date '+%Y-%m-%d %H:%M:%S')] Extracted: tool=$TOOL_NAME file=$FILE_PATH" >> "$LOG_FILE"
|
||||
|
||||
# Validate we got a file path
|
||||
if [ -z "$FILE_PATH" ]; then
|
||||
echo "[$(date '+%Y-%m-%d %H:%M:%S')] [ERROR] Could not extract file path from JSON" >> "$LOG_FILE"
|
||||
exit 0 # Exit successfully to not block Claude
|
||||
fi
|
||||
|
||||
# Skip non-JS/TS files
|
||||
if ! [[ "$FILE_PATH" =~ \.(ts|tsx|js|jsx|mjs|cjs)$ ]]; then
|
||||
echo "[$(date '+%Y-%m-%d %H:%M:%S')] [INFO] Skipping non-JS/TS file: $FILE_PATH" >> "$LOG_FILE"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Call the main QA handler with extracted parameters
|
||||
if [ -f ~/.config/mosaic/tools/qa/qa-hook-handler.sh ]; then
|
||||
echo "[$(date '+%Y-%m-%d %H:%M:%S')] Calling QA handler for $FILE_PATH" >> "$LOG_FILE"
|
||||
~/.config/mosaic/tools/qa/qa-hook-handler.sh "$TOOL_NAME" "$FILE_PATH" 2>&1 | tee -a "$LOG_FILE"
|
||||
else
|
||||
echo "[$(date '+%Y-%m-%d %H:%M:%S')] [ERROR] QA handler script not found" >> "$LOG_FILE"
|
||||
fi
|
||||
19
tools/qa/qa-hook-wrapper.sh
Executable file
19
tools/qa/qa-hook-wrapper.sh
Executable file
@@ -0,0 +1,19 @@
|
||||
#!/bin/bash
|
||||
# Wrapper script that handles hook invocation more robustly
|
||||
|
||||
# Get the most recently modified JS/TS file as a fallback
|
||||
RECENT_FILE=$(find . -type f \( -name "*.ts" -o -name "*.tsx" -o -name "*.js" -o -name "*.jsx" \) -mmin -1 2>/dev/null | head -1)
|
||||
|
||||
# Use provided file path or fallback to recent file
|
||||
FILE_PATH="${2:-$RECENT_FILE}"
|
||||
TOOL_NAME="${1:-Edit}"
|
||||
|
||||
# Log the attempt
|
||||
echo "[$(date '+%Y-%m-%d %H:%M:%S')] Hook wrapper called: tool=$TOOL_NAME file=$FILE_PATH" >> logs/qa-automation.log 2>/dev/null || true
|
||||
|
||||
# Call the actual QA handler if we have a file
|
||||
if [ -n "$FILE_PATH" ]; then
|
||||
~/.config/mosaic/tools/qa/qa-hook-handler.sh "$TOOL_NAME" "$FILE_PATH"
|
||||
else
|
||||
echo "[$(date '+%Y-%m-%d %H:%M:%S')] No file path available for QA check" >> logs/qa-automation.log 2>/dev/null || true
|
||||
fi
|
||||
91
tools/qa/qa-queue-monitor.sh
Executable file
91
tools/qa/qa-queue-monitor.sh
Executable file
@@ -0,0 +1,91 @@
|
||||
#!/bin/bash
|
||||
# Monitor QA queues with graceful handling of missing directories
|
||||
# Location: ~/.config/mosaic/tools/qa/qa-queue-monitor.sh
|
||||
|
||||
PROJECT_ROOT=$(git rev-parse --show-toplevel 2>/dev/null || pwd)
|
||||
|
||||
echo "=== QA Automation Queue Status ==="
|
||||
echo "Project: $(basename "$PROJECT_ROOT")"
|
||||
echo "Time: $(date '+%Y-%m-%d %H:%M:%S')"
|
||||
echo
|
||||
|
||||
# Function to count files safely
|
||||
count_files() {
|
||||
local dir="$1"
|
||||
if [ -d "$dir" ]; then
|
||||
ls "$dir" 2>/dev/null | wc -l
|
||||
else
|
||||
echo "0"
|
||||
fi
|
||||
}
|
||||
|
||||
# Check Epic-specific queues
|
||||
EPIC_BASE="$PROJECT_ROOT/docs/task-management/epics/active"
|
||||
if [ -d "$EPIC_BASE" ]; then
|
||||
for EPIC_DIR in "$EPIC_BASE"/*/; do
|
||||
if [ -d "$EPIC_DIR" ]; then
|
||||
EPIC_NAME=$(basename "$EPIC_DIR")
|
||||
QA_DIR="$EPIC_DIR/reports/qa-automation"
|
||||
|
||||
if [ -d "$QA_DIR" ]; then
|
||||
echo "Epic: $EPIC_NAME"
|
||||
echo " Pending: $(count_files "$QA_DIR/pending")"
|
||||
echo " In Progress: $(count_files "$QA_DIR/in-progress")"
|
||||
echo " Done: $(count_files "$QA_DIR/done")"
|
||||
echo " Escalated: $(count_files "$QA_DIR/escalated")"
|
||||
|
||||
# Show escalated files if any
|
||||
if [ -d "$QA_DIR/escalated" ] && [ "$(ls "$QA_DIR/escalated" 2>/dev/null | wc -l)" -gt 0 ]; then
|
||||
echo " ⚠️ Escalated Issues:"
|
||||
for file in "$QA_DIR/escalated"/*_remediation_needed.md; do
|
||||
if [ -f "$file" ]; then
|
||||
echo " - $(basename "$file")"
|
||||
fi
|
||||
done
|
||||
fi
|
||||
echo
|
||||
fi
|
||||
fi
|
||||
done
|
||||
else
|
||||
echo "[WARN] No Epic structure found at: $EPIC_BASE"
|
||||
echo
|
||||
fi
|
||||
|
||||
# Check general queue
|
||||
GENERAL_DIR="$PROJECT_ROOT/docs/reports/qa-automation"
|
||||
if [ -d "$GENERAL_DIR" ]; then
|
||||
echo "General (Non-Epic):"
|
||||
echo " Pending: $(count_files "$GENERAL_DIR/pending")"
|
||||
echo " In Progress: $(count_files "$GENERAL_DIR/in-progress")"
|
||||
echo " Done: $(count_files "$GENERAL_DIR/done")"
|
||||
echo " Escalated: $(count_files "$GENERAL_DIR/escalated")"
|
||||
|
||||
# Show escalated files
|
||||
if [ -d "$GENERAL_DIR/escalated" ] && [ "$(ls "$GENERAL_DIR/escalated" 2>/dev/null | wc -l)" -gt 0 ]; then
|
||||
echo " ⚠️ Escalated Issues:"
|
||||
for file in "$GENERAL_DIR/escalated"/*_remediation_needed.md; do
|
||||
if [ -f "$file" ]; then
|
||||
echo " - $(basename "$file")"
|
||||
fi
|
||||
done
|
||||
fi
|
||||
else
|
||||
echo "[INFO] No general QA directory found (will be created on first use)"
|
||||
fi
|
||||
|
||||
echo
|
||||
echo "=== Recent Activity ==="
|
||||
# Show last 5 log entries
|
||||
if [ -f "$PROJECT_ROOT/logs/qa-automation.log" ]; then
|
||||
tail -5 "$PROJECT_ROOT/logs/qa-automation.log"
|
||||
else
|
||||
echo "No activity log found"
|
||||
fi
|
||||
|
||||
echo
|
||||
echo "=== Queue Processing Tips ==="
|
||||
echo "• View pending reports: ls -la $PROJECT_ROOT/docs/reports/qa-automation/pending/"
|
||||
echo "• Check stale reports: find $PROJECT_ROOT -path '*/in-progress/*' -mmin +60"
|
||||
echo "• Manual escalation: mv {report} {path}/escalated/"
|
||||
echo "• View full log: tail -f $PROJECT_ROOT/logs/qa-automation.log"
|
||||
66
tools/qa/remediation-hook-handler.sh
Executable file
66
tools/qa/remediation-hook-handler.sh
Executable file
@@ -0,0 +1,66 @@
|
||||
#!/bin/bash
|
||||
# Universal remediation hook handler with error recovery
|
||||
# Location: ~/.config/mosaic/tools/qa/remediation-hook-handler.sh
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
PROJECT_ROOT=$(git rev-parse --show-toplevel 2>/dev/null || pwd)
|
||||
REPORT_FILE="${1:-}"
|
||||
|
||||
# Validate input
|
||||
if [ -z "$REPORT_FILE" ] || [ ! -f "$REPORT_FILE" ]; then
|
||||
echo "[ERROR] Invalid or missing report file: $REPORT_FILE" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
LOG_FILE="$PROJECT_ROOT/logs/qa-automation.log"
|
||||
mkdir -p "$(dirname "$LOG_FILE")"
|
||||
|
||||
echo "[$(date '+%Y-%m-%d %H:%M:%S')] Remediation triggered for: $REPORT_FILE" | tee -a "$LOG_FILE"
|
||||
|
||||
# Extract components from path and filename
|
||||
BASE_NAME=$(basename "$REPORT_FILE" _remediation_needed.md)
|
||||
DIR_PATH=$(dirname "$REPORT_FILE")
|
||||
|
||||
# Validate directory structure
|
||||
if [[ ! "$DIR_PATH" =~ /pending$ ]]; then
|
||||
echo "[ERROR] Report not in pending directory: $DIR_PATH" | tee -a "$LOG_FILE"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Setup in-progress directory
|
||||
IN_PROGRESS_DIR="${DIR_PATH/pending/in-progress}"
|
||||
|
||||
# Handle missing in-progress directory
|
||||
if [ ! -d "$IN_PROGRESS_DIR" ]; then
|
||||
echo "[WARN] Creating missing in-progress directory: $IN_PROGRESS_DIR" | tee -a "$LOG_FILE"
|
||||
mkdir -p "$IN_PROGRESS_DIR"
|
||||
|
||||
# Also ensure done and escalated exist
|
||||
mkdir -p "${DIR_PATH/pending/done}"
|
||||
mkdir -p "${DIR_PATH/pending/escalated}"
|
||||
fi
|
||||
|
||||
# Move from pending to in-progress (with error handling)
|
||||
if ! mv "$REPORT_FILE" "$IN_PROGRESS_DIR/" 2>/dev/null; then
|
||||
echo "[ERROR] Failed to move report to in-progress" | tee -a "$LOG_FILE"
|
||||
# Check if already in progress
|
||||
if [ -f "$IN_PROGRESS_DIR/$(basename "$REPORT_FILE")" ]; then
|
||||
echo "[WARN] Report already in progress, skipping" | tee -a "$LOG_FILE"
|
||||
exit 0
|
||||
fi
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Create actions file
|
||||
ACTIONS_FILE="${BASE_NAME}_remediation_actions.md"
|
||||
ACTIONS_PATH="$IN_PROGRESS_DIR/$ACTIONS_FILE"
|
||||
|
||||
echo "[$(date '+%Y-%m-%d %H:%M:%S')] Starting remediation: $ACTIONS_PATH" | tee -a "$LOG_FILE"
|
||||
|
||||
# Trigger remediation agent
|
||||
claude -p "Use Task tool to launch auto-remediation-agent for:
|
||||
- Remediation Report: $IN_PROGRESS_DIR/$(basename "$REPORT_FILE")
|
||||
- Actions File: $ACTIONS_PATH
|
||||
- Max Iterations: 5
|
||||
Process the report, create action plan using Sequential Thinking, research with Context7, and execute fixes systematically." 2>&1 | tee -a "$LOG_FILE"
|
||||
Reference in New Issue
Block a user