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,130 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# SpecLeap — Instalador de Agent Skills TIER 1
|
|
3
|
+
# Versión 2.0 — Robusto y con progreso visual
|
|
4
|
+
|
|
5
|
+
set -e
|
|
6
|
+
|
|
7
|
+
GREEN='\033[0;32m'
|
|
8
|
+
RED='\033[0;31m'
|
|
9
|
+
YELLOW='\033[1;33m'
|
|
10
|
+
BLUE='\033[0;34m'
|
|
11
|
+
NC='\033[0m'
|
|
12
|
+
|
|
13
|
+
echo -e "${BLUE}━━━ Installing Agent Skills TIER 1 ━━━${NC}"
|
|
14
|
+
echo ""
|
|
15
|
+
|
|
16
|
+
# Lista de skills (repo:path:name)
|
|
17
|
+
SKILLS=(
|
|
18
|
+
# Consistencia (3) - obra/superpowers
|
|
19
|
+
"obra/superpowers:skills/verification-before-completion:verification-before-completion"
|
|
20
|
+
"obra/superpowers:skills/systematic-debugging:systematic-debugging"
|
|
21
|
+
"obra/superpowers:skills/requesting-code-review:requesting-code-review"
|
|
22
|
+
|
|
23
|
+
# Backend (7) - jeffallan/claude-skills
|
|
24
|
+
"jeffallan/claude-skills:skills/laravel-specialist:laravel-specialist"
|
|
25
|
+
"jeffallan/claude-skills:skills/api-designer:api-designer"
|
|
26
|
+
"jeffallan/claude-skills:skills/database-optimizer:database-optimizer"
|
|
27
|
+
"jeffallan/claude-skills:skills/code-reviewer:code-reviewer"
|
|
28
|
+
"jeffallan/claude-skills:skills/debugging-wizard:debugging-wizard"
|
|
29
|
+
"jeffallan/claude-skills:skills/python-pro:python-pro"
|
|
30
|
+
"jeffallan/claude-skills:skills/react-expert:react-expert"
|
|
31
|
+
|
|
32
|
+
# Diseño (4) - anthropics/skills
|
|
33
|
+
"anthropics/skills:skills/frontend-design:frontend-design"
|
|
34
|
+
"anthropics/skills:skills/skill-creator:skill-creator"
|
|
35
|
+
"anthropics/skills:skills/canvas-design:canvas-design"
|
|
36
|
+
"anthropics/skills:skills/algorithmic-art:algorithmic-art"
|
|
37
|
+
|
|
38
|
+
# DevOps (3) - jeffallan/claude-skills
|
|
39
|
+
"jeffallan/claude-skills:skills/devops-engineer:devops-engineer"
|
|
40
|
+
"jeffallan/claude-skills:skills/cloud-architect:cloud-architect"
|
|
41
|
+
"jeffallan/claude-skills:skills/architecture-designer:architecture-designer"
|
|
42
|
+
|
|
43
|
+
# Testing (2) - obra/superpowers
|
|
44
|
+
"obra/superpowers:skills/test-driven-development:test-driven-development"
|
|
45
|
+
"obra/superpowers:skills/receiving-code-review:receiving-code-review"
|
|
46
|
+
|
|
47
|
+
# Frontend (1) - jeffallan/claude-skills
|
|
48
|
+
"jeffallan/claude-skills:skills/typescript-pro:typescript-pro"
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
TOTAL=${#SKILLS[@]}
|
|
52
|
+
INSTALLED=0
|
|
53
|
+
FAILED=0
|
|
54
|
+
SKIPPED=0
|
|
55
|
+
|
|
56
|
+
SKILLS_DIR="$HOME/.skills"
|
|
57
|
+
TMP_DIR="/tmp/specleap-skills-$$"
|
|
58
|
+
|
|
59
|
+
mkdir -p "$SKILLS_DIR"
|
|
60
|
+
mkdir -p "$TMP_DIR"
|
|
61
|
+
|
|
62
|
+
# Función para limpiar temporal
|
|
63
|
+
cleanup() {
|
|
64
|
+
rm -rf "$TMP_DIR"
|
|
65
|
+
}
|
|
66
|
+
trap cleanup EXIT
|
|
67
|
+
|
|
68
|
+
echo -e "Installing to: ${GREEN}$SKILLS_DIR${NC}"
|
|
69
|
+
echo ""
|
|
70
|
+
|
|
71
|
+
for i in "${!SKILLS[@]}"; do
|
|
72
|
+
IFS=':' read -r repo path name <<< "${SKILLS[$i]}"
|
|
73
|
+
num=$((i + 1))
|
|
74
|
+
|
|
75
|
+
printf " [%2d/%2d] %-40s " "$num" "$TOTAL" "$name"
|
|
76
|
+
|
|
77
|
+
# Skip si ya existe
|
|
78
|
+
if [ -d "$SKILLS_DIR/$name" ]; then
|
|
79
|
+
echo -e "${YELLOW}SKIP${NC} (already installed)"
|
|
80
|
+
((SKIPPED++))
|
|
81
|
+
continue
|
|
82
|
+
fi
|
|
83
|
+
|
|
84
|
+
# Clonar repo si no existe
|
|
85
|
+
repo_dir="$TMP_DIR/$(echo $repo | tr '/' '_')"
|
|
86
|
+
if [ ! -d "$repo_dir" ]; then
|
|
87
|
+
if ! git clone -q "https://github.com/$repo.git" "$repo_dir" 2>/dev/null; then
|
|
88
|
+
echo -e "${RED}FAIL${NC} (repo not accessible)"
|
|
89
|
+
((FAILED++))
|
|
90
|
+
continue
|
|
91
|
+
fi
|
|
92
|
+
fi
|
|
93
|
+
|
|
94
|
+
# Verificar SKILL.md
|
|
95
|
+
if [ ! -f "$repo_dir/$path/SKILL.md" ]; then
|
|
96
|
+
echo -e "${RED}FAIL${NC} (SKILL.md not found)"
|
|
97
|
+
((FAILED++))
|
|
98
|
+
continue
|
|
99
|
+
fi
|
|
100
|
+
|
|
101
|
+
# Copiar skill
|
|
102
|
+
cp -r "$repo_dir/$path" "$SKILLS_DIR/$name"
|
|
103
|
+
echo -e "${GREEN}OK${NC}"
|
|
104
|
+
((INSTALLED++))
|
|
105
|
+
done
|
|
106
|
+
|
|
107
|
+
echo ""
|
|
108
|
+
echo -e "${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
|
109
|
+
echo ""
|
|
110
|
+
echo -e "${GREEN}✅ Installed:${NC} $INSTALLED"
|
|
111
|
+
if [ $SKIPPED -gt 0 ]; then
|
|
112
|
+
echo -e "${YELLOW}⚠️ Skipped:${NC} $SKIPPED (already existed)"
|
|
113
|
+
fi
|
|
114
|
+
if [ $FAILED -gt 0 ]; then
|
|
115
|
+
echo -e "${RED}❌ Failed:${NC} $FAILED"
|
|
116
|
+
fi
|
|
117
|
+
echo ""
|
|
118
|
+
|
|
119
|
+
if [ $INSTALLED -eq 0 ] && [ $SKIPPED -eq $TOTAL ]; then
|
|
120
|
+
echo -e "${GREEN}All skills already installed!${NC}"
|
|
121
|
+
exit 0
|
|
122
|
+
fi
|
|
123
|
+
|
|
124
|
+
if [ $FAILED -gt 0 ]; then
|
|
125
|
+
echo -e "${YELLOW}Warning: Some skills failed to install${NC}"
|
|
126
|
+
echo "This may affect code quality. Check network connection and try again."
|
|
127
|
+
exit 1
|
|
128
|
+
fi
|
|
129
|
+
|
|
130
|
+
echo -e "${GREEN}Agent Skills installation complete!${NC}"
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
|
|
3
|
+
# SpecLeap — Utilidades para Asana API
|
|
4
|
+
# Funciones helper para interactuar con Asana
|
|
5
|
+
|
|
6
|
+
# Verificar token
|
|
7
|
+
asana_check_auth() {
|
|
8
|
+
if [[ -z "${ASANA_ACCESS_TOKEN:-}" ]]; then
|
|
9
|
+
echo "❌ Error: ASANA_ACCESS_TOKEN no configurado" >&2
|
|
10
|
+
return 1
|
|
11
|
+
fi
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
# Obtener workspace por defecto
|
|
15
|
+
asana_get_default_workspace() {
|
|
16
|
+
asana_check_auth || return 1
|
|
17
|
+
|
|
18
|
+
local response=$(curl -s \
|
|
19
|
+
-H "Authorization: Bearer $ASANA_ACCESS_TOKEN" \
|
|
20
|
+
"https://app.asana.com/api/1.0/users/me")
|
|
21
|
+
|
|
22
|
+
echo "$response" | jq -r '.data.workspaces[0].gid'
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
# Crear proyecto
|
|
26
|
+
# Uso: asana_create_project "Nombre del Proyecto"
|
|
27
|
+
asana_create_project() {
|
|
28
|
+
local project_name="$1"
|
|
29
|
+
asana_check_auth || return 1
|
|
30
|
+
|
|
31
|
+
if [[ -z "${ASANA_WORKSPACE_GID:-}" ]]; then
|
|
32
|
+
echo "❌ Error: ASANA_WORKSPACE_GID no configurado" >&2
|
|
33
|
+
return 1
|
|
34
|
+
fi
|
|
35
|
+
|
|
36
|
+
local payload=$(jq -n \
|
|
37
|
+
--arg name "$project_name" \
|
|
38
|
+
--arg workspace "$ASANA_WORKSPACE_GID" \
|
|
39
|
+
'{
|
|
40
|
+
data: {
|
|
41
|
+
name: $name,
|
|
42
|
+
workspace: $workspace,
|
|
43
|
+
notes: "Proyecto generado por SpecLeap",
|
|
44
|
+
color: "dark-blue",
|
|
45
|
+
layout: "list"
|
|
46
|
+
}
|
|
47
|
+
}')
|
|
48
|
+
|
|
49
|
+
local response=$(curl -s \
|
|
50
|
+
-X POST \
|
|
51
|
+
-H "Authorization: Bearer $ASANA_ACCESS_TOKEN" \
|
|
52
|
+
-H "Content-Type: application/json" \
|
|
53
|
+
-d "$payload" \
|
|
54
|
+
"https://app.asana.com/api/1.0/projects")
|
|
55
|
+
|
|
56
|
+
local project_gid=$(echo "$response" | jq -r '.data.gid')
|
|
57
|
+
|
|
58
|
+
if [[ "$project_gid" == "null" || -z "$project_gid" ]]; then
|
|
59
|
+
echo "❌ Error al crear proyecto: $(echo "$response" | jq -r '.errors[0].message')" >&2
|
|
60
|
+
return 1
|
|
61
|
+
fi
|
|
62
|
+
|
|
63
|
+
echo "$project_gid"
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
# Crear sección en un proyecto
|
|
67
|
+
# Uso: asana_create_section "PROJECT_GID" "Nombre Sección"
|
|
68
|
+
asana_create_section() {
|
|
69
|
+
local project_gid="$1"
|
|
70
|
+
local section_name="$2"
|
|
71
|
+
asana_check_auth || return 1
|
|
72
|
+
|
|
73
|
+
local payload=$(jq -n \
|
|
74
|
+
--arg name "$section_name" \
|
|
75
|
+
'{
|
|
76
|
+
data: {
|
|
77
|
+
name: $name
|
|
78
|
+
}
|
|
79
|
+
}')
|
|
80
|
+
|
|
81
|
+
local response=$(curl -s \
|
|
82
|
+
-X POST \
|
|
83
|
+
-H "Authorization: Bearer $ASANA_ACCESS_TOKEN" \
|
|
84
|
+
-H "Content-Type: application/json" \
|
|
85
|
+
-d "$payload" \
|
|
86
|
+
"https://app.asana.com/api/1.0/projects/$project_gid/sections")
|
|
87
|
+
|
|
88
|
+
local section_gid=$(echo "$response" | jq -r '.data.gid')
|
|
89
|
+
|
|
90
|
+
if [[ "$section_gid" == "null" || -z "$section_gid" ]]; then
|
|
91
|
+
echo "❌ Error al crear sección: $(echo "$response" | jq -r '.errors[0].message')" >&2
|
|
92
|
+
return 1
|
|
93
|
+
fi
|
|
94
|
+
|
|
95
|
+
echo "$section_gid"
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
# Crear tarea
|
|
99
|
+
# Uso: asana_create_task "PROJECT_GID" "Nombre Tarea" "SECTION_GID" POINTS
|
|
100
|
+
asana_create_task() {
|
|
101
|
+
local project_gid="$1"
|
|
102
|
+
local task_name="$2"
|
|
103
|
+
local section_gid="${3:-}"
|
|
104
|
+
local points="${4:-3}"
|
|
105
|
+
asana_check_auth || return 1
|
|
106
|
+
|
|
107
|
+
local payload=$(jq -n \
|
|
108
|
+
--arg name "$task_name" \
|
|
109
|
+
--arg project "$project_gid" \
|
|
110
|
+
--arg points "$points" \
|
|
111
|
+
'{
|
|
112
|
+
data: {
|
|
113
|
+
name: $name,
|
|
114
|
+
projects: [$project],
|
|
115
|
+
notes: ("Story Points: " + $points)
|
|
116
|
+
}
|
|
117
|
+
}')
|
|
118
|
+
|
|
119
|
+
local response=$(curl -s \
|
|
120
|
+
-X POST \
|
|
121
|
+
-H "Authorization: Bearer $ASANA_ACCESS_TOKEN" \
|
|
122
|
+
-H "Content-Type: application/json" \
|
|
123
|
+
-d "$payload" \
|
|
124
|
+
"https://app.asana.com/api/1.0/tasks")
|
|
125
|
+
|
|
126
|
+
local task_gid=$(echo "$response" | jq -r '.data.gid')
|
|
127
|
+
|
|
128
|
+
if [[ "$task_gid" == "null" || -z "$task_gid" ]]; then
|
|
129
|
+
echo "❌ Error al crear tarea: $(echo "$response" | jq -r '.errors[0].message')" >&2
|
|
130
|
+
return 1
|
|
131
|
+
fi
|
|
132
|
+
|
|
133
|
+
# Si se especificó sección, mover la tarea ahí
|
|
134
|
+
if [[ -n "$section_gid" ]]; then
|
|
135
|
+
curl -s \
|
|
136
|
+
-X POST \
|
|
137
|
+
-H "Authorization: Bearer $ASANA_ACCESS_TOKEN" \
|
|
138
|
+
-H "Content-Type: application/json" \
|
|
139
|
+
-d "{\"data\": {\"task\": \"$task_gid\"}}" \
|
|
140
|
+
"https://app.asana.com/api/1.0/sections/$section_gid/addTask" \
|
|
141
|
+
> /dev/null
|
|
142
|
+
fi
|
|
143
|
+
|
|
144
|
+
echo "$task_gid"
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
# Listar proyectos
|
|
148
|
+
asana_list_projects() {
|
|
149
|
+
asana_check_auth || return 1
|
|
150
|
+
|
|
151
|
+
if [[ -z "${ASANA_WORKSPACE_GID:-}" ]]; then
|
|
152
|
+
echo "❌ Error: ASANA_WORKSPACE_GID no configurado" >&2
|
|
153
|
+
return 1
|
|
154
|
+
fi
|
|
155
|
+
|
|
156
|
+
curl -s \
|
|
157
|
+
-H "Authorization: Bearer $ASANA_ACCESS_TOKEN" \
|
|
158
|
+
"https://app.asana.com/api/1.0/projects?workspace=$ASANA_WORKSPACE_GID" \
|
|
159
|
+
| jq -r '.data[] | "\(.gid)\t\(.name)"'
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
# Buscar proyecto por nombre
|
|
163
|
+
# Uso: asana_find_project "Nombre"
|
|
164
|
+
asana_find_project() {
|
|
165
|
+
local project_name="$1"
|
|
166
|
+
asana_list_projects | grep -i "$project_name" | head -1 | cut -f1
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
# Listar secciones de un proyecto
|
|
170
|
+
# Uso: asana_list_sections "PROJECT_GID"
|
|
171
|
+
asana_list_sections() {
|
|
172
|
+
local project_gid="$1"
|
|
173
|
+
asana_check_auth || return 1
|
|
174
|
+
|
|
175
|
+
curl -s \
|
|
176
|
+
-H "Authorization: Bearer $ASANA_ACCESS_TOKEN" \
|
|
177
|
+
"https://app.asana.com/api/1.0/projects/$project_gid/sections" \
|
|
178
|
+
| jq -r '.data[] | "\(.gid)\t\(.name)"'
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
# Listar tareas de un proyecto
|
|
182
|
+
# Uso: asana_list_tasks "PROJECT_GID"
|
|
183
|
+
asana_list_tasks() {
|
|
184
|
+
local project_gid="$1"
|
|
185
|
+
asana_check_auth || return 1
|
|
186
|
+
|
|
187
|
+
curl -s \
|
|
188
|
+
-H "Authorization: Bearer $ASANA_ACCESS_TOKEN" \
|
|
189
|
+
"https://app.asana.com/api/1.0/projects/$project_gid/tasks" \
|
|
190
|
+
| jq -r '.data[] | "\(.gid)\t\(.name)"'
|
|
191
|
+
}
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
|
|
3
|
+
# SpecLeap — Utilidades para gestión de proyectos Jira
|
|
4
|
+
# Funciones para crear/verificar proyectos Jira independientes
|
|
5
|
+
|
|
6
|
+
set -euo pipefail
|
|
7
|
+
|
|
8
|
+
# Colores
|
|
9
|
+
RED='\033[0;31m'
|
|
10
|
+
GREEN='\033[0;32m'
|
|
11
|
+
YELLOW='\033[1;33m'
|
|
12
|
+
CYAN='\033[0;36m'
|
|
13
|
+
RESET='\033[0m'
|
|
14
|
+
|
|
15
|
+
print_success() {
|
|
16
|
+
echo -e "${GREEN}✅ $1${RESET}" >&2
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
print_error() {
|
|
20
|
+
echo -e "${RED}❌ Error: $1${RESET}" >&2
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
print_warning() {
|
|
24
|
+
echo -e "${YELLOW}⚠️ $1${RESET}" >&2
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
print_info() {
|
|
28
|
+
echo -e "${CYAN}ℹ️ $1${RESET}" >&2
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
# ============================================================================
|
|
32
|
+
# GENERACIÓN DE PROJECT KEY
|
|
33
|
+
# ============================================================================
|
|
34
|
+
|
|
35
|
+
generate_project_key() {
|
|
36
|
+
local project_name="$1"
|
|
37
|
+
|
|
38
|
+
# Convertir nombre a key válida de Jira:
|
|
39
|
+
# - Solo mayúsculas
|
|
40
|
+
# - Sin guiones ni espacios (reemplazar con nada)
|
|
41
|
+
# - Máximo 10 caracteres
|
|
42
|
+
# - Mínimo 2 caracteres
|
|
43
|
+
|
|
44
|
+
# Ejemplos:
|
|
45
|
+
# "casa-de-peli" → "CASADEPELI" → "CASADEPEL" (10 chars)
|
|
46
|
+
# "mi-app" → "MIAPP" (5 chars)
|
|
47
|
+
# "e-commerce" → "ECOMMERCE" → "ECOMMERCE" (9 chars)
|
|
48
|
+
|
|
49
|
+
local key=$(echo "$project_name" | tr '[:lower:]' '[:upper:]' | tr -d '-_ ' | head -c 10)
|
|
50
|
+
|
|
51
|
+
# Validar longitud mínima
|
|
52
|
+
if [[ ${#key} -lt 2 ]]; then
|
|
53
|
+
print_error "Project key muy corto: $key (mínimo 2 caracteres)"
|
|
54
|
+
return 1
|
|
55
|
+
fi
|
|
56
|
+
|
|
57
|
+
echo "$key"
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
# ============================================================================
|
|
61
|
+
# VERIFICACIÓN DE PROYECTO JIRA
|
|
62
|
+
# ============================================================================
|
|
63
|
+
|
|
64
|
+
jira_project_exists() {
|
|
65
|
+
local project_key="$1"
|
|
66
|
+
|
|
67
|
+
# Intentar obtener info del proyecto vía MCP Jira
|
|
68
|
+
# Si el proyecto no existe, el comando fallará
|
|
69
|
+
|
|
70
|
+
# Método 1: Usar MCP Jira (si está configurado)
|
|
71
|
+
if command -v mcp-jira &> /dev/null; then
|
|
72
|
+
if mcp-jira get-project "$project_key" &> /dev/null; then
|
|
73
|
+
return 0 # Proyecto existe
|
|
74
|
+
else
|
|
75
|
+
return 1 # Proyecto no existe
|
|
76
|
+
fi
|
|
77
|
+
fi
|
|
78
|
+
|
|
79
|
+
# Método 2: Usar Jira CLI (si está instalado)
|
|
80
|
+
if command -v jira &> /dev/null; then
|
|
81
|
+
if jira project view "$project_key" &> /dev/null 2>&1; then
|
|
82
|
+
return 0
|
|
83
|
+
else
|
|
84
|
+
return 1
|
|
85
|
+
fi
|
|
86
|
+
fi
|
|
87
|
+
|
|
88
|
+
# Método 3: Verificación manual (sin herramientas)
|
|
89
|
+
print_warning "No se puede verificar proyecto automáticamente (MCP Jira no encontrado)"
|
|
90
|
+
print_info "Por favor verifica manualmente si el proyecto $project_key existe en Jira"
|
|
91
|
+
|
|
92
|
+
# Preguntar al usuario
|
|
93
|
+
read -p "¿El proyecto $project_key existe en Jira? [s/N]: " -n 1 -r >&2
|
|
94
|
+
echo >&2
|
|
95
|
+
|
|
96
|
+
if [[ $REPLY =~ ^[Ss]$ ]]; then
|
|
97
|
+
return 0
|
|
98
|
+
else
|
|
99
|
+
return 1
|
|
100
|
+
fi
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
# ============================================================================
|
|
104
|
+
# CREACIÓN DE PROYECTO JIRA
|
|
105
|
+
# ============================================================================
|
|
106
|
+
|
|
107
|
+
create_jira_project() {
|
|
108
|
+
local project_key="$1"
|
|
109
|
+
local project_name="$2"
|
|
110
|
+
local project_description="$3"
|
|
111
|
+
local lead_email="${4:-}" # Opcional
|
|
112
|
+
|
|
113
|
+
print_info "Intentando crear proyecto Jira: $project_key ($project_name)"
|
|
114
|
+
|
|
115
|
+
# Método 1: Usar MCP Jira (si está configurado)
|
|
116
|
+
if command -v mcp-jira &> /dev/null; then
|
|
117
|
+
print_info "Usando MCP Jira para crear proyecto..."
|
|
118
|
+
|
|
119
|
+
if mcp-jira create-project \
|
|
120
|
+
--key "$project_key" \
|
|
121
|
+
--name "$project_name" \
|
|
122
|
+
--description "$project_description" \
|
|
123
|
+
--type scrum \
|
|
124
|
+
${lead_email:+--lead "$lead_email"} 2>&1; then
|
|
125
|
+
|
|
126
|
+
print_success "Proyecto $project_key creado exitosamente"
|
|
127
|
+
return 0
|
|
128
|
+
else
|
|
129
|
+
print_error "Fallo al crear proyecto con MCP Jira"
|
|
130
|
+
return 1
|
|
131
|
+
fi
|
|
132
|
+
fi
|
|
133
|
+
|
|
134
|
+
# Método 2: Usar Jira CLI (si está instalado)
|
|
135
|
+
if command -v jira &> /dev/null; then
|
|
136
|
+
print_info "Usando Jira CLI para crear proyecto..."
|
|
137
|
+
|
|
138
|
+
if jira project create \
|
|
139
|
+
--key "$project_key" \
|
|
140
|
+
--name "$project_name" \
|
|
141
|
+
--template "Scrum software development" \
|
|
142
|
+
${lead_email:+--lead "$lead_email"} 2>&1; then
|
|
143
|
+
|
|
144
|
+
print_success "Proyecto $project_key creado exitosamente"
|
|
145
|
+
return 0
|
|
146
|
+
else
|
|
147
|
+
print_error "Fallo al crear proyecto con Jira CLI"
|
|
148
|
+
return 1
|
|
149
|
+
fi
|
|
150
|
+
fi
|
|
151
|
+
|
|
152
|
+
# Método 3: Instrucciones manuales (sin herramientas)
|
|
153
|
+
print_warning "No se puede crear proyecto automáticamente (herramientas no encontradas)"
|
|
154
|
+
print_info ""
|
|
155
|
+
print_info "Por favor crea el proyecto manualmente en Jira:"
|
|
156
|
+
print_info ""
|
|
157
|
+
print_info " 1. Ve a Jira → Proyectos → Crear proyecto"
|
|
158
|
+
print_info " 2. Selecciona: Scrum software development"
|
|
159
|
+
print_info " 3. Clave del proyecto: $project_key"
|
|
160
|
+
print_info " 4. Nombre del proyecto: $project_name"
|
|
161
|
+
print_info " 5. Descripción: $project_description"
|
|
162
|
+
print_info ""
|
|
163
|
+
|
|
164
|
+
read -p "¿Ya creaste el proyecto $project_key en Jira? [s/N]: " -n 1 -r >&2
|
|
165
|
+
echo >&2
|
|
166
|
+
|
|
167
|
+
if [[ $REPLY =~ ^[Ss]$ ]]; then
|
|
168
|
+
print_success "Proyecto $project_key confirmado"
|
|
169
|
+
return 0
|
|
170
|
+
else
|
|
171
|
+
print_error "Operación cancelada. Crea el proyecto en Jira y vuelve a ejecutar."
|
|
172
|
+
return 1
|
|
173
|
+
fi
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
# ============================================================================
|
|
177
|
+
# FLUJO COMPLETO: VERIFICAR O CREAR PROYECTO
|
|
178
|
+
# ============================================================================
|
|
179
|
+
|
|
180
|
+
ensure_jira_project_exists() {
|
|
181
|
+
local project_key="$1"
|
|
182
|
+
local project_name="$2"
|
|
183
|
+
local project_description="$3"
|
|
184
|
+
local lead_email="${4:-}" # Opcional
|
|
185
|
+
|
|
186
|
+
print_info "Verificando proyecto Jira: $project_key"
|
|
187
|
+
|
|
188
|
+
if jira_project_exists "$project_key"; then
|
|
189
|
+
print_success "Proyecto $project_key ya existe"
|
|
190
|
+
return 0
|
|
191
|
+
else
|
|
192
|
+
print_warning "Proyecto $project_key no existe. Creando..."
|
|
193
|
+
|
|
194
|
+
if create_jira_project "$project_key" "$project_name" "$project_description" "$lead_email"; then
|
|
195
|
+
return 0
|
|
196
|
+
else
|
|
197
|
+
print_error "No se pudo crear el proyecto $project_key"
|
|
198
|
+
return 1
|
|
199
|
+
fi
|
|
200
|
+
fi
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
# ============================================================================
|
|
204
|
+
# VALIDACIÓN DE KEY
|
|
205
|
+
# ============================================================================
|
|
206
|
+
|
|
207
|
+
validate_project_key() {
|
|
208
|
+
local key="$1"
|
|
209
|
+
|
|
210
|
+
# Reglas de Jira para project keys:
|
|
211
|
+
# - Solo letras mayúsculas (A-Z)
|
|
212
|
+
# - Longitud: 2-10 caracteres
|
|
213
|
+
# - Debe empezar con letra
|
|
214
|
+
|
|
215
|
+
if [[ ! "$key" =~ ^[A-Z][A-Z0-9]{1,9}$ ]]; then
|
|
216
|
+
print_error "Project key inválida: $key"
|
|
217
|
+
print_error "Debe contener solo mayúsculas, 2-10 caracteres, empezar con letra"
|
|
218
|
+
return 1
|
|
219
|
+
fi
|
|
220
|
+
|
|
221
|
+
return 0
|
|
222
|
+
}
|