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,325 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# SpecLeap — Compliance audit
|
|
3
|
+
# Checks 6 dimensions and produces a 0-100 score per dimension + total average.
|
|
4
|
+
# Writes a JSON with full detail to .quality/evidence/compliance/audit-TIMESTAMP.json
|
|
5
|
+
|
|
6
|
+
set +e # don't abort on individual check failures
|
|
7
|
+
|
|
8
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
9
|
+
ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
10
|
+
|
|
11
|
+
source "$ROOT/.specleap/i18n.sh"
|
|
12
|
+
|
|
13
|
+
GREEN='\033[0;32m'
|
|
14
|
+
YELLOW='\033[1;33m'
|
|
15
|
+
RED='\033[0;31m'
|
|
16
|
+
BOLD='\033[1m'
|
|
17
|
+
NC='\033[0m'
|
|
18
|
+
|
|
19
|
+
# ---------- Utility: append an observation (tab-separated: dimension\ttext)
|
|
20
|
+
OBS_FILE=$(mktemp)
|
|
21
|
+
trap 'rm -f "$OBS_FILE"' EXIT
|
|
22
|
+
|
|
23
|
+
observe() {
|
|
24
|
+
echo -e "$1\t$2" >> "$OBS_FILE"
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
# ---------- 1. Version
|
|
28
|
+
check_version() {
|
|
29
|
+
local score=100
|
|
30
|
+
local version
|
|
31
|
+
version=$(grep -E '"version"' "$ROOT/package.json" | head -1 | sed 's/.*"version": *"\([^"]*\)".*/\1/')
|
|
32
|
+
|
|
33
|
+
local last_tag
|
|
34
|
+
last_tag=$(git -C "$ROOT" describe --tags --abbrev=0 2>/dev/null || echo "")
|
|
35
|
+
|
|
36
|
+
if [ -z "$last_tag" ]; then
|
|
37
|
+
# No tags yet: not penalised, common before first release
|
|
38
|
+
:
|
|
39
|
+
elif [ "v$version" != "$last_tag" ] && [ "$version" != "$last_tag" ]; then
|
|
40
|
+
score=50
|
|
41
|
+
observe "version" "$(t 'quality.compliance.version_mismatch') package.json=$version vs tag=$last_tag"
|
|
42
|
+
fi
|
|
43
|
+
|
|
44
|
+
VERSION_SCORE=$score
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
# ---------- 2. Skills count consistency
|
|
48
|
+
check_skills() {
|
|
49
|
+
local score=100
|
|
50
|
+
local skills_installed=0
|
|
51
|
+
if [ -d "$HOME/.skills" ]; then
|
|
52
|
+
skills_installed=$(find "$HOME/.skills" -maxdepth 1 -type d -not -path "$HOME/.skills" 2>/dev/null | wc -l | tr -d ' ')
|
|
53
|
+
fi
|
|
54
|
+
|
|
55
|
+
# Expected from baseline.json
|
|
56
|
+
local baseline_count=0
|
|
57
|
+
if [ -f "$ROOT/.quality/baselines/baseline.json" ] && command -v jq &>/dev/null; then
|
|
58
|
+
baseline_count=$(jq -r '.repo_snapshot.skills_count // 0' "$ROOT/.quality/baselines/baseline.json")
|
|
59
|
+
fi
|
|
60
|
+
|
|
61
|
+
if [ "$skills_installed" -ne "$baseline_count" ] && [ "$baseline_count" -gt 0 ]; then
|
|
62
|
+
score=$((score - 25))
|
|
63
|
+
observe "skills" "$(t 'quality.compliance.skills_mismatch') installed=$skills_installed, baseline=$baseline_count"
|
|
64
|
+
fi
|
|
65
|
+
|
|
66
|
+
# Check README mentions N skills consistently
|
|
67
|
+
local readme_counts
|
|
68
|
+
readme_counts=$(grep -oE '[0-9]+ Agent Skills' "$ROOT/README.md" 2>/dev/null | grep -oE '^[0-9]+' | sort -u)
|
|
69
|
+
if [ -n "$readme_counts" ]; then
|
|
70
|
+
for c in $readme_counts; do
|
|
71
|
+
if [ "$c" != "$skills_installed" ] && [ "$c" != "20" ] && [ "$c" != "14" ]; then
|
|
72
|
+
# 20 (tier 1) and 14 (tier 2) are valid partial counts
|
|
73
|
+
score=$((score - 25))
|
|
74
|
+
observe "skills" "$(t 'quality.compliance.skills_mismatch') README menciona $c Agent Skills"
|
|
75
|
+
fi
|
|
76
|
+
done
|
|
77
|
+
fi
|
|
78
|
+
|
|
79
|
+
[ $score -lt 0 ] && score=0
|
|
80
|
+
SKILLS_SCORE=$score
|
|
81
|
+
SKILLS_COUNT=$skills_installed
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
# ---------- 3. i18n parity
|
|
85
|
+
check_i18n() {
|
|
86
|
+
local score=100
|
|
87
|
+
local es_file="$ROOT/.specleap/i18n/es.json"
|
|
88
|
+
local en_file="$ROOT/.specleap/i18n/en.json"
|
|
89
|
+
|
|
90
|
+
if [ ! -f "$es_file" ] || [ ! -f "$en_file" ]; then
|
|
91
|
+
score=0
|
|
92
|
+
observe "i18n" "$(t 'quality.compliance.i18n_missing_file')"
|
|
93
|
+
I18N_SCORE=$score
|
|
94
|
+
return
|
|
95
|
+
fi
|
|
96
|
+
|
|
97
|
+
if ! command -v jq &>/dev/null; then
|
|
98
|
+
I18N_SCORE=$score # can't verify → generous default
|
|
99
|
+
return
|
|
100
|
+
fi
|
|
101
|
+
|
|
102
|
+
# Flatten keys: extract all leaf paths
|
|
103
|
+
local es_keys en_keys
|
|
104
|
+
es_keys=$(jq -r 'paths(scalars) | map(tostring) | join(".")' "$es_file" 2>/dev/null | sort -u)
|
|
105
|
+
en_keys=$(jq -r 'paths(scalars) | map(tostring) | join(".")' "$en_file" 2>/dev/null | sort -u)
|
|
106
|
+
|
|
107
|
+
local missing_in_en missing_in_es
|
|
108
|
+
missing_in_en=$(comm -23 <(echo "$es_keys") <(echo "$en_keys"))
|
|
109
|
+
missing_in_es=$(comm -13 <(echo "$es_keys") <(echo "$en_keys"))
|
|
110
|
+
|
|
111
|
+
local missing_count=0
|
|
112
|
+
if [ -n "$missing_in_en" ]; then
|
|
113
|
+
while IFS= read -r k; do
|
|
114
|
+
[ -z "$k" ] && continue
|
|
115
|
+
missing_count=$((missing_count + 1))
|
|
116
|
+
observe "i18n" "$(t 'quality.compliance.i18n_missing_key') en.json: $k"
|
|
117
|
+
done <<< "$missing_in_en"
|
|
118
|
+
fi
|
|
119
|
+
if [ -n "$missing_in_es" ]; then
|
|
120
|
+
while IFS= read -r k; do
|
|
121
|
+
[ -z "$k" ] && continue
|
|
122
|
+
missing_count=$((missing_count + 1))
|
|
123
|
+
observe "i18n" "$(t 'quality.compliance.i18n_missing_key') es.json: $k"
|
|
124
|
+
done <<< "$missing_in_es"
|
|
125
|
+
fi
|
|
126
|
+
|
|
127
|
+
score=$((100 - missing_count * 5))
|
|
128
|
+
[ $score -lt 0 ] && score=0
|
|
129
|
+
I18N_SCORE=$score
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
# ---------- 4. Hooks
|
|
133
|
+
check_hooks() {
|
|
134
|
+
local score=0
|
|
135
|
+
local hook="$ROOT/.claude/hooks/spec-guard.sh"
|
|
136
|
+
local template="$ROOT/.claude/settings.json.template"
|
|
137
|
+
|
|
138
|
+
if [ -f "$hook" ]; then
|
|
139
|
+
score=$((score + 25))
|
|
140
|
+
if head -1 "$hook" | grep -q '^#!/bin/bash\|^#!/usr/bin/env bash'; then
|
|
141
|
+
score=$((score + 25))
|
|
142
|
+
else
|
|
143
|
+
observe "hooks" "$(t 'quality.compliance.hook_not_shebang') spec-guard.sh"
|
|
144
|
+
fi
|
|
145
|
+
if [ -x "$hook" ]; then
|
|
146
|
+
score=$((score + 25))
|
|
147
|
+
else
|
|
148
|
+
observe "hooks" "$(t 'quality.compliance.hook_not_exec') spec-guard.sh"
|
|
149
|
+
fi
|
|
150
|
+
else
|
|
151
|
+
observe "hooks" "$(t 'quality.compliance.hook_missing') .claude/hooks/spec-guard.sh"
|
|
152
|
+
fi
|
|
153
|
+
|
|
154
|
+
if [ -f "$template" ] && grep -q "spec-guard.sh" "$template"; then
|
|
155
|
+
score=$((score + 25))
|
|
156
|
+
else
|
|
157
|
+
observe "hooks" "$(t 'quality.compliance.hook_not_in_template')"
|
|
158
|
+
fi
|
|
159
|
+
|
|
160
|
+
HOOKS_SCORE=$score
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
# ---------- 5. Baseline freshness
|
|
164
|
+
check_baseline() {
|
|
165
|
+
local score=100
|
|
166
|
+
local baseline="$ROOT/.quality/baselines/baseline.json"
|
|
167
|
+
|
|
168
|
+
if [ ! -f "$baseline" ] || ! command -v jq &>/dev/null; then
|
|
169
|
+
BASELINE_SCORE=$score
|
|
170
|
+
return
|
|
171
|
+
fi
|
|
172
|
+
|
|
173
|
+
local saved_loc saved_files saved_agents saved_commands
|
|
174
|
+
saved_loc=$(jq -r '.repo_snapshot.loc_total // 0' "$baseline")
|
|
175
|
+
saved_files=$(jq -r '.repo_snapshot.files_count // 0' "$baseline")
|
|
176
|
+
saved_agents=$(jq -r '.repo_snapshot.agents_count // 0' "$baseline")
|
|
177
|
+
saved_commands=$(jq -r '.repo_snapshot.commands_count // 0' "$baseline")
|
|
178
|
+
|
|
179
|
+
# Recompute
|
|
180
|
+
local real_files real_loc real_agents real_commands
|
|
181
|
+
real_files=$(find "$ROOT" -type f \
|
|
182
|
+
\( -name "*.md" -o -name "*.sh" -o -name "*.json" -o -name "*.mdc" \) \
|
|
183
|
+
-not -path "*/node_modules/*" -not -path "*/vendor/*" \
|
|
184
|
+
-not -path "*/dist/*" -not -path "*/.git/*" -not -path "*/.astro/*" \
|
|
185
|
+
-not -path "*/proyectos/*" 2>/dev/null | wc -l | tr -d ' ')
|
|
186
|
+
|
|
187
|
+
real_loc=0
|
|
188
|
+
if [ "$real_files" -gt 0 ]; then
|
|
189
|
+
real_loc=$(find "$ROOT" -type f \
|
|
190
|
+
\( -name "*.md" -o -name "*.sh" -o -name "*.json" -o -name "*.mdc" \) \
|
|
191
|
+
-not -path "*/node_modules/*" -not -path "*/vendor/*" \
|
|
192
|
+
-not -path "*/dist/*" -not -path "*/.git/*" -not -path "*/.astro/*" \
|
|
193
|
+
-not -path "*/proyectos/*" -exec cat {} + 2>/dev/null | wc -l | tr -d ' ')
|
|
194
|
+
fi
|
|
195
|
+
|
|
196
|
+
real_agents=$(find "$ROOT/.agents" -maxdepth 1 -type f -name "*.md" 2>/dev/null | wc -l | tr -d ' ')
|
|
197
|
+
real_commands=$(find "$ROOT/.commands" -maxdepth 1 -type f -name "*.md" 2>/dev/null | wc -l | tr -d ' ')
|
|
198
|
+
|
|
199
|
+
# Compute max diff pct
|
|
200
|
+
local max_diff=0
|
|
201
|
+
local fields=("loc:$saved_loc:$real_loc" "files:$saved_files:$real_files" "agents:$saved_agents:$real_agents" "commands:$saved_commands:$real_commands")
|
|
202
|
+
for fld in "${fields[@]}"; do
|
|
203
|
+
IFS=':' read -r name saved real <<< "$fld"
|
|
204
|
+
if [ "$real" -gt 0 ]; then
|
|
205
|
+
local diff
|
|
206
|
+
diff=$(awk "BEGIN { s=$saved; r=$real; d = (s>r?s-r:r-s); pct = (r>0 ? d/r*100 : 0); printf \"%.0f\", pct }")
|
|
207
|
+
[ "$diff" -gt "$max_diff" ] && max_diff=$diff
|
|
208
|
+
fi
|
|
209
|
+
done
|
|
210
|
+
|
|
211
|
+
if [ "$max_diff" -gt 20 ]; then
|
|
212
|
+
score=60
|
|
213
|
+
observe "baseline" "$(t 'quality.compliance.baseline_stale')"
|
|
214
|
+
elif [ "$max_diff" -gt 5 ]; then
|
|
215
|
+
score=85
|
|
216
|
+
observe "baseline" "$(t 'quality.compliance.baseline_stale')"
|
|
217
|
+
fi
|
|
218
|
+
|
|
219
|
+
BASELINE_SCORE=$score
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
# ---------- 6. Docs residuals
|
|
223
|
+
check_docs() {
|
|
224
|
+
local score=100
|
|
225
|
+
local found=0
|
|
226
|
+
local patterns="iapanderson archspec openclaw pamela"
|
|
227
|
+
|
|
228
|
+
for md in "$ROOT"/*.md; do
|
|
229
|
+
[ -f "$md" ] || continue
|
|
230
|
+
local filename
|
|
231
|
+
filename=$(basename "$md")
|
|
232
|
+
# CHANGELOG is history
|
|
233
|
+
[ "$filename" = "CHANGELOG.md" ] && continue
|
|
234
|
+
|
|
235
|
+
for p in $patterns; do
|
|
236
|
+
if grep -qi "$p" "$md"; then
|
|
237
|
+
found=$((found + 1))
|
|
238
|
+
observe "docs" "$(t 'quality.compliance.docs_residual') $filename: $p"
|
|
239
|
+
fi
|
|
240
|
+
done
|
|
241
|
+
done
|
|
242
|
+
|
|
243
|
+
score=$((100 - found * 10))
|
|
244
|
+
[ $score -lt 0 ] && score=0
|
|
245
|
+
DOCS_SCORE=$score
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
# ---------- Run all checks
|
|
249
|
+
check_version
|
|
250
|
+
check_skills
|
|
251
|
+
check_i18n
|
|
252
|
+
check_hooks
|
|
253
|
+
check_baseline
|
|
254
|
+
check_docs
|
|
255
|
+
|
|
256
|
+
TOTAL=$(( (VERSION_SCORE + SKILLS_SCORE + I18N_SCORE + HOOKS_SCORE + BASELINE_SCORE + DOCS_SCORE) / 6 ))
|
|
257
|
+
|
|
258
|
+
# ---------- Print human-readable summary
|
|
259
|
+
echo -e "${BOLD}$(t 'quality.compliance.header')${NC}"
|
|
260
|
+
echo "──────────────────────────────────────────"
|
|
261
|
+
printf " %-12s %3d/100\n" "$(t 'quality.compliance.version')" "$VERSION_SCORE"
|
|
262
|
+
printf " %-12s %3d/100\n" "$(t 'quality.compliance.skills')" "$SKILLS_SCORE"
|
|
263
|
+
printf " %-12s %3d/100\n" "$(t 'quality.compliance.i18n')" "$I18N_SCORE"
|
|
264
|
+
printf " %-12s %3d/100\n" "$(t 'quality.compliance.hooks')" "$HOOKS_SCORE"
|
|
265
|
+
printf " %-12s %3d/100\n" "$(t 'quality.compliance.baseline')" "$BASELINE_SCORE"
|
|
266
|
+
printf " %-12s %3d/100\n" "$(t 'quality.compliance.docs')" "$DOCS_SCORE"
|
|
267
|
+
echo "──────────────────────────────────────────"
|
|
268
|
+
echo -e " ${BOLD}$(t 'quality.compliance.total'): $TOTAL/100${NC}"
|
|
269
|
+
|
|
270
|
+
# Observations
|
|
271
|
+
if [ -s "$OBS_FILE" ]; then
|
|
272
|
+
echo ""
|
|
273
|
+
echo -e "${YELLOW}$(t 'quality.compliance.observations'):${NC}"
|
|
274
|
+
while IFS=$'\t' read -r dim msg; do
|
|
275
|
+
printf " [%s] %s\n" "$dim" "$msg"
|
|
276
|
+
done < "$OBS_FILE"
|
|
277
|
+
fi
|
|
278
|
+
|
|
279
|
+
# ---------- Write JSON detail
|
|
280
|
+
TS=$(date -u +%Y-%m-%dT%H:%M:%SZ)
|
|
281
|
+
OUT_DIR="$ROOT/.quality/evidence/compliance"
|
|
282
|
+
mkdir -p "$OUT_DIR"
|
|
283
|
+
OUT_FILE="$OUT_DIR/audit-$TS.json"
|
|
284
|
+
|
|
285
|
+
if command -v jq &>/dev/null; then
|
|
286
|
+
# Build observations array
|
|
287
|
+
OBS_JSON="[]"
|
|
288
|
+
if [ -s "$OBS_FILE" ]; then
|
|
289
|
+
OBS_JSON=$(jq -Rs '
|
|
290
|
+
split("\n")
|
|
291
|
+
| map(select(length > 0))
|
|
292
|
+
| map(split("\t"))
|
|
293
|
+
| map({dimension: .[0], message: .[1]})
|
|
294
|
+
' "$OBS_FILE")
|
|
295
|
+
fi
|
|
296
|
+
|
|
297
|
+
jq -n \
|
|
298
|
+
--arg ts "$TS" \
|
|
299
|
+
--argjson version "$VERSION_SCORE" \
|
|
300
|
+
--argjson skills "$SKILLS_SCORE" \
|
|
301
|
+
--argjson i18n "$I18N_SCORE" \
|
|
302
|
+
--argjson hooks "$HOOKS_SCORE" \
|
|
303
|
+
--argjson baseline "$BASELINE_SCORE" \
|
|
304
|
+
--argjson docs "$DOCS_SCORE" \
|
|
305
|
+
--argjson total "$TOTAL" \
|
|
306
|
+
--argjson observations "$OBS_JSON" \
|
|
307
|
+
'{
|
|
308
|
+
timestamp: $ts,
|
|
309
|
+
scores: {
|
|
310
|
+
version: $version,
|
|
311
|
+
skills: $skills,
|
|
312
|
+
i18n: $i18n,
|
|
313
|
+
hooks: $hooks,
|
|
314
|
+
baseline: $baseline,
|
|
315
|
+
docs: $docs
|
|
316
|
+
},
|
|
317
|
+
total: $total,
|
|
318
|
+
observations: $observations
|
|
319
|
+
}' > "$OUT_FILE"
|
|
320
|
+
|
|
321
|
+
echo ""
|
|
322
|
+
echo -e "$(t 'quality.compliance.details_saved') $OUT_FILE"
|
|
323
|
+
fi
|
|
324
|
+
|
|
325
|
+
exit 0
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/bin/bash
|
|
2
2
|
# SpecLeap — Instalador de Agent Skills TIER 1 + TIER 2
|
|
3
|
-
# Versión 2.1 —
|
|
3
|
+
# Versión 2.1.1 — 34 skills profesionales
|
|
4
4
|
|
|
5
5
|
set -e
|
|
6
6
|
|
|
@@ -10,7 +10,7 @@ YELLOW='\033[1;33m'
|
|
|
10
10
|
BLUE='\033[0;34m'
|
|
11
11
|
NC='\033[0m'
|
|
12
12
|
|
|
13
|
-
echo -e "${BLUE}━━━ Installing Agent Skills (
|
|
13
|
+
echo -e "${BLUE}━━━ Installing Agent Skills (34 total) ━━━${NC}"
|
|
14
14
|
echo ""
|
|
15
15
|
|
|
16
16
|
# Lista de skills (repo:path:name)
|
|
@@ -47,7 +47,7 @@ SKILLS=(
|
|
|
47
47
|
# Frontend (1) - jeffallan/claude-skills
|
|
48
48
|
"jeffallan/claude-skills:skills/typescript-pro:typescript-pro"
|
|
49
49
|
|
|
50
|
-
# ━━━ TIER 2 Skills (
|
|
50
|
+
# ━━━ TIER 2 Skills (14 nuevas) ━━━
|
|
51
51
|
|
|
52
52
|
# Design/Frontend (3) - vercel-labs
|
|
53
53
|
"vercel-labs/agent-skills:skills/web-design-guidelines:web-design-guidelines"
|
|
@@ -71,8 +71,9 @@ SKILLS=(
|
|
|
71
71
|
# Documentation (1) - jeffallan/claude-skills
|
|
72
72
|
"jeffallan/claude-skills:skills/code-documenter:code-documenter"
|
|
73
73
|
|
|
74
|
-
# Security (
|
|
74
|
+
# Security (2) - jeffallan/claude-skills + alirezarezvani/claude-skills
|
|
75
75
|
"jeffallan/claude-skills:skills/security-reviewer:security-reviewer"
|
|
76
|
+
"alirezarezvani/claude-skills:ra-qm-team/gdpr-dsgvo-expert:gdpr-dsgvo-expert"
|
|
76
77
|
|
|
77
78
|
# Testing Advanced (2) - jeffallan/claude-skills
|
|
78
79
|
"jeffallan/claude-skills:skills/playwright-expert:playwright-expert"
|
|
@@ -108,31 +109,31 @@ for i in "${!SKILLS[@]}"; do
|
|
|
108
109
|
# Skip si ya existe
|
|
109
110
|
if [ -d "$SKILLS_DIR/$name" ]; then
|
|
110
111
|
echo -e "${YELLOW}SKIP${NC} (already installed)"
|
|
111
|
-
((SKIPPED
|
|
112
|
+
SKIPPED=$((SKIPPED + 1))
|
|
112
113
|
continue
|
|
113
114
|
fi
|
|
114
|
-
|
|
115
|
+
|
|
115
116
|
# Clonar repo si no existe
|
|
116
117
|
repo_dir="$TMP_DIR/$(echo $repo | tr '/' '_')"
|
|
117
118
|
if [ ! -d "$repo_dir" ]; then
|
|
118
119
|
if ! git clone -q "https://github.com/$repo.git" "$repo_dir" 2>/dev/null; then
|
|
119
120
|
echo -e "${RED}FAIL${NC} (repo not accessible)"
|
|
120
|
-
((FAILED
|
|
121
|
+
FAILED=$((FAILED + 1))
|
|
121
122
|
continue
|
|
122
123
|
fi
|
|
123
124
|
fi
|
|
124
|
-
|
|
125
|
+
|
|
125
126
|
# Verificar SKILL.md
|
|
126
127
|
if [ ! -f "$repo_dir/$path/SKILL.md" ]; then
|
|
127
128
|
echo -e "${RED}FAIL${NC} (SKILL.md not found)"
|
|
128
|
-
((FAILED
|
|
129
|
+
FAILED=$((FAILED + 1))
|
|
129
130
|
continue
|
|
130
131
|
fi
|
|
131
|
-
|
|
132
|
+
|
|
132
133
|
# Copiar skill
|
|
133
134
|
cp -r "$repo_dir/$path" "$SKILLS_DIR/$name"
|
|
134
135
|
echo -e "${GREEN}OK${NC}"
|
|
135
|
-
((INSTALLED
|
|
136
|
+
INSTALLED=$((INSTALLED + 1))
|
|
136
137
|
done
|
|
137
138
|
|
|
138
139
|
echo ""
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# SpecLeap — Quality Baseline helper
|
|
3
|
+
# Usage: bash scripts/quality-baseline.sh [init|snapshot|new-evidence FEATURE]
|
|
4
|
+
|
|
5
|
+
set -e
|
|
6
|
+
|
|
7
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
8
|
+
ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
9
|
+
|
|
10
|
+
# Load i18n helper (exposes t "key.path" and SPECLEAP_LANG)
|
|
11
|
+
source "$ROOT/.specleap/i18n.sh"
|
|
12
|
+
|
|
13
|
+
QUALITY_DIR="$ROOT/.quality"
|
|
14
|
+
BASELINE_FILE="$QUALITY_DIR/baselines/baseline.json"
|
|
15
|
+
EVIDENCE_DIR="$QUALITY_DIR/evidence"
|
|
16
|
+
|
|
17
|
+
GREEN='\033[0;32m'
|
|
18
|
+
YELLOW='\033[1;33m'
|
|
19
|
+
RED='\033[0;31m'
|
|
20
|
+
NC='\033[0m'
|
|
21
|
+
|
|
22
|
+
cmd_init() {
|
|
23
|
+
echo -e "$(t 'quality.baseline.init_start')"
|
|
24
|
+
|
|
25
|
+
mkdir -p "$QUALITY_DIR/baselines"
|
|
26
|
+
mkdir -p "$EVIDENCE_DIR"
|
|
27
|
+
|
|
28
|
+
if [ ! -f "$EVIDENCE_DIR/.gitkeep" ]; then
|
|
29
|
+
touch "$EVIDENCE_DIR/.gitkeep"
|
|
30
|
+
fi
|
|
31
|
+
|
|
32
|
+
if [ -f "$BASELINE_FILE" ]; then
|
|
33
|
+
echo -e "${YELLOW}$(t 'quality.baseline.already_exists')${NC} $BASELINE_FILE"
|
|
34
|
+
return 0
|
|
35
|
+
fi
|
|
36
|
+
|
|
37
|
+
cat > "$BASELINE_FILE" <<EOF
|
|
38
|
+
{
|
|
39
|
+
"version": "1.0",
|
|
40
|
+
"updated_at": "$(date -u +%Y-%m-%d)",
|
|
41
|
+
"thresholds": {
|
|
42
|
+
"coverage_min": 80,
|
|
43
|
+
"coverage_target": 90,
|
|
44
|
+
"complexity_max_per_function": 10,
|
|
45
|
+
"duplicates_max_pct": 3
|
|
46
|
+
},
|
|
47
|
+
"repo_snapshot": {
|
|
48
|
+
"loc_total": 0,
|
|
49
|
+
"files_count": 0,
|
|
50
|
+
"agents_count": 0,
|
|
51
|
+
"commands_count": 0,
|
|
52
|
+
"skills_count": 0
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
EOF
|
|
56
|
+
|
|
57
|
+
echo -e "${GREEN}$(t 'quality.baseline.init_done')${NC}"
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
cmd_snapshot() {
|
|
61
|
+
echo -e "$(t 'quality.baseline.snapshot_start')"
|
|
62
|
+
|
|
63
|
+
if [ ! -f "$BASELINE_FILE" ]; then
|
|
64
|
+
echo -e "${RED}$(t 'quality.baseline.missing_baseline')${NC}"
|
|
65
|
+
echo "$(t 'quality.baseline.run_init_first')"
|
|
66
|
+
exit 1
|
|
67
|
+
fi
|
|
68
|
+
|
|
69
|
+
if ! command -v jq &> /dev/null; then
|
|
70
|
+
echo -e "${RED}$(t 'quality.baseline.jq_required')${NC}"
|
|
71
|
+
exit 1
|
|
72
|
+
fi
|
|
73
|
+
|
|
74
|
+
# Count source files (exclude node_modules, vendor, dist, .git, .astro)
|
|
75
|
+
local files_count
|
|
76
|
+
files_count=$(find "$ROOT" \
|
|
77
|
+
-type f \
|
|
78
|
+
\( -name "*.md" -o -name "*.sh" -o -name "*.json" -o -name "*.mdc" \) \
|
|
79
|
+
-not -path "*/node_modules/*" \
|
|
80
|
+
-not -path "*/vendor/*" \
|
|
81
|
+
-not -path "*/dist/*" \
|
|
82
|
+
-not -path "*/.git/*" \
|
|
83
|
+
-not -path "*/.astro/*" \
|
|
84
|
+
-not -path "*/proyectos/*" \
|
|
85
|
+
2>/dev/null | wc -l | tr -d ' ')
|
|
86
|
+
|
|
87
|
+
# Count total lines of code in those files
|
|
88
|
+
local loc_total=0
|
|
89
|
+
if [ "$files_count" -gt 0 ]; then
|
|
90
|
+
loc_total=$(find "$ROOT" \
|
|
91
|
+
-type f \
|
|
92
|
+
\( -name "*.md" -o -name "*.sh" -o -name "*.json" -o -name "*.mdc" \) \
|
|
93
|
+
-not -path "*/node_modules/*" \
|
|
94
|
+
-not -path "*/vendor/*" \
|
|
95
|
+
-not -path "*/dist/*" \
|
|
96
|
+
-not -path "*/.git/*" \
|
|
97
|
+
-not -path "*/.astro/*" \
|
|
98
|
+
-not -path "*/proyectos/*" \
|
|
99
|
+
-exec cat {} + 2>/dev/null | wc -l | tr -d ' ')
|
|
100
|
+
fi
|
|
101
|
+
|
|
102
|
+
local agents_count
|
|
103
|
+
agents_count=$(find "$ROOT/.agents" -maxdepth 1 -type f -name "*.md" 2>/dev/null | wc -l | tr -d ' ')
|
|
104
|
+
|
|
105
|
+
local commands_count
|
|
106
|
+
commands_count=$(find "$ROOT/.commands" -maxdepth 1 -type f -name "*.md" 2>/dev/null | wc -l | tr -d ' ')
|
|
107
|
+
|
|
108
|
+
local skills_count=0
|
|
109
|
+
if [ -d "$HOME/.skills" ]; then
|
|
110
|
+
skills_count=$(find "$HOME/.skills" -maxdepth 1 -type d -not -path "$HOME/.skills" 2>/dev/null | wc -l | tr -d ' ')
|
|
111
|
+
fi
|
|
112
|
+
|
|
113
|
+
local tmp_file
|
|
114
|
+
tmp_file=$(mktemp)
|
|
115
|
+
jq \
|
|
116
|
+
--arg date "$(date -u +%Y-%m-%d)" \
|
|
117
|
+
--argjson loc "$loc_total" \
|
|
118
|
+
--argjson files "$files_count" \
|
|
119
|
+
--argjson agents "$agents_count" \
|
|
120
|
+
--argjson commands "$commands_count" \
|
|
121
|
+
--argjson skills "$skills_count" \
|
|
122
|
+
'.updated_at = $date
|
|
123
|
+
| .repo_snapshot.loc_total = $loc
|
|
124
|
+
| .repo_snapshot.files_count = $files
|
|
125
|
+
| .repo_snapshot.agents_count = $agents
|
|
126
|
+
| .repo_snapshot.commands_count = $commands
|
|
127
|
+
| .repo_snapshot.skills_count = $skills' \
|
|
128
|
+
"$BASELINE_FILE" > "$tmp_file"
|
|
129
|
+
mv "$tmp_file" "$BASELINE_FILE"
|
|
130
|
+
|
|
131
|
+
echo -e "${GREEN}$(t 'quality.baseline.snapshot_done')${NC}"
|
|
132
|
+
echo " LOC: $loc_total | files: $files_count | agents: $agents_count | commands: $commands_count | skills: $skills_count"
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
cmd_new_evidence() {
|
|
136
|
+
local slug="$1"
|
|
137
|
+
|
|
138
|
+
if [ -z "$slug" ]; then
|
|
139
|
+
echo -e "${RED}$(t 'quality.baseline.missing_slug')${NC}"
|
|
140
|
+
echo "$(t 'quality.baseline.usage')"
|
|
141
|
+
exit 1
|
|
142
|
+
fi
|
|
143
|
+
|
|
144
|
+
# Slug validation: lowercase, numbers, hyphens only
|
|
145
|
+
if ! [[ "$slug" =~ ^[a-z0-9-]+$ ]]; then
|
|
146
|
+
echo -e "${RED}$(t 'quality.baseline.invalid_slug')${NC} $slug"
|
|
147
|
+
exit 1
|
|
148
|
+
fi
|
|
149
|
+
|
|
150
|
+
local target="$EVIDENCE_DIR/$slug"
|
|
151
|
+
echo -e "$(t 'quality.baseline.evidence_start') $slug"
|
|
152
|
+
|
|
153
|
+
mkdir -p "$target/tests"
|
|
154
|
+
mkdir -p "$target/security"
|
|
155
|
+
|
|
156
|
+
cat > "$target/metadata.json" <<EOF
|
|
157
|
+
{
|
|
158
|
+
"feature_slug": "$slug",
|
|
159
|
+
"created_at": "$(date -u +%Y-%m-%dT%H:%M:%SZ)",
|
|
160
|
+
"ticket": "",
|
|
161
|
+
"author": "",
|
|
162
|
+
"status": "planning",
|
|
163
|
+
"baseline_version": "1.0"
|
|
164
|
+
}
|
|
165
|
+
EOF
|
|
166
|
+
|
|
167
|
+
cat > "$target/decisions.md" <<EOF
|
|
168
|
+
# Decisiones técnicas — $slug
|
|
169
|
+
|
|
170
|
+
## Contexto
|
|
171
|
+
|
|
172
|
+
(Resumen del ticket y problema que resuelve)
|
|
173
|
+
|
|
174
|
+
## Decisiones
|
|
175
|
+
|
|
176
|
+
### [Título de la decisión]
|
|
177
|
+
|
|
178
|
+
**Fecha:** $(date -u +%Y-%m-%d)
|
|
179
|
+
**Contexto:** …
|
|
180
|
+
**Opciones consideradas:**
|
|
181
|
+
- …
|
|
182
|
+
|
|
183
|
+
**Decisión:** …
|
|
184
|
+
**Consecuencias:** …
|
|
185
|
+
EOF
|
|
186
|
+
|
|
187
|
+
touch "$target/tests/unit.txt"
|
|
188
|
+
touch "$target/tests/integration.txt"
|
|
189
|
+
touch "$target/tests/coverage.json"
|
|
190
|
+
touch "$target/security/audit.txt"
|
|
191
|
+
|
|
192
|
+
echo -e "${GREEN}$(t 'quality.baseline.evidence_done')${NC} $target"
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
# Dispatch
|
|
196
|
+
case "${1:-}" in
|
|
197
|
+
init)
|
|
198
|
+
cmd_init
|
|
199
|
+
;;
|
|
200
|
+
snapshot)
|
|
201
|
+
cmd_snapshot
|
|
202
|
+
;;
|
|
203
|
+
new-evidence)
|
|
204
|
+
cmd_new_evidence "$2"
|
|
205
|
+
;;
|
|
206
|
+
*)
|
|
207
|
+
echo "$(t 'quality.baseline.usage')"
|
|
208
|
+
exit 1
|
|
209
|
+
;;
|
|
210
|
+
esac
|