claude-flow-novice 2.14.34 → 2.14.36
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/commands/CFN_LOOP_TASK_MODE.md +1 -1
- package/.claude/commands/switch-api.md +1 -1
- package/.claude/skills/cfn-agent-discovery/agents-registry.json +1 -1
- package/.claude/skills/cfn-loop-orchestration/orchestrate.sh +2 -1
- package/.claude/skills/cfn-loop-validation/config.json +2 -2
- package/.claude/skills/cfn-redis-coordination/invoke-waiting-mode.sh +221 -0
- package/claude-assets/agents/README-AGENT_LIFECYCLE.md +37 -10
- package/claude-assets/agents/README-VALIDATION.md +0 -8
- package/claude-assets/agents/cfn-dev-team/README.md +0 -8
- package/claude-assets/agents/cfn-dev-team/coordinators/README.md +1 -9
- package/claude-assets/agents/cfn-dev-team/coordinators/cfn-v3-coordinator.md +565 -565
- package/claude-assets/agents/cfn-dev-team/developers/README.md +1 -9
- package/claude-assets/agents/cfn-dev-team/documentation/README-VALIDATION.md +0 -8
- package/claude-assets/agents/cfn-dev-team/documentation/agent-type-guidelines.md +0 -10
- package/claude-assets/agents/cfn-dev-team/reviewers/README.md +1 -9
- package/claude-assets/agents/cfn-dev-team/reviewers/quality/quality-metrics.md +0 -10
- package/claude-assets/agents/cfn-dev-team/test-agent.md +0 -10
- package/claude-assets/agents/cfn-dev-team/testers/README.md +1 -9
- package/claude-assets/agents/csuite/cto-agent.md +0 -10
- package/claude-assets/agents/custom/cfn-system-expert.md +1 -128
- package/claude-assets/agents/docker-coordinators/cfn-docker-v3-coordinator.md +1 -5
- package/claude-assets/agents/docker-team/csuite/c-suite-template.md +1 -5
- package/claude-assets/agents/docker-team/infrastructure/team-coordinator-template.md +1 -5
- package/claude-assets/agents/marketing_hybrid/cost_tracker.md +0 -10
- package/claude-assets/agents/marketing_hybrid/docker_deployer.md +0 -10
- package/claude-assets/agents/marketing_hybrid/zai_worker_spawner.md +0 -10
- package/claude-assets/commands/CFN_LOOP_TASK_MODE.md +1 -1
- package/claude-assets/commands/switch-api.md +1 -1
- package/claude-assets/skills/cfn-agent-discovery/agents-registry.json +1 -1
- package/claude-assets/skills/cfn-loop-orchestration/orchestrate.sh +2 -1
- package/claude-assets/skills/cfn-loop-validation/config.json +2 -2
- package/claude-assets/skills/cfn-process-instrumentation/instrument-process.sh +324 -322
- package/claude-assets/skills/cfn-redis-coordination/invoke-waiting-mode.sh +221 -0
- package/claude-assets/skills/cfn-task-config-init/initialize-config.sh +2 -2
- package/claude-assets/skills/cfn-task-mode-sanitize/task-mode-env-sanitizer.sh +213 -182
- package/claude-assets/skills/cfn-validation-runner-instrumentation/wrapped-executor.sh +233 -271
- package/dist/cli/agent-definition-parser.js.map +1 -1
- package/dist/cli/config-manager.js +109 -91
- package/package.json +1 -1
- package/scripts/docker-build-mcp.sh +155 -0
- package/scripts/docker-test-mcp.sh +260 -0
- package/scripts/mcp-health-check.sh +123 -0
|
@@ -1,327 +1,289 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
|
|
3
|
+
##############################################################################
|
|
4
|
+
# CFN Validation Runner Process Instrumentation
|
|
5
|
+
# Provides process wrapping, monitoring, and instrumentation for CFN agents
|
|
6
|
+
#
|
|
7
|
+
# Usage:
|
|
8
|
+
# source wrapped-executor.sh
|
|
9
|
+
# wrap_execution <command> <timeout> <log_prefix>
|
|
10
|
+
#
|
|
11
|
+
# Example:
|
|
12
|
+
# wrap_execution "npx claude-flow-novice agent coder" 300 "agent-execution"
|
|
13
|
+
##############################################################################
|
|
4
14
|
|
|
5
15
|
set -euo pipefail
|
|
6
16
|
|
|
7
|
-
#
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
LOG_DIR="${CFN_VALIDATION_LOG_DIR:-$PROJECT_ROOT/.claude/logs/validation}"
|
|
15
|
-
MONITOR_INTERVAL="${CFN_MONITOR_INTERVAL:-30}" # 30 seconds
|
|
16
|
-
|
|
17
|
-
# Colors for output
|
|
18
|
-
RED='\033[0;31m'
|
|
19
|
-
GREEN='\033[0;32m'
|
|
20
|
-
YELLOW='\033[1;33m'
|
|
21
|
-
BLUE='\033[0;34m'
|
|
22
|
-
NC='\033[0m' # No Color
|
|
23
|
-
|
|
24
|
-
# Logging functions
|
|
25
|
-
log_info() {
|
|
26
|
-
echo -e "${BLUE}[INFO]${NC} $(date '+%Y-%m-%d %H:%M:%S') - $1" | tee -a "$LOG_FILE"
|
|
27
|
-
}
|
|
17
|
+
# Instrumentation state
|
|
18
|
+
declare -A CFN_INSTRUMENT_STATE
|
|
19
|
+
CFN_INSTRUMENT_STATE["active"]=false
|
|
20
|
+
CFN_INSTRUMENT_STATE["start_time"]=0
|
|
21
|
+
CFN_INSTRUMENT_STATE["pid"]=0
|
|
22
|
+
CFN_INSTRUMENT_STATE["timeout"]=0
|
|
23
|
+
CFN_INSTRUMENT_STATE["log_prefix"]=""
|
|
28
24
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
25
|
+
# Default timeouts (seconds)
|
|
26
|
+
readonly DEFAULT_TIMEOUT=300 # 5 minutes
|
|
27
|
+
readonly MAX_TIMEOUT=3600 # 1 hour
|
|
28
|
+
readonly WARNING_TIMEOUT=240 # 4 minutes
|
|
32
29
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
30
|
+
# Resource limits
|
|
31
|
+
readonly MAX_MEMORY_MB=2048
|
|
32
|
+
readonly MAX_CPU_PERCENT=80
|
|
36
33
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
34
|
+
##############################################################################
|
|
35
|
+
# Core Instrumentation Functions
|
|
36
|
+
##############################################################################
|
|
40
37
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
local
|
|
44
|
-
local
|
|
45
|
-
local command_name="$3"
|
|
46
|
-
local log_file="$4"
|
|
38
|
+
wrap_execution() {
|
|
39
|
+
local command="$1"
|
|
40
|
+
local timeout="${2:-$DEFAULT_TIMEOUT}"
|
|
41
|
+
local log_prefix="${3:-cfn-execution}"
|
|
47
42
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
43
|
+
# Validate timeout
|
|
44
|
+
if (( timeout > MAX_TIMEOUT )); then
|
|
45
|
+
timeout=$MAX_TIMEOUT
|
|
46
|
+
echo "⚠️ Timeout capped at ${MAX_TIMEOUT}s" >&2
|
|
47
|
+
fi
|
|
52
48
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
49
|
+
# Initialize instrumentation state
|
|
50
|
+
CFN_INSTRUMENT_STATE["active"]=true
|
|
51
|
+
CFN_INSTRUMENT_STATE["start_time"]=$(date +%s)
|
|
52
|
+
CFN_INSTRUMENT_STATE["timeout"]=$timeout
|
|
53
|
+
CFN_INSTRUMENT_STATE["log_prefix"]="$log_prefix"
|
|
56
54
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
55
|
+
echo "🔧 CFN Process Instrumentation v1.0.0" >&2
|
|
56
|
+
echo " Command: $command" >&2
|
|
57
|
+
echo " Timeout: ${timeout}s" >&2
|
|
58
|
+
echo " PID: $$" >&2
|
|
59
|
+
echo " Start: $(date -u +%Y-%m-%dT%H:%M:%SZ)" >&2
|
|
60
60
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
log_error "Force killing process $pid ($command_name)"
|
|
64
|
-
kill -KILL "$pid" 2>/dev/null || true
|
|
65
|
-
fi
|
|
66
|
-
|
|
67
|
-
return 124 # Timeout exit code
|
|
68
|
-
fi
|
|
61
|
+
# Setup monitoring
|
|
62
|
+
setup_process_monitoring "$timeout" "$log_prefix"
|
|
69
63
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
fi
|
|
64
|
+
# Execute with instrumentation
|
|
65
|
+
local result
|
|
66
|
+
result=$(execute_with_monitoring "$command" "$timeout" "$log_prefix")
|
|
67
|
+
local exit_code=$?
|
|
75
68
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
done
|
|
69
|
+
# Cleanup and report
|
|
70
|
+
cleanup_instrumentation "$exit_code"
|
|
79
71
|
|
|
80
|
-
return
|
|
72
|
+
return $exit_code
|
|
81
73
|
}
|
|
82
74
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
local
|
|
86
|
-
local timeout="${2:-$DEFAULT_TIMEOUT}"
|
|
87
|
-
local memory_limit="${3:-$DEFAULT_MEMORY}"
|
|
88
|
-
shift 3
|
|
89
|
-
local args=("$@")
|
|
90
|
-
|
|
91
|
-
# Create log directory
|
|
92
|
-
mkdir -p "$LOG_DIR"
|
|
93
|
-
|
|
94
|
-
# Generate unique execution ID
|
|
95
|
-
local execution_id="exec_$(date +%Y%m%d_%H%M%S)_$$"
|
|
96
|
-
local log_file="$LOG_DIR/${execution_id}.log"
|
|
97
|
-
export LOG_FILE="$log_file"
|
|
98
|
-
|
|
99
|
-
# Extract command name for logging
|
|
100
|
-
local command_name=$(basename "$command")
|
|
101
|
-
local full_command="$command ${args[*]}"
|
|
102
|
-
|
|
103
|
-
# Start execution logging
|
|
104
|
-
log_info "=== Starting Instrumented Execution ==="
|
|
105
|
-
log_info "Execution ID: $execution_id"
|
|
106
|
-
log_info "Command: $full_command"
|
|
107
|
-
log_info "Timeout: ${timeout}s"
|
|
108
|
-
log_info "Memory Limit: ${memory_limit}MB"
|
|
109
|
-
log_info "Working Directory: $(pwd)"
|
|
110
|
-
log_info "Environment: CFN_MODE=${CFN_MODE:-unset}, NODE_OPTIONS=${NODE_OPTIONS:-unset}"
|
|
111
|
-
|
|
112
|
-
# Set up resource limits
|
|
113
|
-
local memory_limit_kb=$((memory_limit * 1024))
|
|
114
|
-
|
|
115
|
-
# Start the command with resource limits
|
|
116
|
-
local start_time=$(date +%s)
|
|
117
|
-
log_info "Starting process at $(date)"
|
|
118
|
-
|
|
119
|
-
# Launch command in background with resource limits
|
|
120
|
-
(
|
|
121
|
-
# Apply memory limit
|
|
122
|
-
ulimit -v "$memory_limit_kb" 2>/dev/null || {
|
|
123
|
-
log_warning "Could not set memory limit via ulimit"
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
# Apply Node.js specific memory limit
|
|
127
|
-
if [[ "$command_name" == "node" || "$command_name" == "bun" ]]; then
|
|
128
|
-
export NODE_OPTIONS="${NODE_OPTIONS:-} --max-old-space-size=$memory_limit"
|
|
129
|
-
log_info "Set NODE_OPTIONS: $NODE_OPTIONS"
|
|
130
|
-
fi
|
|
75
|
+
setup_process_monitoring() {
|
|
76
|
+
local timeout="$1"
|
|
77
|
+
local log_prefix="$2"
|
|
131
78
|
|
|
132
|
-
|
|
133
|
-
exec "$command" "${args[@]}" 2>&1
|
|
134
|
-
) &
|
|
79
|
+
echo " Setting up process monitoring..." >&2
|
|
135
80
|
|
|
136
|
-
|
|
137
|
-
|
|
81
|
+
# Start resource monitoring in background
|
|
82
|
+
(
|
|
83
|
+
local monitor_interval=30 # Check every 30 seconds
|
|
84
|
+
local elapsed=0
|
|
138
85
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
86
|
+
while (( elapsed < timeout )); do
|
|
87
|
+
sleep $monitor_interval
|
|
88
|
+
elapsed=$((elapsed + monitor_interval))
|
|
142
89
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
exit_code=$?
|
|
150
|
-
if [[ $exit_code -eq 124 ]]; then
|
|
151
|
-
log_error "Process $pid ($command_name) timed out after ${timeout}s"
|
|
152
|
-
else
|
|
153
|
-
log_error "Process $pid ($command_name) failed with exit code: $exit_code"
|
|
154
|
-
fi
|
|
155
|
-
fi
|
|
90
|
+
if [[ "${CFN_INSTRUMENT_STATE[active]}" == "true" ]]; then
|
|
91
|
+
check_resource_usage "$log_prefix" "$elapsed"
|
|
92
|
+
else
|
|
93
|
+
break
|
|
94
|
+
fi
|
|
95
|
+
done
|
|
156
96
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
97
|
+
# Timeout warning
|
|
98
|
+
if [[ "${CFN_INSTRUMENT_STATE[active]}" == "true" ]]; then
|
|
99
|
+
echo "⚠️ [$log_prefix] Approaching timeout: ${elapsed}s" >&2
|
|
100
|
+
fi
|
|
101
|
+
) &
|
|
160
102
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
local duration=$((end_time - start_time))
|
|
103
|
+
echo " Resource monitoring started (interval: 30s)" >&2
|
|
104
|
+
}
|
|
164
105
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
log_info "Exit Code: $exit_code"
|
|
169
|
-
log_info "Duration: ${duration}s"
|
|
170
|
-
log_info "Memory Limit: ${memory_limit}MB"
|
|
106
|
+
check_resource_usage() {
|
|
107
|
+
local log_prefix="$1"
|
|
108
|
+
local elapsed="$2"
|
|
171
109
|
|
|
172
|
-
#
|
|
110
|
+
# Check memory usage
|
|
173
111
|
if command -v ps >/dev/null 2>&1; then
|
|
174
|
-
local
|
|
175
|
-
log_info "Final Memory: ${final_memory}KB"
|
|
176
|
-
fi
|
|
112
|
+
local memory_mb=$(ps -o rss= -p $$ 2>/dev/null | awk '{print int($1/1024)}' || echo "0")
|
|
177
113
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
kill -TERM "$pid" 2>/dev/null || true
|
|
182
|
-
sleep 2
|
|
183
|
-
kill -KILL "$pid" 2>/dev/null || true
|
|
114
|
+
if (( memory_mb > MAX_MEMORY_MB )); then
|
|
115
|
+
echo "⚠️ [$log_prefix] High memory: ${memory_mb}MB (limit: ${MAX_MEMORY_MB}MB)" >&2
|
|
116
|
+
fi
|
|
184
117
|
fi
|
|
185
118
|
|
|
186
|
-
#
|
|
187
|
-
if
|
|
188
|
-
local
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
mv "$log_file" "$archive_log" 2>/dev/null || true
|
|
194
|
-
log_error "Failed log archived: $archive_log"
|
|
119
|
+
# Check CPU usage (basic check)
|
|
120
|
+
if command -v top >/dev/null 2>&1; then
|
|
121
|
+
local cpu_percent=$(top -b -n 1 -p $$ 2>/dev/null | awk 'NR>7 {print $9}' | head -1 || echo "0")
|
|
122
|
+
|
|
123
|
+
if (( $(echo "$cpu_percent > $MAX_CPU_PERCENT" | bc -l 2>/dev/null || echo "0") )); then
|
|
124
|
+
echo "⚠️ [$log_prefix] High CPU: ${cpu_percent}%" >&2
|
|
125
|
+
fi
|
|
195
126
|
fi
|
|
196
127
|
|
|
197
|
-
|
|
128
|
+
# Warning timeout check
|
|
129
|
+
if (( elapsed > WARNING_TIMEOUT )); then
|
|
130
|
+
echo "⏰ [$log_prefix] Long running: ${elapsed}s" >&2
|
|
131
|
+
fi
|
|
198
132
|
}
|
|
199
133
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
local timeout="$
|
|
203
|
-
|
|
204
|
-
log_info "Executing Node.js with instrumentation"
|
|
205
|
-
execute_instrumented "node" "$timeout" "$DEFAULT_MEMORY" "$@"
|
|
206
|
-
}
|
|
134
|
+
execute_with_monitoring() {
|
|
135
|
+
local command="$1"
|
|
136
|
+
local timeout="$2"
|
|
137
|
+
local log_prefix="$3"
|
|
207
138
|
|
|
208
|
-
|
|
209
|
-
local timeout="${1:-$DEFAULT_TIMEOUT}"
|
|
210
|
-
shift
|
|
211
|
-
log_info "Executing Bun with instrumentation"
|
|
212
|
-
execute_instrumented "bun" "$timeout" "$DEFAULT_MEMORY" "$@"
|
|
213
|
-
}
|
|
139
|
+
echo " Executing: $command" >&2
|
|
214
140
|
|
|
215
|
-
|
|
216
|
-
local
|
|
217
|
-
shift
|
|
218
|
-
# Playwright may need more memory
|
|
219
|
-
local playwright_memory="${CFN_PLAYWRIGHT_MEMORY:-4096}"
|
|
220
|
-
log_info "Executing Playwright with instrumentation"
|
|
221
|
-
execute_instrumented "npx" "$timeout" "$playwright_memory" "playwright" "$@"
|
|
222
|
-
}
|
|
141
|
+
# Store command PID for monitoring
|
|
142
|
+
local child_pid
|
|
223
143
|
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
144
|
+
# Execute command with timeout
|
|
145
|
+
if command -v timeout >/dev/null 2>&1; then
|
|
146
|
+
# Use system timeout if available
|
|
147
|
+
timeout "$timeout" bash -c "$command" &
|
|
148
|
+
child_pid=$!
|
|
149
|
+
CFN_INSTRUMENT_STATE["pid"]=$child_pid
|
|
230
150
|
|
|
231
|
-
|
|
232
|
-
cleanup_logs() {
|
|
233
|
-
local max_days="${CFN_LOG_RETENTION_DAYS:-7}"
|
|
234
|
-
log_info "Cleaning up logs older than $max_days days"
|
|
151
|
+
echo " Process PID: $child_pid" >&2
|
|
235
152
|
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
153
|
+
# Wait for completion with monitoring
|
|
154
|
+
local wait_result=0
|
|
155
|
+
while kill -0 $child_pid 2>/dev/null; do
|
|
156
|
+
sleep 5
|
|
239
157
|
|
|
240
|
-
#
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
158
|
+
# Check if we're approaching timeout
|
|
159
|
+
local elapsed=$(($(date +%s) - CFN_INSTRUMENT_STATE[start_time]))
|
|
160
|
+
if (( elapsed > timeout - 10 )); then
|
|
161
|
+
echo "⚠️ [$log_prefix] Approaching hard timeout, preparing graceful shutdown" >&2
|
|
162
|
+
fi
|
|
163
|
+
done
|
|
164
|
+
|
|
165
|
+
wait $child_pid
|
|
166
|
+
wait_result=$?
|
|
167
|
+
|
|
168
|
+
else
|
|
169
|
+
# Fallback without system timeout
|
|
170
|
+
echo " Running without system timeout (not available)" >&2
|
|
171
|
+
bash -c "$command" &
|
|
172
|
+
child_pid=$!
|
|
173
|
+
CFN_INSTRUMENT_STATE["pid"]=$child_pid
|
|
174
|
+
|
|
175
|
+
# Basic wait (less safe)
|
|
176
|
+
wait $child_pid
|
|
177
|
+
wait_result=$?
|
|
178
|
+
fi
|
|
179
|
+
|
|
180
|
+
return $wait_result
|
|
181
|
+
}
|
|
244
182
|
|
|
245
|
-
|
|
246
|
-
|
|
183
|
+
cleanup_instrumentation() {
|
|
184
|
+
local exit_code="$1"
|
|
185
|
+
local elapsed=$(($(date +%s) - CFN_INSTRUMENT_STATE[start_time]))
|
|
247
186
|
|
|
248
|
-
|
|
249
|
-
|
|
187
|
+
echo "🧹 Process instrumentation cleanup" >&2
|
|
188
|
+
echo " Exit code: $exit_code" >&2
|
|
189
|
+
echo " Duration: ${elapsed}s" >&2
|
|
250
190
|
|
|
251
|
-
#
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
191
|
+
# Kill any remaining background processes
|
|
192
|
+
if [[ -n "${CFN_INSTRUMENT_STATE[pid]:-}" ]] && kill -0 "${CFN_INSTRUMENT_STATE[pid]}" 2>/dev/null; then
|
|
193
|
+
echo " Terminating child process: ${CFN_INSTRUMENT_STATE[pid]}" >&2
|
|
194
|
+
kill -TERM "${CFN_INSTRUMENT_STATE[pid]}" 2>/dev/null || true
|
|
195
|
+
sleep 2
|
|
196
|
+
kill -KILL "${CFN_INSTRUMENT_STATE[pid]}" 2>/dev/null || true
|
|
197
|
+
fi
|
|
256
198
|
|
|
257
|
-
#
|
|
258
|
-
|
|
199
|
+
# Clear state
|
|
200
|
+
CFN_INSTRUMENT_STATE["active"]=false
|
|
201
|
+
CFN_INSTRUMENT_STATE["pid"]=0
|
|
259
202
|
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
CFN_VALIDATION_MEMORY # Default memory limit in MB (default: 2048)
|
|
263
|
-
CFN_VALIDATION_LOG_DIR # Log directory (default: ./.claude/logs/validation)
|
|
264
|
-
CFN_MONITOR_INTERVAL # Monitoring interval in seconds (default: 30)
|
|
265
|
-
CFN_PLAYWRIGHT_MEMORY # Playwright memory limit in MB (default: 4096)
|
|
266
|
-
CFN_LOG_RETENTION_DAYS # Log retention in days (default: 7)
|
|
203
|
+
echo "✅ Instrumentation cleanup complete" >&2
|
|
204
|
+
}
|
|
267
205
|
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
206
|
+
##############################################################################
|
|
207
|
+
# Utility Functions
|
|
208
|
+
##############################################################################
|
|
209
|
+
|
|
210
|
+
get_instrumentation_status() {
|
|
211
|
+
if [[ "${CFN_INSTRUMENT_STATE[active]}" == "true" ]]; then
|
|
212
|
+
local elapsed=$(($(date +%s) - CFN_INSTRUMENT_STATE[start_time]))
|
|
213
|
+
echo "CFN Instrumentation Status: ACTIVE"
|
|
214
|
+
echo " PID: $$"
|
|
215
|
+
echo " Child PID: ${CFN_INSTRUMENT_STATE[pid]}"
|
|
216
|
+
echo " Elapsed: ${elapsed}s"
|
|
217
|
+
echo " Timeout: ${CFN_INSTRUMENT_STATE[timeout]}s"
|
|
218
|
+
echo " Log Prefix: ${CFN_INSTRUMENT_STATE[log_prefix]}"
|
|
219
|
+
else
|
|
220
|
+
echo "CFN Instrumentation Status: INACTIVE"
|
|
221
|
+
fi
|
|
222
|
+
}
|
|
271
223
|
|
|
272
|
-
|
|
273
|
-
|
|
224
|
+
force_kill_execution() {
|
|
225
|
+
local signal="${1:-TERM}"
|
|
274
226
|
|
|
275
|
-
|
|
276
|
-
|
|
227
|
+
if [[ "${CFN_INSTRUMENT_STATE[active]}" == "true" ]] && [[ -n "${CFN_INSTRUMENT_STATE[pid]:-}" ]]; then
|
|
228
|
+
echo "🚨 Force killing execution: ${CFN_INSTRUMENT_STATE[pid]} (signal: $signal)" >&2
|
|
229
|
+
kill -"$signal" "${CFN_INSTRUMENT_STATE[pid]}" 2>/dev/null || true
|
|
277
230
|
|
|
278
|
-
|
|
279
|
-
|
|
231
|
+
if [[ "$signal" == "TERM" ]]; then
|
|
232
|
+
sleep 2
|
|
233
|
+
if kill -0 "${CFN_INSTRUMENT_STATE[pid]}" 2>/dev/null; then
|
|
234
|
+
kill -KILL "${CFN_INSTRUMENT_STATE[pid]}" 2>/dev/null || true
|
|
235
|
+
fi
|
|
236
|
+
fi
|
|
237
|
+
fi
|
|
238
|
+
}
|
|
280
239
|
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
240
|
+
##############################################################################
|
|
241
|
+
# Signal Handlers
|
|
242
|
+
##############################################################################
|
|
284
243
|
|
|
285
|
-
|
|
244
|
+
setup_signal_handlers() {
|
|
245
|
+
trap 'handle_instrumentation_signal INT' INT
|
|
246
|
+
trap 'handle_instrumentation_signal TERM' TERM
|
|
247
|
+
trap 'handle_instrumentation_signal HUP' HUP
|
|
286
248
|
}
|
|
287
249
|
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
250
|
+
handle_instrumentation_signal() {
|
|
251
|
+
local signal="$1"
|
|
252
|
+
echo "🛑 Instrumentation received signal: $signal" >&2
|
|
253
|
+
|
|
254
|
+
if [[ "${CFN_INSTRUMENT_STATE[active]}" == "true" ]]; then
|
|
255
|
+
force_kill_execution TERM
|
|
256
|
+
cleanup_instrumentation 130
|
|
293
257
|
fi
|
|
294
258
|
|
|
295
|
-
|
|
259
|
+
exit 130
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
##############################################################################
|
|
263
|
+
# Auto-initialization
|
|
264
|
+
##############################################################################
|
|
265
|
+
|
|
266
|
+
# If script is sourced, make instrumentation available
|
|
267
|
+
if [[ "${BASH_SOURCE[0]}" != "${0}" ]]; then
|
|
268
|
+
echo "🔧 CFN Process Instrumentation loaded" >&2
|
|
269
|
+
echo " Use: wrap_execution <command> <timeout> <log_prefix>" >&2
|
|
270
|
+
setup_signal_handlers
|
|
271
|
+
fi
|
|
272
|
+
|
|
273
|
+
# If script is executed directly, show usage
|
|
274
|
+
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
|
|
275
|
+
echo "CFN Process Instrumentation v1.0.0"
|
|
276
|
+
echo ""
|
|
277
|
+
echo "Usage:"
|
|
278
|
+
echo " source wrapped-executor.sh"
|
|
279
|
+
echo " wrap_execution \"command\" timeout log_prefix"
|
|
280
|
+
echo ""
|
|
281
|
+
echo "Example:"
|
|
282
|
+
echo " wrap_execution \"npx claude-flow-novice agent coder\" 300 \"agent-execution\""
|
|
283
|
+
echo ""
|
|
284
|
+
|
|
285
|
+
# Test run if arguments provided
|
|
296
286
|
if [[ $# -gt 0 ]]; then
|
|
297
|
-
|
|
298
|
-
"node")
|
|
299
|
-
shift
|
|
300
|
-
execute_node "$@"
|
|
301
|
-
;;
|
|
302
|
-
"bun")
|
|
303
|
-
shift
|
|
304
|
-
execute_bun "$@"
|
|
305
|
-
;;
|
|
306
|
-
"playwright")
|
|
307
|
-
shift
|
|
308
|
-
execute_playwright "$@"
|
|
309
|
-
;;
|
|
310
|
-
"npx")
|
|
311
|
-
shift
|
|
312
|
-
execute_npx "$@"
|
|
313
|
-
;;
|
|
314
|
-
"cleanup")
|
|
315
|
-
cleanup_logs
|
|
316
|
-
;;
|
|
317
|
-
*)
|
|
318
|
-
echo "Unknown command: $1" >&2
|
|
319
|
-
echo "Use --help for usage information" >&2
|
|
320
|
-
exit 1
|
|
321
|
-
;;
|
|
322
|
-
esac
|
|
323
|
-
else
|
|
324
|
-
echo "CFN Validation Runner Instrumentation" >&2
|
|
325
|
-
echo "Use --help for usage information" >&2
|
|
287
|
+
wrap_execution "$@"
|
|
326
288
|
fi
|
|
327
289
|
fi
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/cli/agent-definition-parser.ts"],"sourcesContent":["/**\r\n * Agent Definition Parser\r\n *\r\n * Parses agent definition files (.md) with YAML frontmatter and markdown content.\r\n * Supports agent definitions in .claude/agents/ directory structure.\r\n */\r\n\r\nimport fs from 'fs/promises';\r\nimport path from 'path';\r\nimport { glob } from 'glob';\r\n\r\nexport interface AgentDefinition {\r\n // YAML frontmatter fields\r\n name: string;\r\n description: string;\r\n tools: string[];\r\n model: 'haiku' | 'sonnet' | 'opus';\r\n type?: string;\r\n color?: string;\r\n acl_level?: number;\r\n capabilities?: string[];\r\n validation_hooks?: string[];\r\n lifecycle?: {\r\n pre_task?: string;\r\n post_task?: string;\r\n };\r\n\r\n // Parsed markdown content\r\n content: string;\r\n\r\n // File metadata\r\n filePath: string;\r\n category?: string; // e.g., 'core-agents', 'specialized', 'custom'\r\n}\r\n\r\n/**\r\n * Parse YAML frontmatter from markdown content\r\n */\r\nfunction parseFrontmatter(content: string): { frontmatter: Record<string, any>; body: string } {\r\n const frontmatterRegex = /^---\\n([\\s\\S]*?)\\n---\\n([\\s\\S]*)$/;\r\n const match = content.match(frontmatterRegex);\r\n\r\n if (!match) {\r\n return { frontmatter: {}, body: content };\r\n }\r\n\r\n const [, yamlContent, body] = match;\r\n\r\n // Simple YAML parser (handles basic key-value pairs, arrays, and objects)\r\n const frontmatter: Record<string, any> = {};\r\n const lines = yamlContent.split('\\n');\r\n let currentKey = '';\r\n let currentArray: string[] = [];\r\n let isInArray = false;\r\n let isInObject = false;\r\n let currentObject: Record<string, string> = {};\r\n let objectKey = '';\r\n\r\n for (const line of lines) {\r\n const trimmed = line.trim();\r\n if (!trimmed || trimmed.startsWith('#')) continue;\r\n\r\n // Array item\r\n if (trimmed.startsWith('- ')) {\r\n if (!isInArray) {\r\n isInArray = true;\r\n currentArray = [];\r\n }\r\n currentArray.push(trimmed.substring(2).trim());\r\n continue;\r\n }\r\n\r\n // End of array\r\n if (isInArray && !trimmed.startsWith('- ')) {\r\n frontmatter[currentKey] = currentArray;\r\n isInArray = false;\r\n currentArray = [];\r\n }\r\n\r\n // Object field (indented key-value)\r\n if (trimmed.match(/^\\s+\\w+:/) && isInObject) {\r\n const [objKey, ...objValueParts] = trimmed.split(':');\r\n const objValue = objValueParts.join(':').trim().replace(/^[\"']|[\"']$/g, '');\r\n currentObject[objKey.trim()] = objValue;\r\n continue;\r\n }\r\n\r\n // Key-value pair\r\n const colonIndex = trimmed.indexOf(':');\r\n if (colonIndex !== -1) {\r\n const key = trimmed.substring(0, colonIndex).trim();\r\n const value = trimmed.substring(colonIndex + 1).trim();\r\n\r\n // Check if this starts an object\r\n if (value === '') {\r\n isInObject = true;\r\n currentObject = {};\r\n objectKey = key;\r\n continue;\r\n }\r\n\r\n // End previous object if any\r\n if (isInObject && !trimmed.match(/^\\s+/)) {\r\n frontmatter[objectKey] = currentObject;\r\n isInObject = false;\r\n currentObject = {};\r\n }\r\n\r\n currentKey = key;\r\n\r\n // Multi-line string (starts with |)\r\n if (value === '|') {\r\n continue; // Will be handled by next lines\r\n }\r\n\r\n // Inline array (e.g., [item1, item2, item3])\r\n if (value.startsWith('[') && value.endsWith(']')) {\r\n const arrayContent = value.substring(1, value.length - 1);\r\n const items = arrayContent.split(',').map(item => item.trim());\r\n frontmatter[key] = items;\r\n continue;\r\n }\r\n\r\n // Remove quotes\r\n const cleanValue = value.replace(/^[\"']|[\"']$/g, '');\r\n frontmatter[key] = cleanValue;\r\n } else if (currentKey && trimmed && !isInArray && !isInObject) {\r\n // Continuation of multi-line string\r\n const existingValue = frontmatter[currentKey];\r\n frontmatter[currentKey] = existingValue\r\n ? `${existingValue}\\n${trimmed}`\r\n : trimmed;\r\n }\r\n }\r\n\r\n // Handle trailing array or object\r\n if (isInArray) {\r\n frontmatter[currentKey] = currentArray;\r\n }\r\n if (isInObject) {\r\n frontmatter[objectKey] = currentObject;\r\n }\r\n\r\n return { frontmatter, body: body.trim() };\r\n}\r\n\r\n/**\r\n * Find agent definition file by agent type/name\r\n */\r\nasync function findAgentFile(agentType: string, baseDir: string = '.claude/agents'): Promise<string | null> {\r\n // Normalize agent type (handle both kebab-case and underscores)\r\n const normalizedType = agentType.toLowerCase().replace(/_/g, '-');\r\n\r\n // Search patterns (in order of priority)\r\n const patterns = [\r\n // Exact match in any subdirectory\r\n `${baseDir}/**/${normalizedType}.md`,\r\n // Match with different casing\r\n `${baseDir}/**/*${normalizedType}*.md`,\r\n ];\r\n\r\n for (const pattern of patterns) {\r\n const files = await glob(pattern, { nodir: true, absolute: true });\r\n if (files.length > 0) {\r\n // Prefer exact match over partial match\r\n const exactMatch = files.find(f => {\r\n const basename = path.basename(f, '.md').toLowerCase();\r\n return basename === normalizedType;\r\n });\r\n return exactMatch || files[0];\r\n }\r\n }\r\n\r\n return null;\r\n}\r\n\r\n/**\r\n * Parse agent definition from file\r\n */\r\nexport async function parseAgentDefinition(agentType: string): Promise<AgentDefinition> {\r\n // Find agent file\r\n const filePath = await findAgentFile(agentType);\r\n\r\n if (!filePath) {\r\n throw new Error(`Agent definition not found: ${agentType}`);\r\n }\r\n\r\n // Read file content\r\n const content = await fs.readFile(filePath, 'utf-8');\r\n\r\n // Parse frontmatter and body\r\n const { frontmatter, body } = parseFrontmatter(content);\r\n\r\n // Extract category from path\r\n const relativePath = path.relative('.claude/agents', filePath);\r\n const category = relativePath.includes('/')\r\n ? relativePath.split('/')[0]\r\n : undefined;\r\n\r\n // Build agent definition\r\n const definition: AgentDefinition = {\r\n name: frontmatter.name || agentType,\r\n description: frontmatter.description || '',\r\n tools: Array.isArray(frontmatter.tools) ? frontmatter.tools : [],\r\n model: frontmatter.model || 'haiku',\r\n type: frontmatter.type,\r\n color: frontmatter.color,\r\n acl_level: frontmatter.acl_level ? parseInt(String(frontmatter.acl_level), 10) : undefined,\r\n capabilities: frontmatter.capabilities,\r\n validation_hooks: frontmatter.validation_hooks,\r\n lifecycle: frontmatter.lifecycle,\r\n content: body,\r\n filePath,\r\n category,\r\n };\r\n\r\n return definition;\r\n}\r\n\r\n/**\r\n * List all available agent definitions\r\n */\r\nexport async function listAgentDefinitions(baseDir: string = '.claude/agents'): Promise<string[]> {\r\n const pattern = `${baseDir}/**/*.md`;\r\n const files = await glob(pattern, { nodir: true });\r\n\r\n return files.map(f => path.basename(f, '.md'));\r\n}\r\n\r\n/**\r\n * Check if agent definition includes CFN Loop protocol\r\n */\r\nexport function hasCFNLoopProtocol(definition: AgentDefinition): boolean {\r\n const content = definition.content.toLowerCase();\r\n return (\r\n content.includes('cfn loop') &&\r\n content.includes('redis completion protocol') ||\r\n content.includes('invoke-waiting-mode.sh')\r\n );\r\n}\r\n"],"names":["fs","path","glob","parseFrontmatter","content","frontmatterRegex","match","frontmatter","body","yamlContent","lines","split","currentKey","currentArray","isInArray","isInObject","currentObject","objectKey","line","trimmed","trim","startsWith","push","substring","objKey","objValueParts","objValue","join","replace","colonIndex","indexOf","key","value","endsWith","arrayContent","length","items","map","item","cleanValue","existingValue","findAgentFile","agentType","baseDir","normalizedType","toLowerCase","patterns","pattern","files","nodir","absolute","exactMatch","find","f","basename","parseAgentDefinition","filePath","Error","readFile","relativePath","relative","category","includes","undefined","definition","name","description","tools","Array","isArray","model","type","color","acl_level","parseInt","String","capabilities","validation_hooks","lifecycle","listAgentDefinitions","hasCFNLoopProtocol"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../src/cli/agent-definition-parser.ts"],"sourcesContent":["/**\r\n * Agent Definition Parser\r\n *\r\n * Parses agent definition files (.md) with YAML frontmatter and markdown content.\r\n * Supports agent definitions in .claude/agents/ directory structure.\r\n */\r\n\r\nimport fs from 'fs/promises';\r\nimport path from 'path';\r\nimport { glob } from 'glob';\r\n\r\nexport interface AgentDefinition {\r\n // YAML frontmatter fields\r\n name: string;\r\n description: string;\r\n tools: string[];\r\n model: 'haiku' | 'sonnet' | 'opus';\r\n type?: string;\r\n color?: string;\r\n acl_level?: number;\r\n capabilities?: string[];\r\n validation_hooks?: string[];\r\n lifecycle?: {\r\n pre_task?: string;\r\n post_task?: string;\r\n };\r\n\r\n // Parsed markdown content\r\n content: string;\r\n\r\n // File metadata\r\n filePath: string;\r\n category?: string; // e.g., 'core-agents', 'specialized', 'custom'\r\n}\r\n\r\n/**\r\n * Parse YAML frontmatter from markdown content\r\n */\r\nfunction parseFrontmatter(content: string): { frontmatter: Record<string, any>; body: string } {\r\n const frontmatterRegex = /^---\\n([\\s\\S]*?)\\n---\\n([\\s\\S]*)$/;\r\n const match = content.match(frontmatterRegex);\r\n\r\n if (!match) {\r\n return { frontmatter: {}, body: content };\r\n }\r\n\r\n const [, yamlContent, body] = match;\r\n\r\n \r\n // Simple YAML parser (handles basic key-value pairs, arrays, and objects)\r\n const frontmatter: Record<string, any> = {};\r\n const lines = yamlContent.split('\\n');\r\n let currentKey = '';\r\n let currentArray: string[] = [];\r\n let isInArray = false;\r\n let isInObject = false;\r\n let currentObject: Record<string, string> = {};\r\n let objectKey = '';\r\n\r\n for (const line of lines) {\r\n const trimmed = line.trim();\r\n if (!trimmed || trimmed.startsWith('#')) continue;\r\n\r\n // Array item\r\n if (trimmed.startsWith('- ')) {\r\n if (!isInArray) {\r\n isInArray = true;\r\n currentArray = [];\r\n }\r\n currentArray.push(trimmed.substring(2).trim());\r\n continue;\r\n }\r\n\r\n // End of array\r\n if (isInArray && !trimmed.startsWith('- ')) {\r\n frontmatter[currentKey] = currentArray;\r\n isInArray = false;\r\n currentArray = [];\r\n }\r\n\r\n // Object field (indented key-value)\r\n if (trimmed.match(/^\\s+\\w+:/) && isInObject) {\r\n const [objKey, ...objValueParts] = trimmed.split(':');\r\n const objValue = objValueParts.join(':').trim().replace(/^[\"']|[\"']$/g, '');\r\n currentObject[objKey.trim()] = objValue;\r\n continue;\r\n }\r\n\r\n // Key-value pair\r\n const colonIndex = trimmed.indexOf(':');\r\n if (colonIndex !== -1) {\r\n const key = trimmed.substring(0, colonIndex).trim();\r\n const value = trimmed.substring(colonIndex + 1).trim();\r\n\r\n // Check if this starts an object\r\n if (value === '') {\r\n isInObject = true;\r\n currentObject = {};\r\n objectKey = key;\r\n continue;\r\n }\r\n\r\n // End previous object if any\r\n if (isInObject && !trimmed.match(/^\\s+/)) {\r\n frontmatter[objectKey] = currentObject;\r\n isInObject = false;\r\n currentObject = {};\r\n }\r\n\r\n currentKey = key;\r\n\r\n // Multi-line string (starts with |)\r\n if (value === '|') {\r\n continue; // Will be handled by next lines\r\n }\r\n\r\n // Inline array (e.g., [item1, item2, item3])\r\n if (value.startsWith('[') && value.endsWith(']')) {\r\n const arrayContent = value.substring(1, value.length - 1);\r\n const items = arrayContent.split(',').map(item => item.trim());\r\n frontmatter[key] = items;\r\n continue;\r\n }\r\n\r\n // Remove quotes\r\n const cleanValue = value.replace(/^[\"']|[\"']$/g, '');\r\n frontmatter[key] = cleanValue;\r\n } else if (currentKey && trimmed && !isInArray && !isInObject) {\r\n // Continuation of multi-line string\r\n const existingValue = frontmatter[currentKey];\r\n frontmatter[currentKey] = existingValue\r\n ? `${existingValue}\\n${trimmed}`\r\n : trimmed;\r\n }\r\n }\r\n\r\n // Handle trailing array or object\r\n if (isInArray) {\r\n frontmatter[currentKey] = currentArray;\r\n }\r\n if (isInObject) {\r\n frontmatter[objectKey] = currentObject;\r\n }\r\n\r\n return { frontmatter, body: body.trim() };\r\n}\r\n\r\n/**\r\n * Find agent definition file by agent type/name\r\n */\r\nasync function findAgentFile(agentType: string, baseDir: string = '.claude/agents'): Promise<string | null> {\r\n // Normalize agent type (handle both kebab-case and underscores)\r\n const normalizedType = agentType.toLowerCase().replace(/_/g, '-');\r\n\r\n // Search patterns (in order of priority)\r\n const patterns = [\r\n // Exact match in any subdirectory\r\n `${baseDir}/**/${normalizedType}.md`,\r\n // Match with different casing\r\n `${baseDir}/**/*${normalizedType}*.md`,\r\n ];\r\n\r\n for (const pattern of patterns) {\r\n const files = await glob(pattern, { nodir: true, absolute: true });\r\n if (files.length > 0) {\r\n // Prefer exact match over partial match\r\n const exactMatch = files.find(f => {\r\n const basename = path.basename(f, '.md').toLowerCase();\r\n return basename === normalizedType;\r\n });\r\n return exactMatch || files[0];\r\n }\r\n }\r\n\r\n return null;\r\n}\r\n\r\n/**\r\n * Parse agent definition from file\r\n */\r\nexport async function parseAgentDefinition(agentType: string): Promise<AgentDefinition> {\r\n // Find agent file\r\n const filePath = await findAgentFile(agentType);\r\n\r\n if (!filePath) {\r\n throw new Error(`Agent definition not found: ${agentType}`);\r\n }\r\n\r\n // Read file content\r\n const content = await fs.readFile(filePath, 'utf-8');\r\n\r\n \r\n // Parse frontmatter and body\r\n const { frontmatter, body } = parseFrontmatter(content);\r\n\r\n // Extract category from path\r\n const relativePath = path.relative('.claude/agents', filePath);\r\n const category = relativePath.includes('/')\r\n ? relativePath.split('/')[0]\r\n : undefined;\r\n\r\n \r\n // Build agent definition\r\n const definition: AgentDefinition = {\r\n name: frontmatter.name || agentType,\r\n description: frontmatter.description || '',\r\n tools: Array.isArray(frontmatter.tools) ? frontmatter.tools : [],\r\n model: frontmatter.model || 'haiku',\r\n type: frontmatter.type,\r\n color: frontmatter.color,\r\n acl_level: frontmatter.acl_level ? parseInt(String(frontmatter.acl_level), 10) : undefined,\r\n capabilities: frontmatter.capabilities,\r\n validation_hooks: frontmatter.validation_hooks,\r\n lifecycle: frontmatter.lifecycle,\r\n content: body,\r\n filePath,\r\n category,\r\n };\r\n\r\n return definition;\r\n}\r\n\r\n/**\r\n * List all available agent definitions\r\n */\r\nexport async function listAgentDefinitions(baseDir: string = '.claude/agents'): Promise<string[]> {\r\n const pattern = `${baseDir}/**/*.md`;\r\n const files = await glob(pattern, { nodir: true });\r\n\r\n return files.map(f => path.basename(f, '.md'));\r\n}\r\n\r\n/**\r\n * Check if agent definition includes CFN Loop protocol\r\n */\r\nexport function hasCFNLoopProtocol(definition: AgentDefinition): boolean {\r\n const content = definition.content.toLowerCase();\r\n return (\r\n content.includes('cfn loop') &&\r\n content.includes('redis completion protocol') ||\r\n content.includes('invoke-waiting-mode.sh')\r\n );\r\n}\r\n"],"names":["fs","path","glob","parseFrontmatter","content","frontmatterRegex","match","frontmatter","body","yamlContent","lines","split","currentKey","currentArray","isInArray","isInObject","currentObject","objectKey","line","trimmed","trim","startsWith","push","substring","objKey","objValueParts","objValue","join","replace","colonIndex","indexOf","key","value","endsWith","arrayContent","length","items","map","item","cleanValue","existingValue","findAgentFile","agentType","baseDir","normalizedType","toLowerCase","patterns","pattern","files","nodir","absolute","exactMatch","find","f","basename","parseAgentDefinition","filePath","Error","readFile","relativePath","relative","category","includes","undefined","definition","name","description","tools","Array","isArray","model","type","color","acl_level","parseInt","String","capabilities","validation_hooks","lifecycle","listAgentDefinitions","hasCFNLoopProtocol"],"mappings":"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"}
|