specleap-framework 2.1.0 → 2.1.5
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 +3 -3
- package/.agents/frontend.md +2 -2
- package/.agents/producto.md +9 -11
- package/.claude/hooks/spec-guard.sh +132 -0
- package/.claude/settings.json.template +15 -0
- package/.clinerules +3 -3
- package/.coderabbit.yaml +2 -4
- package/.commands/compliance.md +89 -0
- package/.commands/inicio.md +15 -15
- package/.commands/nuevo/README.md +2 -2
- package/.commands/planificar.md +1 -1
- package/.continue/rules/04-git-workflow.md +5 -5
- package/.continuerules +3 -4
- package/.cursorrules +1 -1
- package/.github/copilot-instructions.md +1 -1
- package/.specleap/i18n/en.json +177 -0
- package/.specleap/i18n/es.json +177 -0
- package/.specleap/i18n.sh +63 -0
- package/CHANGELOG.md +276 -0
- package/CLAUDE.md +54 -13
- package/README.md +169 -528
- package/SETUP.md +16 -13
- package/openspec/INDEX.md +53 -0
- package/openspec/README.md +104 -0
- package/openspec/SPEC-FORMAT.md +168 -0
- package/openspec/changes/.gitkeep +0 -0
- package/openspec/cli/COMMAND_REFERENCE.md +817 -0
- package/openspec/cli/README.md +189 -0
- package/openspec/cli/apply.sh +229 -0
- package/openspec/cli/archive.sh +240 -0
- package/openspec/cli/code-review.sh +207 -0
- package/openspec/cli/common.sh +171 -0
- package/openspec/cli/enrich.sh +188 -0
- package/openspec/cli/ff.sh +329 -0
- package/openspec/cli/new.sh +260 -0
- package/openspec/cli/openspec +82 -0
- package/openspec/cli/report.sh +244 -0
- package/openspec/cli/status.sh +178 -0
- package/openspec/cli/verify.sh +246 -0
- package/openspec/config.yaml +76 -0
- package/openspec/examples/CHANGE-SAMPLE-001-user-authentication/00-original-user-story.md +5 -0
- package/openspec/examples/CHANGE-SAMPLE-001-user-authentication/01-refined-user-story.md +106 -0
- package/openspec/examples/CHANGE-SAMPLE-001-user-authentication/README.md +333 -0
- package/openspec/examples/CHANGE-SAMPLE-001-user-authentication/design.md +461 -0
- package/openspec/examples/CHANGE-SAMPLE-001-user-authentication/proposal.md +124 -0
- package/openspec/examples/CHANGE-SAMPLE-001-user-authentication/specs/functional/F001-authentication.spec.md +399 -0
- package/openspec/examples/CHANGE-SAMPLE-001-user-authentication/specs/technical/T001-jwt-implementation.spec.md +606 -0
- package/openspec/examples/CHANGE-SAMPLE-001-user-authentication/tasks.md +433 -0
- package/openspec/examples/MERMAID_DIAGRAMS.md +481 -0
- package/openspec/examples/README.md +334 -0
- package/openspec/specs/functional/.gitkeep +0 -0
- package/openspec/specs/integration/.gitkeep +0 -0
- package/openspec/specs/security/.gitkeep +0 -0
- package/openspec/specs/technical/.gitkeep +0 -0
- package/openspec/templates/.coderabbit.yaml +259 -0
- package/openspec/templates/design.md +181 -0
- package/openspec/templates/proposal.md +79 -0
- package/openspec/templates/tasks.md +193 -0
- package/package.json +10 -5
- package/rules/git-workflow.md +3 -3
- package/rules/session-protocol.md +3 -3
- package/scripts/README.md +13 -25
- package/scripts/compliance-audit.sh +325 -0
- package/scripts/generate-contract.sh +4 -4
- package/scripts/install-skills.sh +12 -11
- package/scripts/lib/render-contrato.py +1 -1
- package/scripts/quality-baseline.sh +210 -0
- package/scripts/quality-healing.sh +241 -0
- package/setup.sh +3 -3
- package/.claude/skills/ui-ux-pro-max/scripts/__pycache__/core.cpython-314.pyc +0 -0
- package/.claude/skills/ui-ux-pro-max/scripts/__pycache__/design_system.cpython-314.pyc +0 -0
- package/.claude/skills/ui-ux-pro-max/scripts/__pycache__/search.cpython-314.pyc +0 -0
- package/scripts/lib/jira-project-utils.sh +0 -222
- package/scripts/setup-mcp.sh +0 -654
- package/scripts/test-cuestionario.sh +0 -428
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# SpecLeap — Healing telemetry log per feature
|
|
3
|
+
# Writes JSONL entries to .quality/evidence/[feature]/healing.jsonl
|
|
4
|
+
#
|
|
5
|
+
# Verbs:
|
|
6
|
+
# log append a new entry
|
|
7
|
+
# show print last N entries (default 20)
|
|
8
|
+
# stats count entries by severity, phase and action
|
|
9
|
+
|
|
10
|
+
set -e
|
|
11
|
+
|
|
12
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
13
|
+
ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
14
|
+
|
|
15
|
+
source "$ROOT/.specleap/i18n.sh"
|
|
16
|
+
|
|
17
|
+
EVIDENCE_DIR="$ROOT/.quality/evidence"
|
|
18
|
+
|
|
19
|
+
GREEN='\033[0;32m'
|
|
20
|
+
YELLOW='\033[1;33m'
|
|
21
|
+
RED='\033[0;31m'
|
|
22
|
+
NC='\033[0m'
|
|
23
|
+
|
|
24
|
+
# -----------------------------------------------------------------------------
|
|
25
|
+
# Argument parser (long options only)
|
|
26
|
+
# Sets: ARG_FEATURE, ARG_PHASE, ARG_SEVERITY, ARG_ACTION, ARG_DETAIL, ARG_FILE, ARG_LAST
|
|
27
|
+
# -----------------------------------------------------------------------------
|
|
28
|
+
parse_args() {
|
|
29
|
+
ARG_FEATURE=""
|
|
30
|
+
ARG_PHASE=""
|
|
31
|
+
ARG_SEVERITY=""
|
|
32
|
+
ARG_ACTION=""
|
|
33
|
+
ARG_DETAIL=""
|
|
34
|
+
ARG_FILE=""
|
|
35
|
+
ARG_LAST="20"
|
|
36
|
+
|
|
37
|
+
while [[ $# -gt 0 ]]; do
|
|
38
|
+
case "$1" in
|
|
39
|
+
--feature) ARG_FEATURE="$2"; shift 2 ;;
|
|
40
|
+
--phase) ARG_PHASE="$2"; shift 2 ;;
|
|
41
|
+
--severity) ARG_SEVERITY="$2"; shift 2 ;;
|
|
42
|
+
--action) ARG_ACTION="$2"; shift 2 ;;
|
|
43
|
+
--detail) ARG_DETAIL="$2"; shift 2 ;;
|
|
44
|
+
--file) ARG_FILE="$2"; shift 2 ;;
|
|
45
|
+
--last) ARG_LAST="$2"; shift 2 ;;
|
|
46
|
+
*) shift ;;
|
|
47
|
+
esac
|
|
48
|
+
done
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
# -----------------------------------------------------------------------------
|
|
52
|
+
# Resolve path to the healing.jsonl of a given feature
|
|
53
|
+
# Outputs path via stdout; returns non-zero if evidence folder missing
|
|
54
|
+
# -----------------------------------------------------------------------------
|
|
55
|
+
log_path_for() {
|
|
56
|
+
local feature="$1"
|
|
57
|
+
local dir="$EVIDENCE_DIR/$feature"
|
|
58
|
+
if [ ! -d "$dir" ]; then
|
|
59
|
+
return 1
|
|
60
|
+
fi
|
|
61
|
+
echo "$dir/healing.jsonl"
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
# -----------------------------------------------------------------------------
|
|
65
|
+
# JSON-escape a string (wraps jq)
|
|
66
|
+
# -----------------------------------------------------------------------------
|
|
67
|
+
json_escape() {
|
|
68
|
+
if command -v jq &> /dev/null; then
|
|
69
|
+
jq -Rn --arg s "$1" '$s'
|
|
70
|
+
else
|
|
71
|
+
# Fallback: crude escaping for common cases
|
|
72
|
+
local s="$1"
|
|
73
|
+
s="${s//\\/\\\\}"
|
|
74
|
+
s="${s//\"/\\\"}"
|
|
75
|
+
s="${s//$'\n'/\\n}"
|
|
76
|
+
s="${s//$'\t'/\\t}"
|
|
77
|
+
echo "\"$s\""
|
|
78
|
+
fi
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
cmd_log() {
|
|
82
|
+
parse_args "$@"
|
|
83
|
+
|
|
84
|
+
if [ -z "$ARG_FEATURE" ]; then
|
|
85
|
+
echo -e "${RED}$(t 'quality.healing.missing_feature')${NC}"
|
|
86
|
+
echo "$(t 'quality.healing.usage_log')"
|
|
87
|
+
exit 1
|
|
88
|
+
fi
|
|
89
|
+
if [ -z "$ARG_PHASE" ]; then
|
|
90
|
+
echo -e "${RED}$(t 'quality.healing.missing_phase')${NC}"
|
|
91
|
+
exit 1
|
|
92
|
+
fi
|
|
93
|
+
if [ -z "$ARG_ACTION" ]; then
|
|
94
|
+
echo -e "${RED}$(t 'quality.healing.missing_action')${NC}"
|
|
95
|
+
exit 1
|
|
96
|
+
fi
|
|
97
|
+
|
|
98
|
+
# Default severity = info
|
|
99
|
+
[ -z "$ARG_SEVERITY" ] && ARG_SEVERITY="info"
|
|
100
|
+
|
|
101
|
+
case "$ARG_SEVERITY" in
|
|
102
|
+
info|warn|error) ;;
|
|
103
|
+
*)
|
|
104
|
+
echo -e "${RED}$(t 'quality.healing.severity_invalid')${NC} $ARG_SEVERITY"
|
|
105
|
+
exit 1
|
|
106
|
+
;;
|
|
107
|
+
esac
|
|
108
|
+
|
|
109
|
+
local log_file
|
|
110
|
+
if ! log_file=$(log_path_for "$ARG_FEATURE"); then
|
|
111
|
+
echo -e "${RED}$(t 'quality.healing.no_evidence')${NC} $ARG_FEATURE"
|
|
112
|
+
echo "$(t 'quality.healing.run_new_evidence_first')"
|
|
113
|
+
exit 1
|
|
114
|
+
fi
|
|
115
|
+
|
|
116
|
+
local ts
|
|
117
|
+
ts=$(date -u +%Y-%m-%dT%H:%M:%SZ)
|
|
118
|
+
|
|
119
|
+
# Build JSON entry one line
|
|
120
|
+
local entry
|
|
121
|
+
if command -v jq &> /dev/null; then
|
|
122
|
+
entry=$(jq -cn \
|
|
123
|
+
--arg ts "$ts" \
|
|
124
|
+
--arg phase "$ARG_PHASE" \
|
|
125
|
+
--arg severity "$ARG_SEVERITY" \
|
|
126
|
+
--arg action "$ARG_ACTION" \
|
|
127
|
+
--arg detail "$ARG_DETAIL" \
|
|
128
|
+
--arg file "$ARG_FILE" \
|
|
129
|
+
'{ts: $ts, phase: $phase, severity: $severity, action: $action, detail: $detail, file: $file}
|
|
130
|
+
| if .file == "" then del(.file) else . end
|
|
131
|
+
| if .detail == "" then del(.detail) else . end')
|
|
132
|
+
else
|
|
133
|
+
# Fallback without jq
|
|
134
|
+
local json="{\"ts\":$(json_escape "$ts"),\"phase\":$(json_escape "$ARG_PHASE"),\"severity\":$(json_escape "$ARG_SEVERITY"),\"action\":$(json_escape "$ARG_ACTION")"
|
|
135
|
+
[ -n "$ARG_DETAIL" ] && json="$json,\"detail\":$(json_escape "$ARG_DETAIL")"
|
|
136
|
+
[ -n "$ARG_FILE" ] && json="$json,\"file\":$(json_escape "$ARG_FILE")"
|
|
137
|
+
json="$json}"
|
|
138
|
+
entry="$json"
|
|
139
|
+
fi
|
|
140
|
+
|
|
141
|
+
echo "$entry" >> "$log_file"
|
|
142
|
+
|
|
143
|
+
echo -e "${GREEN}$(t 'quality.healing.log_done')${NC} $log_file"
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
cmd_show() {
|
|
147
|
+
parse_args "$@"
|
|
148
|
+
|
|
149
|
+
if [ -z "$ARG_FEATURE" ]; then
|
|
150
|
+
echo -e "${RED}$(t 'quality.healing.missing_feature')${NC}"
|
|
151
|
+
echo "$(t 'quality.healing.usage_show')"
|
|
152
|
+
exit 1
|
|
153
|
+
fi
|
|
154
|
+
|
|
155
|
+
local log_file
|
|
156
|
+
if ! log_file=$(log_path_for "$ARG_FEATURE"); then
|
|
157
|
+
echo -e "${RED}$(t 'quality.healing.no_evidence')${NC} $ARG_FEATURE"
|
|
158
|
+
exit 1
|
|
159
|
+
fi
|
|
160
|
+
|
|
161
|
+
if [ ! -f "$log_file" ]; then
|
|
162
|
+
echo -e "${YELLOW}$(t 'quality.healing.no_log')${NC} $ARG_FEATURE"
|
|
163
|
+
exit 0
|
|
164
|
+
fi
|
|
165
|
+
|
|
166
|
+
local total
|
|
167
|
+
total=$(wc -l < "$log_file" | tr -d ' ')
|
|
168
|
+
|
|
169
|
+
echo "healing.jsonl — $ARG_FEATURE — $total $(t 'quality.healing.entries')"
|
|
170
|
+
echo ""
|
|
171
|
+
|
|
172
|
+
if command -v jq &> /dev/null; then
|
|
173
|
+
tail -n "$ARG_LAST" "$log_file" | jq -r '"[\(.ts)] \(.severity | ascii_upcase) \(.phase) / \(.action)\t\(.detail // "")"'
|
|
174
|
+
else
|
|
175
|
+
tail -n "$ARG_LAST" "$log_file"
|
|
176
|
+
fi
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
cmd_stats() {
|
|
180
|
+
parse_args "$@"
|
|
181
|
+
|
|
182
|
+
if [ -z "$ARG_FEATURE" ]; then
|
|
183
|
+
echo -e "${RED}$(t 'quality.healing.missing_feature')${NC}"
|
|
184
|
+
echo "$(t 'quality.healing.usage_stats')"
|
|
185
|
+
exit 1
|
|
186
|
+
fi
|
|
187
|
+
|
|
188
|
+
local log_file
|
|
189
|
+
if ! log_file=$(log_path_for "$ARG_FEATURE"); then
|
|
190
|
+
echo -e "${RED}$(t 'quality.healing.no_evidence')${NC} $ARG_FEATURE"
|
|
191
|
+
exit 1
|
|
192
|
+
fi
|
|
193
|
+
|
|
194
|
+
if [ ! -f "$log_file" ]; then
|
|
195
|
+
echo -e "${YELLOW}$(t 'quality.healing.no_log')${NC} $ARG_FEATURE"
|
|
196
|
+
exit 0
|
|
197
|
+
fi
|
|
198
|
+
|
|
199
|
+
if ! command -v jq &> /dev/null; then
|
|
200
|
+
echo -e "${RED}$(t 'quality.healing.jq_required')${NC}"
|
|
201
|
+
exit 1
|
|
202
|
+
fi
|
|
203
|
+
|
|
204
|
+
local total
|
|
205
|
+
total=$(wc -l < "$log_file" | tr -d ' ')
|
|
206
|
+
|
|
207
|
+
echo "healing.jsonl — $ARG_FEATURE"
|
|
208
|
+
echo "$(t 'quality.healing.total_entries'): $total"
|
|
209
|
+
echo ""
|
|
210
|
+
|
|
211
|
+
echo "$(t 'quality.healing.by_severity'):"
|
|
212
|
+
jq -r '.severity' "$log_file" | sort | uniq -c | sort -rn | awk '{printf " %-8s %s\n", $2, $1}'
|
|
213
|
+
echo ""
|
|
214
|
+
|
|
215
|
+
echo "$(t 'quality.healing.by_phase'):"
|
|
216
|
+
jq -r '.phase' "$log_file" | sort | uniq -c | sort -rn | awk '{printf " %-20s %s\n", $2, $1}'
|
|
217
|
+
echo ""
|
|
218
|
+
|
|
219
|
+
echo "$(t 'quality.healing.by_action'):"
|
|
220
|
+
jq -r '.action' "$log_file" | sort | uniq -c | sort -rn | awk '{printf " %-25s %s\n", $2, $1}'
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
# Dispatch
|
|
224
|
+
case "${1:-}" in
|
|
225
|
+
log)
|
|
226
|
+
shift
|
|
227
|
+
cmd_log "$@"
|
|
228
|
+
;;
|
|
229
|
+
show)
|
|
230
|
+
shift
|
|
231
|
+
cmd_show "$@"
|
|
232
|
+
;;
|
|
233
|
+
stats)
|
|
234
|
+
shift
|
|
235
|
+
cmd_stats "$@"
|
|
236
|
+
;;
|
|
237
|
+
*)
|
|
238
|
+
echo "$(t 'quality.healing.usage')"
|
|
239
|
+
exit 1
|
|
240
|
+
;;
|
|
241
|
+
esac
|
package/setup.sh
CHANGED
|
@@ -72,7 +72,7 @@ if [ "$LANG" = "es" ]; then
|
|
|
72
72
|
MSG_REQUIREMENTS="Verificando requisitos del sistema..."
|
|
73
73
|
MSG_GITHUB="Paso 2/6: Configuración GitHub"
|
|
74
74
|
MSG_ASANA="Paso 3/6: Configuración Asana (Obligatorio)"
|
|
75
|
-
MSG_SKILLS="Paso 4/6: Agent Skills (
|
|
75
|
+
MSG_SKILLS="Paso 4/6: Agent Skills (34 skills)"
|
|
76
76
|
MSG_CODERABBIT="Paso 5/6: CodeRabbit"
|
|
77
77
|
MSG_FINAL="Paso 6/6: Finalizando"
|
|
78
78
|
MSG_SUCCESS="✅ ¡Instalación completa!"
|
|
@@ -84,7 +84,7 @@ else
|
|
|
84
84
|
MSG_REQUIREMENTS="Checking system requirements..."
|
|
85
85
|
MSG_GITHUB="Step 2/6: GitHub Setup"
|
|
86
86
|
MSG_ASANA="Step 3/6: Asana Setup (Required)"
|
|
87
|
-
MSG_SKILLS="Step 4/6: Agent Skills (
|
|
87
|
+
MSG_SKILLS="Step 4/6: Agent Skills (34 skills)"
|
|
88
88
|
MSG_CODERABBIT="Step 5/6: CodeRabbit"
|
|
89
89
|
MSG_FINAL="Step 6/6: Finishing"
|
|
90
90
|
MSG_SUCCESS="✅ Installation complete!"
|
|
@@ -290,7 +290,7 @@ echo ""
|
|
|
290
290
|
sleep 1
|
|
291
291
|
|
|
292
292
|
# ============================================
|
|
293
|
-
# PASO 4: Agent Skills (20 TIER 1)
|
|
293
|
+
# PASO 4: Agent Skills (34 skills: 20 TIER 1 + 14 TIER 2)
|
|
294
294
|
# ============================================
|
|
295
295
|
|
|
296
296
|
echo -e "${CYAN}${LINE}${NC}"
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -1,222 +0,0 @@
|
|
|
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
|
-
}
|