@cristiancorreau/forge 2.9.6 → 2.9.7
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/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/doctor.js +2 -1
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/init.js +1 -1
- package/dist/lib/paths.d.ts +1 -2
- package/dist/lib/paths.d.ts.map +1 -1
- package/dist/lib/paths.js +12 -16
- package/dist/lib/paths.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
- package/assets/adapters/claude-code/generate-claude-md.py +0 -304
- package/assets/adapters/codex/generate-codex-config.py +0 -269
- package/assets/adapters/kiro/generate-steering.py +0 -367
- package/assets/adapters/opencode/generate-agents-md.py +0 -262
- package/assets/core/hooks/pre-bash-check.py +0 -202
- package/assets/core/hooks/pre-edit-check.py +0 -317
- package/assets/forge.py +0 -1265
- package/assets/requirements.txt +0 -2
- package/assets/scripts/aitmpl-search.py +0 -808
- package/assets/scripts/forge-add-opportunities.py +0 -92
- package/assets/scripts/forge-audit.py +0 -1061
- package/assets/scripts/forge-generate-all.py +0 -283
- package/assets/scripts/forge-init.py +0 -900
- package/assets/scripts/forge-migrate-project-yaml.py +0 -397
- package/assets/scripts/forge-scaffold-profile.py +0 -181
- package/assets/scripts/forge-teardown.py +0 -193
- package/assets/scripts/forge-validate-project-yaml.py +0 -457
- package/assets/scripts/forge-wizard.py +0 -1003
- package/assets/scripts/setup-codex.sh +0 -229
- package/assets/scripts/team-install.sh +0 -147
- package/assets/scripts/token-stats.py +0 -201
- package/dist/lib/python.d.ts +0 -4
- package/dist/lib/python.d.ts.map +0 -1
- package/dist/lib/python.js +0 -46
- package/dist/lib/python.js.map +0 -1
|
@@ -1,283 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
"""
|
|
3
|
-
forge-generate-all.py — Regenera configuración para todos los runtimes instalados.
|
|
4
|
-
|
|
5
|
-
Usage:
|
|
6
|
-
python3 scripts/forge-generate-all.py
|
|
7
|
-
python3 scripts/forge-generate-all.py --runtime claude-code
|
|
8
|
-
python3 scripts/forge-generate-all.py --runtime opencode
|
|
9
|
-
python3 scripts/forge-generate-all.py --runtime codex
|
|
10
|
-
python3 scripts/forge-generate-all.py --runtime kiro
|
|
11
|
-
python3 scripts/forge-generate-all.py --dry-run
|
|
12
|
-
python3 scripts/forge-generate-all.py --runtime claude-code --dry-run
|
|
13
|
-
|
|
14
|
-
Lee project.yaml y llama a los generadores de cada runtime instalado.
|
|
15
|
-
|
|
16
|
-
Detección automática de runtimes activos:
|
|
17
|
-
- claude-code → existe .claude/ en la raíz del proyecto
|
|
18
|
-
- opencode → existe .opencode/ en la raíz del proyecto
|
|
19
|
-
- codex → existe AGENTS.md Y no hay .claude/ (AGENTS.md es de Codex, no OpenCode)
|
|
20
|
-
- kiro → existe .kiro/ en la raíz del proyecto
|
|
21
|
-
|
|
22
|
-
Si project.yaml tiene una sección `runtimes.active`, esa lista tiene prioridad
|
|
23
|
-
sobre la auto-detección.
|
|
24
|
-
|
|
25
|
-
Nota: forge-init.py llama a los generadores individuales por separado.
|
|
26
|
-
Este script es el punto de entrada unificado para regeneración post-init.
|
|
27
|
-
|
|
28
|
-
Requiere: pyyaml
|
|
29
|
-
"""
|
|
30
|
-
from __future__ import annotations
|
|
31
|
-
|
|
32
|
-
import subprocess
|
|
33
|
-
import sys
|
|
34
|
-
from pathlib import Path
|
|
35
|
-
|
|
36
|
-
try:
|
|
37
|
-
import yaml
|
|
38
|
-
except ImportError:
|
|
39
|
-
print("ERROR: pyyaml requerido. pip install pyyaml", file=sys.stderr)
|
|
40
|
-
sys.exit(1)
|
|
41
|
-
|
|
42
|
-
# --- Constantes ---
|
|
43
|
-
|
|
44
|
-
ALL_RUNTIMES = ["claude-code", "opencode", "codex", "kiro"]
|
|
45
|
-
|
|
46
|
-
ADAPTER_SCRIPTS: dict[str, str] = {
|
|
47
|
-
"claude-code": "adapters/claude-code/generate-claude-md.py",
|
|
48
|
-
"opencode": "adapters/opencode/generate-agents-md.py",
|
|
49
|
-
"codex": "adapters/codex/generate-codex-config.py",
|
|
50
|
-
"kiro": "adapters/kiro/generate-steering.py",
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
# Archivos / dirs que indican que un runtime está instalado en el proyecto
|
|
54
|
-
RUNTIME_MARKERS: dict[str, list[str]] = {
|
|
55
|
-
"claude-code": [".claude"],
|
|
56
|
-
"opencode": [".opencode"],
|
|
57
|
-
"kiro": [".kiro"],
|
|
58
|
-
# Codex: AGENTS.md existe y no hay .claude/ (OpenCode también usa AGENTS.md,
|
|
59
|
-
# así que sólo lo activamos si no hay .opencode/ ni .claude/).
|
|
60
|
-
"codex": ["AGENTS.md"],
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
# --- Helpers ---
|
|
65
|
-
|
|
66
|
-
def find_project_root() -> Path:
|
|
67
|
-
here = Path.cwd()
|
|
68
|
-
for p in [here] + list(here.parents):
|
|
69
|
-
if (p / "project.yaml").exists():
|
|
70
|
-
return p
|
|
71
|
-
raise FileNotFoundError(
|
|
72
|
-
"No se encontró project.yaml.\n"
|
|
73
|
-
" → Copiar la plantilla: cp templates/project.yaml.tpl project.yaml\n"
|
|
74
|
-
" → O correr el wizard: python3 scripts/forge-wizard.py"
|
|
75
|
-
)
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
def find_forge_dir() -> Path:
|
|
79
|
-
root = find_project_root()
|
|
80
|
-
for candidate in [root / ".agentic", root / "forge", Path(__file__).parent.parent]:
|
|
81
|
-
if (candidate / "core").exists():
|
|
82
|
-
return candidate
|
|
83
|
-
raise FileNotFoundError("No se encontró el directorio forge con core/")
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
def load_config(root: Path) -> dict:
|
|
87
|
-
with open(root / "project.yaml") as f:
|
|
88
|
-
return yaml.safe_load(f) or {}
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
def detect_active_runtimes(root: Path, config: dict) -> list[str]:
|
|
92
|
-
"""
|
|
93
|
-
Determina qué runtimes están activos.
|
|
94
|
-
|
|
95
|
-
Prioridad:
|
|
96
|
-
1. project.yaml → runtimes.active (explícito, gana siempre)
|
|
97
|
-
2. Auto-detección por marcadores en el sistema de archivos
|
|
98
|
-
"""
|
|
99
|
-
# 1. Declaración explícita en project.yaml
|
|
100
|
-
runtimes_cfg = config.get("runtimes", {})
|
|
101
|
-
declared = runtimes_cfg.get("active") if runtimes_cfg else None
|
|
102
|
-
if declared:
|
|
103
|
-
return [r for r in declared if r in ALL_RUNTIMES]
|
|
104
|
-
|
|
105
|
-
# 2. Auto-detección
|
|
106
|
-
active: list[str] = []
|
|
107
|
-
has_claude = (root / ".claude").exists()
|
|
108
|
-
has_opencode = (root / ".opencode").exists()
|
|
109
|
-
|
|
110
|
-
for runtime, markers in RUNTIME_MARKERS.items():
|
|
111
|
-
for marker in markers:
|
|
112
|
-
if (root / marker).exists():
|
|
113
|
-
# AGENTS.md sin .claude y sin .opencode → Codex
|
|
114
|
-
if runtime == "codex" and (has_claude or has_opencode):
|
|
115
|
-
continue
|
|
116
|
-
active.append(runtime)
|
|
117
|
-
break
|
|
118
|
-
|
|
119
|
-
return active
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
def parse_args() -> tuple[str | None, bool]:
|
|
123
|
-
"""Retorna (runtime_override, dry_run)."""
|
|
124
|
-
runtime: str | None = None
|
|
125
|
-
dry_run = "--dry-run" in sys.argv
|
|
126
|
-
|
|
127
|
-
args = sys.argv[1:]
|
|
128
|
-
for i, arg in enumerate(args):
|
|
129
|
-
if arg == "--runtime" and i + 1 < len(args):
|
|
130
|
-
runtime = args[i + 1]
|
|
131
|
-
elif arg.startswith("--runtime="):
|
|
132
|
-
runtime = arg.split("=", 1)[1]
|
|
133
|
-
|
|
134
|
-
if runtime and runtime not in ALL_RUNTIMES:
|
|
135
|
-
print(f"ERROR: runtime desconocido '{runtime}'. Opciones: {', '.join(ALL_RUNTIMES)}", file=sys.stderr)
|
|
136
|
-
sys.exit(1)
|
|
137
|
-
|
|
138
|
-
return runtime, dry_run
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
def run_generator(
|
|
142
|
-
forge: Path,
|
|
143
|
-
root: Path,
|
|
144
|
-
runtime: str,
|
|
145
|
-
dry_run: bool,
|
|
146
|
-
force: bool,
|
|
147
|
-
) -> tuple[str, list[str]]:
|
|
148
|
-
"""
|
|
149
|
-
Ejecuta el generador para el runtime dado.
|
|
150
|
-
|
|
151
|
-
Retorna (status, archivos_generados).
|
|
152
|
-
Status: "OK" | "DRY-RUN" | "MISS" | "ERR"
|
|
153
|
-
"""
|
|
154
|
-
rel_script = ADAPTER_SCRIPTS[runtime]
|
|
155
|
-
script = forge / rel_script
|
|
156
|
-
|
|
157
|
-
if not script.exists():
|
|
158
|
-
return "MISS", []
|
|
159
|
-
|
|
160
|
-
if dry_run:
|
|
161
|
-
return "DRY-RUN", _expected_files(runtime, root)
|
|
162
|
-
|
|
163
|
-
args = ["python3", str(script)]
|
|
164
|
-
if force:
|
|
165
|
-
args.append("--force")
|
|
166
|
-
|
|
167
|
-
result = subprocess.run(args, cwd=str(root), capture_output=True, text=True)
|
|
168
|
-
|
|
169
|
-
if result.returncode != 0:
|
|
170
|
-
err = result.stderr.strip() or result.stdout.strip()
|
|
171
|
-
return f"ERR: {err[:80]}", []
|
|
172
|
-
|
|
173
|
-
return "OK", _expected_files(runtime, root)
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
def _expected_files(runtime: str, root: Path) -> list[str]:
|
|
177
|
-
"""Retorna la lista de archivos que genera cada runtime (para el resumen)."""
|
|
178
|
-
files_map = {
|
|
179
|
-
"claude-code": ["CLAUDE.md"],
|
|
180
|
-
"opencode": ["AGENTS.md"],
|
|
181
|
-
"codex": ["AGENTS.md"],
|
|
182
|
-
"kiro": [
|
|
183
|
-
".kiro/steering/product.md",
|
|
184
|
-
".kiro/steering/structure.md",
|
|
185
|
-
".kiro/steering/agents.md",
|
|
186
|
-
],
|
|
187
|
-
}
|
|
188
|
-
return files_map.get(runtime, [])
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
def print_summary(results: list[tuple[str, str, list[str]]]):
|
|
192
|
-
"""Imprime la tabla resumen: Runtime | Status | Archivos."""
|
|
193
|
-
col_w = [13, 14, 35]
|
|
194
|
-
sep = "+" + "+".join("-" * (w + 2) for w in col_w) + "+"
|
|
195
|
-
header = "| {:<{}} | {:<{}} | {:<{}} |".format(
|
|
196
|
-
"Runtime", col_w[0], "Status", col_w[1], "Archivos generados", col_w[2]
|
|
197
|
-
)
|
|
198
|
-
|
|
199
|
-
print()
|
|
200
|
-
print(sep)
|
|
201
|
-
print(header)
|
|
202
|
-
print(sep)
|
|
203
|
-
|
|
204
|
-
for runtime, status, files in results:
|
|
205
|
-
files_str = ", ".join(files) if files else "—"
|
|
206
|
-
# Truncar si es muy largo
|
|
207
|
-
if len(files_str) > col_w[2]:
|
|
208
|
-
files_str = files_str[: col_w[2] - 1] + "…"
|
|
209
|
-
icon = "OK" if status == "OK" else status
|
|
210
|
-
print("| {:<{}} | {:<{}} | {:<{}} |".format(
|
|
211
|
-
runtime, col_w[0], icon, col_w[1], files_str, col_w[2]
|
|
212
|
-
))
|
|
213
|
-
|
|
214
|
-
print(sep)
|
|
215
|
-
print()
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
# --- Entry point ---
|
|
219
|
-
|
|
220
|
-
def main():
|
|
221
|
-
runtime_filter, dry_run = parse_args()
|
|
222
|
-
force = "--force" in sys.argv
|
|
223
|
-
|
|
224
|
-
try:
|
|
225
|
-
root = find_project_root()
|
|
226
|
-
forge = find_forge_dir()
|
|
227
|
-
except FileNotFoundError as e:
|
|
228
|
-
print(f"ERROR: {e}", file=sys.stderr)
|
|
229
|
-
sys.exit(1)
|
|
230
|
-
|
|
231
|
-
config = load_config(root)
|
|
232
|
-
|
|
233
|
-
print(f"forge-generate-all")
|
|
234
|
-
print(f" Proyecto : {config.get('project', {}).get('name', '?')}")
|
|
235
|
-
print(f" Root : {root}")
|
|
236
|
-
print(f" Forge : {forge}")
|
|
237
|
-
if dry_run:
|
|
238
|
-
print(f" Modo : DRY-RUN (no escribe archivos)")
|
|
239
|
-
if force:
|
|
240
|
-
print(f" Force : sí — sobreescribe existentes")
|
|
241
|
-
print()
|
|
242
|
-
|
|
243
|
-
# Runtimes a procesar
|
|
244
|
-
if runtime_filter:
|
|
245
|
-
runtimes_to_run = [runtime_filter]
|
|
246
|
-
print(f" Runtime : {runtime_filter} (explícito via --runtime)")
|
|
247
|
-
else:
|
|
248
|
-
runtimes_to_run = detect_active_runtimes(root, config)
|
|
249
|
-
if not runtimes_to_run:
|
|
250
|
-
print(" No se detectaron runtimes activos.")
|
|
251
|
-
print(" → Instalar un runtime primero (ej: python3 scripts/forge-init.py --tool claude-code)")
|
|
252
|
-
print(" → O declarar runtimes.active en project.yaml")
|
|
253
|
-
sys.exit(0)
|
|
254
|
-
source = "project.yaml" if config.get("runtimes", {}).get("active") else "auto-detectado"
|
|
255
|
-
print(f" Runtimes : {', '.join(runtimes_to_run)} ({source})")
|
|
256
|
-
|
|
257
|
-
print()
|
|
258
|
-
|
|
259
|
-
results: list[tuple[str, str, list[str]]] = []
|
|
260
|
-
errors = 0
|
|
261
|
-
|
|
262
|
-
for runtime in runtimes_to_run:
|
|
263
|
-
print(f" [{runtime}] generando...", end="", flush=True)
|
|
264
|
-
status, files = run_generator(forge, root, runtime, dry_run, force)
|
|
265
|
-
print(f" {status}")
|
|
266
|
-
if status.startswith("ERR"):
|
|
267
|
-
errors += 1
|
|
268
|
-
results.append((runtime, status, files))
|
|
269
|
-
|
|
270
|
-
print_summary(results)
|
|
271
|
-
|
|
272
|
-
if dry_run:
|
|
273
|
-
print(" DRY-RUN: ningún archivo fue modificado.")
|
|
274
|
-
print(" Correr sin --dry-run para aplicar los cambios.")
|
|
275
|
-
elif errors == 0:
|
|
276
|
-
print(" Configuración regenerada. Revisar los archivos antes de commitear.")
|
|
277
|
-
else:
|
|
278
|
-
print(f" {errors} runtime(s) con errores. Ver detalles arriba.")
|
|
279
|
-
sys.exit(1)
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
if __name__ == "__main__":
|
|
283
|
-
main()
|