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,207 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# openspec code-review — Solicitar review de CodeRabbit
|
|
3
|
+
set -euo pipefail
|
|
4
|
+
|
|
5
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
6
|
+
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
|
7
|
+
|
|
8
|
+
source "$SCRIPT_DIR/common.sh"
|
|
9
|
+
|
|
10
|
+
usage() {
|
|
11
|
+
cat <<EOF
|
|
12
|
+
openspec code-review — Solicitar review de CodeRabbit
|
|
13
|
+
|
|
14
|
+
USAGE:
|
|
15
|
+
openspec code-review <CHANGE-ID> [options]
|
|
16
|
+
|
|
17
|
+
OPTIONS:
|
|
18
|
+
--create-pr Crear Pull Request automáticamente
|
|
19
|
+
--base <branch> Branch base para PR (default: develop)
|
|
20
|
+
--draft Crear PR como draft
|
|
21
|
+
-h, --help Mostrar ayuda
|
|
22
|
+
|
|
23
|
+
DESCRIPTION:
|
|
24
|
+
Solicita code review para una propuesta:
|
|
25
|
+
1. Verifica que tests pasen (openspec verify)
|
|
26
|
+
2. Crea PR en GitHub (si --create-pr)
|
|
27
|
+
3. CodeRabbit hace review automático
|
|
28
|
+
4. Genera reporte de review
|
|
29
|
+
|
|
30
|
+
EXAMPLES:
|
|
31
|
+
openspec code-review CHANGE-001
|
|
32
|
+
openspec code-review CHANGE-002 --create-pr
|
|
33
|
+
openspec code-review CHANGE-003 --create-pr --draft
|
|
34
|
+
|
|
35
|
+
PREREQUISITES:
|
|
36
|
+
- Branch feature creado
|
|
37
|
+
- Tests pasando
|
|
38
|
+
- GitHub CLI configurado (gh)
|
|
39
|
+
- CodeRabbit activo en el repo
|
|
40
|
+
|
|
41
|
+
EOF
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
# Parse arguments
|
|
45
|
+
CHANGE_ID=""
|
|
46
|
+
CREATE_PR=false
|
|
47
|
+
BASE_BRANCH="develop"
|
|
48
|
+
DRAFT=false
|
|
49
|
+
|
|
50
|
+
while [[ $# -gt 0 ]]; do
|
|
51
|
+
case $1 in
|
|
52
|
+
-h|--help)
|
|
53
|
+
usage
|
|
54
|
+
exit 0
|
|
55
|
+
;;
|
|
56
|
+
--create-pr)
|
|
57
|
+
CREATE_PR=true
|
|
58
|
+
shift
|
|
59
|
+
;;
|
|
60
|
+
--base)
|
|
61
|
+
BASE_BRANCH="$2"
|
|
62
|
+
shift 2
|
|
63
|
+
;;
|
|
64
|
+
--draft)
|
|
65
|
+
DRAFT=true
|
|
66
|
+
shift
|
|
67
|
+
;;
|
|
68
|
+
CHANGE-*)
|
|
69
|
+
CHANGE_ID="$1"
|
|
70
|
+
shift
|
|
71
|
+
;;
|
|
72
|
+
*)
|
|
73
|
+
error "Argumento desconocido: $1"
|
|
74
|
+
usage
|
|
75
|
+
exit 1
|
|
76
|
+
;;
|
|
77
|
+
esac
|
|
78
|
+
done
|
|
79
|
+
|
|
80
|
+
# Validate
|
|
81
|
+
validate_project
|
|
82
|
+
|
|
83
|
+
if [[ -z "$CHANGE_ID" ]]; then
|
|
84
|
+
error "Debe proporcionar CHANGE-ID"
|
|
85
|
+
usage
|
|
86
|
+
exit 1
|
|
87
|
+
fi
|
|
88
|
+
|
|
89
|
+
# Find change directory
|
|
90
|
+
CHANGE_DIR=$(find "$PROJECT_ROOT/openspec/changes" -maxdepth 1 -type d -name "${CHANGE_ID}-*" | head -n1)
|
|
91
|
+
|
|
92
|
+
if [[ -z "$CHANGE_DIR" ]] || [[ ! -d "$CHANGE_DIR" ]]; then
|
|
93
|
+
error "Propuesta no encontrada: $CHANGE_ID"
|
|
94
|
+
exit 1
|
|
95
|
+
fi
|
|
96
|
+
|
|
97
|
+
CHANGE_NAME=$(basename "$CHANGE_DIR" | sed "s/^${CHANGE_ID}-//")
|
|
98
|
+
|
|
99
|
+
info "Solicitando code review para: $CHANGE_ID — $CHANGE_NAME"
|
|
100
|
+
|
|
101
|
+
# Run verification first
|
|
102
|
+
step "Verificando tests y specs..."
|
|
103
|
+
if ! "$SCRIPT_DIR/verify.sh" "$CHANGE_ID"; then
|
|
104
|
+
error "La verificación falló. Corrige los problemas antes de solicitar review."
|
|
105
|
+
exit 1
|
|
106
|
+
fi
|
|
107
|
+
|
|
108
|
+
# Check Git status
|
|
109
|
+
CURRENT_BRANCH=$(git_current_branch)
|
|
110
|
+
if [[ "$CURRENT_BRANCH" == "main" ]] || [[ "$CURRENT_BRANCH" == "master" ]] || [[ "$CURRENT_BRANCH" == "develop" ]]; then
|
|
111
|
+
error "No puedes crear PR desde branch principal: $CURRENT_BRANCH"
|
|
112
|
+
info "Usa: openspec apply $CHANGE_ID para crear feature branch"
|
|
113
|
+
exit 1
|
|
114
|
+
fi
|
|
115
|
+
|
|
116
|
+
# Push current branch
|
|
117
|
+
step "Pusheando branch a remoto..."
|
|
118
|
+
if ! git push -u origin "$CURRENT_BRANCH" 2>&1; then
|
|
119
|
+
# Branch may already exist
|
|
120
|
+
if ! git push origin "$CURRENT_BRANCH" 2>&1; then
|
|
121
|
+
error "No se pudo pushear branch"
|
|
122
|
+
exit 1
|
|
123
|
+
fi
|
|
124
|
+
fi
|
|
125
|
+
success "Branch pusheado: $CURRENT_BRANCH"
|
|
126
|
+
|
|
127
|
+
# Create PR if requested
|
|
128
|
+
if [[ "$CREATE_PR" == true ]]; then
|
|
129
|
+
require_command "gh" "Instalar GitHub CLI: https://cli.github.com"
|
|
130
|
+
|
|
131
|
+
step "Creando Pull Request..."
|
|
132
|
+
|
|
133
|
+
# Extract ticket id from proposal
|
|
134
|
+
TICKET_ID=$(grep -oP 'Ticket.*\|\s*\K[a-zA-Z0-9-]+-\d+' "$CHANGE_DIR/proposal.md" || echo "")
|
|
135
|
+
|
|
136
|
+
# PR title
|
|
137
|
+
PR_TITLE="$CHANGE_ID: $CHANGE_NAME"
|
|
138
|
+
if [[ -n "$TICKET_ID" ]]; then
|
|
139
|
+
PR_TITLE="$TICKET_ID — $CHANGE_NAME"
|
|
140
|
+
fi
|
|
141
|
+
|
|
142
|
+
# PR body from proposal
|
|
143
|
+
PR_BODY=$(cat <<EOF
|
|
144
|
+
## Propuesta
|
|
145
|
+
$CHANGE_ID — $CHANGE_NAME
|
|
146
|
+
|
|
147
|
+
## Descripción
|
|
148
|
+
$(grep -A 10 "## Resumen Ejecutivo" "$CHANGE_DIR/proposal.md" | tail -n +2 || echo "Ver propuesta completa en openspec/changes/$CHANGE_ID-$CHANGE_NAME/")
|
|
149
|
+
|
|
150
|
+
## Archivos Clave
|
|
151
|
+
- \`openspec/changes/$CHANGE_ID-$CHANGE_NAME/proposal.md\` — QUÉ y POR QUÉ
|
|
152
|
+
- \`openspec/changes/$CHANGE_ID-$CHANGE_NAME/design.md\` — CÓMO (diseño técnico)
|
|
153
|
+
- \`openspec/changes/$CHANGE_ID-$CHANGE_NAME/tasks.md\` — Tareas y Testing Report
|
|
154
|
+
|
|
155
|
+
## Testing Report
|
|
156
|
+
Ver \`tasks.md\` para resultados completos.
|
|
157
|
+
|
|
158
|
+
## Referencias
|
|
159
|
+
- Propuesta: openspec/changes/$CHANGE_ID-$CHANGE_NAME/
|
|
160
|
+
$(if [[ -n "$TICKET_ID" ]]; then echo "- Ticket: $TICKET_ID"; fi)
|
|
161
|
+
|
|
162
|
+
---
|
|
163
|
+
**Code Review:** CodeRabbit está activo. Revisa comentarios automáticos.
|
|
164
|
+
EOF
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
# Create PR
|
|
168
|
+
PR_ARGS=(
|
|
169
|
+
--base "$BASE_BRANCH"
|
|
170
|
+
--head "$CURRENT_BRANCH"
|
|
171
|
+
--title "$PR_TITLE"
|
|
172
|
+
--body "$PR_BODY"
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
if [[ "$DRAFT" == true ]]; then
|
|
176
|
+
PR_ARGS+=(--draft)
|
|
177
|
+
fi
|
|
178
|
+
|
|
179
|
+
PR_URL=$(gh pr create "${PR_ARGS[@]}" 2>&1)
|
|
180
|
+
|
|
181
|
+
if [[ $? -eq 0 ]]; then
|
|
182
|
+
success "Pull Request creado: $PR_URL"
|
|
183
|
+
|
|
184
|
+
# Update proposal state
|
|
185
|
+
sed -i.bak 's/Estado | in_progress/Estado | testing/' "$CHANGE_DIR/proposal.md"
|
|
186
|
+
rm -f "$CHANGE_DIR/proposal.md.bak"
|
|
187
|
+
|
|
188
|
+
git add "$CHANGE_DIR/proposal.md"
|
|
189
|
+
git commit -m "chore(openspec): PR creado para $CHANGE_ID" || true
|
|
190
|
+
git push origin "$CURRENT_BRANCH" || true
|
|
191
|
+
else
|
|
192
|
+
error "No se pudo crear PR"
|
|
193
|
+
echo "$PR_URL"
|
|
194
|
+
exit 1
|
|
195
|
+
fi
|
|
196
|
+
else
|
|
197
|
+
info "Para crear PR manualmente:"
|
|
198
|
+
info " gh pr create --base $BASE_BRANCH --head $CURRENT_BRANCH"
|
|
199
|
+
fi
|
|
200
|
+
|
|
201
|
+
success "Code review solicitado para $CHANGE_ID"
|
|
202
|
+
info ""
|
|
203
|
+
info "CodeRabbit revisará automáticamente el PR"
|
|
204
|
+
info "Revisa los comentarios y aplica cambios sugeridos"
|
|
205
|
+
info ""
|
|
206
|
+
info "Cuando el review esté aprobado:"
|
|
207
|
+
info " openspec archive $CHANGE_ID"
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Common functions for OpenSpec CLI
|
|
3
|
+
|
|
4
|
+
# Colors
|
|
5
|
+
RED='\033[0;31m'
|
|
6
|
+
GREEN='\033[0;32m'
|
|
7
|
+
YELLOW='\033[1;33m'
|
|
8
|
+
BLUE='\033[0;34m'
|
|
9
|
+
CYAN='\033[0;36m'
|
|
10
|
+
NC='\033[0m' # No Color
|
|
11
|
+
|
|
12
|
+
# Output helpers
|
|
13
|
+
info() { echo -e "${BLUE}ℹ${NC} $*"; }
|
|
14
|
+
success() { echo -e "${GREEN}✓${NC} $*"; }
|
|
15
|
+
warning() { echo -e "${YELLOW}⚠${NC} $*"; }
|
|
16
|
+
error() { echo -e "${RED}✗${NC} $*" >&2; }
|
|
17
|
+
step() { echo -e "${CYAN}→${NC} $*"; }
|
|
18
|
+
|
|
19
|
+
# Validate project structure
|
|
20
|
+
validate_project() {
|
|
21
|
+
if [[ ! -f "$PROJECT_ROOT/openspec/config.yaml" ]]; then
|
|
22
|
+
error "No se encuentra openspec/config.yaml"
|
|
23
|
+
error "Asegúrate de estar en la raíz del proyecto"
|
|
24
|
+
exit 1
|
|
25
|
+
fi
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
# Get next ID for spec/change
|
|
29
|
+
get_next_id() {
|
|
30
|
+
local type="$1" # "spec" or "change"
|
|
31
|
+
local dir=""
|
|
32
|
+
|
|
33
|
+
case $type in
|
|
34
|
+
spec)
|
|
35
|
+
dir="$PROJECT_ROOT/openspec/specs"
|
|
36
|
+
;;
|
|
37
|
+
change)
|
|
38
|
+
dir="$PROJECT_ROOT/openspec/changes"
|
|
39
|
+
;;
|
|
40
|
+
*)
|
|
41
|
+
error "Tipo desconocido: $type"
|
|
42
|
+
return 1
|
|
43
|
+
;;
|
|
44
|
+
esac
|
|
45
|
+
|
|
46
|
+
# Find highest existing ID
|
|
47
|
+
local max_id=0
|
|
48
|
+
if [[ -d "$dir" ]]; then
|
|
49
|
+
for item in "$dir"/*; do
|
|
50
|
+
if [[ -d "$item" ]] || [[ -f "$item" ]]; then
|
|
51
|
+
local basename=$(basename "$item")
|
|
52
|
+
if [[ $basename =~ ^(SPEC|CHANGE)-([0-9]+) ]]; then
|
|
53
|
+
local num="${BASH_REMATCH[2]}"
|
|
54
|
+
if (( num > max_id )); then
|
|
55
|
+
max_id=$num
|
|
56
|
+
fi
|
|
57
|
+
fi
|
|
58
|
+
fi
|
|
59
|
+
done
|
|
60
|
+
fi
|
|
61
|
+
|
|
62
|
+
# Next ID
|
|
63
|
+
local next_id=$((max_id + 1))
|
|
64
|
+
printf "%04d" "$next_id"
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
# Check if command exists
|
|
68
|
+
require_command() {
|
|
69
|
+
local cmd="$1"
|
|
70
|
+
local install_hint="${2:-}"
|
|
71
|
+
|
|
72
|
+
if ! command -v "$cmd" &> /dev/null; then
|
|
73
|
+
error "Comando requerido no encontrado: $cmd"
|
|
74
|
+
if [[ -n "$install_hint" ]]; then
|
|
75
|
+
info "$install_hint"
|
|
76
|
+
fi
|
|
77
|
+
exit 1
|
|
78
|
+
fi
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
# Validate YAML file
|
|
82
|
+
validate_yaml() {
|
|
83
|
+
local file="$1"
|
|
84
|
+
|
|
85
|
+
if ! command -v yq &> /dev/null; then
|
|
86
|
+
warning "yq no instalado, omitiendo validación YAML"
|
|
87
|
+
return 0
|
|
88
|
+
fi
|
|
89
|
+
|
|
90
|
+
if ! yq eval "$file" > /dev/null 2>&1; then
|
|
91
|
+
error "YAML inválido: $file"
|
|
92
|
+
return 1
|
|
93
|
+
fi
|
|
94
|
+
|
|
95
|
+
return 0
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
# Parse config value from config.yaml
|
|
99
|
+
get_config() {
|
|
100
|
+
local key="$1"
|
|
101
|
+
local config_file="$PROJECT_ROOT/openspec/config.yaml"
|
|
102
|
+
|
|
103
|
+
if command -v yq &> /dev/null; then
|
|
104
|
+
yq eval ".$key" "$config_file" 2>/dev/null || echo ""
|
|
105
|
+
else
|
|
106
|
+
# Fallback: simple grep
|
|
107
|
+
grep "^${key}:" "$config_file" | cut -d':' -f2 | xargs
|
|
108
|
+
fi
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
# Create directory if not exists
|
|
112
|
+
ensure_dir() {
|
|
113
|
+
local dir="$1"
|
|
114
|
+
if [[ ! -d "$dir" ]]; then
|
|
115
|
+
mkdir -p "$dir"
|
|
116
|
+
fi
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
# Copy template with replacements
|
|
120
|
+
copy_template() {
|
|
121
|
+
local template="$1"
|
|
122
|
+
local output="$2"
|
|
123
|
+
shift 2
|
|
124
|
+
local replacements=("$@")
|
|
125
|
+
|
|
126
|
+
if [[ ! -f "$template" ]]; then
|
|
127
|
+
error "Template no encontrado: $template"
|
|
128
|
+
return 1
|
|
129
|
+
fi
|
|
130
|
+
|
|
131
|
+
local content=$(cat "$template")
|
|
132
|
+
|
|
133
|
+
# Apply replacements
|
|
134
|
+
for replacement in "${replacements[@]}"; do
|
|
135
|
+
local key="${replacement%%=*}"
|
|
136
|
+
local value="${replacement#*=}"
|
|
137
|
+
content="${content//$key/$value}"
|
|
138
|
+
done
|
|
139
|
+
|
|
140
|
+
echo "$content" > "$output"
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
# Git helpers
|
|
144
|
+
git_current_branch() {
|
|
145
|
+
git rev-parse --abbrev-ref HEAD 2>/dev/null || echo "unknown"
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
git_is_clean() {
|
|
149
|
+
[[ -z $(git status --porcelain) ]]
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
git_has_changes() {
|
|
153
|
+
! git_is_clean
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
# Ticket integration helpers
|
|
157
|
+
parse_ticket_id() {
|
|
158
|
+
local text="$1"
|
|
159
|
+
if [[ $text =~ ([a-zA-Z0-9-]+-[0-9]+) ]]; then
|
|
160
|
+
echo "${BASH_REMATCH[1]}"
|
|
161
|
+
fi
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
# Date helpers
|
|
165
|
+
current_date() {
|
|
166
|
+
date +%Y-%m-%d
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
current_datetime() {
|
|
170
|
+
date +%Y-%m-%d\ %H:%M:%S
|
|
171
|
+
}
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# openspec enrich — Refinar user story con AI
|
|
3
|
+
set -euo pipefail
|
|
4
|
+
|
|
5
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
6
|
+
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
|
7
|
+
|
|
8
|
+
source "$SCRIPT_DIR/common.sh"
|
|
9
|
+
|
|
10
|
+
usage() {
|
|
11
|
+
cat <<EOF
|
|
12
|
+
openspec enrich — Refinar user story con AI
|
|
13
|
+
|
|
14
|
+
USAGE:
|
|
15
|
+
openspec enrich <user-story> [options]
|
|
16
|
+
openspec enrich --file <path>
|
|
17
|
+
|
|
18
|
+
OPTIONS:
|
|
19
|
+
--file <path> Leer user story desde archivo
|
|
20
|
+
--output <path> Guardar resultado (default: STDOUT)
|
|
21
|
+
--model <model> Modelo AI a usar (default: auto)
|
|
22
|
+
-h, --help Mostrar ayuda
|
|
23
|
+
|
|
24
|
+
DESCRIPTION:
|
|
25
|
+
Toma una user story simple y la enriquece con:
|
|
26
|
+
- Contexto técnico
|
|
27
|
+
- Criterios de aceptación detallados
|
|
28
|
+
- Edge cases identificados
|
|
29
|
+
- Estimación de complejidad
|
|
30
|
+
|
|
31
|
+
EXAMPLE:
|
|
32
|
+
openspec enrich "Como usuario quiero hacer login"
|
|
33
|
+
openspec enrich --file stories/US-123.md --output stories/US-123-refined.md
|
|
34
|
+
|
|
35
|
+
OUTPUT:
|
|
36
|
+
User story refinada en formato markdown con:
|
|
37
|
+
- Título y descripción
|
|
38
|
+
- Contexto del negocio
|
|
39
|
+
- Criterios de aceptación (GIVEN/WHEN/THEN)
|
|
40
|
+
- Casos de uso adicionales
|
|
41
|
+
- Estimación inicial
|
|
42
|
+
|
|
43
|
+
EOF
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
# Parse arguments
|
|
47
|
+
USER_STORY=""
|
|
48
|
+
INPUT_FILE=""
|
|
49
|
+
OUTPUT_FILE=""
|
|
50
|
+
MODEL="auto"
|
|
51
|
+
|
|
52
|
+
while [[ $# -gt 0 ]]; do
|
|
53
|
+
case $1 in
|
|
54
|
+
-h|--help)
|
|
55
|
+
usage
|
|
56
|
+
exit 0
|
|
57
|
+
;;
|
|
58
|
+
--file)
|
|
59
|
+
INPUT_FILE="$2"
|
|
60
|
+
shift 2
|
|
61
|
+
;;
|
|
62
|
+
--output)
|
|
63
|
+
OUTPUT_FILE="$2"
|
|
64
|
+
shift 2
|
|
65
|
+
;;
|
|
66
|
+
--model)
|
|
67
|
+
MODEL="$2"
|
|
68
|
+
shift 2
|
|
69
|
+
;;
|
|
70
|
+
-*)
|
|
71
|
+
error "Opción desconocida: $1"
|
|
72
|
+
usage
|
|
73
|
+
exit 1
|
|
74
|
+
;;
|
|
75
|
+
*)
|
|
76
|
+
USER_STORY="$1"
|
|
77
|
+
shift
|
|
78
|
+
;;
|
|
79
|
+
esac
|
|
80
|
+
done
|
|
81
|
+
|
|
82
|
+
# Read input
|
|
83
|
+
if [[ -n "$INPUT_FILE" ]]; then
|
|
84
|
+
if [[ ! -f "$INPUT_FILE" ]]; then
|
|
85
|
+
error "Archivo no encontrado: $INPUT_FILE"
|
|
86
|
+
exit 1
|
|
87
|
+
fi
|
|
88
|
+
USER_STORY=$(cat "$INPUT_FILE")
|
|
89
|
+
elif [[ -z "$USER_STORY" ]]; then
|
|
90
|
+
error "Debe proporcionar una user story o usar --file"
|
|
91
|
+
usage
|
|
92
|
+
exit 1
|
|
93
|
+
fi
|
|
94
|
+
|
|
95
|
+
info "Refinando user story con AI..."
|
|
96
|
+
|
|
97
|
+
# Prompt para el AI
|
|
98
|
+
PROMPT=$(cat <<EOF
|
|
99
|
+
Eres un Product Owner experto en Spec-Driven Development.
|
|
100
|
+
|
|
101
|
+
Refina la siguiente user story básica en una especificación detallada:
|
|
102
|
+
|
|
103
|
+
---
|
|
104
|
+
$USER_STORY
|
|
105
|
+
---
|
|
106
|
+
|
|
107
|
+
Genera una user story refinada con este formato:
|
|
108
|
+
|
|
109
|
+
# [ID] Título Descriptivo
|
|
110
|
+
|
|
111
|
+
## User Story
|
|
112
|
+
Como [rol], quiero [acción] para [beneficio]
|
|
113
|
+
|
|
114
|
+
## Contexto del Negocio
|
|
115
|
+
Por qué esta funcionalidad es importante.
|
|
116
|
+
|
|
117
|
+
## Criterios de Aceptación
|
|
118
|
+
|
|
119
|
+
### Escenario 1: [Nombre del escenario]
|
|
120
|
+
- **GIVEN** [contexto inicial]
|
|
121
|
+
- **WHEN** [acción del usuario]
|
|
122
|
+
- **THEN** [resultado esperado]
|
|
123
|
+
- **AND** [condición adicional]
|
|
124
|
+
|
|
125
|
+
### Escenario 2: [Edge case]
|
|
126
|
+
...
|
|
127
|
+
|
|
128
|
+
## Casos de Uso Adicionales
|
|
129
|
+
- Caso 1: Descripción
|
|
130
|
+
- Caso 2: Descripción
|
|
131
|
+
|
|
132
|
+
## Requisitos No Funcionales
|
|
133
|
+
- Performance: ...
|
|
134
|
+
- Seguridad: ...
|
|
135
|
+
- UX: ...
|
|
136
|
+
|
|
137
|
+
## Estimación Inicial
|
|
138
|
+
- **Complejidad:** Baja / Media / Alta
|
|
139
|
+
- **Esfuerzo:** S / M / L / XL
|
|
140
|
+
- **Riesgos:** ...
|
|
141
|
+
|
|
142
|
+
## Fuera de Alcance
|
|
143
|
+
- Qué NO incluye esta user story
|
|
144
|
+
|
|
145
|
+
Usa español técnico claro y preciso.
|
|
146
|
+
EOF
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
# Detect available AI CLI
|
|
150
|
+
AI_CMD=""
|
|
151
|
+
if command -v oracle &> /dev/null; then
|
|
152
|
+
AI_CMD="oracle"
|
|
153
|
+
elif command -v gh &> /dev/null && gh copilot --version &> /dev/null 2>&1; then
|
|
154
|
+
AI_CMD="gh_copilot"
|
|
155
|
+
elif command -v ai-cli &> /dev/null; then
|
|
156
|
+
AI_CMD="ai-cli"
|
|
157
|
+
else
|
|
158
|
+
error "No se encontró CLI de AI disponible (oracle, gh copilot, ai-cli)"
|
|
159
|
+
exit 1
|
|
160
|
+
fi
|
|
161
|
+
|
|
162
|
+
# Execute AI enrichment
|
|
163
|
+
REFINED_STORY=""
|
|
164
|
+
case $AI_CMD in
|
|
165
|
+
oracle)
|
|
166
|
+
info "Usando Oracle CLI..."
|
|
167
|
+
REFINED_STORY=$(echo "$PROMPT" | oracle --model "${MODEL}" --no-stream)
|
|
168
|
+
;;
|
|
169
|
+
gh_copilot)
|
|
170
|
+
info "Usando GitHub Copilot CLI..."
|
|
171
|
+
REFINED_STORY=$(echo "$PROMPT" | gh copilot suggest -t shell)
|
|
172
|
+
;;
|
|
173
|
+
ai-cli)
|
|
174
|
+
info "Usando AI CLI..."
|
|
175
|
+
# Use sessions_send or direct prompt
|
|
176
|
+
REFINED_STORY=$(echo "$PROMPT" | ai-cli prompt)
|
|
177
|
+
;;
|
|
178
|
+
esac
|
|
179
|
+
|
|
180
|
+
# Output result
|
|
181
|
+
if [[ -n "$OUTPUT_FILE" ]]; then
|
|
182
|
+
echo "$REFINED_STORY" > "$OUTPUT_FILE"
|
|
183
|
+
success "User story refinada guardada en: $OUTPUT_FILE"
|
|
184
|
+
else
|
|
185
|
+
echo "$REFINED_STORY"
|
|
186
|
+
fi
|
|
187
|
+
|
|
188
|
+
success "User story refinada exitosamente"
|