@cristiancorreau/forge 2.9.5 → 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.
Files changed (50) hide show
  1. package/assets/adapters/claude-code/commands/new-feature.md +10 -5
  2. package/assets/adapters/claude-code/commands/plan.md +61 -36
  3. package/assets/adapters/claude-code/commands/session-start.md +1 -1
  4. package/assets/adapters/claude-code/commands/ship.md +6 -4
  5. package/assets/adapters/claude-code/commands/work.md +8 -6
  6. package/assets/core/skills/README.md +2 -2
  7. package/assets/core/skills/aitmpl-search/SKILL.md +7 -19
  8. package/assets/core/skills/local2prod/SKILL.md +1 -1
  9. package/assets/core/skills/new-feature/SKILL.md +1 -1
  10. package/assets/core/skills/phase-kickoff/SKILL.md +2 -0
  11. package/assets/core/skills/spec/SKILL.md +2 -0
  12. package/assets/core/skills/wiki-ingest/SKILL.md +7 -7
  13. package/assets/core/skills/wiki-lint/SKILL.md +4 -4
  14. package/assets/core/skills/wiki-query/SKILL.md +3 -3
  15. package/dist/commands/doctor.d.ts.map +1 -1
  16. package/dist/commands/doctor.js +2 -1
  17. package/dist/commands/doctor.js.map +1 -1
  18. package/dist/commands/init.js +1 -1
  19. package/dist/lib/paths.d.ts +1 -2
  20. package/dist/lib/paths.d.ts.map +1 -1
  21. package/dist/lib/paths.js +12 -16
  22. package/dist/lib/paths.js.map +1 -1
  23. package/dist/version.d.ts +1 -1
  24. package/dist/version.js +1 -1
  25. package/package.json +2 -2
  26. package/assets/adapters/claude-code/generate-claude-md.py +0 -304
  27. package/assets/adapters/codex/generate-codex-config.py +0 -269
  28. package/assets/adapters/kiro/generate-steering.py +0 -367
  29. package/assets/adapters/opencode/generate-agents-md.py +0 -262
  30. package/assets/core/hooks/pre-bash-check.py +0 -202
  31. package/assets/core/hooks/pre-edit-check.py +0 -317
  32. package/assets/forge.py +0 -1265
  33. package/assets/requirements.txt +0 -2
  34. package/assets/scripts/aitmpl-search.py +0 -808
  35. package/assets/scripts/forge-add-opportunities.py +0 -92
  36. package/assets/scripts/forge-audit.py +0 -1061
  37. package/assets/scripts/forge-generate-all.py +0 -283
  38. package/assets/scripts/forge-init.py +0 -900
  39. package/assets/scripts/forge-migrate-project-yaml.py +0 -397
  40. package/assets/scripts/forge-scaffold-profile.py +0 -181
  41. package/assets/scripts/forge-teardown.py +0 -193
  42. package/assets/scripts/forge-validate-project-yaml.py +0 -457
  43. package/assets/scripts/forge-wizard.py +0 -1003
  44. package/assets/scripts/setup-codex.sh +0 -229
  45. package/assets/scripts/team-install.sh +0 -147
  46. package/assets/scripts/token-stats.py +0 -201
  47. package/dist/lib/python.d.ts +0 -4
  48. package/dist/lib/python.d.ts.map +0 -1
  49. package/dist/lib/python.js +0 -46
  50. 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()