@cristiancorreau/forge 2.2.0 → 2.4.1
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/README.md +52 -109
- package/assets/core/hooks/pre-bash-check.js +144 -0
- package/assets/core/hooks/pre-edit-check.js +166 -0
- package/dist/cli.js +1 -1
- package/dist/commands/audit.d.ts.map +1 -1
- package/dist/commands/audit.js +27 -7
- package/dist/commands/audit.js.map +1 -1
- package/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/doctor.js +54 -19
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/generate.d.ts.map +1 -1
- package/dist/commands/generate.js +88 -37
- package/dist/commands/generate.js.map +1 -1
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +43 -5
- package/dist/commands/init.js.map +1 -1
- package/dist/lib/detect.d.ts +13 -0
- package/dist/lib/detect.d.ts.map +1 -0
- package/dist/lib/detect.js +85 -0
- package/dist/lib/detect.js.map +1 -0
- package/dist/lib/generators/claude-code.d.ts.map +1 -1
- package/dist/lib/generators/claude-code.js +7 -1
- package/dist/lib/generators/claude-code.js.map +1 -1
- package/dist/lib/lock.d.ts +15 -0
- package/dist/lib/lock.d.ts.map +1 -0
- package/dist/lib/lock.js +48 -0
- package/dist/lib/lock.js.map +1 -0
- package/dist/lib/wizard.d.ts +3 -2
- package/dist/lib/wizard.d.ts.map +1 -1
- package/dist/lib/wizard.js +211 -142
- package/dist/lib/wizard.js.map +1 -1
- package/dist/ui/box.d.ts +2 -0
- package/dist/ui/box.d.ts.map +1 -0
- package/dist/ui/box.js +15 -0
- package/dist/ui/box.js.map +1 -0
- package/dist/ui/colors.d.ts +15 -0
- package/dist/ui/colors.d.ts.map +1 -0
- package/dist/ui/colors.js +19 -0
- package/dist/ui/colors.js.map +1 -0
- package/dist/ui/spinner.d.ts +9 -0
- package/dist/ui/spinner.d.ts.map +1 -0
- package/dist/ui/spinner.js +46 -0
- package/dist/ui/spinner.js.map +1 -0
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -1,156 +1,99 @@
|
|
|
1
1
|
# forge
|
|
2
2
|
|
|
3
3
|
[](https://github.com/cristiancorreau/forge/actions/workflows/tests.yml)
|
|
4
|
-
[](https://www.npmjs.com/package/@cristiancorreau/forge)
|
|
5
5
|
[](LICENSE)
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
**Configura cualquier proyecto para trabajar con agentes IA en un comando.**
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
Wizard interactivo que detecta tu stack, instala agentes especializados, genera guardrails y mantiene un manifest con SHA-256 para auditar cada cambio.
|
|
10
10
|
|
|
11
11
|
---
|
|
12
12
|
|
|
13
|
-
##
|
|
14
|
-
|
|
15
|
-
- **Los agentes olvidan.** Sin memoria y reglas persistentes, cada sesión empieza desde cero. Forge mantiene el contexto entre sesiones.
|
|
16
|
-
- **El contexto se pierde entre archivos.** Un CLAUDE.md plano no sabe qué agente ejecuta qué tarea. Forge separa responsabilidades con agentes especializados por tier.
|
|
17
|
-
- **Cada runtime tiene su formato.** Claude Code, OpenCode, Kiro y Codex CLI esperan archivos distintos. Forge los genera todos desde una sola fuente de verdad.
|
|
18
|
-
- **La calidad baja con el tiempo.** Sin auditoría, los agentes se desactualizan silenciosamente. Forge detecta gaps y ofrece correcciones.
|
|
19
|
-
|
|
20
|
-
---
|
|
21
|
-
|
|
22
|
-
## Diferencias con otras herramientas
|
|
23
|
-
|
|
24
|
-
| Herramienta | Qué hace | Alcance | Runtimes | Hooks | Equipos |
|
|
25
|
-
|-------------|----------|---------|----------|-------|---------|
|
|
26
|
-
| **Forge** | Framework completo: agentes, skills, reglas, auditoría | Proyecto + team | Claude Code, OpenCode, Kiro, Codex CLI | Sí (pre-commit) | Sí (tiers) |
|
|
27
|
-
| `cc-sdd` | Plantillas SDD para Claude Code | Proyecto individual | Claude Code | No | No |
|
|
28
|
-
| `Bridle` | Guardrails para prompts | Prompt-level | Agnóstico | No | No |
|
|
29
|
-
| `wshobson/agents` | Colección de agent files | Proyecto individual | Claude Code | No | No |
|
|
30
|
-
| CLAUDE.md manual | Instrucciones en texto plano | Sesión | Claude Code | No | No |
|
|
31
|
-
|
|
32
|
-
**Lo que Forge NO es:** no es un modelo de IA, no es un servicio en la nube, no es una plataforma de deployment, no ejecuta agentes por sí solo. Es una capa de configuración y estructura que vive en tu repositorio.
|
|
33
|
-
|
|
34
|
-
---
|
|
35
|
-
|
|
36
|
-
## Quick start (5 minutos)
|
|
37
|
-
|
|
38
|
-
**Requisitos:** Python 3.9+, git, Claude Code (u otro runtime soportado).
|
|
13
|
+
## Quick start
|
|
39
14
|
|
|
40
15
|
```bash
|
|
41
|
-
|
|
42
|
-
git submodule add https://github.com/cristiancorreau/forge .agentic
|
|
43
|
-
pip3 install -r .agentic/requirements.txt
|
|
16
|
+
npx @cristiancorreau/forge init
|
|
44
17
|
```
|
|
45
18
|
|
|
46
|
-
|
|
47
|
-
# 2. Copiar y completar project.yaml
|
|
48
|
-
cp .agentic/templates/project.yaml.tpl project.yaml
|
|
49
|
-
```
|
|
50
|
-
|
|
51
|
-
Editar `project.yaml` con los datos del proyecto:
|
|
52
|
-
|
|
53
|
-
```yaml
|
|
54
|
-
project:
|
|
55
|
-
name: "Mi SaaS"
|
|
56
|
-
mode: "standard" # startup | standard | enterprise
|
|
19
|
+
Sin instalación global. Sin Python. Solo Node.js.
|
|
57
20
|
|
|
58
|
-
|
|
59
|
-
backend: "hono"
|
|
60
|
-
frontend: "nextjs"
|
|
21
|
+
---
|
|
61
22
|
|
|
62
|
-
|
|
63
|
-
active: [orchestrator, test-engineer, docs-writer]
|
|
64
|
-
profiles: [hono-drizzle, nextjs-admin]
|
|
23
|
+
## Cómo funciona
|
|
65
24
|
|
|
66
|
-
|
|
67
|
-
active: [new-feature, security-audit, db-migrate]
|
|
68
|
-
```
|
|
25
|
+
El wizard detecta y configura el proyecto en cinco pasos:
|
|
69
26
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
```bash
|
|
76
|
-
# 4. Abrir Claude Code y ejecutar
|
|
77
|
-
/session-start
|
|
78
|
-
```
|
|
79
|
-
|
|
80
|
-
Después del paso 4 verás: agentes instalados en `.claude/agents/`, un `CLAUDE.md` generado con el roster del proyecto, y slash commands disponibles en Claude Code.
|
|
27
|
+
1. **Detecta el stack** — lee `package.json`, lockfiles y `Dockerfile` para identificar framework, lenguaje y dependencias.
|
|
28
|
+
2. **Selecciona agentes** — muestra un selector de flechas con los agentes disponibles para tu stack (TypeScript, Python, Ruby, Go, PHP).
|
|
29
|
+
3. **Instala configuración** — escribe `.claude/agents/`, `CLAUDE.md`, `settings.json` y `architecture.rules` en el repositorio.
|
|
30
|
+
4. **Instala hooks** — genera hooks de guardrail en JavaScript puro; sin dependencias de Python ni binarios externos.
|
|
31
|
+
5. **Crea el manifest** — `forge/.forge/manifest.json` con SHA-256 de cada archivo gestionado para rastrear derivaciones futuras.
|
|
81
32
|
|
|
82
33
|
---
|
|
83
34
|
|
|
84
|
-
##
|
|
85
|
-
|
|
86
|
-
| Capa | Qué hace |
|
|
87
|
-
|------|----------|
|
|
88
|
-
| **Memory** | `project.yaml` como fuente de verdad; wiki de proyecto persistente entre sesiones |
|
|
89
|
-
| **Knowledge** | Agentes especializados (Tier 1/2/3) con scope declarado y reglas no-negociables |
|
|
90
|
-
| **Guardrail** | Compliance by design, hook de pre-commit, auditoría con `forge-audit.py` |
|
|
91
|
-
| **Delegation** | Orquestador que descompone tareas y delega a agentes especializados |
|
|
92
|
-
| **Distribution** | Un solo `project.yaml` genera configs para todos los runtimes soportados |
|
|
35
|
+
## Comandos
|
|
93
36
|
|
|
94
|
-
|
|
37
|
+
| Comando | Qué hace |
|
|
38
|
+
|---------|----------|
|
|
39
|
+
| `forge init` | Wizard completo: detecta stack, instala agentes, hooks y genera configuración |
|
|
40
|
+
| `forge audit` | Verifica el estado del proyecto contra el manifest; detecta archivos modificados o faltantes |
|
|
41
|
+
| `forge generate` | Regenera configuración desde el estado actual del proyecto sin ejecutar el wizard completo |
|
|
42
|
+
| `forge validate` | Valida que los archivos generados cumplan el esquema esperado |
|
|
43
|
+
| `forge doctor` | Health-check del entorno: Node.js, git, runtime de IA activo, permisos |
|
|
95
44
|
|
|
96
45
|
---
|
|
97
46
|
|
|
98
47
|
## Runtimes soportados
|
|
99
48
|
|
|
100
|
-
| Runtime | Soporte |
|
|
101
|
-
|
|
102
|
-
| **Claude Code** | Completo
|
|
103
|
-
| **OpenCode** |
|
|
104
|
-
| **Codex CLI** |
|
|
105
|
-
| **Kiro** |
|
|
106
|
-
| **Todos** | Genera los cuatro | `--tool all` |
|
|
107
|
-
|
|
108
|
-
"Soporte completo" significa integración con hooks, settings.json y slash commands. "Serial" significa generación del archivo de instrucciones sin integración profunda con el runtime.
|
|
49
|
+
| Runtime | Soporte |
|
|
50
|
+
|---------|---------|
|
|
51
|
+
| **Claude Code** | Completo — agentes, `CLAUDE.md`, `settings.json`, hooks |
|
|
52
|
+
| **OpenCode** | `AGENTS.md` generado |
|
|
53
|
+
| **Codex CLI** | `AGENTS.md` enriquecido para contexto de proyecto |
|
|
54
|
+
| **Kiro** | Steering files |
|
|
109
55
|
|
|
110
56
|
---
|
|
111
57
|
|
|
112
|
-
##
|
|
113
|
-
|
|
114
|
-
15 perfiles listos para usar, declarados en `agents.profiles` del `project.yaml`:
|
|
58
|
+
## Stacks soportados
|
|
115
59
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
60
|
+
| Lenguaje | Frameworks |
|
|
61
|
+
|----------|------------|
|
|
62
|
+
| TypeScript | Hono, Next.js, NestJS, Astro |
|
|
63
|
+
| Python | FastAPI, Django |
|
|
64
|
+
| Ruby | Rails |
|
|
65
|
+
| Go | Gin |
|
|
66
|
+
| PHP | Laravel |
|
|
119
67
|
|
|
120
|
-
Cada
|
|
121
|
-
|
|
122
|
-
Para stacks no cubiertos:
|
|
123
|
-
|
|
124
|
-
```bash
|
|
125
|
-
python3 .agentic/scripts/forge-scaffold-profile.py --name <stack> --engineer <agente>
|
|
126
|
-
```
|
|
68
|
+
Cada stack instala agentes especializados con reglas de arquitectura, convenciones de código y patrones específicos del framework.
|
|
127
69
|
|
|
128
70
|
---
|
|
129
71
|
|
|
130
|
-
##
|
|
72
|
+
## Sin Python requerido
|
|
131
73
|
|
|
132
|
-
|
|
74
|
+
Toda la CLI corre en Node.js. Los hooks de guardrail son JavaScript puro.
|
|
133
75
|
|
|
134
|
-
|
|
76
|
+
No hay `pip install`, no hay `requirements.txt`, no hay dependencias de sistema fuera de Node.js 18+.
|
|
135
77
|
|
|
136
|
-
|
|
137
|
-
python3 .agentic/scripts/forge-scaffold-profile.py --name <stack> --engineer <agente>
|
|
138
|
-
```
|
|
78
|
+
---
|
|
139
79
|
|
|
140
|
-
|
|
80
|
+
## Comparativa
|
|
141
81
|
|
|
142
|
-
|
|
82
|
+
| Herramienta | Agentes especializados | Hooks de guardrail | Manifest con SHA-256 | Multi-runtime |
|
|
83
|
+
|-------------|------------------------|-------------------|----------------------|---------------|
|
|
84
|
+
| **forge** | Sí | Sí | Sí | Claude Code, OpenCode, Codex, Kiro |
|
|
85
|
+
| `cc-sdd` | No — plantillas SDD | No | No | Claude Code |
|
|
86
|
+
| `autoskills` | No — skills genéricos | No | No | Claude Code |
|
|
143
87
|
|
|
144
|
-
|
|
88
|
+
---
|
|
145
89
|
|
|
146
|
-
|
|
90
|
+
## Documentación
|
|
147
91
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
```
|
|
92
|
+
- [Guía completa](docs/guide.md)
|
|
93
|
+
- [Runtimes](docs/runtimes/)
|
|
151
94
|
|
|
152
95
|
---
|
|
153
96
|
|
|
154
97
|
## Licencia
|
|
155
98
|
|
|
156
|
-
Apache 2.0 — [Cristian Correa](https://github.com/cristiancorreau), 2026.
|
|
99
|
+
Apache 2.0 — Copyright [Cristian Correa](https://github.com/cristiancorreau), 2026.
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* forge — PreToolUse hook: pre-bash-check.js
|
|
4
|
+
* Blocks destructive commands in production context. Zero Python dependency.
|
|
5
|
+
*
|
|
6
|
+
* Context: on 2026-04-28, --force-reset was accidentally executed against
|
|
7
|
+
* production DB, deleting 225 users and 35 forms. This hook prevents recurrence.
|
|
8
|
+
*/
|
|
9
|
+
'use strict';
|
|
10
|
+
|
|
11
|
+
const fs = require('fs');
|
|
12
|
+
const path = require('path');
|
|
13
|
+
|
|
14
|
+
const DEBUG = !['', '0', 'false', 'False'].includes(process.env.DEBUG || '');
|
|
15
|
+
const dbg = msg => DEBUG && process.stdout.write(`[forge-hook-debug] ${msg}\n`);
|
|
16
|
+
|
|
17
|
+
// ---------------------------------------------------------------------------
|
|
18
|
+
// Load project.yaml (minimal YAML parser for flat key: value)
|
|
19
|
+
// ---------------------------------------------------------------------------
|
|
20
|
+
function loadProjectYaml() {
|
|
21
|
+
try {
|
|
22
|
+
let dir = process.cwd();
|
|
23
|
+
for (let i = 0; i < 6; i++) {
|
|
24
|
+
const candidate = path.join(dir, 'project.yaml');
|
|
25
|
+
if (fs.existsSync(candidate)) {
|
|
26
|
+
const text = fs.readFileSync(candidate, 'utf8');
|
|
27
|
+
return parseYamlMinimal(text);
|
|
28
|
+
}
|
|
29
|
+
const parent = path.dirname(dir);
|
|
30
|
+
if (parent === dir) break;
|
|
31
|
+
dir = parent;
|
|
32
|
+
}
|
|
33
|
+
} catch (e) { dbg(`project.yaml load error: ${e}`); }
|
|
34
|
+
return {};
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function parseYamlMinimal(text) {
|
|
38
|
+
// Minimal parser: handles nested sections and key: value pairs
|
|
39
|
+
const result = {};
|
|
40
|
+
let currentSection = null;
|
|
41
|
+
let currentSubSection = null;
|
|
42
|
+
for (const rawLine of text.split('\n')) {
|
|
43
|
+
const line = rawLine.trimEnd();
|
|
44
|
+
if (!line || line.trim().startsWith('#')) continue;
|
|
45
|
+
const indent = line.length - line.trimStart().length;
|
|
46
|
+
const trimmed = line.trimStart();
|
|
47
|
+
const colonIdx = trimmed.indexOf(':');
|
|
48
|
+
if (colonIdx === -1) continue;
|
|
49
|
+
const key = trimmed.slice(0, colonIdx).trim();
|
|
50
|
+
const val = trimmed.slice(colonIdx + 1).trim();
|
|
51
|
+
if (indent === 0) { currentSection = key; currentSubSection = null; if (val) result[key] = val; else result[key] = {}; }
|
|
52
|
+
else if (indent === 2 && currentSection) { currentSubSection = key; if (!result[currentSection] || typeof result[currentSection] !== 'object') result[currentSection] = {}; if (val) result[currentSection][key] = val; else result[currentSection][key] = {}; }
|
|
53
|
+
else if (indent === 4 && currentSection && currentSubSection) { if (!result[currentSection][currentSubSection] || typeof result[currentSection][currentSubSection] !== 'object') result[currentSection][currentSubSection] = {}; if (val) result[currentSection][currentSubSection][key] = val; }
|
|
54
|
+
}
|
|
55
|
+
return result;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// ---------------------------------------------------------------------------
|
|
59
|
+
// Dangerous patterns
|
|
60
|
+
// ---------------------------------------------------------------------------
|
|
61
|
+
const DANGEROUS = [
|
|
62
|
+
[/--force-reset/, '--force-reset'],
|
|
63
|
+
[/prisma\s+migrate\s+reset/i, 'prisma migrate reset'],
|
|
64
|
+
[/DROP\s+TABLE/i, 'DROP TABLE'],
|
|
65
|
+
[/TRUNCATE\s+/i, 'TRUNCATE'],
|
|
66
|
+
[/DELETE\s+FROM\s+\w+\s*;/i, 'DELETE FROM sin WHERE'],
|
|
67
|
+
[/DROP\s+DATABASE/i, 'DROP DATABASE'],
|
|
68
|
+
[/dropdb\s+/, 'dropdb'],
|
|
69
|
+
[/rm\s+-rf\s+\//, 'rm -rf /'],
|
|
70
|
+
[/git\s+push\s+--force(?!\s+--with-lease)/, 'git push --force sin --with-lease'],
|
|
71
|
+
];
|
|
72
|
+
|
|
73
|
+
function matchDangerous(command) {
|
|
74
|
+
for (const [re, label] of DANGEROUS) {
|
|
75
|
+
if (re.test(command)) return label;
|
|
76
|
+
}
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function matchForbidden(command, project) {
|
|
81
|
+
try {
|
|
82
|
+
const forbidden = (project.rules || {}).forbidden_in_production;
|
|
83
|
+
if (!Array.isArray(forbidden)) return null;
|
|
84
|
+
for (const pattern of forbidden) {
|
|
85
|
+
if (new RegExp(pattern).test(command)) return pattern;
|
|
86
|
+
}
|
|
87
|
+
} catch (e) { dbg(`forbidden_in_production error: ${e}`); }
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function isProductionContext(command, project) {
|
|
92
|
+
const deploy = project.deploy || {};
|
|
93
|
+
const prodUrl = deploy.production_url || '';
|
|
94
|
+
const projectId = deploy.project_id || '';
|
|
95
|
+
if (prodUrl && command.includes(prodUrl)) { dbg(`prod context: URL match`); return true; }
|
|
96
|
+
if (projectId && command.includes(projectId)) { dbg(`prod context: project_id match`); return true; }
|
|
97
|
+
for (const key of Object.keys(process.env)) {
|
|
98
|
+
if (/^(PROD_|PRODUCTION_|PROD$|PRODUCTION$)/i.test(key) && process.env[key]) { dbg(`prod context: env ${key}`); return true; }
|
|
99
|
+
}
|
|
100
|
+
return false;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// ---------------------------------------------------------------------------
|
|
104
|
+
// Main
|
|
105
|
+
// ---------------------------------------------------------------------------
|
|
106
|
+
let raw = '';
|
|
107
|
+
process.stdin.setEncoding('utf8');
|
|
108
|
+
process.stdin.on('data', chunk => { raw += chunk; });
|
|
109
|
+
process.stdin.on('end', () => {
|
|
110
|
+
if (!raw.trim()) { dbg('empty stdin'); process.exit(0); }
|
|
111
|
+
let data;
|
|
112
|
+
try { data = JSON.parse(raw); } catch (e) { dbg(`parse error: ${e}`); process.exit(0); }
|
|
113
|
+
|
|
114
|
+
const toolName = data.tool_name || '';
|
|
115
|
+
if (toolName !== 'Bash') process.exit(0);
|
|
116
|
+
|
|
117
|
+
const command = (data.tool_input || {}).command || '';
|
|
118
|
+
if (!command) process.exit(0);
|
|
119
|
+
dbg(`command: ${command.slice(0, 200)}`);
|
|
120
|
+
|
|
121
|
+
const project = loadProjectYaml();
|
|
122
|
+
const label = matchDangerous(command) || matchForbidden(command, project);
|
|
123
|
+
if (!label) process.exit(0);
|
|
124
|
+
|
|
125
|
+
const snippet = command.slice(0, 120) + (command.length > 120 ? '...' : '');
|
|
126
|
+
const inProd = isProductionContext(command, project);
|
|
127
|
+
|
|
128
|
+
if (inProd) {
|
|
129
|
+
process.stdout.write(
|
|
130
|
+
`forge: BLOQUEADO — comando destructivo detectado en contexto de producción.\n\n` +
|
|
131
|
+
` Comando: ${snippet}\n Patrón: ${label}\n\n` +
|
|
132
|
+
` Ejecutá esto MANUALMENTE con plena consciencia de que afecta producción.\n\n` +
|
|
133
|
+
` Lección del 2026-04-28: --force-reset borró 225 usuarios en producción.\n`
|
|
134
|
+
);
|
|
135
|
+
process.exit(2);
|
|
136
|
+
} else {
|
|
137
|
+
process.stdout.write(
|
|
138
|
+
`forge: ADVERTENCIA — comando potencialmente destructivo.\n\n` +
|
|
139
|
+
` Comando: ${snippet}\n Patrón: ${label}\n\n` +
|
|
140
|
+
` Si apunta a producción, cancelá y ejecutá manualmente.\n`
|
|
141
|
+
);
|
|
142
|
+
process.exit(0);
|
|
143
|
+
}
|
|
144
|
+
});
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* forge — PreToolUse hook: pre-edit-check.js
|
|
4
|
+
* Branch guard, debug detection, hardcoded secret detection. Zero Python dependency.
|
|
5
|
+
*/
|
|
6
|
+
'use strict';
|
|
7
|
+
|
|
8
|
+
const { execSync } = require('child_process');
|
|
9
|
+
const path = require('path');
|
|
10
|
+
|
|
11
|
+
const DEBUG = !['', '0', 'false', 'False'].includes(process.env.DEBUG || '');
|
|
12
|
+
const dbg = msg => DEBUG && process.stdout.write(`[forge-hook-debug] ${msg}\n`);
|
|
13
|
+
|
|
14
|
+
// ---------------------------------------------------------------------------
|
|
15
|
+
// File classification
|
|
16
|
+
// ---------------------------------------------------------------------------
|
|
17
|
+
const CODE_EXTS = new Set(['.py','.ts','.js','.tsx','.jsx','.php','.rb','.go','.rs','.java','.cs','.cpp','.c','.sh']);
|
|
18
|
+
const NON_CODE_EXTS = new Set(['.md','.yaml','.yml','.json','.toml','.txt','.lock']);
|
|
19
|
+
const ROOT_PROTECTED = new Set(['README.md','CLAUDE.md','CHANGELOG.md','AGENTS.md']);
|
|
20
|
+
const PROTECTED_DIRS = ['docs/', '.claude/'];
|
|
21
|
+
|
|
22
|
+
function isCodeFile(filePath) {
|
|
23
|
+
const ext = path.extname(filePath).toLowerCase();
|
|
24
|
+
if (CODE_EXTS.has(ext)) return true;
|
|
25
|
+
if (NON_CODE_EXTS.has(ext)) return false;
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function isExemptFromBranchGuard(filePath) {
|
|
30
|
+
const norm = filePath.replace(/\\/g, '/');
|
|
31
|
+
for (const d of PROTECTED_DIRS) {
|
|
32
|
+
if (norm.startsWith(d) || norm.includes(`/${d.replace(/\/$/, '')}`)) return true;
|
|
33
|
+
}
|
|
34
|
+
const base = path.basename(norm);
|
|
35
|
+
if (ROOT_PROTECTED.has(base)) return true;
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// ---------------------------------------------------------------------------
|
|
40
|
+
// Branch guard
|
|
41
|
+
// ---------------------------------------------------------------------------
|
|
42
|
+
function getCurrentBranch() {
|
|
43
|
+
try {
|
|
44
|
+
return execSync('git branch --show-current', { encoding: 'utf8', timeout: 3000 }).trim();
|
|
45
|
+
} catch { return ''; }
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// ---------------------------------------------------------------------------
|
|
49
|
+
// Debug detection patterns per language
|
|
50
|
+
// ---------------------------------------------------------------------------
|
|
51
|
+
const DEBUG_PATTERNS = [
|
|
52
|
+
{ re: /\bconsole\.(log|debug|warn|error|trace)\s*\(/, lang: 'TypeScript/JavaScript' },
|
|
53
|
+
{ re: /\bprint\s*\(/, lang: 'Python' },
|
|
54
|
+
{ re: /\bdd\s*\(/, lang: 'PHP (dd)' },
|
|
55
|
+
{ re: /\bvar_dump\s*\(/, lang: 'PHP (var_dump)' },
|
|
56
|
+
{ re: /\bdump\s*\(/, lang: 'PHP (dump)' },
|
|
57
|
+
{ re: /\bdebugger\b/, lang: 'JavaScript debugger' },
|
|
58
|
+
{ re: /\bbinding\.pry\b/, lang: 'Ruby (binding.pry)' },
|
|
59
|
+
{ re: /\bbyebug\b/, lang: 'Ruby (byebug)' },
|
|
60
|
+
{ re: /\bp\s+\w+/, lang: 'Ruby (p)' },
|
|
61
|
+
];
|
|
62
|
+
|
|
63
|
+
function detectDebugStatements(content) {
|
|
64
|
+
const found = [];
|
|
65
|
+
const lines = content.split('\n');
|
|
66
|
+
lines.forEach((line, idx) => {
|
|
67
|
+
for (const { re, lang } of DEBUG_PATTERNS) {
|
|
68
|
+
if (re.test(line)) {
|
|
69
|
+
found.push({ line: idx + 1, snippet: line.trim().slice(0, 80), lang });
|
|
70
|
+
break;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
return found;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// ---------------------------------------------------------------------------
|
|
78
|
+
// Secret detection
|
|
79
|
+
// ---------------------------------------------------------------------------
|
|
80
|
+
const SECRET_PATTERNS = [
|
|
81
|
+
{ re: /(?:password|passwd|pwd)\s*=\s*['"][^'"]{4,}['"]/i, label: 'password hardcodeado' },
|
|
82
|
+
{ re: /(?:api_?key|apikey)\s*=\s*['"][^'"]{8,}['"]/i, label: 'API key hardcodeada' },
|
|
83
|
+
{ re: /(?:secret|token)\s*=\s*['"][^'"]{8,}['"]/i, label: 'secret/token hardcodeado' },
|
|
84
|
+
{ re: /sk-[a-zA-Z0-9]{20,}/, label: 'OpenAI API key' },
|
|
85
|
+
{ re: /ghp_[a-zA-Z0-9]{30,}/, label: 'GitHub Personal Access Token' },
|
|
86
|
+
{ re: /eyJ[a-zA-Z0-9_-]{20,}\.[a-zA-Z0-9_-]{20,}/, label: 'JWT token' },
|
|
87
|
+
];
|
|
88
|
+
|
|
89
|
+
function detectSecrets(content) {
|
|
90
|
+
const found = [];
|
|
91
|
+
const lines = content.split('\n');
|
|
92
|
+
lines.forEach((line, idx) => {
|
|
93
|
+
for (const { re, label } of SECRET_PATTERNS) {
|
|
94
|
+
if (re.test(line)) {
|
|
95
|
+
found.push({ line: idx + 1, label, snippet: line.trim().slice(0, 60) + '...' });
|
|
96
|
+
break;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
return found;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// ---------------------------------------------------------------------------
|
|
104
|
+
// Main
|
|
105
|
+
// ---------------------------------------------------------------------------
|
|
106
|
+
let raw = '';
|
|
107
|
+
process.stdin.setEncoding('utf8');
|
|
108
|
+
process.stdin.on('data', chunk => { raw += chunk; });
|
|
109
|
+
process.stdin.on('end', () => {
|
|
110
|
+
if (!raw.trim()) { process.exit(0); }
|
|
111
|
+
let data;
|
|
112
|
+
try { data = JSON.parse(raw); } catch { process.exit(0); }
|
|
113
|
+
|
|
114
|
+
const toolName = data.tool_name || '';
|
|
115
|
+
if (!['Write', 'Edit', 'MultiEdit'].includes(toolName)) process.exit(0);
|
|
116
|
+
|
|
117
|
+
const toolInput = data.tool_input || {};
|
|
118
|
+
const filePath = toolInput.file_path || toolInput.path || '';
|
|
119
|
+
if (!filePath) process.exit(0);
|
|
120
|
+
|
|
121
|
+
dbg(`file: ${filePath}`);
|
|
122
|
+
|
|
123
|
+
const warnings = [];
|
|
124
|
+
|
|
125
|
+
// 1. Branch guard
|
|
126
|
+
if (!isExemptFromBranchGuard(filePath) && isCodeFile(filePath)) {
|
|
127
|
+
const branch = getCurrentBranch();
|
|
128
|
+
dbg(`branch: ${branch}`);
|
|
129
|
+
if (branch === 'main' || branch === 'master') {
|
|
130
|
+
process.stdout.write(
|
|
131
|
+
`forge: BLOQUEADO — editando código directamente en ${branch}.\n\n` +
|
|
132
|
+
` Archivo: ${filePath}\n\n` +
|
|
133
|
+
` Creá una rama antes de editar código:\n` +
|
|
134
|
+
` git checkout -b feat/descripcion\n\n` +
|
|
135
|
+
` Ramas de documentación (.md, .yaml, .json) están permitidas en ${branch}.\n`
|
|
136
|
+
);
|
|
137
|
+
process.exit(2);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// 2. Debug and secret detection on new content
|
|
142
|
+
const newContent = toolInput.new_string || toolInput.content || '';
|
|
143
|
+
if (newContent && isCodeFile(filePath)) {
|
|
144
|
+
const debugHits = detectDebugStatements(newContent);
|
|
145
|
+
const secretHits = detectSecrets(newContent);
|
|
146
|
+
|
|
147
|
+
if (debugHits.length > 0) {
|
|
148
|
+
const items = debugHits.map(h => ` línea ${h.line}: ${h.snippet} (${h.lang})`).join('\n');
|
|
149
|
+
warnings.push(`Debug statements detectados:\n${items}`);
|
|
150
|
+
}
|
|
151
|
+
if (secretHits.length > 0) {
|
|
152
|
+
const items = secretHits.map(h => ` línea ${h.line}: ${h.label} — ${h.snippet}`).join('\n');
|
|
153
|
+
warnings.push(`Posibles secrets hardcodeados:\n${items}\n Usar variables de entorno en su lugar.`);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
if (warnings.length > 0) {
|
|
158
|
+
process.stdout.write(
|
|
159
|
+
`forge: ADVERTENCIA — revisá antes de continuar:\n\n` +
|
|
160
|
+
warnings.map(w => ` • ${w}`).join('\n\n') + '\n'
|
|
161
|
+
);
|
|
162
|
+
// Don't block — just warn
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
process.exit(0);
|
|
166
|
+
});
|
package/dist/cli.js
CHANGED
|
@@ -4,7 +4,7 @@ import { audit } from './commands/audit.js';
|
|
|
4
4
|
import { generate } from './commands/generate.js';
|
|
5
5
|
import { validate } from './commands/validate.js';
|
|
6
6
|
import { doctor } from './commands/doctor.js';
|
|
7
|
-
const VERSION = '2.
|
|
7
|
+
const VERSION = '2.4.1';
|
|
8
8
|
const HELP = `forge v${VERSION} — Agentic development framework
|
|
9
9
|
|
|
10
10
|
Usage: forge <command> [options]
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"audit.d.ts","sourceRoot":"","sources":["../../src/commands/audit.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"audit.d.ts","sourceRoot":"","sources":["../../src/commands/audit.ts"],"names":[],"mappings":"AAmEA,wBAAsB,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAyI3D"}
|
package/dist/commands/audit.js
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import { existsSync, readFileSync, readdirSync } from 'fs';
|
|
2
2
|
import { join } from 'path';
|
|
3
3
|
import { findProjectYaml, loadProjectYaml } from '../lib/yaml.js';
|
|
4
|
+
import { bold, dim, green, red, yellow, cyan, gray, icons } from '../ui/colors.js';
|
|
5
|
+
import { box } from '../ui/box.js';
|
|
6
|
+
import { loadManifest, checkOutdated } from '../lib/lock.js';
|
|
4
7
|
const HELP = `Usage: forge audit [options]
|
|
5
8
|
|
|
6
9
|
Audit a project against the forge standard. Checks installed agents,
|
|
@@ -140,6 +143,20 @@ export async function audit(args) {
|
|
|
140
143
|
if (!hasClaudeDir && !hasAgentsMd && !hasKiro) {
|
|
141
144
|
issues.push({ level: 'warn', check: 'runtime', message: 'No se detectó ningún runtime — ejecutar forge init' });
|
|
142
145
|
}
|
|
146
|
+
// Manifest check
|
|
147
|
+
const manifest = loadManifest(root);
|
|
148
|
+
if (manifest) {
|
|
149
|
+
const outdated = checkOutdated(root, manifest);
|
|
150
|
+
if (outdated.length === 0) {
|
|
151
|
+
issues.push({ level: 'ok', check: 'manifest', message: `forge v${manifest.forgeVersion} — todos los archivos al día` });
|
|
152
|
+
}
|
|
153
|
+
else {
|
|
154
|
+
issues.push({ level: 'warn', check: 'manifest', message: `${outdated.length} archivo(s) modificados desde forge init` });
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
else {
|
|
158
|
+
issues.push({ level: 'info', check: 'manifest', message: 'Sin .forge/manifest.json — ejecutar forge init para generarlo' });
|
|
159
|
+
}
|
|
143
160
|
// Summary
|
|
144
161
|
const errors = issues.filter(i => i.level === 'error').length;
|
|
145
162
|
const warnings = issues.filter(i => i.level === 'warn').length;
|
|
@@ -151,15 +168,18 @@ export async function audit(args) {
|
|
|
151
168
|
}, null, 2));
|
|
152
169
|
}
|
|
153
170
|
else {
|
|
154
|
-
console.log('forge audit\n');
|
|
171
|
+
console.log(cyan(bold('forge audit')) + '\n');
|
|
155
172
|
for (const issue of issues) {
|
|
156
|
-
const
|
|
157
|
-
|
|
158
|
-
console.log(` [${icon}] ${issue.check.padEnd(20)} ${issue.message}`);
|
|
173
|
+
const levelIcon = icons[issue.level] ?? gray('·');
|
|
174
|
+
console.log(` [${levelIcon}] ${bold(issue.check.padEnd(20))} ${dim(issue.message)}`);
|
|
159
175
|
}
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
176
|
+
const summaryLine = `Resumen: ${green(String(ok) + ' OK')} · ${yellow(String(warnings) + ' warn')} · ${red(String(errors) + ' ✗')}`;
|
|
177
|
+
const boxTitle = errors === 0 && warnings === 0
|
|
178
|
+
? green('Todo en orden')
|
|
179
|
+
: errors > 0
|
|
180
|
+
? red('Se encontraron errores')
|
|
181
|
+
: yellow('Advertencias encontradas');
|
|
182
|
+
console.log('\n' + box(boxTitle, [summaryLine]));
|
|
163
183
|
}
|
|
164
184
|
return errors > 0 ? 1 : 0;
|
|
165
185
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"audit.js","sourceRoot":"","sources":["../../src/commands/audit.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,IAAI,CAAC;AAC3D,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"audit.js","sourceRoot":"","sources":["../../src/commands/audit.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../../src/commands/doctor.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../../src/commands/doctor.ts"],"names":[],"mappings":"AAOA,wBAAsB,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAqG7D"}
|