elsabro 2.3.0 → 3.8.0
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/README.md +698 -20
- package/bin/install.js +0 -0
- package/flows/development-flow.json +452 -0
- package/flows/quick-flow.json +118 -0
- package/hooks/hooks-config-updated.json +285 -0
- package/hooks/skill-discovery.sh +539 -0
- package/package.json +3 -2
- package/references/SYSTEM_INDEX.md +400 -5
- package/references/agent-marketplace.md +2274 -0
- package/references/agent-protocol.md +1126 -0
- package/references/ai-code-suggestions.md +2413 -0
- package/references/checkpointing.md +595 -0
- package/references/collaboration-patterns.md +851 -0
- package/references/collaborative-sessions.md +1081 -0
- package/references/configuration-management.md +1810 -0
- package/references/cost-tracking.md +1095 -0
- package/references/enterprise-sso.md +2001 -0
- package/references/error-contracts-v2.md +968 -0
- package/references/event-driven.md +1031 -0
- package/references/flow-orchestration.md +940 -0
- package/references/flow-visualization.md +1557 -0
- package/references/ide-integrations.md +3513 -0
- package/references/interrupt-system.md +681 -0
- package/references/kubernetes-deployment.md +3099 -0
- package/references/memory-system.md +683 -0
- package/references/mobile-companion.md +3236 -0
- package/references/multi-llm-providers.md +2494 -0
- package/references/multi-project-memory.md +1182 -0
- package/references/observability.md +793 -0
- package/references/output-schemas.md +858 -0
- package/references/performance-profiler.md +955 -0
- package/references/plugin-system.md +1526 -0
- package/references/prompt-management.md +292 -0
- package/references/sandbox-execution.md +303 -0
- package/references/security-system.md +1253 -0
- package/references/skill-marketplace-integration.md +3901 -0
- package/references/streaming.md +696 -0
- package/references/testing-framework.md +1151 -0
- package/references/time-travel.md +802 -0
- package/references/tool-registry.md +886 -0
- package/references/voice-commands.md +3296 -0
- package/templates/agent-marketplace-config.json +220 -0
- package/templates/agent-protocol-config.json +136 -0
- package/templates/ai-suggestions-config.json +100 -0
- package/templates/checkpoint-state.json +61 -0
- package/templates/collaboration-config.json +157 -0
- package/templates/collaborative-sessions-config.json +153 -0
- package/templates/configuration-config.json +245 -0
- package/templates/cost-tracking-config.json +148 -0
- package/templates/enterprise-sso-config.json +438 -0
- package/templates/events-config.json +148 -0
- package/templates/flow-visualization-config.json +196 -0
- package/templates/ide-integrations-config.json +442 -0
- package/templates/kubernetes-config.json +764 -0
- package/templates/memory-state.json +84 -0
- package/templates/mobile-companion-config.json +600 -0
- package/templates/multi-llm-config.json +544 -0
- package/templates/multi-project-memory-config.json +145 -0
- package/templates/observability-config.json +109 -0
- package/templates/performance-profiler-config.json +125 -0
- package/templates/plugin-config.json +170 -0
- package/templates/prompt-management-config.json +86 -0
- package/templates/sandbox-config.json +185 -0
- package/templates/schemas-config.json +65 -0
- package/templates/security-config.json +120 -0
- package/templates/skill-marketplace-config.json +441 -0
- package/templates/streaming-config.json +72 -0
- package/templates/testing-config.json +81 -0
- package/templates/timetravel-config.json +62 -0
- package/templates/tool-registry-config.json +109 -0
- package/templates/voice-commands-config.json +658 -0
|
@@ -0,0 +1,539 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# skill-discovery.sh - ELSABRO Hook para descubrir skills externos antes del planning
|
|
3
|
+
#
|
|
4
|
+
# Uso: bash ./hooks/skill-discovery.sh "descripción de tarea" "complejidad"
|
|
5
|
+
# Output: JSON con skills descubiertos, validados y rankeados
|
|
6
|
+
#
|
|
7
|
+
# Responsabilidades:
|
|
8
|
+
# 1. Extraer keywords de la descripción
|
|
9
|
+
# 2. Buscar skills en ELSABRO interno
|
|
10
|
+
# 3. Buscar skills en marketplace externo (skills.sh)
|
|
11
|
+
# 4. Validar y rankear resultados
|
|
12
|
+
# 5. Retornar JSON con estructura normalizada
|
|
13
|
+
|
|
14
|
+
set -e
|
|
15
|
+
|
|
16
|
+
# ============================================================================
|
|
17
|
+
# CONFIGURACIÓN Y CONSTANTES
|
|
18
|
+
# ============================================================================
|
|
19
|
+
|
|
20
|
+
# Directorios
|
|
21
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
22
|
+
PROJECT_ROOT="$(dirname "$SCRIPT_DIR")"
|
|
23
|
+
ELSABRO_SKILLS_DIR="${PROJECT_ROOT}/skills"
|
|
24
|
+
SKILLS_CACHE_FILE="${PROJECT_ROOT}/.cache/skills-discovery-cache.json"
|
|
25
|
+
SKILLS_REGISTRY="${HOME}/.elsabro/skills-registry.json"
|
|
26
|
+
|
|
27
|
+
# Configuración de descubrimiento
|
|
28
|
+
KEYWORDS_MIN_MATCH=1
|
|
29
|
+
MAX_SKILLS_RETURN=10
|
|
30
|
+
EXTERNAL_API_TIMEOUT=15
|
|
31
|
+
CACHE_TTL_SECONDS=3600
|
|
32
|
+
|
|
33
|
+
# Colores para stderr (feedback visual)
|
|
34
|
+
RED='\033[0;31m'
|
|
35
|
+
GREEN='\033[0;32m'
|
|
36
|
+
YELLOW='\033[1;33m'
|
|
37
|
+
BLUE='\033[0;34m'
|
|
38
|
+
CYAN='\033[0;36m'
|
|
39
|
+
NC='\033[0m'
|
|
40
|
+
|
|
41
|
+
# Prefijo de logs
|
|
42
|
+
PREFIX="[ELSABRO:skill-discovery]"
|
|
43
|
+
|
|
44
|
+
# ============================================================================
|
|
45
|
+
# UTILIDADES
|
|
46
|
+
# ============================================================================
|
|
47
|
+
|
|
48
|
+
log_info() {
|
|
49
|
+
echo -e "${BLUE}${PREFIX}${NC} $1" >&2
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
log_success() {
|
|
53
|
+
echo -e "${GREEN}${PREFIX}${NC} ✓ $1" >&2
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
log_warn() {
|
|
57
|
+
echo -e "${YELLOW}${PREFIX}${NC} ⚠ $1" >&2
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
log_error() {
|
|
61
|
+
echo -e "${RED}${PREFIX}${NC} ✗ $1" >&2
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
# Crear directorios si no existen
|
|
65
|
+
ensure_dirs() {
|
|
66
|
+
mkdir -p "$(dirname "$SKILLS_CACHE_FILE")" 2>/dev/null || true
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
# Verificar si caché es válido
|
|
70
|
+
is_cache_valid() {
|
|
71
|
+
local cache_file="$1"
|
|
72
|
+
|
|
73
|
+
if [ ! -f "$cache_file" ]; then
|
|
74
|
+
return 1
|
|
75
|
+
fi
|
|
76
|
+
|
|
77
|
+
# Verificar edad del caché (en segundos)
|
|
78
|
+
local file_age=$(($(date +%s) - $(stat -f%m "$cache_file" 2>/dev/null || echo 0)))
|
|
79
|
+
|
|
80
|
+
if [ "$file_age" -gt "$CACHE_TTL_SECONDS" ]; then
|
|
81
|
+
return 1
|
|
82
|
+
fi
|
|
83
|
+
|
|
84
|
+
return 0
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
# ============================================================================
|
|
88
|
+
# 1. EXTRACCIÓN DE KEYWORDS
|
|
89
|
+
# ============================================================================
|
|
90
|
+
|
|
91
|
+
# Extrae palabras clave de la descripción de tarea
|
|
92
|
+
# Entrada: string (descripción)
|
|
93
|
+
# Salida: JSON array con keywords encontradas
|
|
94
|
+
extract_keywords() {
|
|
95
|
+
local task_description="$1"
|
|
96
|
+
|
|
97
|
+
# Convertir a lowercase para búsqueda insensible
|
|
98
|
+
local lowercase=$(echo "$task_description" | tr '[:upper:]' '[:lower:]')
|
|
99
|
+
|
|
100
|
+
# Definir patrones de keywords por categoría
|
|
101
|
+
# Formato: categoria|sinónimo1|sinónimo2|sinónimo3
|
|
102
|
+
local keyword_patterns=(
|
|
103
|
+
"api|api|backend|endpoint|rest|graphql|http|server"
|
|
104
|
+
"auth|auth|login|signin|oauth|jwt|session|password|signup|credential"
|
|
105
|
+
"db|database|db|postgres|postgresql|mysql|mongodb|firebase|sql|orm"
|
|
106
|
+
"payments|payment|stripe|paypal|billing|checkout|commerce|transaction"
|
|
107
|
+
"mobile|mobile|expo|react-native|ios|android|app|swift|kotlin"
|
|
108
|
+
"web|website|web|nextjs|react|vue|angular|frontend|ui|ux|html|css"
|
|
109
|
+
"devops|devops|docker|kubernetes|k8s|ci|cd|github|gitlab|deploy"
|
|
110
|
+
"testing|test|jest|vitest|mocha|pytest|coverage|unit|e2e|qa"
|
|
111
|
+
"security|security|encrypt|https|cors|ssl|tls|hash|salt|breach"
|
|
112
|
+
"monitoring|monitor|sentry|logging|logs|error|analytics|observability"
|
|
113
|
+
"cli|cli|command|tool|script|automation|terminal|bash|shell"
|
|
114
|
+
"ai|ai|machine-learning|ml|gpt|claude|llm|nlp|transformer"
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
# Array para guardar keywords encontrados
|
|
118
|
+
local found_keywords=()
|
|
119
|
+
local found_categories=()
|
|
120
|
+
|
|
121
|
+
# Buscar cada patrón en la descripción
|
|
122
|
+
for pattern in "${keyword_patterns[@]}"; do
|
|
123
|
+
IFS='|' read -r category synmap <<< "$pattern"
|
|
124
|
+
|
|
125
|
+
# Crear regex de sinónimos
|
|
126
|
+
local regex=$(echo "$synmap" | sed 's/|/\\|/g')
|
|
127
|
+
|
|
128
|
+
# Buscar con grep case-insensitive
|
|
129
|
+
if echo "$lowercase" | grep -qiE "\b($regex)\b"; then
|
|
130
|
+
found_keywords+=("\"$category\"")
|
|
131
|
+
found_categories+=("$category")
|
|
132
|
+
fi
|
|
133
|
+
done
|
|
134
|
+
|
|
135
|
+
# Retornar como JSON array
|
|
136
|
+
# Si no encontró keywords, retornar array vacío
|
|
137
|
+
if [ ${#found_keywords[@]} -eq 0 ]; then
|
|
138
|
+
echo "[]"
|
|
139
|
+
else
|
|
140
|
+
printf "["
|
|
141
|
+
printf "%s" "${found_keywords[0]}"
|
|
142
|
+
for kw in "${found_keywords[@]:1}"; do
|
|
143
|
+
printf ",$kw"
|
|
144
|
+
done
|
|
145
|
+
printf "]\n"
|
|
146
|
+
fi
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
# ============================================================================
|
|
150
|
+
# 2. DESCUBRIMIENTO DE SKILLS - FUENTE 1: ELSABRO INTERNO
|
|
151
|
+
# ============================================================================
|
|
152
|
+
|
|
153
|
+
# Busca skills en ./skills/*.md
|
|
154
|
+
# Entrada: JSON array de keywords
|
|
155
|
+
# Salida: JSON array con skills ELSABRO encontrados
|
|
156
|
+
discover_elsabro_skills() {
|
|
157
|
+
local keywords="$1"
|
|
158
|
+
|
|
159
|
+
local skills_found=()
|
|
160
|
+
|
|
161
|
+
# Verificar que directorio existe
|
|
162
|
+
if [ ! -d "$ELSABRO_SKILLS_DIR" ]; then
|
|
163
|
+
echo "[]"
|
|
164
|
+
return
|
|
165
|
+
fi
|
|
166
|
+
|
|
167
|
+
# Iterar sobre cada archivo .md en skills/
|
|
168
|
+
for skill_file in "$ELSABRO_SKILLS_DIR"/*.md; do
|
|
169
|
+
[ -f "$skill_file" ] || continue
|
|
170
|
+
|
|
171
|
+
local skill_name=$(basename "$skill_file" .md)
|
|
172
|
+
local skill_content=$(cat "$skill_file")
|
|
173
|
+
|
|
174
|
+
# ---- Extraer metadata YAML ----
|
|
175
|
+
# Buscar secciones YAML en el front matter
|
|
176
|
+
|
|
177
|
+
local description=$(
|
|
178
|
+
echo "$skill_content" | sed -n '/^description:/s/^description: *//p' | head -1
|
|
179
|
+
)
|
|
180
|
+
description="${description:0:80}" # Limitar a 80 caracteres
|
|
181
|
+
|
|
182
|
+
local difficulty=$(
|
|
183
|
+
echo "$skill_content" | sed -n '/^difficulty:/s/^difficulty: *//p' | head -1
|
|
184
|
+
)
|
|
185
|
+
difficulty="${difficulty:-intermediate}"
|
|
186
|
+
|
|
187
|
+
local estimated_time=$(
|
|
188
|
+
echo "$skill_content" | sed -n '/^estimated_time:/s/^estimated_time: *//p' | head -1
|
|
189
|
+
)
|
|
190
|
+
|
|
191
|
+
# Extraer tags (múltiples líneas)
|
|
192
|
+
local tags=$(
|
|
193
|
+
echo "$skill_content" |
|
|
194
|
+
sed -n '/^tags:/,/^[^ -]/p' |
|
|
195
|
+
grep "^ - " |
|
|
196
|
+
sed 's/^ - //' |
|
|
197
|
+
head -10
|
|
198
|
+
)
|
|
199
|
+
|
|
200
|
+
# ---- Calcular match score ----
|
|
201
|
+
# Score = cantidad de keywords que coinciden con tags/description
|
|
202
|
+
|
|
203
|
+
local match_score=0
|
|
204
|
+
|
|
205
|
+
# Convertir keywords de JSON array a array bash
|
|
206
|
+
local kw_array=()
|
|
207
|
+
if command -v jq &> /dev/null; then
|
|
208
|
+
while IFS= read -r kw; do
|
|
209
|
+
kw_array+=("$kw")
|
|
210
|
+
done < <(echo "$keywords" | jq -r '.[]' 2>/dev/null)
|
|
211
|
+
fi
|
|
212
|
+
|
|
213
|
+
# Buscar keywords en tags y descripción
|
|
214
|
+
for keyword in "${kw_array[@]}"; do
|
|
215
|
+
if echo "$tags" | grep -qi "$keyword"; then
|
|
216
|
+
((match_score++))
|
|
217
|
+
fi
|
|
218
|
+
if echo "$description" | grep -qi "$keyword"; then
|
|
219
|
+
((match_score++))
|
|
220
|
+
fi
|
|
221
|
+
done
|
|
222
|
+
|
|
223
|
+
# Solo incluir si hay al menos un match
|
|
224
|
+
if [ "$match_score" -ge "$KEYWORDS_MIN_MATCH" ]; then
|
|
225
|
+
# Construir objeto skill JSON
|
|
226
|
+
local tags_json=$(echo "$tags" | jq -R -s -c 'split("\n") | map(select(length > 0))')
|
|
227
|
+
|
|
228
|
+
local skill_obj=$(cat <<EOF
|
|
229
|
+
{
|
|
230
|
+
"id": "$skill_name",
|
|
231
|
+
"source": "elsabro",
|
|
232
|
+
"status": "available",
|
|
233
|
+
"match_score": $match_score,
|
|
234
|
+
"description": "$description",
|
|
235
|
+
"difficulty": "$difficulty",
|
|
236
|
+
"estimated_time": "$estimated_time",
|
|
237
|
+
"tags": $tags_json,
|
|
238
|
+
"rank": 0
|
|
239
|
+
}
|
|
240
|
+
EOF
|
|
241
|
+
)
|
|
242
|
+
|
|
243
|
+
skills_found+=("$skill_obj")
|
|
244
|
+
fi
|
|
245
|
+
done
|
|
246
|
+
|
|
247
|
+
# Retornar como JSON array
|
|
248
|
+
if [ ${#skills_found[@]} -eq 0 ]; then
|
|
249
|
+
echo "[]"
|
|
250
|
+
else
|
|
251
|
+
# Usar jq para construir array válido
|
|
252
|
+
printf '%s\n' "${skills_found[@]}" | jq -s '.'
|
|
253
|
+
fi
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
# ============================================================================
|
|
257
|
+
# 3. DESCUBRIMIENTO DE SKILLS - FUENTE 2: MARKETPLACE EXTERNO
|
|
258
|
+
# ============================================================================
|
|
259
|
+
|
|
260
|
+
# Consulta API de skills.sh para encontrar skills externos
|
|
261
|
+
# Entrada: JSON array de keywords
|
|
262
|
+
# Salida: JSON array con skills de marketplace
|
|
263
|
+
discover_external_skills() {
|
|
264
|
+
local keywords="$1"
|
|
265
|
+
|
|
266
|
+
# Si no está disponible curl o jq, retornar array vacío
|
|
267
|
+
if ! command -v curl &> /dev/null || ! command -v jq &> /dev/null; then
|
|
268
|
+
log_warn "curl o jq no disponible, saltando external skills"
|
|
269
|
+
echo "[]"
|
|
270
|
+
return
|
|
271
|
+
fi
|
|
272
|
+
|
|
273
|
+
# URL de la API
|
|
274
|
+
local api_url="https://api.skills.sh/v1/discover"
|
|
275
|
+
|
|
276
|
+
# Payload: array de keywords
|
|
277
|
+
local payload=$(echo "$keywords" | jq -c '.')
|
|
278
|
+
|
|
279
|
+
log_info "Consultando API: $api_url con keywords: $payload"
|
|
280
|
+
|
|
281
|
+
# Hacer petición HTTP con timeout
|
|
282
|
+
local response=$(
|
|
283
|
+
timeout "$EXTERNAL_API_TIMEOUT" curl -s \
|
|
284
|
+
-X POST "$api_url" \
|
|
285
|
+
-H "Content-Type: application/json" \
|
|
286
|
+
-d "{\"keywords\": $payload}" \
|
|
287
|
+
2>/dev/null || echo "[]"
|
|
288
|
+
)
|
|
289
|
+
|
|
290
|
+
# Procesar respuesta (asumir estructura: { "skills": [...] })
|
|
291
|
+
if echo "$response" | jq empty 2>/dev/null; then
|
|
292
|
+
# Extractar y enriquecer skills
|
|
293
|
+
echo "$response" | jq -c '
|
|
294
|
+
.skills[]? // empty |
|
|
295
|
+
. + {
|
|
296
|
+
status: "available",
|
|
297
|
+
rank: 0,
|
|
298
|
+
install_command: ("curl -s https://skills.sh/install/" + .id + " | bash")
|
|
299
|
+
}
|
|
300
|
+
' | jq -s '.'
|
|
301
|
+
else
|
|
302
|
+
log_warn "API response inválido, retornando array vacío"
|
|
303
|
+
echo "[]"
|
|
304
|
+
fi
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
# ============================================================================
|
|
308
|
+
# 4. VALIDACIÓN Y RANKING
|
|
309
|
+
# ============================================================================
|
|
310
|
+
|
|
311
|
+
# Valida y rankea todos los skills descubiertos
|
|
312
|
+
# Entrada: JSON array de skills
|
|
313
|
+
# Salida: JSON array validado y ordenado por rank
|
|
314
|
+
validate_and_rank_skills() {
|
|
315
|
+
local all_skills="$1"
|
|
316
|
+
|
|
317
|
+
if ! command -v jq &> /dev/null; then
|
|
318
|
+
log_warn "jq no disponible, retornando skills sin validación"
|
|
319
|
+
echo "$all_skills"
|
|
320
|
+
return
|
|
321
|
+
fi
|
|
322
|
+
|
|
323
|
+
# Validar y rankear con jq
|
|
324
|
+
echo "$all_skills" | jq -c '
|
|
325
|
+
# Filtrar skills válidos
|
|
326
|
+
map(
|
|
327
|
+
select(
|
|
328
|
+
.id != null and
|
|
329
|
+
.source != null and
|
|
330
|
+
(.status == "available" or .status == "installable")
|
|
331
|
+
)
|
|
332
|
+
) |
|
|
333
|
+
# Agregar score de ranking
|
|
334
|
+
map(
|
|
335
|
+
. + {
|
|
336
|
+
rank: (
|
|
337
|
+
# Base: match_score
|
|
338
|
+
(.match_score // 0) * 100 +
|
|
339
|
+
# Bonus por dificultad (beginner > advanced)
|
|
340
|
+
(if .difficulty == "beginner" then 50 else
|
|
341
|
+
if .difficulty == "intermediate" then 30 else
|
|
342
|
+
if .difficulty == "advanced" then 10 else 0 end end) +
|
|
343
|
+
# Bonus por source (elsabro es más confiable)
|
|
344
|
+
(if .source == "elsabro" then 20 else 0 end) +
|
|
345
|
+
# Penalty por external que requiere instalación
|
|
346
|
+
(if (.install_command != null) then 0 else 5 end)
|
|
347
|
+
)
|
|
348
|
+
}
|
|
349
|
+
) |
|
|
350
|
+
# Ordenar por ranking descendente
|
|
351
|
+
sort_by(.rank) | reverse |
|
|
352
|
+
# Limitar a top 10
|
|
353
|
+
.[0:10]
|
|
354
|
+
'
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
# ============================================================================
|
|
358
|
+
# 5. CACHÉ
|
|
359
|
+
# ============================================================================
|
|
360
|
+
|
|
361
|
+
# Intenta obtener skills del caché
|
|
362
|
+
# Entrada: keywords (string)
|
|
363
|
+
# Salida: JSON array con skills si caché válido, "null" si no
|
|
364
|
+
get_from_cache() {
|
|
365
|
+
local keywords="$1"
|
|
366
|
+
local keywords_hash=$(echo -n "$keywords" | md5 2>/dev/null || echo "$keywords" | md5sum | awk '{print $1}')
|
|
367
|
+
|
|
368
|
+
if ! is_cache_valid "$SKILLS_CACHE_FILE"; then
|
|
369
|
+
return 1
|
|
370
|
+
fi
|
|
371
|
+
|
|
372
|
+
# Buscar en caché con jq
|
|
373
|
+
if command -v jq &> /dev/null; then
|
|
374
|
+
local cached=$(
|
|
375
|
+
jq --arg hash "$keywords_hash" '.cache[$hash]?' "$SKILLS_CACHE_FILE" 2>/dev/null
|
|
376
|
+
)
|
|
377
|
+
|
|
378
|
+
if [ "$cached" != "null" ] && [ -n "$cached" ]; then
|
|
379
|
+
log_success "Skill cache hit"
|
|
380
|
+
echo "$cached"
|
|
381
|
+
return 0
|
|
382
|
+
fi
|
|
383
|
+
fi
|
|
384
|
+
|
|
385
|
+
return 1
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
# Guardar resultado en caché
|
|
389
|
+
save_to_cache() {
|
|
390
|
+
local keywords="$1"
|
|
391
|
+
local skills="$2"
|
|
392
|
+
local keywords_hash=$(echo -n "$keywords" | md5 2>/dev/null || echo "$keywords" | md5sum | awk '{print $1}')
|
|
393
|
+
|
|
394
|
+
ensure_dirs
|
|
395
|
+
|
|
396
|
+
# Crear o actualizar caché
|
|
397
|
+
if [ -f "$SKILLS_CACHE_FILE" ]; then
|
|
398
|
+
jq --arg hash "$keywords_hash" --argjson skills "$skills" \
|
|
399
|
+
'.cache[$hash] = $skills' "$SKILLS_CACHE_FILE" > "$SKILLS_CACHE_FILE.tmp"
|
|
400
|
+
mv "$SKILLS_CACHE_FILE.tmp" "$SKILLS_CACHE_FILE"
|
|
401
|
+
else
|
|
402
|
+
# Crear caché nuevo
|
|
403
|
+
jq -n --arg hash "$keywords_hash" --argjson skills "$skills" \
|
|
404
|
+
'{cache: {($hash): $skills}}' > "$SKILLS_CACHE_FILE"
|
|
405
|
+
fi
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
# ============================================================================
|
|
409
|
+
# 6. OUTPUT Y FORMATEO
|
|
410
|
+
# ============================================================================
|
|
411
|
+
|
|
412
|
+
# Formatea output final como JSON normalizado
|
|
413
|
+
# Entrada: keywords, skills de cada fuente, skills rankeados
|
|
414
|
+
# Salida: JSON con estructura completa
|
|
415
|
+
format_discovery_output() {
|
|
416
|
+
local keywords="$1"
|
|
417
|
+
local elsabro_skills="$2"
|
|
418
|
+
local external_skills="$3"
|
|
419
|
+
local ranked_skills="$4"
|
|
420
|
+
|
|
421
|
+
if ! command -v jq &> /dev/null; then
|
|
422
|
+
# Fallback si jq no está disponible
|
|
423
|
+
cat <<EOF
|
|
424
|
+
{
|
|
425
|
+
"timestamp": "$(date -u +%Y-%m-%dT%H:%M:%SZ)",
|
|
426
|
+
"status": "error",
|
|
427
|
+
"message": "jq not available"
|
|
428
|
+
}
|
|
429
|
+
EOF
|
|
430
|
+
return 1
|
|
431
|
+
fi
|
|
432
|
+
|
|
433
|
+
# Contar elementos
|
|
434
|
+
local elsabro_count=$(echo "$elsabro_skills" | jq 'length')
|
|
435
|
+
local external_count=$(echo "$external_skills" | jq 'length')
|
|
436
|
+
local ranked_count=$(echo "$ranked_skills" | jq 'length')
|
|
437
|
+
|
|
438
|
+
# Extraer top 3 recomendaciones
|
|
439
|
+
local recommendations=$(
|
|
440
|
+
echo "$ranked_skills" | jq -r '.[0:3] | map(.id) | .[]' | jq -Rs 'split("\n") | map(select(length > 0))'
|
|
441
|
+
)
|
|
442
|
+
|
|
443
|
+
# Construir output final
|
|
444
|
+
cat <<EOF | jq -c '.'
|
|
445
|
+
{
|
|
446
|
+
"timestamp": "$(date -u +%Y-%m-%dT%H:%M:%SZ)",
|
|
447
|
+
"keywords_detected": $keywords,
|
|
448
|
+
"discovery": {
|
|
449
|
+
"elsabro": {
|
|
450
|
+
"count": $elsabro_count,
|
|
451
|
+
"skills": $elsabro_skills
|
|
452
|
+
},
|
|
453
|
+
"external": {
|
|
454
|
+
"count": $external_count,
|
|
455
|
+
"skills": $external_skills
|
|
456
|
+
}
|
|
457
|
+
},
|
|
458
|
+
"ranked": {
|
|
459
|
+
"count": $ranked_count,
|
|
460
|
+
"skills": $ranked_skills,
|
|
461
|
+
"recommendations": $recommendations
|
|
462
|
+
},
|
|
463
|
+
"status": "success"
|
|
464
|
+
}
|
|
465
|
+
EOF
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
# ============================================================================
|
|
469
|
+
# 7. FUNCIÓN PRINCIPAL
|
|
470
|
+
# ============================================================================
|
|
471
|
+
|
|
472
|
+
main() {
|
|
473
|
+
local task_description="${1:-}"
|
|
474
|
+
local complexity="${2:-medium}"
|
|
475
|
+
|
|
476
|
+
# Validación de entrada
|
|
477
|
+
if [ -z "$task_description" ]; then
|
|
478
|
+
cat <<EOF | jq -c '.'
|
|
479
|
+
{
|
|
480
|
+
"status": "error",
|
|
481
|
+
"message": "Task description required",
|
|
482
|
+
"timestamp": "$(date -u +%Y-%m-%dT%H:%M:%SZ)"
|
|
483
|
+
}
|
|
484
|
+
EOF
|
|
485
|
+
exit 1
|
|
486
|
+
fi
|
|
487
|
+
|
|
488
|
+
log_info "Analizando: $task_description (complejidad: $complexity)"
|
|
489
|
+
|
|
490
|
+
# ---- FASE 1: Extraer keywords ----
|
|
491
|
+
log_info "Extrayendo keywords..."
|
|
492
|
+
local keywords=$(extract_keywords "$task_description")
|
|
493
|
+
log_success "Keywords: $(echo $keywords | jq -r 'join(", ")')"
|
|
494
|
+
|
|
495
|
+
# ---- FASE 2: Buscar skills (con caché) ----
|
|
496
|
+
local keywords_str=$(echo "$keywords" | jq -r 'join(",")')
|
|
497
|
+
local cached_result=""
|
|
498
|
+
|
|
499
|
+
if cached_result=$(get_from_cache "$keywords_str"); then
|
|
500
|
+
log_success "Usando resultados del caché"
|
|
501
|
+
# Output y exit
|
|
502
|
+
echo "$cached_result"
|
|
503
|
+
exit 0
|
|
504
|
+
fi
|
|
505
|
+
|
|
506
|
+
# ---- FASE 3: Descubrir skills de múltiples fuentes ----
|
|
507
|
+
log_info "Descubriendo skills internos (ELSABRO)..."
|
|
508
|
+
local elsabro_skills=$(discover_elsabro_skills "$keywords")
|
|
509
|
+
log_success "ELSABRO skills encontrados: $(echo $elsabro_skills | jq 'length')"
|
|
510
|
+
|
|
511
|
+
log_info "Descubriendo skills externos (skills.sh)..."
|
|
512
|
+
local external_skills=$(discover_external_skills "$keywords")
|
|
513
|
+
log_success "External skills encontrados: $(echo $external_skills | jq 'length')"
|
|
514
|
+
|
|
515
|
+
# ---- FASE 4: Combinar y rankear ----
|
|
516
|
+
log_info "Combinando y rankeando skills..."
|
|
517
|
+
local all_skills=$(echo "[$elsabro_skills, $external_skills]" | jq -s 'add // []')
|
|
518
|
+
local ranked=$(validate_and_rank_skills "$all_skills")
|
|
519
|
+
log_success "Skills rankeados: $(echo $ranked | jq 'length')"
|
|
520
|
+
|
|
521
|
+
# ---- FASE 5: Formatear output ----
|
|
522
|
+
log_info "Formateando output..."
|
|
523
|
+
local output=$(format_discovery_output "$keywords" "$elsabro_skills" "$external_skills" "$ranked")
|
|
524
|
+
|
|
525
|
+
# Guardar en caché
|
|
526
|
+
save_to_cache "$keywords_str" "$output"
|
|
527
|
+
|
|
528
|
+
# Output final (STDOUT) - esto es lo que captura el Flow Engine
|
|
529
|
+
echo "$output"
|
|
530
|
+
|
|
531
|
+
log_success "Discovery completado"
|
|
532
|
+
exit 0
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
# ============================================================================
|
|
536
|
+
# EJECUCIÓN
|
|
537
|
+
# ============================================================================
|
|
538
|
+
|
|
539
|
+
main "$@"
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "elsabro",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "Sistema de desarrollo AI-powered para Claude Code -
|
|
3
|
+
"version": "3.8.0",
|
|
4
|
+
"description": "Sistema de desarrollo AI-powered para Claude Code - Orquestación avanzada con checkpointing, memoria multi-nivel y flows declarativos",
|
|
5
5
|
"bin": {
|
|
6
6
|
"elsabro": "bin/install.js"
|
|
7
7
|
},
|
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
"skills",
|
|
13
13
|
"templates",
|
|
14
14
|
"workflows",
|
|
15
|
+
"flows",
|
|
15
16
|
"references",
|
|
16
17
|
"hooks",
|
|
17
18
|
"scripts"
|