@cristiancorreau/forge 2.1.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/CHANGELOG.md +228 -0
- package/LICENSE +191 -0
- package/README.md +156 -0
- package/assets/adapters/claude-code/commands/deploy-check.md +12 -0
- package/assets/adapters/claude-code/commands/new-feature.md +11 -0
- package/assets/adapters/claude-code/commands/plan.md +116 -0
- package/assets/adapters/claude-code/commands/review.md +219 -0
- package/assets/adapters/claude-code/commands/session-close.md +109 -0
- package/assets/adapters/claude-code/commands/session-start.md +59 -0
- package/assets/adapters/claude-code/commands/ship.md +133 -0
- package/assets/adapters/claude-code/commands/wiki-ingest.md +7 -0
- package/assets/adapters/claude-code/commands/wiki-lint.md +5 -0
- package/assets/adapters/claude-code/commands/wiki-query.md +7 -0
- package/assets/adapters/claude-code/commands/work.md +101 -0
- package/assets/adapters/claude-code/generate-claude-md.py +304 -0
- package/assets/adapters/codex/commands/plan.md +63 -0
- package/assets/adapters/codex/commands/review.md +53 -0
- package/assets/adapters/codex/commands/session-close.md +53 -0
- package/assets/adapters/codex/commands/session-start.md +49 -0
- package/assets/adapters/codex/commands/ship.md +53 -0
- package/assets/adapters/codex/commands/work.md +53 -0
- package/assets/adapters/codex/generate-codex-config.py +269 -0
- package/assets/adapters/codex/hooks/codex.yaml.tpl +43 -0
- package/assets/adapters/codex/hooks/forge-codex-finish.sh +158 -0
- package/assets/adapters/codex/hooks/forge-codex-start.sh +186 -0
- package/assets/adapters/kiro/generate-steering.py +367 -0
- package/assets/adapters/opencode/HOOKS.md +123 -0
- package/assets/adapters/opencode/commands/plan.md +119 -0
- package/assets/adapters/opencode/commands/review.md +164 -0
- package/assets/adapters/opencode/commands/session-close.md +111 -0
- package/assets/adapters/opencode/commands/session-start.md +62 -0
- package/assets/adapters/opencode/commands/ship.md +135 -0
- package/assets/adapters/opencode/commands/work.md +82 -0
- package/assets/adapters/opencode/generate-agents-md.py +262 -0
- package/assets/core/agents/backend-engineer.md +61 -0
- package/assets/core/agents/compliance-reviewer.md +83 -0
- package/assets/core/agents/docs-writer.md +77 -0
- package/assets/core/agents/frontend-engineer.md +70 -0
- package/assets/core/agents/orchestrator.md +104 -0
- package/assets/core/agents/security-auditor.md +54 -0
- package/assets/core/agents/test-engineer.md +57 -0
- package/assets/core/hooks/hooks-registry.yaml +48 -0
- package/assets/core/hooks/post-turn-check.sh +139 -0
- package/assets/core/hooks/pre-bash-check.py +202 -0
- package/assets/core/hooks/pre-edit-check.py +317 -0
- package/assets/core/hooks/session-start.sh +184 -0
- package/assets/core/schemas/project.schema.json +503 -0
- package/assets/core/skills/README.md +88 -0
- package/assets/core/skills/aitmpl-search/SKILL.md +74 -0
- package/assets/core/skills/browser-test/SKILL.md +177 -0
- package/assets/core/skills/db-migrate/SKILL.md +163 -0
- package/assets/core/skills/local2prod/SKILL.md +147 -0
- package/assets/core/skills/new-feature/SKILL.md +155 -0
- package/assets/core/skills/obsidian-sync/SKILL.md +152 -0
- package/assets/core/skills/phase-kickoff/SKILL.md +69 -0
- package/assets/core/skills/security-audit/SKILL.md +125 -0
- package/assets/core/skills/spec/SKILL.md +72 -0
- package/assets/core/skills/wiki-ingest/SKILL.md +183 -0
- package/assets/core/skills/wiki-lint/SKILL.md +109 -0
- package/assets/core/skills/wiki-query/SKILL.md +100 -0
- package/assets/core/templates/claude-md/architecture.rules +20 -0
- package/assets/core/templates/claude-md/global.md +30 -0
- package/assets/core/templates/claude-md/project.md +36 -0
- package/assets/core/templates/daily-note.md +38 -0
- package/assets/core/templates/spec-template.md +43 -0
- package/assets/core/workflows/sdd.md +69 -0
- package/assets/core/workflows/sprint.md +59 -0
- package/assets/forge.py +1265 -0
- package/assets/hooks/pre-commit +43 -0
- package/assets/manifest.json +274 -0
- package/assets/profiles/astro/README.md +24 -0
- package/assets/profiles/astro/agents/frontend-engineer.md +74 -0
- package/assets/profiles/django/agents/api-engineer.md +83 -0
- package/assets/profiles/expo/README.md +24 -0
- package/assets/profiles/expo/agents/mobile-engineer.md +69 -0
- package/assets/profiles/express/agents/api-engineer.md +60 -0
- package/assets/profiles/fastapi/README.md +32 -0
- package/assets/profiles/fastapi/agents/api-engineer.md +87 -0
- package/assets/profiles/go-gin/agents/api-engineer.md +98 -0
- package/assets/profiles/hono-drizzle/README.md +31 -0
- package/assets/profiles/hono-drizzle/agents/api-engineer.md +82 -0
- package/assets/profiles/laravel/README.md +32 -0
- package/assets/profiles/laravel/agents/api-engineer.md +114 -0
- package/assets/profiles/laravel/agents/fullstack-engineer.md +67 -0
- package/assets/profiles/laravel/agents/migration-specialist.md +420 -0
- package/assets/profiles/nestjs/agents/api-engineer.md +79 -0
- package/assets/profiles/nextjs-admin/README.md +32 -0
- package/assets/profiles/nextjs-admin/agents/admin-engineer.md +78 -0
- package/assets/profiles/playwright-crawler/agents/scanner-engineer.md +51 -0
- package/assets/profiles/rails/agents/fullstack-engineer.md +61 -0
- package/assets/profiles/sveltekit/agents/frontend-engineer.md +96 -0
- package/assets/profiles/vuenuxt/agents/frontend-engineer.md +82 -0
- package/assets/profiles/wordpress/README.md +30 -0
- package/assets/profiles/wordpress/agents/divi-engineer.md +273 -0
- package/assets/profiles/wordpress/agents/elementor-engineer.md +310 -0
- package/assets/profiles/wordpress/agents/wp-engineer.md +216 -0
- package/assets/requirements.txt +2 -0
- package/assets/scripts/aitmpl-search.py +808 -0
- package/assets/scripts/forge-add-opportunities.py +92 -0
- package/assets/scripts/forge-audit.py +1061 -0
- package/assets/scripts/forge-generate-all.py +283 -0
- package/assets/scripts/forge-init.py +900 -0
- package/assets/scripts/forge-migrate-project-yaml.py +397 -0
- package/assets/scripts/forge-scaffold-profile.py +181 -0
- package/assets/scripts/forge-teardown.py +193 -0
- package/assets/scripts/forge-validate-project-yaml.py +457 -0
- package/assets/scripts/forge-wizard.py +1003 -0
- package/assets/scripts/setup-codex.sh +229 -0
- package/assets/scripts/team-install.sh +147 -0
- package/assets/scripts/token-stats.py +201 -0
- package/assets/templates/modes/enterprise.yaml.tpl +114 -0
- package/assets/templates/modes/multi-runtime.yaml.tpl +89 -0
- package/assets/templates/modes/new-stack.yaml.tpl +101 -0
- package/assets/templates/modes/startup.yaml.tpl +74 -0
- package/assets/templates/project.yaml.tpl +185 -0
- package/assets/templates/wiki/concepts/_template.md +22 -0
- package/assets/templates/wiki/entities/_template.md +19 -0
- package/assets/templates/wiki/index.md +32 -0
- package/assets/templates/wiki/log.md +6 -0
- package/assets/templates/wiki/sources/_template.md +25 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +64 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/audit.d.ts +2 -0
- package/dist/commands/audit.d.ts.map +1 -0
- package/dist/commands/audit.js +21 -0
- package/dist/commands/audit.js.map +1 -0
- package/dist/commands/doctor.d.ts +2 -0
- package/dist/commands/doctor.d.ts.map +1 -0
- package/dist/commands/doctor.js +58 -0
- package/dist/commands/doctor.js.map +1 -0
- package/dist/commands/generate.d.ts +2 -0
- package/dist/commands/generate.d.ts.map +1 -0
- package/dist/commands/generate.js +27 -0
- package/dist/commands/generate.js.map +1 -0
- package/dist/commands/init.d.ts +2 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +22 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/validate.d.ts +2 -0
- package/dist/commands/validate.d.ts.map +1 -0
- package/dist/commands/validate.js +20 -0
- package/dist/commands/validate.js.map +1 -0
- package/dist/lib/paths.d.ts +10 -0
- package/dist/lib/paths.d.ts.map +1 -0
- package/dist/lib/paths.js +49 -0
- package/dist/lib/paths.js.map +1 -0
- package/dist/lib/python.d.ts +4 -0
- package/dist/lib/python.d.ts.map +1 -0
- package/dist/lib/python.js +46 -0
- package/dist/lib/python.js.map +1 -0
- package/package.json +46 -0
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# setup-codex.sh — Inicializar soporte de Forge v2 para Codex CLI
|
|
3
|
+
#
|
|
4
|
+
# Uso:
|
|
5
|
+
# bash scripts/setup-codex.sh
|
|
6
|
+
#
|
|
7
|
+
# Qué hace:
|
|
8
|
+
# 1. Verifica que git y python3 están disponibles
|
|
9
|
+
# 2. Inicializa el submódulo de forge si es necesario
|
|
10
|
+
# 3. Crea .codex/codex.yaml desde la plantilla (si no existe)
|
|
11
|
+
# 4. Copia los scripts de hooks a .codex/
|
|
12
|
+
# 5. Ejecuta generate-codex-config.py para generar AGENTS.md
|
|
13
|
+
# 6. Imprime resumen de lo que se instaló
|
|
14
|
+
|
|
15
|
+
set -euo pipefail
|
|
16
|
+
|
|
17
|
+
# ---------------------------------------------------------------------------
|
|
18
|
+
# Colores para output (solo si el terminal los soporta)
|
|
19
|
+
# ---------------------------------------------------------------------------
|
|
20
|
+
if [ -t 1 ] && command -v tput &>/dev/null && tput colors &>/dev/null 2>&1; then
|
|
21
|
+
GREEN="$(tput setaf 2)"
|
|
22
|
+
YELLOW="$(tput setaf 3)"
|
|
23
|
+
RED="$(tput setaf 1)"
|
|
24
|
+
RESET="$(tput sgr0)"
|
|
25
|
+
else
|
|
26
|
+
GREEN=""
|
|
27
|
+
YELLOW=""
|
|
28
|
+
RED=""
|
|
29
|
+
RESET=""
|
|
30
|
+
fi
|
|
31
|
+
|
|
32
|
+
ok() { printf " %s[OK]%s %s\n" "$GREEN" "$RESET" "$1"; }
|
|
33
|
+
warn() { printf " %s[WARN]%s %s\n" "$YELLOW" "$RESET" "$1"; }
|
|
34
|
+
fail() { printf " %s[FAIL]%s %s\n" "$RED" "$RESET" "$1"; }
|
|
35
|
+
|
|
36
|
+
echo ""
|
|
37
|
+
echo "forge — Setup Codex CLI adapter"
|
|
38
|
+
echo "================================"
|
|
39
|
+
echo ""
|
|
40
|
+
|
|
41
|
+
# ---------------------------------------------------------------------------
|
|
42
|
+
# Step 1 — Verificar herramientas requeridas
|
|
43
|
+
# ---------------------------------------------------------------------------
|
|
44
|
+
echo "Verificando herramientas..."
|
|
45
|
+
|
|
46
|
+
if ! command -v git &>/dev/null; then
|
|
47
|
+
fail "git no está instalado o no está en PATH"
|
|
48
|
+
echo ""
|
|
49
|
+
echo "Instalar git: https://git-scm.com/downloads"
|
|
50
|
+
exit 1
|
|
51
|
+
fi
|
|
52
|
+
ok "git $(git --version | awk '{print $3}')"
|
|
53
|
+
|
|
54
|
+
if ! command -v python3 &>/dev/null; then
|
|
55
|
+
fail "python3 no está instalado o no está en PATH"
|
|
56
|
+
echo ""
|
|
57
|
+
echo "Instalar python3: https://www.python.org/downloads/"
|
|
58
|
+
exit 1
|
|
59
|
+
fi
|
|
60
|
+
ok "python3 $(python3 --version | awk '{print $2}')"
|
|
61
|
+
|
|
62
|
+
if ! python3 -c "import yaml" &>/dev/null 2>&1; then
|
|
63
|
+
warn "pyyaml no está instalado — instalando..."
|
|
64
|
+
if python3 -m pip install pyyaml --quiet; then
|
|
65
|
+
ok "pyyaml instalado"
|
|
66
|
+
else
|
|
67
|
+
fail "No se pudo instalar pyyaml. Instalar manualmente: pip install pyyaml"
|
|
68
|
+
exit 1
|
|
69
|
+
fi
|
|
70
|
+
else
|
|
71
|
+
ok "pyyaml disponible"
|
|
72
|
+
fi
|
|
73
|
+
|
|
74
|
+
echo ""
|
|
75
|
+
|
|
76
|
+
# ---------------------------------------------------------------------------
|
|
77
|
+
# Step 2 — Verificar que estamos en la raíz del repositorio con project.yaml
|
|
78
|
+
# ---------------------------------------------------------------------------
|
|
79
|
+
echo "Verificando proyecto..."
|
|
80
|
+
|
|
81
|
+
REPO_ROOT="$(git rev-parse --show-toplevel 2>/dev/null || echo "")"
|
|
82
|
+
if [[ -z "$REPO_ROOT" ]]; then
|
|
83
|
+
fail "No se encontró repositorio git. Ejecutar desde dentro del repositorio."
|
|
84
|
+
exit 1
|
|
85
|
+
fi
|
|
86
|
+
|
|
87
|
+
if [[ ! -f "$REPO_ROOT/project.yaml" ]]; then
|
|
88
|
+
warn "project.yaml no encontrado en $REPO_ROOT"
|
|
89
|
+
warn "Ejecutar primero: python3 scripts/forge-wizard.py"
|
|
90
|
+
warn "Continuando de todas formas — AGENTS.md puede quedar incompleto."
|
|
91
|
+
else
|
|
92
|
+
ok "project.yaml encontrado"
|
|
93
|
+
fi
|
|
94
|
+
|
|
95
|
+
echo ""
|
|
96
|
+
|
|
97
|
+
# ---------------------------------------------------------------------------
|
|
98
|
+
# Step 3 — Encontrar el directorio de forge
|
|
99
|
+
# ---------------------------------------------------------------------------
|
|
100
|
+
echo "Localizando forge..."
|
|
101
|
+
|
|
102
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
103
|
+
FORGE_DIR="$(dirname "$SCRIPT_DIR")"
|
|
104
|
+
|
|
105
|
+
if [[ ! -d "$FORGE_DIR/core" ]]; then
|
|
106
|
+
# Intentar inicializar submódulo si hay .gitmodules
|
|
107
|
+
if [[ -f "$REPO_ROOT/.gitmodules" ]] && grep -q "forge\|agentic" "$REPO_ROOT/.gitmodules" 2>/dev/null; then
|
|
108
|
+
warn "Directorio core/ no encontrado — intentando inicializar submódulo..."
|
|
109
|
+
if git -C "$REPO_ROOT" submodule update --init --recursive 2>/dev/null; then
|
|
110
|
+
ok "Submódulo inicializado"
|
|
111
|
+
else
|
|
112
|
+
fail "No se pudo inicializar el submódulo. Verificar .gitmodules."
|
|
113
|
+
exit 1
|
|
114
|
+
fi
|
|
115
|
+
else
|
|
116
|
+
fail "No se encontró el directorio core/ en $FORGE_DIR"
|
|
117
|
+
fail "Asegurarse de que forge está instalado como submódulo o en el PATH correcto."
|
|
118
|
+
exit 1
|
|
119
|
+
fi
|
|
120
|
+
else
|
|
121
|
+
ok "forge encontrado en $FORGE_DIR"
|
|
122
|
+
fi
|
|
123
|
+
|
|
124
|
+
CODEX_ADAPTER_DIR="$FORGE_DIR/adapters/codex"
|
|
125
|
+
if [[ ! -d "$CODEX_ADAPTER_DIR" ]]; then
|
|
126
|
+
fail "No se encontró $CODEX_ADAPTER_DIR"
|
|
127
|
+
exit 1
|
|
128
|
+
fi
|
|
129
|
+
|
|
130
|
+
echo ""
|
|
131
|
+
|
|
132
|
+
# ---------------------------------------------------------------------------
|
|
133
|
+
# Step 4 — Crear .codex/ y copiar codex.yaml desde plantilla
|
|
134
|
+
# ---------------------------------------------------------------------------
|
|
135
|
+
echo "Configurando .codex/..."
|
|
136
|
+
|
|
137
|
+
DOTCODEX_DIR="$REPO_ROOT/.codex"
|
|
138
|
+
mkdir -p "$DOTCODEX_DIR"
|
|
139
|
+
ok "Directorio .codex/ listo"
|
|
140
|
+
|
|
141
|
+
YAML_TPL="$CODEX_ADAPTER_DIR/hooks/codex.yaml.tpl"
|
|
142
|
+
YAML_DST="$DOTCODEX_DIR/codex.yaml"
|
|
143
|
+
|
|
144
|
+
if [[ -f "$YAML_DST" ]]; then
|
|
145
|
+
warn "codex.yaml ya existe — no sobreescribiendo. Revisar manualmente si hay cambios en la plantilla."
|
|
146
|
+
else
|
|
147
|
+
if [[ -f "$YAML_TPL" ]]; then
|
|
148
|
+
cp "$YAML_TPL" "$YAML_DST"
|
|
149
|
+
ok "codex.yaml copiado desde plantilla"
|
|
150
|
+
else
|
|
151
|
+
fail "Plantilla no encontrada: $YAML_TPL"
|
|
152
|
+
exit 1
|
|
153
|
+
fi
|
|
154
|
+
fi
|
|
155
|
+
|
|
156
|
+
# ---------------------------------------------------------------------------
|
|
157
|
+
# Step 5 — Copiar scripts de hooks a .codex/
|
|
158
|
+
# ---------------------------------------------------------------------------
|
|
159
|
+
echo ""
|
|
160
|
+
echo "Instalando hooks..."
|
|
161
|
+
|
|
162
|
+
HOOKS_SRC="$CODEX_ADAPTER_DIR/hooks"
|
|
163
|
+
|
|
164
|
+
for hook_script in forge-codex-start.sh forge-codex-finish.sh; do
|
|
165
|
+
SRC="$HOOKS_SRC/$hook_script"
|
|
166
|
+
DST="$DOTCODEX_DIR/$hook_script"
|
|
167
|
+
if [[ ! -f "$SRC" ]]; then
|
|
168
|
+
fail "Script de hook no encontrado: $SRC"
|
|
169
|
+
exit 1
|
|
170
|
+
fi
|
|
171
|
+
cp "$SRC" "$DST"
|
|
172
|
+
chmod +x "$DST"
|
|
173
|
+
ok "$hook_script instalado en .codex/"
|
|
174
|
+
done
|
|
175
|
+
|
|
176
|
+
echo ""
|
|
177
|
+
|
|
178
|
+
# ---------------------------------------------------------------------------
|
|
179
|
+
# Step 6 — Generar AGENTS.md
|
|
180
|
+
# ---------------------------------------------------------------------------
|
|
181
|
+
echo "Generando AGENTS.md..."
|
|
182
|
+
|
|
183
|
+
GENERATOR="$CODEX_ADAPTER_DIR/generate-codex-config.py"
|
|
184
|
+
if [[ ! -f "$GENERATOR" ]]; then
|
|
185
|
+
fail "generate-codex-config.py no encontrado en $CODEX_ADAPTER_DIR"
|
|
186
|
+
exit 1
|
|
187
|
+
fi
|
|
188
|
+
|
|
189
|
+
if python3 "$GENERATOR"; then
|
|
190
|
+
ok "AGENTS.md generado"
|
|
191
|
+
else
|
|
192
|
+
fail "Error al generar AGENTS.md"
|
|
193
|
+
exit 1
|
|
194
|
+
fi
|
|
195
|
+
|
|
196
|
+
echo ""
|
|
197
|
+
|
|
198
|
+
# ---------------------------------------------------------------------------
|
|
199
|
+
# Step 7 — Diff summary
|
|
200
|
+
# ---------------------------------------------------------------------------
|
|
201
|
+
echo "Resumen de cambios:"
|
|
202
|
+
echo ""
|
|
203
|
+
|
|
204
|
+
AGENTS_MD="$REPO_ROOT/AGENTS.md"
|
|
205
|
+
if git -C "$REPO_ROOT" diff --name-only HEAD -- AGENTS.md 2>/dev/null | grep -q "AGENTS.md"; then
|
|
206
|
+
LINES=$(wc -l < "$AGENTS_MD" | tr -d ' ')
|
|
207
|
+
ok "AGENTS.md actualizado ($LINES líneas)"
|
|
208
|
+
elif [[ -f "$AGENTS_MD" ]]; then
|
|
209
|
+
LINES=$(wc -l < "$AGENTS_MD" | tr -d ' ')
|
|
210
|
+
ok "AGENTS.md generado ($LINES líneas) — sin cambios vs HEAD"
|
|
211
|
+
fi
|
|
212
|
+
|
|
213
|
+
echo ""
|
|
214
|
+
echo "Archivos instalados en .codex/:"
|
|
215
|
+
for f in "$DOTCODEX_DIR"/*; do
|
|
216
|
+
printf " - .codex/%s\n" "$(basename "$f")"
|
|
217
|
+
done
|
|
218
|
+
|
|
219
|
+
echo ""
|
|
220
|
+
echo "================================"
|
|
221
|
+
ok "Setup completado. Codex CLI está listo para usar con Forge v2."
|
|
222
|
+
echo ""
|
|
223
|
+
echo "Próximos pasos:"
|
|
224
|
+
echo " 1. Revisar .codex/codex.yaml y ajustar el modelo si es necesario."
|
|
225
|
+
echo " 2. Commitear los archivos generados:"
|
|
226
|
+
echo " git add AGENTS.md .codex/"
|
|
227
|
+
echo " git commit -m 'chore(codex): initialize Forge v2 Codex adapter'"
|
|
228
|
+
echo " 3. Consultar adapters/codex/commands/ para los prompt templates de cada comando."
|
|
229
|
+
echo ""
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# team-install.sh — Automates forge setup for a new developer joining a project.
|
|
3
|
+
#
|
|
4
|
+
# Usage:
|
|
5
|
+
# bash .agentic/scripts/team-install.sh
|
|
6
|
+
# bash scripts/team-install.sh (if running from repo root and forge is at .agentic/)
|
|
7
|
+
#
|
|
8
|
+
# What it does:
|
|
9
|
+
# 1. Checks git is available
|
|
10
|
+
# 2. Ensures the forge submodule is initialized
|
|
11
|
+
# 3. Installs Python dependencies (pyyaml)
|
|
12
|
+
# 4. Runs forge-init in non-interactive mode
|
|
13
|
+
# 5. Prints next steps
|
|
14
|
+
|
|
15
|
+
set -euo pipefail
|
|
16
|
+
|
|
17
|
+
# ---------------------------------------------------------------------------
|
|
18
|
+
# Colors
|
|
19
|
+
# ---------------------------------------------------------------------------
|
|
20
|
+
RED='\033[0;31m'
|
|
21
|
+
GREEN='\033[0;32m'
|
|
22
|
+
YELLOW='\033[1;33m'
|
|
23
|
+
BOLD='\033[1m'
|
|
24
|
+
RESET='\033[0m'
|
|
25
|
+
|
|
26
|
+
info() { echo -e "${BOLD}[forge]${RESET} $*"; }
|
|
27
|
+
success() { echo -e "${GREEN}[forge]${RESET} $*"; }
|
|
28
|
+
warn() { echo -e "${YELLOW}[forge] WARNING:${RESET} $*"; }
|
|
29
|
+
error() { echo -e "${RED}[forge] ERROR:${RESET} $*" >&2; }
|
|
30
|
+
|
|
31
|
+
# ---------------------------------------------------------------------------
|
|
32
|
+
# 1. Check git
|
|
33
|
+
# ---------------------------------------------------------------------------
|
|
34
|
+
info "Checking requirements..."
|
|
35
|
+
|
|
36
|
+
if ! command -v git &>/dev/null; then
|
|
37
|
+
error "git is not installed or not in PATH."
|
|
38
|
+
error "Install git from https://git-scm.com and retry."
|
|
39
|
+
exit 1
|
|
40
|
+
fi
|
|
41
|
+
|
|
42
|
+
if ! command -v python3 &>/dev/null; then
|
|
43
|
+
error "python3 is not installed or not in PATH."
|
|
44
|
+
error "Install Python 3.9+ from https://www.python.org/downloads/ and retry."
|
|
45
|
+
exit 1
|
|
46
|
+
fi
|
|
47
|
+
|
|
48
|
+
PYTHON_VERSION=$(python3 -c 'import sys; print(f"{sys.version_info.major}.{sys.version_info.minor}")')
|
|
49
|
+
PYTHON_MAJOR=$(echo "$PYTHON_VERSION" | cut -d. -f1)
|
|
50
|
+
PYTHON_MINOR=$(echo "$PYTHON_VERSION" | cut -d. -f2)
|
|
51
|
+
|
|
52
|
+
if [ "$PYTHON_MAJOR" -lt 3 ] || { [ "$PYTHON_MAJOR" -eq 3 ] && [ "$PYTHON_MINOR" -lt 9 ]; }; then
|
|
53
|
+
error "Python 3.9+ is required. Found: $PYTHON_VERSION"
|
|
54
|
+
exit 1
|
|
55
|
+
fi
|
|
56
|
+
|
|
57
|
+
success "git and python3 ($PYTHON_VERSION) are available."
|
|
58
|
+
|
|
59
|
+
# ---------------------------------------------------------------------------
|
|
60
|
+
# 2. Locate repo root
|
|
61
|
+
# ---------------------------------------------------------------------------
|
|
62
|
+
if ! ROOT=$(git rev-parse --show-toplevel 2>/dev/null); then
|
|
63
|
+
error "Not inside a git repository. Run this script from within the project directory."
|
|
64
|
+
exit 1
|
|
65
|
+
fi
|
|
66
|
+
|
|
67
|
+
info "Repository root: $ROOT"
|
|
68
|
+
|
|
69
|
+
# ---------------------------------------------------------------------------
|
|
70
|
+
# 3. Initialize submodule if needed
|
|
71
|
+
# ---------------------------------------------------------------------------
|
|
72
|
+
FORGE_PY=""
|
|
73
|
+
|
|
74
|
+
for candidate in "$ROOT/.agentic" "$ROOT/forge" "$ROOT/.forge"; do
|
|
75
|
+
if [ -f "$candidate/forge.py" ]; then
|
|
76
|
+
FORGE_PY="$candidate/forge.py"
|
|
77
|
+
FORGE_DIR="$candidate"
|
|
78
|
+
break
|
|
79
|
+
fi
|
|
80
|
+
done
|
|
81
|
+
|
|
82
|
+
if [ -z "$FORGE_PY" ]; then
|
|
83
|
+
info "forge not found — initializing git submodules..."
|
|
84
|
+
git -C "$ROOT" submodule update --init --recursive
|
|
85
|
+
|
|
86
|
+
# Try again after submodule init
|
|
87
|
+
for candidate in "$ROOT/.agentic" "$ROOT/forge" "$ROOT/.forge"; do
|
|
88
|
+
if [ -f "$candidate/forge.py" ]; then
|
|
89
|
+
FORGE_PY="$candidate/forge.py"
|
|
90
|
+
FORGE_DIR="$candidate"
|
|
91
|
+
break
|
|
92
|
+
fi
|
|
93
|
+
done
|
|
94
|
+
fi
|
|
95
|
+
|
|
96
|
+
if [ -z "$FORGE_PY" ]; then
|
|
97
|
+
error "forge.py not found after submodule initialization."
|
|
98
|
+
error "Checked: .agentic/, forge/, .forge/"
|
|
99
|
+
error "Ask a team lead for the correct forge submodule path."
|
|
100
|
+
exit 1
|
|
101
|
+
fi
|
|
102
|
+
|
|
103
|
+
success "forge found at: $FORGE_DIR"
|
|
104
|
+
|
|
105
|
+
# ---------------------------------------------------------------------------
|
|
106
|
+
# 4. Install Python dependencies
|
|
107
|
+
# ---------------------------------------------------------------------------
|
|
108
|
+
REQUIREMENTS="$FORGE_DIR/requirements.txt"
|
|
109
|
+
|
|
110
|
+
if [ -f "$REQUIREMENTS" ]; then
|
|
111
|
+
info "Installing Python dependencies..."
|
|
112
|
+
python3 -m pip install -r "$REQUIREMENTS" --quiet
|
|
113
|
+
success "Dependencies installed."
|
|
114
|
+
else
|
|
115
|
+
warn "requirements.txt not found at $REQUIREMENTS — skipping pip install."
|
|
116
|
+
warn "If forge fails, run: pip3 install pyyaml"
|
|
117
|
+
fi
|
|
118
|
+
|
|
119
|
+
# ---------------------------------------------------------------------------
|
|
120
|
+
# 5. Run forge-init (non-interactive)
|
|
121
|
+
# ---------------------------------------------------------------------------
|
|
122
|
+
INIT_SCRIPT="$FORGE_DIR/scripts/forge-init.py"
|
|
123
|
+
|
|
124
|
+
if [ -f "$INIT_SCRIPT" ]; then
|
|
125
|
+
info "Running forge-init (non-interactive)..."
|
|
126
|
+
python3 "$INIT_SCRIPT" --tool claude-code
|
|
127
|
+
else
|
|
128
|
+
warn "forge-init.py not found at $INIT_SCRIPT"
|
|
129
|
+
warn "Run the interactive CLI manually: python3 $FORGE_PY"
|
|
130
|
+
warn "Then select 'Inicializar agentes' from the menu."
|
|
131
|
+
exit 0
|
|
132
|
+
fi
|
|
133
|
+
|
|
134
|
+
# ---------------------------------------------------------------------------
|
|
135
|
+
# 6. Done
|
|
136
|
+
# ---------------------------------------------------------------------------
|
|
137
|
+
echo ""
|
|
138
|
+
success "forge is ready."
|
|
139
|
+
echo ""
|
|
140
|
+
echo -e " ${BOLD}Next steps:${RESET}"
|
|
141
|
+
echo -e " 1. Open Claude Code in this project directory"
|
|
142
|
+
echo -e " 2. Run ${BOLD}/session-start${RESET} to begin your first session"
|
|
143
|
+
echo -e " 3. The orchestrator will brief you on the current sprint"
|
|
144
|
+
echo ""
|
|
145
|
+
echo -e " ${BOLD}Agents installed:${RESET} $(ls "$ROOT/.claude/agents/" 2>/dev/null | wc -l | tr -d ' ') agents in .claude/agents/"
|
|
146
|
+
echo -e " ${BOLD}Commands installed:${RESET} $(ls "$ROOT/.claude/commands/" 2>/dev/null | wc -l | tr -d ' ') commands in .claude/commands/"
|
|
147
|
+
echo ""
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Token usage stats per agent/team — forge framework.
|
|
4
|
+
|
|
5
|
+
Usage:
|
|
6
|
+
python3 scripts/token-stats.py # tabla en terminal
|
|
7
|
+
python3 scripts/token-stats.py --json # JSON para embeber en progress.html
|
|
8
|
+
python3 scripts/token-stats.py --patch-html PATH # actualiza el HTML directamente
|
|
9
|
+
python3 scripts/token-stats.py --project-dir PATH # usar directorio de sesiones específico
|
|
10
|
+
|
|
11
|
+
PROJECT_DIR se detecta automáticamente desde el directorio de trabajo actual,
|
|
12
|
+
o se puede pasar explícitamente con --project-dir.
|
|
13
|
+
|
|
14
|
+
Identifica agentes lanzados con el protocolo teammate-message.
|
|
15
|
+
Las sesiones sin ese marcador se agrupan como "orquestador".
|
|
16
|
+
"""
|
|
17
|
+
import json, glob, os, re, sys
|
|
18
|
+
from collections import defaultdict
|
|
19
|
+
from datetime import datetime, timezone
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def detect_project_dir() -> str:
|
|
23
|
+
"""Detecta el directorio de sesiones Claude Code para el proyecto actual.
|
|
24
|
+
|
|
25
|
+
Claude Code usa el path completo con cada '/' reemplazado por '-',
|
|
26
|
+
manteniendo el '-' inicial (e.g. /Users/foo/proj → -Users-foo-proj).
|
|
27
|
+
"""
|
|
28
|
+
cwd = os.getcwd()
|
|
29
|
+
project_slug = cwd.replace("/", "-") # /Users/foo → -Users-foo (mantiene el '-' inicial)
|
|
30
|
+
return os.path.expanduser(f"~/.claude/projects/{project_slug}")
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
# Respetar --project-dir si se pasa, sino detectar automáticamente
|
|
34
|
+
if "--project-dir" in sys.argv:
|
|
35
|
+
idx = sys.argv.index("--project-dir")
|
|
36
|
+
PROJECT_DIR = sys.argv[idx + 1] if idx + 1 < len(sys.argv) else detect_project_dir()
|
|
37
|
+
else:
|
|
38
|
+
PROJECT_DIR = detect_project_dir()
|
|
39
|
+
|
|
40
|
+
# NOTA: precios actualizados a 2026-05. Verificar en https://www.anthropic.com/pricing
|
|
41
|
+
# si los precios cambiaron antes de confiar en estos reportes.
|
|
42
|
+
_PRICING_DATE = "2026-05"
|
|
43
|
+
|
|
44
|
+
# Precios por millón de tokens (USD) — actualizar si cambian
|
|
45
|
+
PRICING = {
|
|
46
|
+
"claude-sonnet-4-6": {"input": 3.00, "output": 15.00, "cache_write": 3.75, "cache_read": 0.30},
|
|
47
|
+
"claude-opus-4-7": {"input": 15.00, "output": 75.00, "cache_write": 18.75, "cache_read": 1.50},
|
|
48
|
+
"claude-haiku-4-5": {"input": 0.80, "output": 4.00, "cache_write": 1.00, "cache_read": 0.08},
|
|
49
|
+
}
|
|
50
|
+
DEFAULT_PRICE = PRICING["claude-sonnet-4-6"]
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def parse_sessions():
|
|
54
|
+
files = sorted(glob.glob(f"{PROJECT_DIR}/*.jsonl"))
|
|
55
|
+
results = []
|
|
56
|
+
|
|
57
|
+
for f in files:
|
|
58
|
+
session_id = os.path.basename(f).replace(".jsonl", "")[:8]
|
|
59
|
+
agent_name = None
|
|
60
|
+
team_name = None
|
|
61
|
+
per_model = defaultdict(lambda: {"input": 0, "output": 0, "cache_read": 0, "cache_write": 0, "msgs": 0})
|
|
62
|
+
first_msgs = 0
|
|
63
|
+
|
|
64
|
+
with open(f) as fh:
|
|
65
|
+
for line in fh:
|
|
66
|
+
try:
|
|
67
|
+
obj = json.loads(line)
|
|
68
|
+
except Exception:
|
|
69
|
+
continue
|
|
70
|
+
|
|
71
|
+
# Detect agent identity from first teammate-messages
|
|
72
|
+
if first_msgs < 15 and obj.get("type") == "user":
|
|
73
|
+
content = obj.get("message", {}).get("content", "")
|
|
74
|
+
if isinstance(content, list):
|
|
75
|
+
for block in content:
|
|
76
|
+
if isinstance(block, dict) and block.get("type") == "text":
|
|
77
|
+
content = block.get("text", "")
|
|
78
|
+
break
|
|
79
|
+
if isinstance(content, str):
|
|
80
|
+
m = re.search(r'Eres el teammate "([^"]+)" en el equipo "([^"]+)"', content)
|
|
81
|
+
if m and agent_name is None:
|
|
82
|
+
agent_name = m.group(1)
|
|
83
|
+
team_name = m.group(2)
|
|
84
|
+
first_msgs += 1
|
|
85
|
+
|
|
86
|
+
if obj.get("type") == "assistant":
|
|
87
|
+
msg = obj.get("message", {})
|
|
88
|
+
usage = msg.get("usage")
|
|
89
|
+
model = msg.get("model", "claude-sonnet-4-6")
|
|
90
|
+
if usage:
|
|
91
|
+
pm = per_model[model]
|
|
92
|
+
pm["input"] += usage.get("input_tokens", 0)
|
|
93
|
+
pm["output"] += usage.get("output_tokens", 0)
|
|
94
|
+
pm["cache_read"] += usage.get("cache_read_input_tokens", 0)
|
|
95
|
+
pm["cache_write"] += usage.get("cache_creation_input_tokens", 0)
|
|
96
|
+
pm["msgs"] += 1
|
|
97
|
+
|
|
98
|
+
total_cost = 0.0
|
|
99
|
+
totals = {"input": 0, "output": 0, "cache_read": 0, "cache_write": 0, "msgs": 0}
|
|
100
|
+
models_used = sorted(per_model.keys())
|
|
101
|
+
|
|
102
|
+
for model, v in per_model.items():
|
|
103
|
+
p = PRICING.get(model, DEFAULT_PRICE)
|
|
104
|
+
cost = (
|
|
105
|
+
v["input"] * p["input"] +
|
|
106
|
+
v["output"] * p["output"] +
|
|
107
|
+
v["cache_write"] * p["cache_write"] +
|
|
108
|
+
v["cache_read"] * p["cache_read"]
|
|
109
|
+
) / 1_000_000
|
|
110
|
+
total_cost += cost
|
|
111
|
+
for k in totals:
|
|
112
|
+
totals[k] += v[k]
|
|
113
|
+
|
|
114
|
+
label = f"{agent_name} [{team_name}]" if agent_name else f"orquestador/{session_id}"
|
|
115
|
+
results.append({
|
|
116
|
+
"label": label,
|
|
117
|
+
"session_id": session_id,
|
|
118
|
+
"agent": agent_name,
|
|
119
|
+
"team": team_name,
|
|
120
|
+
"models": models_used,
|
|
121
|
+
"msgs": totals["msgs"],
|
|
122
|
+
"input": totals["input"],
|
|
123
|
+
"output": totals["output"],
|
|
124
|
+
"cache_read": totals["cache_read"],
|
|
125
|
+
"cache_write":totals["cache_write"],
|
|
126
|
+
"cost_usd": round(total_cost, 2),
|
|
127
|
+
})
|
|
128
|
+
|
|
129
|
+
results.sort(key=lambda x: x["cost_usd"], reverse=True)
|
|
130
|
+
return results
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def fmt(n):
|
|
134
|
+
return f"{n:,}"
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def main():
|
|
138
|
+
rows = parse_sessions()
|
|
139
|
+
|
|
140
|
+
grand = {"input": 0, "output": 0, "cache_read": 0, "cache_write": 0, "msgs": 0, "cost_usd": 0.0}
|
|
141
|
+
for r in rows:
|
|
142
|
+
for k in grand:
|
|
143
|
+
grand[k] += r[k]
|
|
144
|
+
grand["cost_usd"] = round(grand["cost_usd"], 2)
|
|
145
|
+
|
|
146
|
+
output = {
|
|
147
|
+
"rows": rows,
|
|
148
|
+
"totals": grand,
|
|
149
|
+
"generated_at": datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ"),
|
|
150
|
+
"pricing_note": "USD estimates: Sonnet $3/$15 · Opus $15/$75 per M input/output tokens",
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
if "--json" in sys.argv:
|
|
154
|
+
print(json.dumps(output, indent=2))
|
|
155
|
+
return
|
|
156
|
+
|
|
157
|
+
if "--patch-html" in sys.argv:
|
|
158
|
+
idx = sys.argv.index("--patch-html")
|
|
159
|
+
html_path = sys.argv[idx + 1] if idx + 1 < len(sys.argv) else "docs/progress.html"
|
|
160
|
+
patch_html(html_path, output)
|
|
161
|
+
return
|
|
162
|
+
|
|
163
|
+
# Terminal table
|
|
164
|
+
print(f"\n{'Agente':<32} {'Modelo(s)':<22} {'Output':>9} {'CacheR':>12} {'CacheW':>10} {'Msgs':>5} {'USD est.':>9}")
|
|
165
|
+
print("─" * 105)
|
|
166
|
+
for r in rows:
|
|
167
|
+
m = ", ".join(r["models"])
|
|
168
|
+
print(f"{r['label']:<32} {m:<22} {fmt(r['output']):>9} {fmt(r['cache_read']):>12} {fmt(r['cache_write']):>10} {r['msgs']:>5,} ${r['cost_usd']:>8.2f}")
|
|
169
|
+
|
|
170
|
+
print("─" * 105)
|
|
171
|
+
print(f"{'TOTAL':<56} {fmt(grand['output']):>9} {fmt(grand['cache_read']):>12} {fmt(grand['cache_write']):>10} {grand['msgs']:>5,} ${grand['cost_usd']:>8.2f}")
|
|
172
|
+
print(f"\nGenerado: {output['generated_at']}")
|
|
173
|
+
print(f"Nota: {output['pricing_note']}")
|
|
174
|
+
print(f"Nota: precios al {_PRICING_DATE} — verificar anthropic.com/pricing si el reporte parece incorrecto.")
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
def patch_html(html_path: str, data: dict) -> None:
|
|
178
|
+
"""Inject fresh JSON data into the <script id="token-data"> block in the HTML file."""
|
|
179
|
+
import re as _re
|
|
180
|
+
try:
|
|
181
|
+
with open(html_path, "r") as f:
|
|
182
|
+
html = f.read()
|
|
183
|
+
except FileNotFoundError:
|
|
184
|
+
print(f"[token-stats] HTML not found: {html_path}", file=sys.stderr)
|
|
185
|
+
return
|
|
186
|
+
|
|
187
|
+
new_json = json.dumps(data, separators=(",", ":"))
|
|
188
|
+
pattern = r'(<script id="token-data" type="application/json">\n)([^\n]+)(\n</script>)'
|
|
189
|
+
new_html, n = _re.subn(pattern, lambda m: m.group(1) + new_json + m.group(3), html)
|
|
190
|
+
|
|
191
|
+
if n == 0:
|
|
192
|
+
print("[token-stats] token-data script block not found in HTML", file=sys.stderr)
|
|
193
|
+
return
|
|
194
|
+
|
|
195
|
+
with open(html_path, "w") as f:
|
|
196
|
+
f.write(new_html)
|
|
197
|
+
print(f"[token-stats] Patched {html_path} ({data['generated_at']})")
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
if __name__ == "__main__":
|
|
201
|
+
main()
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
# project.yaml — Modo enterprise (9+ personas)
|
|
2
|
+
# Roster completo, compliance activo, fases de sprint con specs, CI habilitado.
|
|
3
|
+
# Renombrar a project.yaml en la raíz del repositorio.
|
|
4
|
+
#
|
|
5
|
+
# CI: ejecutar forge-audit con --json para integrar en pipelines:
|
|
6
|
+
# python3 .agentic/scripts/forge-audit.py --json | jq '.summary'
|
|
7
|
+
# Retorna código de salida 1 si hay errores de severidad "error" o "critical".
|
|
8
|
+
|
|
9
|
+
project:
|
|
10
|
+
name: "Mi Proyecto Enterprise"
|
|
11
|
+
slug: "mi-proyecto-enterprise"
|
|
12
|
+
description: "Descripción del sistema"
|
|
13
|
+
language: "typescript" # typescript | python | ruby | go | php | mixed
|
|
14
|
+
status: "active"
|
|
15
|
+
mode: "enterprise" # Activa validaciones estrictas de compliance y auditoría
|
|
16
|
+
|
|
17
|
+
team:
|
|
18
|
+
name: "Equipo Principal"
|
|
19
|
+
members:
|
|
20
|
+
- name: "Tech Lead"
|
|
21
|
+
role: "lead"
|
|
22
|
+
email: "lead@empresa.com"
|
|
23
|
+
- name: "Backend Senior"
|
|
24
|
+
role: "backend"
|
|
25
|
+
email: "backend@empresa.com"
|
|
26
|
+
- name: "Frontend Senior"
|
|
27
|
+
role: "frontend"
|
|
28
|
+
email: "frontend@empresa.com"
|
|
29
|
+
- name: "QA Engineer"
|
|
30
|
+
role: "qa"
|
|
31
|
+
email: "qa@empresa.com"
|
|
32
|
+
- name: "Security Engineer"
|
|
33
|
+
role: "backend"
|
|
34
|
+
email: "security@empresa.com"
|
|
35
|
+
|
|
36
|
+
stack:
|
|
37
|
+
backend: null # "hono" | "fastapi" | "rails" | "express" | "laravel" | null
|
|
38
|
+
frontend: null # "nextjs" | "nuxt" | "remix" | "rails-views" | null
|
|
39
|
+
database: null # "postgresql" | "mysql" | "sqlite" | null
|
|
40
|
+
cache: null # "redis" | "memcached" | null
|
|
41
|
+
testing: ["vitest", "playwright"]
|
|
42
|
+
|
|
43
|
+
agents:
|
|
44
|
+
active:
|
|
45
|
+
- orchestrator
|
|
46
|
+
- backend-engineer
|
|
47
|
+
- frontend-engineer
|
|
48
|
+
- test-engineer
|
|
49
|
+
- docs-writer
|
|
50
|
+
# Compliance y seguridad obligatorios en enterprise
|
|
51
|
+
compliance:
|
|
52
|
+
- compliance-reviewer # GDPR, LGPD, Ley 21.719, CCPA
|
|
53
|
+
- security-auditor # Auditoría de vulnerabilidades — obligatorio pre-merge
|
|
54
|
+
profiles: [] # Ejemplo: [hono-drizzle, nextjs-admin]
|
|
55
|
+
|
|
56
|
+
sprint:
|
|
57
|
+
current: 1
|
|
58
|
+
length_days: 14
|
|
59
|
+
phases:
|
|
60
|
+
- id: "A"
|
|
61
|
+
name: "Core"
|
|
62
|
+
specs:
|
|
63
|
+
- "docs/specs/A1-autenticacion.md"
|
|
64
|
+
- "docs/specs/A2-modelo-datos.md"
|
|
65
|
+
- id: "B"
|
|
66
|
+
name: "Features"
|
|
67
|
+
specs:
|
|
68
|
+
- "docs/specs/B1-feature-principal.md"
|
|
69
|
+
- "docs/specs/B2-integraciones.md"
|
|
70
|
+
- id: "C"
|
|
71
|
+
name: "Compliance & Security"
|
|
72
|
+
specs:
|
|
73
|
+
- "docs/specs/C1-audit-trail.md"
|
|
74
|
+
- "docs/specs/C2-dsar-flow.md"
|
|
75
|
+
|
|
76
|
+
skills:
|
|
77
|
+
active:
|
|
78
|
+
- security-audit
|
|
79
|
+
- db-migrate
|
|
80
|
+
- local2prod
|
|
81
|
+
- new-feature
|
|
82
|
+
integrations: []
|
|
83
|
+
|
|
84
|
+
deploy:
|
|
85
|
+
provider: null # "vercel" | "railway" | "fly" | "github-actions" | "custom"
|
|
86
|
+
branch: "main"
|
|
87
|
+
|
|
88
|
+
# Compliance enterprise: múltiples marcos regulatorios
|
|
89
|
+
# forge-audit verifica automáticamente los frameworks listados aquí.
|
|
90
|
+
# Usar --json en CI: python3 .agentic/scripts/forge-audit.py --json
|
|
91
|
+
compliance:
|
|
92
|
+
frameworks:
|
|
93
|
+
- gdpr # Unión Europea
|
|
94
|
+
- lgpd # Brasil
|
|
95
|
+
- ley-21719 # Chile
|
|
96
|
+
- ccpa # California, EE.UU.
|
|
97
|
+
pii_handling: true # El sistema procesa datos personales
|
|
98
|
+
audit_logs: true # Logs inmutables requeridos (append-only, sin UPDATE/DELETE)
|
|
99
|
+
|
|
100
|
+
paths:
|
|
101
|
+
specs: "docs/specs"
|
|
102
|
+
progress: "docs/progress.html"
|
|
103
|
+
migrations: null # "packages/api/migrations" | "db/migrate" | null
|
|
104
|
+
tests: null # "tests/" | "__tests__/" | null
|
|
105
|
+
|
|
106
|
+
integrations:
|
|
107
|
+
obsidian:
|
|
108
|
+
vault_path: null
|
|
109
|
+
map:
|
|
110
|
+
api: null
|
|
111
|
+
database: null
|
|
112
|
+
frontend: null
|
|
113
|
+
deploy: null
|
|
114
|
+
decisions: null
|