@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.
Files changed (153) hide show
  1. package/CHANGELOG.md +228 -0
  2. package/LICENSE +191 -0
  3. package/README.md +156 -0
  4. package/assets/adapters/claude-code/commands/deploy-check.md +12 -0
  5. package/assets/adapters/claude-code/commands/new-feature.md +11 -0
  6. package/assets/adapters/claude-code/commands/plan.md +116 -0
  7. package/assets/adapters/claude-code/commands/review.md +219 -0
  8. package/assets/adapters/claude-code/commands/session-close.md +109 -0
  9. package/assets/adapters/claude-code/commands/session-start.md +59 -0
  10. package/assets/adapters/claude-code/commands/ship.md +133 -0
  11. package/assets/adapters/claude-code/commands/wiki-ingest.md +7 -0
  12. package/assets/adapters/claude-code/commands/wiki-lint.md +5 -0
  13. package/assets/adapters/claude-code/commands/wiki-query.md +7 -0
  14. package/assets/adapters/claude-code/commands/work.md +101 -0
  15. package/assets/adapters/claude-code/generate-claude-md.py +304 -0
  16. package/assets/adapters/codex/commands/plan.md +63 -0
  17. package/assets/adapters/codex/commands/review.md +53 -0
  18. package/assets/adapters/codex/commands/session-close.md +53 -0
  19. package/assets/adapters/codex/commands/session-start.md +49 -0
  20. package/assets/adapters/codex/commands/ship.md +53 -0
  21. package/assets/adapters/codex/commands/work.md +53 -0
  22. package/assets/adapters/codex/generate-codex-config.py +269 -0
  23. package/assets/adapters/codex/hooks/codex.yaml.tpl +43 -0
  24. package/assets/adapters/codex/hooks/forge-codex-finish.sh +158 -0
  25. package/assets/adapters/codex/hooks/forge-codex-start.sh +186 -0
  26. package/assets/adapters/kiro/generate-steering.py +367 -0
  27. package/assets/adapters/opencode/HOOKS.md +123 -0
  28. package/assets/adapters/opencode/commands/plan.md +119 -0
  29. package/assets/adapters/opencode/commands/review.md +164 -0
  30. package/assets/adapters/opencode/commands/session-close.md +111 -0
  31. package/assets/adapters/opencode/commands/session-start.md +62 -0
  32. package/assets/adapters/opencode/commands/ship.md +135 -0
  33. package/assets/adapters/opencode/commands/work.md +82 -0
  34. package/assets/adapters/opencode/generate-agents-md.py +262 -0
  35. package/assets/core/agents/backend-engineer.md +61 -0
  36. package/assets/core/agents/compliance-reviewer.md +83 -0
  37. package/assets/core/agents/docs-writer.md +77 -0
  38. package/assets/core/agents/frontend-engineer.md +70 -0
  39. package/assets/core/agents/orchestrator.md +104 -0
  40. package/assets/core/agents/security-auditor.md +54 -0
  41. package/assets/core/agents/test-engineer.md +57 -0
  42. package/assets/core/hooks/hooks-registry.yaml +48 -0
  43. package/assets/core/hooks/post-turn-check.sh +139 -0
  44. package/assets/core/hooks/pre-bash-check.py +202 -0
  45. package/assets/core/hooks/pre-edit-check.py +317 -0
  46. package/assets/core/hooks/session-start.sh +184 -0
  47. package/assets/core/schemas/project.schema.json +503 -0
  48. package/assets/core/skills/README.md +88 -0
  49. package/assets/core/skills/aitmpl-search/SKILL.md +74 -0
  50. package/assets/core/skills/browser-test/SKILL.md +177 -0
  51. package/assets/core/skills/db-migrate/SKILL.md +163 -0
  52. package/assets/core/skills/local2prod/SKILL.md +147 -0
  53. package/assets/core/skills/new-feature/SKILL.md +155 -0
  54. package/assets/core/skills/obsidian-sync/SKILL.md +152 -0
  55. package/assets/core/skills/phase-kickoff/SKILL.md +69 -0
  56. package/assets/core/skills/security-audit/SKILL.md +125 -0
  57. package/assets/core/skills/spec/SKILL.md +72 -0
  58. package/assets/core/skills/wiki-ingest/SKILL.md +183 -0
  59. package/assets/core/skills/wiki-lint/SKILL.md +109 -0
  60. package/assets/core/skills/wiki-query/SKILL.md +100 -0
  61. package/assets/core/templates/claude-md/architecture.rules +20 -0
  62. package/assets/core/templates/claude-md/global.md +30 -0
  63. package/assets/core/templates/claude-md/project.md +36 -0
  64. package/assets/core/templates/daily-note.md +38 -0
  65. package/assets/core/templates/spec-template.md +43 -0
  66. package/assets/core/workflows/sdd.md +69 -0
  67. package/assets/core/workflows/sprint.md +59 -0
  68. package/assets/forge.py +1265 -0
  69. package/assets/hooks/pre-commit +43 -0
  70. package/assets/manifest.json +274 -0
  71. package/assets/profiles/astro/README.md +24 -0
  72. package/assets/profiles/astro/agents/frontend-engineer.md +74 -0
  73. package/assets/profiles/django/agents/api-engineer.md +83 -0
  74. package/assets/profiles/expo/README.md +24 -0
  75. package/assets/profiles/expo/agents/mobile-engineer.md +69 -0
  76. package/assets/profiles/express/agents/api-engineer.md +60 -0
  77. package/assets/profiles/fastapi/README.md +32 -0
  78. package/assets/profiles/fastapi/agents/api-engineer.md +87 -0
  79. package/assets/profiles/go-gin/agents/api-engineer.md +98 -0
  80. package/assets/profiles/hono-drizzle/README.md +31 -0
  81. package/assets/profiles/hono-drizzle/agents/api-engineer.md +82 -0
  82. package/assets/profiles/laravel/README.md +32 -0
  83. package/assets/profiles/laravel/agents/api-engineer.md +114 -0
  84. package/assets/profiles/laravel/agents/fullstack-engineer.md +67 -0
  85. package/assets/profiles/laravel/agents/migration-specialist.md +420 -0
  86. package/assets/profiles/nestjs/agents/api-engineer.md +79 -0
  87. package/assets/profiles/nextjs-admin/README.md +32 -0
  88. package/assets/profiles/nextjs-admin/agents/admin-engineer.md +78 -0
  89. package/assets/profiles/playwright-crawler/agents/scanner-engineer.md +51 -0
  90. package/assets/profiles/rails/agents/fullstack-engineer.md +61 -0
  91. package/assets/profiles/sveltekit/agents/frontend-engineer.md +96 -0
  92. package/assets/profiles/vuenuxt/agents/frontend-engineer.md +82 -0
  93. package/assets/profiles/wordpress/README.md +30 -0
  94. package/assets/profiles/wordpress/agents/divi-engineer.md +273 -0
  95. package/assets/profiles/wordpress/agents/elementor-engineer.md +310 -0
  96. package/assets/profiles/wordpress/agents/wp-engineer.md +216 -0
  97. package/assets/requirements.txt +2 -0
  98. package/assets/scripts/aitmpl-search.py +808 -0
  99. package/assets/scripts/forge-add-opportunities.py +92 -0
  100. package/assets/scripts/forge-audit.py +1061 -0
  101. package/assets/scripts/forge-generate-all.py +283 -0
  102. package/assets/scripts/forge-init.py +900 -0
  103. package/assets/scripts/forge-migrate-project-yaml.py +397 -0
  104. package/assets/scripts/forge-scaffold-profile.py +181 -0
  105. package/assets/scripts/forge-teardown.py +193 -0
  106. package/assets/scripts/forge-validate-project-yaml.py +457 -0
  107. package/assets/scripts/forge-wizard.py +1003 -0
  108. package/assets/scripts/setup-codex.sh +229 -0
  109. package/assets/scripts/team-install.sh +147 -0
  110. package/assets/scripts/token-stats.py +201 -0
  111. package/assets/templates/modes/enterprise.yaml.tpl +114 -0
  112. package/assets/templates/modes/multi-runtime.yaml.tpl +89 -0
  113. package/assets/templates/modes/new-stack.yaml.tpl +101 -0
  114. package/assets/templates/modes/startup.yaml.tpl +74 -0
  115. package/assets/templates/project.yaml.tpl +185 -0
  116. package/assets/templates/wiki/concepts/_template.md +22 -0
  117. package/assets/templates/wiki/entities/_template.md +19 -0
  118. package/assets/templates/wiki/index.md +32 -0
  119. package/assets/templates/wiki/log.md +6 -0
  120. package/assets/templates/wiki/sources/_template.md +25 -0
  121. package/dist/cli.d.ts +3 -0
  122. package/dist/cli.d.ts.map +1 -0
  123. package/dist/cli.js +64 -0
  124. package/dist/cli.js.map +1 -0
  125. package/dist/commands/audit.d.ts +2 -0
  126. package/dist/commands/audit.d.ts.map +1 -0
  127. package/dist/commands/audit.js +21 -0
  128. package/dist/commands/audit.js.map +1 -0
  129. package/dist/commands/doctor.d.ts +2 -0
  130. package/dist/commands/doctor.d.ts.map +1 -0
  131. package/dist/commands/doctor.js +58 -0
  132. package/dist/commands/doctor.js.map +1 -0
  133. package/dist/commands/generate.d.ts +2 -0
  134. package/dist/commands/generate.d.ts.map +1 -0
  135. package/dist/commands/generate.js +27 -0
  136. package/dist/commands/generate.js.map +1 -0
  137. package/dist/commands/init.d.ts +2 -0
  138. package/dist/commands/init.d.ts.map +1 -0
  139. package/dist/commands/init.js +22 -0
  140. package/dist/commands/init.js.map +1 -0
  141. package/dist/commands/validate.d.ts +2 -0
  142. package/dist/commands/validate.d.ts.map +1 -0
  143. package/dist/commands/validate.js +20 -0
  144. package/dist/commands/validate.js.map +1 -0
  145. package/dist/lib/paths.d.ts +10 -0
  146. package/dist/lib/paths.d.ts.map +1 -0
  147. package/dist/lib/paths.js +49 -0
  148. package/dist/lib/paths.js.map +1 -0
  149. package/dist/lib/python.d.ts +4 -0
  150. package/dist/lib/python.d.ts.map +1 -0
  151. package/dist/lib/python.js +46 -0
  152. package/dist/lib/python.js.map +1 -0
  153. package/package.json +46 -0
@@ -0,0 +1,53 @@
1
+ ---
2
+ name: work
3
+ description: Template para implementar una feature con Codex CLI siguiendo SDD
4
+ usage: Copia el contenido de Prompt en tu sesión de Codex
5
+ ---
6
+
7
+ ## Prompt para Codex
8
+
9
+ Implementa la siguiente feature: [NOMBRE O RUTA A LA SPEC]
10
+
11
+ Sigue estos pasos en orden estricto:
12
+
13
+ 1. **Verificar spec aprobada**
14
+ - Lee `docs/specs/[ID]-[nombre].md`.
15
+ - Verifica que el estado sea `APPROVED`. Si dice `DRAFT`, detente: la spec necesita aprobación antes de implementar.
16
+
17
+ 2. **Leer contexto existente**
18
+ - Lee `project.yaml` para entender el stack y los paths configurados.
19
+ - Lee los archivos que la spec indica que serán afectados.
20
+ - Si el proyecto tiene `CLAUDE.md` o `AGENTS.md`, léelo para entender las convenciones del proyecto.
21
+
22
+ 3. **Implementar en este orden**
23
+ ```
24
+ a. Schema de base de datos (si hay modelos nuevos o modificados)
25
+ b. Types / interfaces compartidos
26
+ c. Backend — lógica de negocio, servicios, API routes
27
+ d. Frontend — componentes, páginas (si aplica)
28
+ e. Tests junto con cada capa, no al final
29
+ ```
30
+
31
+ 4. **Checklist de seguridad** (aplicar a cada endpoint o función que crees):
32
+ - [ ] ¿Verifica autenticación?
33
+ - [ ] ¿Verifica autorización por rol/ownership?
34
+ - [ ] ¿El input está validado con schema explícito?
35
+ - [ ] ¿Las queries usan parámetros preparados (no interpolación de strings)?
36
+ - [ ] ¿No hay secrets hardcodeados?
37
+
38
+ 5. **Al terminar cada archivo**
39
+ - Verifica que el código compila / no tiene errores de sintaxis.
40
+ - Si es TypeScript: asegúrate que los tipos son correctos.
41
+ - Si es Python: sin errores de sintaxis.
42
+
43
+ 6. **Al terminar la implementación completa**
44
+ - Actualiza `docs/specs/[ID]-[nombre].md`: agrega decisiones tomadas en la sección "Notas de implementación".
45
+ - Cambia el estado de la spec de `APPROVED` a `IMPLEMENTED`.
46
+ - Lista todos los archivos creados o modificados.
47
+
48
+ Restricciones absolutas:
49
+ - Sin hardcodear tokens, passwords ni secrets — usar variables de entorno.
50
+ - Sin force push a main/master.
51
+ - Sin eliminar archivos sin confirmación explícita.
52
+ - Correr los tests antes de declarar la tarea completa.
53
+ - Nunca commitear directamente a main/master.
@@ -0,0 +1,269 @@
1
+ #!/usr/bin/env python3
2
+ # Copyright 2026 Cristian Correa — Apache License 2.0
3
+ # https://github.com/cristiancorreau/forge
4
+ """
5
+ generate-codex-config.py — Genera AGENTS.md para Codex CLI (OpenAI).
6
+
7
+ Usage:
8
+ python3 .agentic/adapters/codex/generate-codex-config.py
9
+
10
+ Lee project.yaml en la raíz y genera AGENTS.md enriquecido para Codex CLI.
11
+
12
+ Diferencias respecto al adapter OpenCode:
13
+ - Incluye sección "Workflow SDD" con pasos explícitos (Codex opera autónomamente)
14
+ - Incluye "Security rules" y "Autonomy limits" inline (crítico para ejecución sin supervisión)
15
+ - Header identifica el archivo como generado para Codex CLI
16
+
17
+ Codex CLI lee AGENTS.md desde la raíz del repositorio como contexto del agente.
18
+ Referencia: https://github.com/openai/codex
19
+
20
+ Requiere: pyyaml
21
+ """
22
+ from __future__ import annotations
23
+
24
+ import sys
25
+ from pathlib import Path
26
+
27
+ try:
28
+ import yaml
29
+ except ImportError:
30
+ print("ERROR: pyyaml requerido. pip install pyyaml", file=sys.stderr)
31
+ sys.exit(1)
32
+
33
+
34
+ def find_project_root() -> Path:
35
+ here = Path.cwd()
36
+ for p in [here] + list(here.parents):
37
+ if (p / "project.yaml").exists():
38
+ return p
39
+ raise FileNotFoundError("No se encontró project.yaml")
40
+
41
+
42
+ def find_forge_dir() -> Path:
43
+ root = find_project_root()
44
+ for candidate in [root / ".agentic", root / "forge", Path(__file__).parent.parent.parent]:
45
+ if (candidate / "core").exists():
46
+ return candidate
47
+ raise FileNotFoundError("No se encontró el directorio forge con core/")
48
+
49
+
50
+ def read_agent_description(forge: Path, name: str, profiles: list[str]) -> str:
51
+ """Lee el frontmatter description del agente desde forge (profiles > core)."""
52
+ for profile in profiles:
53
+ p = forge / "profiles" / profile / "agents" / f"{name}.md"
54
+ if p.exists():
55
+ content = p.read_text()
56
+ for line in content.splitlines():
57
+ if line.startswith("description:"):
58
+ return line.split(":", 1)[1].strip().strip('"')
59
+ p = forge / "core" / "agents" / f"{name}.md"
60
+ if p.exists():
61
+ content = p.read_text()
62
+ for line in content.splitlines():
63
+ if line.startswith("description:"):
64
+ return line.split(":", 1)[1].strip().strip('"')
65
+ return "Agente de implementación"
66
+
67
+
68
+ def generate_agents_md(config: dict, forge: Path) -> str:
69
+ """
70
+ Genera AGENTS.md enriquecido para Codex CLI.
71
+
72
+ Codex CLI opera autónomamente en terminal: las reglas de seguridad y los
73
+ límites de autonomía deben estar inline en AGENTS.md, no en un archivo
74
+ separado, para que el agente las reciba como contexto en cada sesión.
75
+ """
76
+ proj = config.get("project", {})
77
+ agents_cfg = config.get("agents", {})
78
+ compliance_cfg = config.get("compliance", {})
79
+ stack = config.get("stack", {})
80
+ paths = config.get("paths", {})
81
+
82
+ name = proj.get("name", "Mi Proyecto")
83
+ description = proj.get("description", "")
84
+ language = proj.get("language", "typescript")
85
+ active = agents_cfg.get("active", [])
86
+ compliance = agents_cfg.get("compliance", [])
87
+ specialized = agents_cfg.get("specialized", [])
88
+ profiles = agents_cfg.get("profiles", [])
89
+ frameworks = compliance_cfg.get("frameworks", [])
90
+ specs_path = paths.get("specs", "docs/specs")
91
+
92
+ if frameworks and "compliance-reviewer" not in active + compliance:
93
+ compliance = list(set(compliance + ["compliance-reviewer"]))
94
+
95
+ lines = [
96
+ f"# AGENTS.md — {name}",
97
+ "",
98
+ f"> Generado por forge (adapter Codex CLI).",
99
+ f"> Fuente de verdad: `project.yaml`. Re-ejecutar `generate-codex-config.py` al cambiar agentes.",
100
+ "",
101
+ ]
102
+
103
+ if description:
104
+ lines += [description, ""]
105
+
106
+ lines += [
107
+ "## Stack",
108
+ "",
109
+ f"- **Lenguaje:** {language}",
110
+ f"- **Backend:** {stack.get('backend') or 'N/A'}",
111
+ f"- **Frontend:** {stack.get('frontend') or 'N/A'}",
112
+ f"- **Base de datos:** {stack.get('database') or 'N/A'}",
113
+ f"- **Testing:** {', '.join(stack.get('testing', []))}",
114
+ "",
115
+ "## Workflow: Spec-Driven Development",
116
+ "",
117
+ "Antes de implementar cualquier feature:",
118
+ "",
119
+ f"1. Verificar que existe una spec en `{specs_path}/`.",
120
+ "2. Si no existe, crearla y esperar aprobación antes de codificar.",
121
+ "3. Implementar con tests junto al código, no al final.",
122
+ "4. Actualizar la spec con decisiones tomadas durante la implementación.",
123
+ "",
124
+ "## Reglas de seguridad (no negociables)",
125
+ "",
126
+ "- Sin hardcodear tokens, passwords ni secrets — usar variables de entorno.",
127
+ "- Parámetros preparados en todas las queries SQL — nunca concatenar input del usuario.",
128
+ "- PII nunca en logs de stdout.",
129
+ "- Verificar autenticación Y autorización en cada endpoint de API.",
130
+ "- Sin force push a main/master.",
131
+ "",
132
+ "## Límites de autonomía",
133
+ "",
134
+ "- Modificar solo archivos dentro del scope declarado del agente.",
135
+ "- Preguntar antes de eliminar archivos o ejecutar comandos destructivos.",
136
+ "- Nunca hacer commit directo a main/master.",
137
+ "- Correr los tests antes de marcar una tarea como completa.",
138
+ "- Si una tarea requiere acceso a credenciales no disponibles, detenerse y reportar.",
139
+ "",
140
+ "## Roster de agentes",
141
+ "",
142
+ ]
143
+
144
+ if active:
145
+ lines += ["### Activos", ""]
146
+ for agent in active:
147
+ desc = read_agent_description(forge, agent, profiles)
148
+ lines.append(f"#### `{agent}`")
149
+ lines.append(desc)
150
+ lines.append("")
151
+
152
+ if compliance:
153
+ lines += ["### Compliance y revisión", ""]
154
+ for agent in compliance:
155
+ desc = read_agent_description(forge, agent, profiles)
156
+ lines.append(f"#### `{agent}`")
157
+ lines.append(desc)
158
+ lines.append("")
159
+
160
+ if specialized:
161
+ lines += ["### Especializados del proyecto", ""]
162
+ for agent in specialized:
163
+ desc = read_agent_description(forge, agent, profiles)
164
+ lines.append(f"#### `{agent}`")
165
+ lines.append(desc)
166
+ lines.append("")
167
+
168
+ if frameworks:
169
+ lines += [
170
+ "## Compliance activo",
171
+ "",
172
+ f"Marcos regulatorios: {', '.join(f.upper() for f in frameworks)}",
173
+ "",
174
+ "Incluir `compliance-reviewer` en toda tarea que toque:",
175
+ "- Datos de usuarios o consentimientos",
176
+ "- Logs de auditoría",
177
+ "- Endpoints de derechos del titular (DSAR)",
178
+ "",
179
+ ]
180
+
181
+ lines += [
182
+ "## Forge v2 Commands",
183
+ "",
184
+ "Codex CLI no soporta slash commands. Usa las plantillas de prompt de",
185
+ "`adapters/codex/commands/` copiando su contenido directamente en tu sesión.",
186
+ "",
187
+ "| Comando | Archivo | Cuándo usarlo |",
188
+ "|---------|---------|---------------|",
189
+ "| plan | `adapters/codex/commands/plan.md` | Antes de implementar — verifica o crea la spec |",
190
+ "| work | `adapters/codex/commands/work.md` | Implementar una feature con spec APPROVED |",
191
+ "| review | `adapters/codex/commands/review.md` | Revisar código o cambios antes del merge |",
192
+ "| ship | `adapters/codex/commands/ship.md` | Verificar que el proyecto está listo para deploy |",
193
+ "| session-start | `adapters/codex/commands/session-start.md` | Al iniciar cada sesión de trabajo |",
194
+ "| session-close | `adapters/codex/commands/session-close.md` | Al cerrar cada sesión de trabajo |",
195
+ "",
196
+ "### Flujo SDD recomendado",
197
+ "",
198
+ "```",
199
+ "1. session-start → verificar ambiente y branch",
200
+ "2. plan → spec antes que código (DRAFT → APPROVED)",
201
+ "3. work → implementar con tests (spec APPROVED → IMPLEMENTED)",
202
+ "4. review → checklist de seguridad y calidad",
203
+ "5. ship → verificar build, tests y variables antes del deploy",
204
+ "6. session-close → registrar progreso y limpiar",
205
+ "```",
206
+ "",
207
+ "## Reglas de producción (pre-bash-check)",
208
+ "",
209
+ "Los siguientes comandos están bloqueados en contexto de producción.",
210
+ "Si necesitás ejecutarlos, hacelo manualmente en la terminal con plena consciencia:",
211
+ "",
212
+ "- `--force-reset` — puede destruir datos de base de datos",
213
+ "- `prisma migrate reset` — borra y re-crea la base de datos",
214
+ "- `DROP TABLE` / `DROP DATABASE` — destruye tablas o bases de datos",
215
+ "- `TRUNCATE` — elimina todos los registros de una tabla",
216
+ "- `DELETE FROM <tabla>;` — sin cláusula WHERE, elimina todo",
217
+ "- `dropdb` — elimina una base de datos completa",
218
+ "- `rm -rf /` — elimina recursivamente desde la raíz",
219
+ "- `git push --force` sin `--with-lease` — sobreescribe historial remoto",
220
+ "",
221
+ "Contexto de producción = la URL de producción o el project_id aparecen",
222
+ "en el comando, o hay variables de entorno `PROD_*` / `PRODUCTION_*` activas.",
223
+ "",
224
+ "Lección del 2026-04-28: --force-reset borró 225 usuarios y 35 formularios",
225
+ "en producción. Estas reglas existen por eso.",
226
+ "",
227
+ "## Branch guard (pre-edit-check)",
228
+ "",
229
+ "No editar archivos de código fuente directamente en `main` o `master`.",
230
+ "",
231
+ "Archivos de código = `.py`, `.ts`, `.js`, `.tsx`, `.jsx`, `.php`, `.rb`,",
232
+ "`.go`, `.rs`, `.java`, `.cs`, `.cpp`, `.c`, `.sh`",
233
+ "",
234
+ "Excenciones permitidas en rama protegida:",
235
+ "- Archivos de documentación (`docs/`, `.md`)",
236
+ "- Archivos de configuración en raíz (`.yaml`, `.yml`, `.json`)",
237
+ "- `CLAUDE.md`, `AGENTS.md`, `README.md`, `CHANGELOG.md`",
238
+ "",
239
+ "Si estás en `main` o `master`, crea una feature branch primero:",
240
+ "```bash",
241
+ "git checkout -b feature/<tema>-$(date +%Y-%m-%d)",
242
+ "```",
243
+ "",
244
+ ]
245
+
246
+ return "\n".join(lines)
247
+
248
+
249
+ def main():
250
+ try:
251
+ root = find_project_root()
252
+ forge = find_forge_dir()
253
+ except FileNotFoundError as e:
254
+ print(f"ERROR: {e}", file=sys.stderr)
255
+ sys.exit(1)
256
+
257
+ with open(root / "project.yaml") as f:
258
+ config = yaml.safe_load(f)
259
+
260
+ content = generate_agents_md(config, forge)
261
+ output_path = root / "AGENTS.md"
262
+ with open(output_path, "w") as f:
263
+ f.write(content)
264
+ print(f" [OK] AGENTS.md generado en {output_path}")
265
+ print(" (adapter Codex: incluye SDD workflow + security rules + autonomy limits)")
266
+
267
+
268
+ if __name__ == "__main__":
269
+ main()
@@ -0,0 +1,43 @@
1
+ # .codex/codex.yaml — Forge v2 hooks para Codex CLI
2
+ #
3
+ # Generar con: cp adapters/codex/hooks/codex.yaml.tpl .codex/codex.yaml
4
+ # O usar: bash scripts/setup-codex.sh
5
+ #
6
+ # SOPORTE DE HOOKS EN CODEX CLI (estado a 2025):
7
+ # onStart — se ejecuta al inicio de cada sesión de Codex. Equivalente
8
+ # al hook SessionStart / UserPromptSubmit de Claude Code.
9
+ # onFinish — se ejecuta al finalizar cada sesión de Codex. Equivalente
10
+ # al hook Stop de Claude Code.
11
+ # onDiff — se ejecuta cuando Codex aplica cambios al filesystem.
12
+ # No tiene equivalente directo en Claude Code.
13
+ #
14
+ # NO SOPORTADO en Codex CLI (a diferencia de Claude Code):
15
+ # - PreToolUse: Codex no puede interceptar herramientas individuales
16
+ # antes de ejecutarlas. Las reglas de pre-bash-check.py y
17
+ # pre-edit-check.py NO tienen equivalente automático en Codex.
18
+ # Esas reglas están embebidas en AGENTS.md como instrucciones de texto
19
+ # para que el agente las respete voluntariamente.
20
+ # - PostToolUse: tampoco soportado.
21
+ #
22
+ # IMPLICACIÓN: La capa de seguridad de Codex es instruccional (AGENTS.md),
23
+ # no mecánica (hooks que bloquean). Para proyectos de alto riesgo, preferir
24
+ # Claude Code que sí puede bloquear comandos destructivos antes de ejecutarlos.
25
+
26
+ model: o4-mini # Cambiar según preferencia: o4-mini | o3 | gpt-4o
27
+
28
+ hooks:
29
+ # Ejecutado al inicio de cada sesión.
30
+ # Equivalent: Claude Code SessionStart / UserPromptSubmit hook.
31
+ # Verifica herramientas disponibles, branch, cambios sin commitear,
32
+ # project.yaml y variables de entorno de producción activas.
33
+ onStart: bash .codex/forge-codex-start.sh
34
+
35
+ # Ejecutado al finalizar cada sesión.
36
+ # Equivalent: Claude Code Stop hook (post-turn-check.sh).
37
+ # Detecta archivos modificados y corre type-check / syntax-check.
38
+ onFinish: bash .codex/forge-codex-finish.sh
39
+
40
+ # onDiff: no configurado por defecto.
41
+ # Se puede usar para correr linters en los archivos cambiados.
42
+ # Ejemplo: onDiff: bash -c 'echo "$CODEX_DIFF_FILES" | xargs eslint --fix'
43
+ # onDiff: ""
@@ -0,0 +1,158 @@
1
+ #!/usr/bin/env bash
2
+ # Forge v2 — Codex onFinish hook: forge-codex-finish.sh
3
+ #
4
+ # Se ejecuta al finalizar cada sesión de Codex CLI.
5
+ # Equivalente al hook post-turn-check.sh de Claude Code (Stop event).
6
+ #
7
+ # DIFERENCIAS vs Claude Code:
8
+ # - Claude Code: post-turn-check.sh corre después de CADA turno del agente.
9
+ # - Codex CLI: forge-codex-finish.sh corre al FINALIZAR la sesión completa,
10
+ # no después de cada mensaje individual. Codex no tiene un equivalente
11
+ # granular de "Stop por turno".
12
+ # - Claude Code Stop hook: puede imprimir al contexto del agente (el output
13
+ # se muestra como resultado del hook).
14
+ # - Codex CLI onFinish: el output se muestra en la terminal del usuario,
15
+ # pero puede no ser visible en el contexto del agente dependiendo de la
16
+ # versión de Codex.
17
+ #
18
+ # LIMITACIONES de Codex vs Claude Code:
19
+ # - No hay PreToolUse/PostToolUse hooks para interceptar herramientas individuales.
20
+ # - Los checks de este script son post-sesión, no post-turno.
21
+ #
22
+ # Instalación: este script debe copiarse o enlazarse en .codex/forge-codex-finish.sh
23
+ # El setup-codex.sh lo hace automáticamente.
24
+
25
+ set -uo pipefail
26
+
27
+ # ---------------------------------------------------------------------------
28
+ # Step 1 — Find modified files
29
+ # ---------------------------------------------------------------------------
30
+ MODIFIED=$(git diff --name-only HEAD 2>/dev/null || echo "")
31
+ STAGED=$(git diff --name-only --cached 2>/dev/null || echo "")
32
+ ALL_CHANGED="$MODIFIED $STAGED"
33
+
34
+ if [ -z "$(echo "$ALL_CHANGED" | tr -d '[:space:]')" ]; then
35
+ exit 0
36
+ fi
37
+
38
+ # ---------------------------------------------------------------------------
39
+ # Step 2 — Read project config
40
+ # ---------------------------------------------------------------------------
41
+ PKG_MGR=""
42
+ CUSTOM_CHECK=""
43
+
44
+ if [ -f "project.yaml" ]; then
45
+ PKG_MGR=$(python3 -c "
46
+ import yaml, sys
47
+ try:
48
+ d = yaml.safe_load(open('project.yaml'))
49
+ print(d.get('stack', {}).get('package_manager', ''))
50
+ except:
51
+ print('')
52
+ " 2>/dev/null || echo "")
53
+
54
+ CUSTOM_CHECK=$(python3 -c "
55
+ import yaml, sys
56
+ try:
57
+ d = yaml.safe_load(open('project.yaml'))
58
+ print(d.get('scripts', {}).get('check', ''))
59
+ except:
60
+ print('')
61
+ " 2>/dev/null || echo "")
62
+ fi
63
+
64
+ CHECK_OUTPUT=""
65
+
66
+ # ---------------------------------------------------------------------------
67
+ # Step 3 — Run checks
68
+ # ---------------------------------------------------------------------------
69
+
70
+ # Helper: check if any changed file matches a glob pattern
71
+ files_matching() {
72
+ local pattern="$1"
73
+ echo "$ALL_CHANGED" | tr ' ' '\n' | grep -E "$pattern" 2>/dev/null || true
74
+ }
75
+
76
+ if [ -n "$CUSTOM_CHECK" ]; then
77
+ # Run user-defined check command
78
+ CHECK_OUTPUT=$(eval "$CUSTOM_CHECK" 2>&1 | head -20 || true)
79
+
80
+ else
81
+ # Auto-detect by file type
82
+
83
+ # TypeScript / JavaScript
84
+ TS_FILES=$(files_matching '\.(ts|tsx)$')
85
+ if [ -n "$TS_FILES" ]; then
86
+ if [ -f "turbo.json" ] && command -v pnpm &>/dev/null; then
87
+ TSC_OUTPUT=$(pnpm turbo typecheck 2>&1 | head -20 || pnpm tsc --noEmit 2>&1 | head -20 || true)
88
+ elif command -v pnpm &>/dev/null; then
89
+ TSC_OUTPUT=$(pnpm tsc --noEmit 2>&1 | head -20 || true)
90
+ elif command -v npx &>/dev/null; then
91
+ TSC_OUTPUT=$(npx tsc --noEmit 2>&1 | head -20 || true)
92
+ else
93
+ TSC_OUTPUT=""
94
+ fi
95
+ if [ -n "${TSC_OUTPUT:-}" ]; then
96
+ CHECK_OUTPUT="${CHECK_OUTPUT:+$CHECK_OUTPUT$'\n'}[tsc] $TSC_OUTPUT"
97
+ fi
98
+ fi
99
+
100
+ # PHP
101
+ PHP_FILES=$(files_matching '\.php$')
102
+ if [ -n "$PHP_FILES" ] && [ -f "composer.json" ]; then
103
+ if command -v composer &>/dev/null; then
104
+ PHP_OUTPUT=$(composer validate --no-check-publish 2>&1 | head -10 || true)
105
+ if [ -n "$PHP_OUTPUT" ]; then
106
+ CHECK_OUTPUT="${CHECK_OUTPUT:+$CHECK_OUTPUT$'\n'}[composer] $PHP_OUTPUT"
107
+ fi
108
+ fi
109
+ fi
110
+
111
+ # Python
112
+ PY_FILES=$(files_matching '\.py$')
113
+ if [ -n "$PY_FILES" ]; then
114
+ PY_OUTPUT=""
115
+ while IFS= read -r f; do
116
+ [ -z "$f" ] && continue
117
+ [ -f "$f" ] || continue
118
+ RESULT=$(python3 -m py_compile "$f" 2>&1 || true)
119
+ if [ -n "$RESULT" ]; then
120
+ PY_OUTPUT="${PY_OUTPUT:+$PY_OUTPUT$'\n'}$f: $RESULT"
121
+ fi
122
+ done <<< "$PY_FILES"
123
+ if [ -n "$PY_OUTPUT" ]; then
124
+ CHECK_OUTPUT="${CHECK_OUTPUT:+$CHECK_OUTPUT$'\n'}[python] $PY_OUTPUT"
125
+ fi
126
+ fi
127
+
128
+ # Ruby
129
+ RB_FILES=$(files_matching '\.rb$')
130
+ if [ -n "$RB_FILES" ] && [ -f "Gemfile" ]; then
131
+ RB_OUTPUT=""
132
+ if command -v bundle &>/dev/null; then
133
+ while IFS= read -r f; do
134
+ [ -z "$f" ] && continue
135
+ [ -f "$f" ] || continue
136
+ RESULT=$(bundle exec ruby -c "$f" 2>&1 | head -10 || true)
137
+ if [ -n "$RESULT" ]; then
138
+ RB_OUTPUT="${RB_OUTPUT:+$RB_OUTPUT$'\n'}$f: $RESULT"
139
+ fi
140
+ done <<< "$RB_FILES"
141
+ fi
142
+ if [ -n "$RB_OUTPUT" ]; then
143
+ CHECK_OUTPUT="${CHECK_OUTPUT:+$CHECK_OUTPUT$'\n'}[ruby] $RB_OUTPUT"
144
+ fi
145
+ fi
146
+ fi
147
+
148
+ # ---------------------------------------------------------------------------
149
+ # Step 4 — Report
150
+ # ---------------------------------------------------------------------------
151
+ if [ -n "$CHECK_OUTPUT" ]; then
152
+ echo "── Forge Codex post-session check ───────────"
153
+ echo "$CHECK_OUTPUT"
154
+ echo "─────────────────────────────────────────────"
155
+ fi
156
+
157
+ # Siempre exit 0 — este hook nunca bloquea.
158
+ exit 0