specleap-framework 2.0.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/.agents/backend.md +419 -0
- package/.agents/frontend.md +577 -0
- package/.agents/producto.md +516 -0
- package/.commands/adoptar.md +323 -0
- package/.commands/ayuda.md +142 -0
- package/.commands/crear-tickets.md +55 -0
- package/.commands/documentar.md +285 -0
- package/.commands/explicar.md +234 -0
- package/.commands/implementar.md +383 -0
- package/.commands/inicio.md +824 -0
- package/.commands/nuevo/README.md +292 -0
- package/.commands/nuevo/questions-base.yaml +320 -0
- package/.commands/nuevo/responses-example.yaml +53 -0
- package/.commands/planificar.md +253 -0
- package/.commands/refinar.md +306 -0
- package/LICENSE +21 -0
- package/README.md +603 -0
- package/SETUP.md +351 -0
- package/install.sh +152 -0
- package/package.json +60 -0
- package/proyectos/_template/.gitkeep +1 -0
- package/proyectos/_template/ANEXOS.md +21 -0
- package/proyectos/_template/CONTRATO.md +26 -0
- package/proyectos/_template/context/.gitkeep +1 -0
- package/rules/development-rules.md +113 -0
- package/rules/environment-protection.md +97 -0
- package/rules/git-workflow.md +142 -0
- package/rules/session-protocol.md +121 -0
- package/scripts/README.md +129 -0
- package/scripts/analyze-project.sh +826 -0
- package/scripts/create-asana-tasks.sh +133 -0
- package/scripts/detect-project-type.sh +141 -0
- package/scripts/estimate-effort.sh +290 -0
- package/scripts/generate-asana-structure.sh +262 -0
- package/scripts/generate-contract.sh +360 -0
- package/scripts/generate-contrato.sh +555 -0
- package/scripts/install-git-hooks.sh +141 -0
- package/scripts/install-skills.sh +130 -0
- package/scripts/lib/asana-utils.sh +191 -0
- package/scripts/lib/jira-project-utils.sh +222 -0
- package/scripts/lib/questions.json +831 -0
- package/scripts/lib/render-contrato.py +195 -0
- package/scripts/lib/validate.sh +325 -0
- package/scripts/parse-contrato.sh +190 -0
- package/scripts/setup-mcp.sh +654 -0
- package/scripts/test-cuestionario.sh +428 -0
- package/setup.sh +458 -0
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
|
|
3
|
+
# Cargar sistema i18n
|
|
4
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
5
|
+
source "$SCRIPT_DIR/../.specleap/i18n.sh"
|
|
6
|
+
|
|
7
|
+
# SpecLeap — Generador de estructura Asana
|
|
8
|
+
# Convierte CONTRATO.md → proyecto Asana con secciones y tareas
|
|
9
|
+
|
|
10
|
+
set -euo pipefail
|
|
11
|
+
|
|
12
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
13
|
+
PARSE_SCRIPT="$SCRIPT_DIR/parse-contrato.sh"
|
|
14
|
+
ASANA_UTILS="$SCRIPT_DIR/lib/asana-utils.sh"
|
|
15
|
+
|
|
16
|
+
# Colores
|
|
17
|
+
RED='\033[0;31m'
|
|
18
|
+
GREEN='\033[0;32m'
|
|
19
|
+
YELLOW='\033[1;33m'
|
|
20
|
+
BLUE='\033[0;34m'
|
|
21
|
+
MAGENTA='\033[0;35m'
|
|
22
|
+
CYAN='\033[0;36m'
|
|
23
|
+
BOLD='\033[1m'
|
|
24
|
+
RESET='\033[0m'
|
|
25
|
+
|
|
26
|
+
source "$PARSE_SCRIPT"
|
|
27
|
+
source "$ASANA_UTILS"
|
|
28
|
+
|
|
29
|
+
print_header() {
|
|
30
|
+
echo -e "${CYAN}${BOLD}"
|
|
31
|
+
echo "════════════════════════════════════════════════════════════════"
|
|
32
|
+
echo " $(t 'asana.header')"
|
|
33
|
+
echo "════════════════════════════════════════════════════════════════"
|
|
34
|
+
echo -e "${RESET}"
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
print_section() {
|
|
38
|
+
echo -e "\n${MAGENTA}${BOLD}━━━ $1 ━━━${RESET}\n" >&2
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
print_success() {
|
|
42
|
+
echo -e "${GREEN}✅ $1${RESET}" >&2
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
print_error() {
|
|
46
|
+
echo -e "${RED}❌ Error: $1${RESET}" >&2
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
print_warning() {
|
|
50
|
+
echo -e "${YELLOW}⚠️ $1${RESET}" >&2
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
print_info() {
|
|
54
|
+
echo -e "${CYAN}ℹ️ $1${RESET}" >&2
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
usage() {
|
|
58
|
+
cat << EOF
|
|
59
|
+
Uso: $(basename "$0") [OPCIONES]
|
|
60
|
+
|
|
61
|
+
Genera estructura completa en Asana desde CONTRATO.md
|
|
62
|
+
|
|
63
|
+
OPCIONES:
|
|
64
|
+
-p, --proyecto RUTA Ruta al proyecto (default: directorio actual)
|
|
65
|
+
-n, --nombre NOMBRE Nombre del proyecto en Asana (sobreescribe CONTRATO)
|
|
66
|
+
-d, --dry-run Modo simulación (no crea nada en Asana)
|
|
67
|
+
-h, --help Muestra esta ayuda
|
|
68
|
+
|
|
69
|
+
EJEMPLOS:
|
|
70
|
+
# Desde directorio del proyecto:
|
|
71
|
+
$(basename "$0")
|
|
72
|
+
|
|
73
|
+
# Especificar proyecto:
|
|
74
|
+
$(basename "$0") -p ~/proyectos/mi-app
|
|
75
|
+
|
|
76
|
+
# Modo simulación:
|
|
77
|
+
$(basename "$0") --dry-run
|
|
78
|
+
|
|
79
|
+
CONFIGURACIÓN:
|
|
80
|
+
Variables de entorno requeridas:
|
|
81
|
+
- ASANA_ACCESS_TOKEN: Token de API personal
|
|
82
|
+
- ASANA_WORKSPACE_GID: ID del workspace (opcional, se detecta automáticamente)
|
|
83
|
+
|
|
84
|
+
EOF
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
# Parsear argumentos
|
|
88
|
+
PROYECTO_DIR="$(pwd)"
|
|
89
|
+
NOMBRE_PROYECTO=""
|
|
90
|
+
DRY_RUN=false
|
|
91
|
+
|
|
92
|
+
while [[ $# -gt 0 ]]; do
|
|
93
|
+
case $1 in
|
|
94
|
+
-p|--proyecto)
|
|
95
|
+
PROYECTO_DIR="$2"
|
|
96
|
+
shift 2
|
|
97
|
+
;;
|
|
98
|
+
-n|--nombre)
|
|
99
|
+
NOMBRE_PROYECTO="$2"
|
|
100
|
+
shift 2
|
|
101
|
+
;;
|
|
102
|
+
-d|--dry-run)
|
|
103
|
+
DRY_RUN=true
|
|
104
|
+
shift
|
|
105
|
+
;;
|
|
106
|
+
-h|--help)
|
|
107
|
+
usage
|
|
108
|
+
exit 0
|
|
109
|
+
;;
|
|
110
|
+
*)
|
|
111
|
+
print_error "Opción desconocida: $1"
|
|
112
|
+
usage
|
|
113
|
+
exit 1
|
|
114
|
+
;;
|
|
115
|
+
esac
|
|
116
|
+
done
|
|
117
|
+
|
|
118
|
+
# Verificar token
|
|
119
|
+
if [[ -z "${ASANA_ACCESS_TOKEN:-}" ]]; then
|
|
120
|
+
print_error "ASANA_ACCESS_TOKEN no está configurado"
|
|
121
|
+
echo ""
|
|
122
|
+
echo "Configúralo en ~/.zshrc o ~/.bashrc:"
|
|
123
|
+
echo " export ASANA_ACCESS_TOKEN=\"tu-token-aqui\""
|
|
124
|
+
exit 1
|
|
125
|
+
fi
|
|
126
|
+
|
|
127
|
+
# Verificar CONTRATO.md
|
|
128
|
+
CONTRATO_PATH="$PROYECTO_DIR/CONTRATO.md"
|
|
129
|
+
if [[ ! -f "$CONTRATO_PATH" ]]; then
|
|
130
|
+
print_error "No se encontró CONTRATO.md en: $PROYECTO_DIR"
|
|
131
|
+
exit 1
|
|
132
|
+
fi
|
|
133
|
+
|
|
134
|
+
print_header
|
|
135
|
+
|
|
136
|
+
# Parsear CONTRATO.md
|
|
137
|
+
print_section "Analizando CONTRATO.md"
|
|
138
|
+
CONTRATO_JSON=$(parse_contrato "$CONTRATO_PATH")
|
|
139
|
+
|
|
140
|
+
# Extraer datos básicos
|
|
141
|
+
PROJECT_NAME=$(echo "$CONTRATO_JSON" | jq -r '.project_name // "Proyecto Sin Nombre"')
|
|
142
|
+
if [[ -n "$NOMBRE_PROYECTO" ]]; then
|
|
143
|
+
PROJECT_NAME="$NOMBRE_PROYECTO"
|
|
144
|
+
fi
|
|
145
|
+
|
|
146
|
+
print_info "Proyecto: $PROJECT_NAME"
|
|
147
|
+
|
|
148
|
+
# Obtener workspace si no está configurado
|
|
149
|
+
if [[ -z "${ASANA_WORKSPACE_GID:-}" ]]; then
|
|
150
|
+
print_info "Detectando workspace..."
|
|
151
|
+
ASANA_WORKSPACE_GID=$(asana_get_default_workspace)
|
|
152
|
+
export ASANA_WORKSPACE_GID
|
|
153
|
+
fi
|
|
154
|
+
|
|
155
|
+
print_info "Workspace: $ASANA_WORKSPACE_GID"
|
|
156
|
+
|
|
157
|
+
if [[ "$DRY_RUN" == true ]]; then
|
|
158
|
+
print_warning "Modo DRY-RUN: no se creará nada en Asana"
|
|
159
|
+
fi
|
|
160
|
+
|
|
161
|
+
# Crear proyecto en Asana
|
|
162
|
+
print_section "Creando proyecto en Asana"
|
|
163
|
+
|
|
164
|
+
if [[ "$DRY_RUN" == false ]]; then
|
|
165
|
+
PROJECT_GID=$(asana_create_project "$PROJECT_NAME")
|
|
166
|
+
print_success "Proyecto creado: $PROJECT_GID"
|
|
167
|
+
else
|
|
168
|
+
PROJECT_GID="DRY-RUN-PROJECT-123"
|
|
169
|
+
print_info "Proyecto (simulado): $PROJECT_GID"
|
|
170
|
+
fi
|
|
171
|
+
|
|
172
|
+
# Generar épicas como secciones
|
|
173
|
+
print_section "Generando estructura de secciones y tareas"
|
|
174
|
+
|
|
175
|
+
EPIC_COUNT=0
|
|
176
|
+
STORY_COUNT=0
|
|
177
|
+
|
|
178
|
+
# Épica 1: Infraestructura
|
|
179
|
+
EPIC_COUNT=$((EPIC_COUNT + 1))
|
|
180
|
+
if [[ "$DRY_RUN" == false ]]; then
|
|
181
|
+
INFRA_SECTION=$(asana_create_section "$PROJECT_GID" "🛠️ Infraestructura")
|
|
182
|
+
print_success "Sección: Infraestructura"
|
|
183
|
+
|
|
184
|
+
# Tareas de infraestructura
|
|
185
|
+
asana_create_task "$PROJECT_GID" "Setup inicial del proyecto" "$INFRA_SECTION" 3
|
|
186
|
+
asana_create_task "$PROJECT_GID" "Configurar CI/CD" "$INFRA_SECTION" 5
|
|
187
|
+
asana_create_task "$PROJECT_GID" "Configurar entornos (dev/staging/prod)" "$INFRA_SECTION" 3
|
|
188
|
+
STORY_COUNT=$((STORY_COUNT + 3))
|
|
189
|
+
else
|
|
190
|
+
print_info " 📋 Sección: Infraestructura (3 tareas)"
|
|
191
|
+
STORY_COUNT=$((STORY_COUNT + 3))
|
|
192
|
+
fi
|
|
193
|
+
|
|
194
|
+
# Épica 2: Backend
|
|
195
|
+
EPIC_COUNT=$((EPIC_COUNT + 1))
|
|
196
|
+
if [[ "$DRY_RUN" == false ]]; then
|
|
197
|
+
BACKEND_SECTION=$(asana_create_section "$PROJECT_GID" "⚙️ Backend")
|
|
198
|
+
print_success "Sección: Backend"
|
|
199
|
+
|
|
200
|
+
# Tareas de backend desde CONTRATO
|
|
201
|
+
FEATURES=$(echo "$CONTRATO_JSON" | jq -r '.features[]? // empty')
|
|
202
|
+
if [[ -n "$FEATURES" ]]; then
|
|
203
|
+
while IFS= read -r feature; do
|
|
204
|
+
asana_create_task "$PROJECT_GID" "Implementar: $feature" "$BACKEND_SECTION" 5
|
|
205
|
+
STORY_COUNT=$((STORY_COUNT + 1))
|
|
206
|
+
done <<< "$FEATURES"
|
|
207
|
+
else
|
|
208
|
+
asana_create_task "$PROJECT_GID" "Implementar API REST básica" "$BACKEND_SECTION" 8
|
|
209
|
+
asana_create_task "$PROJECT_GID" "Implementar autenticación" "$BACKEND_SECTION" 5
|
|
210
|
+
asana_create_task "$PROJECT_GID" "Implementar autorización (roles)" "$BACKEND_SECTION" 5
|
|
211
|
+
STORY_COUNT=$((STORY_COUNT + 3))
|
|
212
|
+
fi
|
|
213
|
+
else
|
|
214
|
+
print_info " 📋 Sección: Backend (~5-8 tareas)"
|
|
215
|
+
STORY_COUNT=$((STORY_COUNT + 5))
|
|
216
|
+
fi
|
|
217
|
+
|
|
218
|
+
# Épica 3: Frontend
|
|
219
|
+
EPIC_COUNT=$((EPIC_COUNT + 1))
|
|
220
|
+
if [[ "$DRY_RUN" == false ]]; then
|
|
221
|
+
FRONTEND_SECTION=$(asana_create_section "$PROJECT_GID" "🎨 Frontend")
|
|
222
|
+
print_success "Sección: Frontend"
|
|
223
|
+
|
|
224
|
+
asana_create_task "$PROJECT_GID" "Implementar sistema de diseño base" "$FRONTEND_SECTION" 8
|
|
225
|
+
asana_create_task "$PROJECT_GID" "Implementar autenticación UI" "$FRONTEND_SECTION" 5
|
|
226
|
+
asana_create_task "$PROJECT_GID" "Implementar navegación principal" "$FRONTEND_SECTION" 3
|
|
227
|
+
STORY_COUNT=$((STORY_COUNT + 3))
|
|
228
|
+
else
|
|
229
|
+
print_info " 📋 Sección: Frontend (3 tareas)"
|
|
230
|
+
STORY_COUNT=$((STORY_COUNT + 3))
|
|
231
|
+
fi
|
|
232
|
+
|
|
233
|
+
# Épica 4: Testing
|
|
234
|
+
EPIC_COUNT=$((EPIC_COUNT + 1))
|
|
235
|
+
if [[ "$DRY_RUN" == false ]]; then
|
|
236
|
+
TESTING_SECTION=$(asana_create_section "$PROJECT_GID" "🧪 Testing")
|
|
237
|
+
print_success "Sección: Testing"
|
|
238
|
+
|
|
239
|
+
asana_create_task "$PROJECT_GID" "Tests unitarios backend (>=90% coverage)" "$TESTING_SECTION" 5
|
|
240
|
+
asana_create_task "$PROJECT_GID" "Tests E2E críticos" "$TESTING_SECTION" 5
|
|
241
|
+
asana_create_task "$PROJECT_GID" "Tests de integración" "$TESTING_SECTION" 3
|
|
242
|
+
STORY_COUNT=$((STORY_COUNT + 3))
|
|
243
|
+
else
|
|
244
|
+
print_info " 📋 Sección: Testing (3 tareas)"
|
|
245
|
+
STORY_COUNT=$((STORY_COUNT + 3))
|
|
246
|
+
fi
|
|
247
|
+
|
|
248
|
+
# Resumen final
|
|
249
|
+
print_section "Resumen"
|
|
250
|
+
echo -e "${BOLD}Proyecto:${RESET} $PROJECT_NAME"
|
|
251
|
+
echo -e "${BOLD}Secciones creadas:${RESET} $EPIC_COUNT"
|
|
252
|
+
echo -e "${BOLD}Tareas creadas:${RESET} $STORY_COUNT"
|
|
253
|
+
|
|
254
|
+
if [[ "$DRY_RUN" == false ]]; then
|
|
255
|
+
echo -e "\n${GREEN}${BOLD}✅ Estructura generada exitosamente en Asana${RESET}"
|
|
256
|
+
echo -e "\n📊 Ver proyecto: https://app.asana.com/0/$PROJECT_GID"
|
|
257
|
+
else
|
|
258
|
+
echo -e "\n${YELLOW}${BOLD}ℹ️ Modo DRY-RUN: ningún cambio realizado${RESET}"
|
|
259
|
+
echo -e "Ejecuta sin --dry-run para crear la estructura real"
|
|
260
|
+
fi
|
|
261
|
+
|
|
262
|
+
echo ""
|
|
@@ -0,0 +1,360 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# generate-contract.sh
|
|
3
|
+
# Genera CONTRATO.md desde archivo de respuestas YAML
|
|
4
|
+
|
|
5
|
+
set -euo pipefail
|
|
6
|
+
|
|
7
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
8
|
+
SPECLEAP_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
9
|
+
RESPONSES_FILE="${1:-}"
|
|
10
|
+
OUTPUT_DIR="${2:-}"
|
|
11
|
+
|
|
12
|
+
if [[ -z "$RESPONSES_FILE" || ! -f "$RESPONSES_FILE" ]]; then
|
|
13
|
+
echo "❌ Error: Archivo de respuestas no encontrado"
|
|
14
|
+
echo "Uso: $0 <responses.yaml> [output_dir]"
|
|
15
|
+
exit 1
|
|
16
|
+
fi
|
|
17
|
+
|
|
18
|
+
# Colores
|
|
19
|
+
GREEN='\033[0;32m'
|
|
20
|
+
BLUE='\033[0;34m'
|
|
21
|
+
YELLOW='\033[1;33m'
|
|
22
|
+
NC='\033[0m' # No Color
|
|
23
|
+
|
|
24
|
+
echo -e "${BLUE}📝 Generando CONTRATO.md desde respuestas...${NC}"
|
|
25
|
+
echo ""
|
|
26
|
+
|
|
27
|
+
# Extraer PROJECT_NAME
|
|
28
|
+
PROJECT_NAME=$(grep "^project_name:" "$RESPONSES_FILE" | cut -d: -f2 | xargs)
|
|
29
|
+
|
|
30
|
+
if [[ -z "$PROJECT_NAME" ]]; then
|
|
31
|
+
echo "❌ Error: project_name no encontrado en respuestas"
|
|
32
|
+
exit 1
|
|
33
|
+
fi
|
|
34
|
+
|
|
35
|
+
# Determinar OUTPUT_DIR si no se especificó
|
|
36
|
+
if [[ -z "$OUTPUT_DIR" ]]; then
|
|
37
|
+
OUTPUT_DIR="$SPECLEAP_ROOT/proyectos/$PROJECT_NAME"
|
|
38
|
+
fi
|
|
39
|
+
|
|
40
|
+
# Crear directorio del proyecto
|
|
41
|
+
mkdir -p "$OUTPUT_DIR"
|
|
42
|
+
mkdir -p "$OUTPUT_DIR/context"
|
|
43
|
+
mkdir -p "$OUTPUT_DIR/memory-bank"
|
|
44
|
+
|
|
45
|
+
# Copiar template
|
|
46
|
+
TEMPLATE="$SPECLEAP_ROOT/proyectos/_template/CONTRATO.md"
|
|
47
|
+
OUTPUT_CONTRACT="$OUTPUT_DIR/CONTRATO.md"
|
|
48
|
+
|
|
49
|
+
if [[ ! -f "$TEMPLATE" ]]; then
|
|
50
|
+
echo "❌ Error: Template CONTRATO.md no encontrado en $TEMPLATE"
|
|
51
|
+
exit 1
|
|
52
|
+
fi
|
|
53
|
+
|
|
54
|
+
echo -e "${GREEN}✓${NC} Copiando template..."
|
|
55
|
+
cp "$TEMPLATE" "$OUTPUT_CONTRACT"
|
|
56
|
+
|
|
57
|
+
# ===== FUNCIÓN: Reemplazar placeholders =====
|
|
58
|
+
replace_placeholder() {
|
|
59
|
+
local key="$1"
|
|
60
|
+
local value="$2"
|
|
61
|
+
local file="$3"
|
|
62
|
+
|
|
63
|
+
# Escapar caracteres especiales en value
|
|
64
|
+
value=$(echo "$value" | sed 's/[&/\]/\\&/g')
|
|
65
|
+
|
|
66
|
+
# Reemplazar en el archivo
|
|
67
|
+
sed -i '' "s/{{ $key }}/$value/g" "$file"
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
# ===== EXTRAER VALORES DEL YAML =====
|
|
71
|
+
echo -e "${GREEN}✓${NC} Extrayendo valores..."
|
|
72
|
+
|
|
73
|
+
# Función para extraer valores del YAML
|
|
74
|
+
get_yaml_value() {
|
|
75
|
+
local key="$1"
|
|
76
|
+
grep "^$key:" "$RESPONSES_FILE" | cut -d: -f2- | xargs
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
# Valores básicos
|
|
80
|
+
PROJECT_DISPLAY_NAME=$(get_yaml_value "project_display_name")
|
|
81
|
+
PROJECT_OBJECTIVE=$(get_yaml_value "project_objective")
|
|
82
|
+
PROBLEM_SOLVED=$(get_yaml_value "problem_solved")
|
|
83
|
+
TARGET_AUDIENCE=$(get_yaml_value "target_audience")
|
|
84
|
+
COMPETITORS=$(get_yaml_value "competitors")
|
|
85
|
+
BACKEND_FRAMEWORK=$(get_yaml_value "backend_framework")
|
|
86
|
+
FRONTEND_FRAMEWORK=$(get_yaml_value "frontend_framework")
|
|
87
|
+
DATABASE=$(get_yaml_value "database")
|
|
88
|
+
HOSTING=$(get_yaml_value "hosting")
|
|
89
|
+
VISUAL_STYLE=$(get_yaml_value "visual_style")
|
|
90
|
+
TIME_LIMIT=$(get_yaml_value "time_limit")
|
|
91
|
+
|
|
92
|
+
# Fecha actual
|
|
93
|
+
CREATED_AT=$(date +%Y-%m-%d)
|
|
94
|
+
|
|
95
|
+
# ===== GENERAR FRONTMATTER YAML =====
|
|
96
|
+
echo -e "${GREEN}✓${NC} Generando frontmatter..."
|
|
97
|
+
|
|
98
|
+
# Crear archivo temporal con el frontmatter
|
|
99
|
+
TEMP_FRONTMATTER=$(mktemp)
|
|
100
|
+
|
|
101
|
+
cat > "$TEMP_FRONTMATTER" << EOF
|
|
102
|
+
---
|
|
103
|
+
# ===== METADATA (Parseable por scripts) =====
|
|
104
|
+
project:
|
|
105
|
+
name: "$PROJECT_NAME"
|
|
106
|
+
display_name: "$PROJECT_DISPLAY_NAME"
|
|
107
|
+
created_at: "$CREATED_AT"
|
|
108
|
+
status: draft
|
|
109
|
+
version: "1.0"
|
|
110
|
+
responsible: ""
|
|
111
|
+
|
|
112
|
+
identity:
|
|
113
|
+
objective: "$PROJECT_OBJECTIVE"
|
|
114
|
+
problem_solved: "$PROBLEM_SOLVED"
|
|
115
|
+
target_audience: "$TARGET_AUDIENCE"
|
|
116
|
+
competitors: "$COMPETITORS"
|
|
117
|
+
|
|
118
|
+
stack:
|
|
119
|
+
backend:
|
|
120
|
+
framework: "$BACKEND_FRAMEWORK"
|
|
121
|
+
language: ""
|
|
122
|
+
version: ""
|
|
123
|
+
frontend:
|
|
124
|
+
framework: "$FRONTEND_FRAMEWORK"
|
|
125
|
+
language: ""
|
|
126
|
+
build_tool: ""
|
|
127
|
+
ui_library: ""
|
|
128
|
+
database:
|
|
129
|
+
engine: "$DATABASE"
|
|
130
|
+
version: ""
|
|
131
|
+
devops:
|
|
132
|
+
hosting: "$HOSTING"
|
|
133
|
+
ci_cd: ""
|
|
134
|
+
containers: ""
|
|
135
|
+
|
|
136
|
+
features:
|
|
137
|
+
core: []
|
|
138
|
+
secondary: []
|
|
139
|
+
auth:
|
|
140
|
+
enabled: false
|
|
141
|
+
methods: []
|
|
142
|
+
two_factor: false
|
|
143
|
+
admin_panel:
|
|
144
|
+
enabled: false
|
|
145
|
+
level: ""
|
|
146
|
+
file_uploads:
|
|
147
|
+
enabled: false
|
|
148
|
+
storage: ""
|
|
149
|
+
payments:
|
|
150
|
+
enabled: false
|
|
151
|
+
providers: []
|
|
152
|
+
notifications:
|
|
153
|
+
email: false
|
|
154
|
+
push: false
|
|
155
|
+
in_app: false
|
|
156
|
+
|
|
157
|
+
users:
|
|
158
|
+
registration: false
|
|
159
|
+
roles: []
|
|
160
|
+
|
|
161
|
+
design:
|
|
162
|
+
color_palette: ""
|
|
163
|
+
primary_color: ""
|
|
164
|
+
secondary_color: ""
|
|
165
|
+
accent_color: ""
|
|
166
|
+
visual_style: "$VISUAL_STYLE"
|
|
167
|
+
dark_mode: false
|
|
168
|
+
responsive: true
|
|
169
|
+
|
|
170
|
+
architecture:
|
|
171
|
+
pattern: "mvc"
|
|
172
|
+
separation: "monolith"
|
|
173
|
+
|
|
174
|
+
deployment:
|
|
175
|
+
ssl: true
|
|
176
|
+
custom_domain: ""
|
|
177
|
+
environments: []
|
|
178
|
+
|
|
179
|
+
security:
|
|
180
|
+
level: "basic"
|
|
181
|
+
gdpr_compliant: false
|
|
182
|
+
sensitive_data: false
|
|
183
|
+
|
|
184
|
+
performance:
|
|
185
|
+
load_time_target: ""
|
|
186
|
+
api_response_target: ""
|
|
187
|
+
concurrent_users: 0
|
|
188
|
+
|
|
189
|
+
testing:
|
|
190
|
+
unit: false
|
|
191
|
+
integration: false
|
|
192
|
+
e2e: false
|
|
193
|
+
coverage_target: 0
|
|
194
|
+
|
|
195
|
+
constraints:
|
|
196
|
+
time_limit: "$TIME_LIMIT"
|
|
197
|
+
budget_limit: ""
|
|
198
|
+
out_of_scope: []
|
|
199
|
+
|
|
200
|
+
jira:
|
|
201
|
+
project_key: ""
|
|
202
|
+
epic_count: 0
|
|
203
|
+
story_count: 0
|
|
204
|
+
synced_at: ""
|
|
205
|
+
---
|
|
206
|
+
EOF
|
|
207
|
+
|
|
208
|
+
# Reemplazar el frontmatter en el CONTRATO
|
|
209
|
+
# (Eliminar frontmatter del template y agregar el nuevo)
|
|
210
|
+
sed -i '' '1,/^---$/d' "$OUTPUT_CONTRACT" # Eliminar primera sección ---
|
|
211
|
+
sed -i '' '1,/^---$/d' "$OUTPUT_CONTRACT" # Eliminar segunda sección ---
|
|
212
|
+
|
|
213
|
+
# Agregar nuevo frontmatter al inicio
|
|
214
|
+
cat "$TEMP_FRONTMATTER" "$OUTPUT_CONTRACT" > "${OUTPUT_CONTRACT}.tmp"
|
|
215
|
+
mv "${OUTPUT_CONTRACT}.tmp" "$OUTPUT_CONTRACT"
|
|
216
|
+
|
|
217
|
+
rm "$TEMP_FRONTMATTER"
|
|
218
|
+
|
|
219
|
+
# ===== REEMPLAZAR PLACEHOLDERS EN MARKDOWN =====
|
|
220
|
+
echo -e "${GREEN}✓${NC} Reemplazando placeholders..."
|
|
221
|
+
|
|
222
|
+
# Reemplazos básicos (los que no están en frontmatter)
|
|
223
|
+
sed -i '' "s/{{ project.name }}/$PROJECT_NAME/g" "$OUTPUT_CONTRACT"
|
|
224
|
+
sed -i '' "s/{{ project.display_name }}/$PROJECT_DISPLAY_NAME/g" "$OUTPUT_CONTRACT"
|
|
225
|
+
sed -i '' "s/{{ project.created_at }}/$CREATED_AT/g" "$OUTPUT_CONTRACT"
|
|
226
|
+
sed -i '' "s/{{ identity.objective }}/$PROJECT_OBJECTIVE/g" "$OUTPUT_CONTRACT"
|
|
227
|
+
|
|
228
|
+
# ===== GENERAR ARCHIVOS CONTEXT =====
|
|
229
|
+
echo -e "${GREEN}✓${NC} Generando archivos context..."
|
|
230
|
+
|
|
231
|
+
# brief.md
|
|
232
|
+
cat > "$OUTPUT_DIR/context/brief.md" << EOF
|
|
233
|
+
# Brief — $PROJECT_DISPLAY_NAME
|
|
234
|
+
|
|
235
|
+
**Fecha:** $CREATED_AT
|
|
236
|
+
|
|
237
|
+
## Resumen Ejecutivo
|
|
238
|
+
|
|
239
|
+
$PROJECT_OBJECTIVE
|
|
240
|
+
|
|
241
|
+
## Problema
|
|
242
|
+
|
|
243
|
+
$PROBLEM_SOLVED
|
|
244
|
+
|
|
245
|
+
## Público Objetivo
|
|
246
|
+
|
|
247
|
+
$TARGET_AUDIENCE
|
|
248
|
+
|
|
249
|
+
## Competencia / Referencias
|
|
250
|
+
|
|
251
|
+
$COMPETITORS
|
|
252
|
+
|
|
253
|
+
## Stack Tecnológico
|
|
254
|
+
|
|
255
|
+
- **Backend:** $BACKEND_FRAMEWORK
|
|
256
|
+
- **Frontend:** $FRONTEND_FRAMEWORK
|
|
257
|
+
- **Base de Datos:** $DATABASE
|
|
258
|
+
- **Hosting:** $HOSTING
|
|
259
|
+
EOF
|
|
260
|
+
|
|
261
|
+
# architecture.md (placeholder)
|
|
262
|
+
cat > "$OUTPUT_DIR/context/architecture.md" << EOF
|
|
263
|
+
# Arquitectura — $PROJECT_DISPLAY_NAME
|
|
264
|
+
|
|
265
|
+
## Patrón de Arquitectura
|
|
266
|
+
|
|
267
|
+
MVC (Model-View-Controller)
|
|
268
|
+
|
|
269
|
+
## Capas
|
|
270
|
+
|
|
271
|
+
- **Presentation:** Controllers, Views, Components
|
|
272
|
+
- **Application:** Services, Use Cases
|
|
273
|
+
- **Domain:** Models, Business Logic
|
|
274
|
+
- **Infrastructure:** Database, External APIs
|
|
275
|
+
|
|
276
|
+
## Decisiones Arquitectónicas
|
|
277
|
+
|
|
278
|
+
[Se irán documentando durante el desarrollo]
|
|
279
|
+
EOF
|
|
280
|
+
|
|
281
|
+
# tech-stack.md
|
|
282
|
+
cat > "$OUTPUT_DIR/context/tech-stack.md" << EOF
|
|
283
|
+
# Stack Tecnológico — $PROJECT_DISPLAY_NAME
|
|
284
|
+
|
|
285
|
+
## Backend
|
|
286
|
+
|
|
287
|
+
**Framework:** $BACKEND_FRAMEWORK
|
|
288
|
+
|
|
289
|
+
**Justificación:** [A definir]
|
|
290
|
+
|
|
291
|
+
## Frontend
|
|
292
|
+
|
|
293
|
+
**Framework:** $FRONTEND_FRAMEWORK
|
|
294
|
+
|
|
295
|
+
**Justificación:** [A definir]
|
|
296
|
+
|
|
297
|
+
## Base de Datos
|
|
298
|
+
|
|
299
|
+
**Motor:** $DATABASE
|
|
300
|
+
|
|
301
|
+
**Justificación:** [A definir]
|
|
302
|
+
|
|
303
|
+
## Hosting
|
|
304
|
+
|
|
305
|
+
**Proveedor:** $HOSTING
|
|
306
|
+
|
|
307
|
+
**Justificación:** [A definir]
|
|
308
|
+
EOF
|
|
309
|
+
|
|
310
|
+
# conventions.md (placeholder)
|
|
311
|
+
cat > "$OUTPUT_DIR/context/conventions.md" << EOF
|
|
312
|
+
# Convenciones — $PROJECT_DISPLAY_NAME
|
|
313
|
+
|
|
314
|
+
## Nomenclatura
|
|
315
|
+
|
|
316
|
+
[A definir según el stack]
|
|
317
|
+
|
|
318
|
+
## Estructura de Archivos
|
|
319
|
+
|
|
320
|
+
[A definir]
|
|
321
|
+
|
|
322
|
+
## Patrones de Código
|
|
323
|
+
|
|
324
|
+
[A definir]
|
|
325
|
+
EOF
|
|
326
|
+
|
|
327
|
+
# decisions.md (vacío, se llena durante desarrollo)
|
|
328
|
+
cat > "$OUTPUT_DIR/context/decisions.md" << EOF
|
|
329
|
+
# Log de Decisiones — $PROJECT_DISPLAY_NAME
|
|
330
|
+
|
|
331
|
+
## Formato
|
|
332
|
+
|
|
333
|
+
Cada decisión sigue el formato ADR (Architecture Decision Record):
|
|
334
|
+
|
|
335
|
+
### [YYYY-MM-DD] Título de la Decisión
|
|
336
|
+
|
|
337
|
+
**Estado:** Propuesta | Aceptada | Rechazada | Obsoleta
|
|
338
|
+
|
|
339
|
+
**Contexto:** ¿Por qué necesitamos tomar esta decisión?
|
|
340
|
+
|
|
341
|
+
**Decisión:** ¿Qué decidimos?
|
|
342
|
+
|
|
343
|
+
**Consecuencias:** ¿Qué implica esta decisión?
|
|
344
|
+
|
|
345
|
+
---
|
|
346
|
+
|
|
347
|
+
EOF
|
|
348
|
+
|
|
349
|
+
echo ""
|
|
350
|
+
echo -e "${GREEN}✅ CONTRATO.md generado exitosamente${NC}"
|
|
351
|
+
echo ""
|
|
352
|
+
echo -e "${BLUE}📁 Archivos creados:${NC}"
|
|
353
|
+
echo " - $OUTPUT_CONTRACT"
|
|
354
|
+
echo " - $OUTPUT_DIR/context/brief.md"
|
|
355
|
+
echo " - $OUTPUT_DIR/context/architecture.md"
|
|
356
|
+
echo " - $OUTPUT_DIR/context/tech-stack.md"
|
|
357
|
+
echo " - $OUTPUT_DIR/context/conventions.md"
|
|
358
|
+
echo " - $OUTPUT_DIR/context/decisions.md"
|
|
359
|
+
echo ""
|
|
360
|
+
echo -e "${YELLOW}Siguiente paso:${NC} Revisar y refinar el CONTRATO con /refinar"
|