@ronaldjdevfs/forge 1.0.2 → 1.2.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 (54) hide show
  1. package/README.md +132 -10
  2. package/package.json +6 -3
  3. package/skills/forge/SKILL.md +86 -15
  4. package/skills/forge/profiles/express-drizzle.md +107 -0
  5. package/skills/forge/profiles/fastify-mongodb.md +103 -0
  6. package/skills/forge/profiles/fastify-prisma.md +81 -0
  7. package/skills/forge/profiles/nestjs-mongodb.md +92 -0
  8. package/skills/forge/profiles/nestjs-postgres.md +98 -0
  9. package/skills/forge/reference/api-design.md +62 -0
  10. package/skills/forge/reference/assay.md +82 -0
  11. package/skills/forge/reference/cast.md +81 -7
  12. package/skills/forge/reference/data-patterns.md +86 -0
  13. package/skills/forge/reference/di-strategies.md +50 -0
  14. package/skills/forge/reference/errors.md +65 -0
  15. package/skills/forge/reference/events.md +95 -0
  16. package/skills/forge/reference/help.md +40 -0
  17. package/skills/forge/reference/hooks.md +62 -0
  18. package/skills/forge/reference/observability.md +66 -0
  19. package/skills/forge/reference/patterns.md +52 -0
  20. package/skills/forge/reference/principles.md +6 -0
  21. package/skills/forge/reference/reforge.md +69 -5
  22. package/skills/forge/reference/relocate.md +15 -2
  23. package/skills/forge/reference/security-patterns.md +87 -0
  24. package/skills/forge/reference/testing-patterns.md +69 -0
  25. package/skills/forge/scripts/assay.mjs +481 -0
  26. package/skills/forge/scripts/context.mjs +147 -43
  27. package/skills/forge/scripts/detect.mjs +371 -22
  28. package/skills/forge/scripts/forge-api.mjs +373 -0
  29. package/skills/forge/scripts/forge-config.mjs +268 -0
  30. package/skills/forge/scripts/forge-signals.mjs +131 -0
  31. package/skills/forge/scripts/forge-state.mjs +97 -0
  32. package/skills/forge/scripts/formatter.mjs +133 -0
  33. package/skills/forge/scripts/graph.mjs +5 -21
  34. package/skills/forge/scripts/hook.mjs +250 -0
  35. package/skills/forge/scripts/inspect.mjs +171 -22
  36. package/skills/forge/scripts/parse-imports.mjs +249 -0
  37. package/skills/forge/scripts/pin.mjs +151 -0
  38. package/skills/forge/scripts/posttool.mjs +224 -0
  39. package/skills/forge/scripts/profile.mjs +124 -20
  40. package/skills/forge/scripts/registry/rules.mjs +344 -0
  41. package/skills/forge/scripts/rename.mjs +669 -0
  42. package/skills/forge/scripts/rollback.mjs +213 -0
  43. package/skills/forge/scripts/update.mjs +114 -0
  44. package/skills/forge/templates/agents/claude/CLAUDE.md +98 -0
  45. package/skills/forge/templates/agents/cursor/.cursorrules +80 -0
  46. package/skills/forge/templates/feature/domain-error.ts.md +9 -0
  47. package/skills/forge/templates/feature/domain-event.ts.md +9 -0
  48. package/skills/forge/templates/feature/event-handler.ts.md +10 -0
  49. package/skills/forge/templates/feature/use-case.ts.md +10 -2
  50. package/skills/forge/tests/core.test.mjs +403 -0
  51. package/src/agents.mjs +55 -0
  52. package/src/cli.js +127 -54
  53. package/src/wizard.mjs +474 -0
  54. package/logo.png +0 -0
package/src/cli.js CHANGED
@@ -1,13 +1,15 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- import { copyFileSync, mkdirSync, existsSync, readdirSync, statSync, writeFileSync, readFileSync } from "fs";
3
+ import { copyFileSync, mkdirSync, existsSync, writeFileSync, readFileSync, cpSync } from "fs";
4
4
  import { join, dirname, relative } from "path";
5
5
  import { fileURLToPath } from "url";
6
6
  import { execSync } from "child_process";
7
+ import { spinner, log } from "@clack/prompts";
8
+ import { runWizard } from "./wizard.mjs";
7
9
 
8
10
  const __dirname = dirname(fileURLToPath(import.meta.url));
9
- const PKG_DIR = join(__dirname, "..");
10
- const SKILL_SRC = join(PKG_DIR, "skills", "forge");
11
+ const SKILL_SRC = join(__dirname, "..", "skills", "forge");
12
+ const AGENTS_TEMPLATES = join(SKILL_SRC, "templates", "agents");
11
13
 
12
14
  function getTargetDir(global) {
13
15
  if (global) {
@@ -30,16 +32,7 @@ function getConfigDir(global) {
30
32
  }
31
33
 
32
34
  function copyRecursive(src, dest) {
33
- mkdirSync(dest, { recursive: true });
34
- for (const entry of readdirSync(src)) {
35
- const srcPath = join(src, entry);
36
- const destPath = join(dest, entry);
37
- if (statSync(srcPath).isDirectory()) {
38
- copyRecursive(srcPath, destPath);
39
- } else {
40
- copyFileSync(srcPath, destPath);
41
- }
42
- }
35
+ cpSync(src, dest, { recursive: true, force: true });
43
36
  }
44
37
 
45
38
  function detectPM(configDir) {
@@ -62,7 +55,6 @@ function ensureDependencies(configDir) {
62
55
  if (!pkg.dependencies["@opencode-ai/plugin"]) {
63
56
  pkg.dependencies["@opencode-ai/plugin"] = "1.17.9";
64
57
  writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + "\n");
65
- console.log(` ✓ Actualizado ${relative(process.cwd(), pkgPath)}`);
66
58
  }
67
59
 
68
60
  const pm = detectPM(configDir);
@@ -74,30 +66,28 @@ function ensureDependencies(configDir) {
74
66
 
75
67
  try {
76
68
  runPM(pm);
77
- console.log(` ✓ Dependencias instaladas (${pm})`);
78
69
  } catch {
79
70
  for (const fallback of order[pm]) {
80
71
  try {
81
72
  runPM(fallback);
82
- console.log(` ✓ Dependencias instaladas (${fallback})`);
83
73
  return;
84
- } catch {}
74
+ } catch { }
85
75
  }
86
- console.warn("No se pudieron instalar dependencias. Ejecuta 'pnpm install' | 'npm install' manualmente en " + configDir);
76
+ log.warn("No se pudieron instalar dependencias. Ejecutá 'pnpm install' | 'npm install' manualmente en " + configDir);
87
77
  }
88
78
  }
89
79
 
90
80
  const COMMANDS = [
91
- { name: "forge-forge", desc: "Forge — inicializar proyecto arquitectónicamente" },
92
- { name: "forge-cast", desc: "Cast — crear un nuevo feature hexagonal desde cero" },
81
+ { name: "forge-forge", desc: "Forge — inicializar proyecto arquitectónicamente" },
82
+ { name: "forge-cast", desc: "Cast — crear un nuevo feature hexagonal desde cero" },
93
83
  { name: "forge-inspect", desc: "Inspect — inspeccionar la conformidad arquitectónica" },
94
- { name: "forge-relocate",desc: "Relocate — migrar feature legacy a la nueva estructura" },
84
+ { name: "forge-relocate", desc: "Relocate — migrar feature legacy a la nueva estructura" },
95
85
  { name: "forge-reforge", desc: "Reforge — refactorizar la arquitectura de un feature" },
96
- { name: "forge-quench", desc: "Quench — verificar reglas arquitectónicas del proyecto" },
97
- { name: "forge-temper", desc: "Temper — endurecer la arquitectura (DI, seguridad)" },
98
- { name: "forge-chain", desc: "Chain — analizar cadena de dependencias entre features" },
99
- { name: "forge-inscribe",desc: "Inscribe — generar y mantener ARCHITECTURE.md" },
100
- { name: "forge-smelt", desc: "Smelt — extraer código reutilizable a shared/" },
86
+ { name: "forge-quench", desc: "Quench — verificar reglas arquitectónicas del proyecto" },
87
+ { name: "forge-temper", desc: "Temper — endurecer la arquitectura (DI, seguridad)" },
88
+ { name: "forge-chain", desc: "Chain — analizar cadena de dependencias entre features" },
89
+ { name: "forge-inscribe", desc: "Inscribe — generar y mantener ARCHITECTURE.md" },
90
+ { name: "forge-smelt", desc: "Smelt — extraer código reutilizable a shared/" },
101
91
  ];
102
92
 
103
93
  function generateCommands(configDir) {
@@ -116,46 +106,111 @@ function generateCommands(configDir) {
116
106
  ].join("\n");
117
107
  writeFileSync(join(cmdsDir, `${cmd.name}.md`), content);
118
108
  }
119
- console.log(` ✓ Comandos /forge-* generados en .opencode/commands/`);
120
109
  }
121
110
 
122
- function printHelp() {
123
- console.log(`
124
- Forge — Architecture OS
125
-
126
- USO
127
- forge install Instalar skill en el proyecto actual
128
- forge install --global Instalar globalmente (~/.config/opencode/)
129
- forge skills install (alias del comando install)
130
-
131
- OPCIONES
132
- -g, --global Instalar en ~/.config/opencode/skills/forge/
133
- -h, --help Mostrar esta ayuda
134
- `);
135
- }
111
+ // --- Direct installers ---
136
112
 
137
- function install(global) {
113
+ async function installOpenCode(isGlobal = false) {
138
114
  if (!existsSync(SKILL_SRC)) {
139
- console.error("[ERROR] No se encuentra skills/forge/ en el paquete. ¿Corrupto?");
115
+ log.error("No se encuentra skills/forge/ en el paquete. ¿Corrupto?");
140
116
  process.exit(1);
141
117
  }
142
118
 
143
- const target = getTargetDir(global);
144
- const configDir = getConfigDir(global);
119
+ const target = getTargetDir(isGlobal);
120
+ const configDir = getConfigDir(isGlobal);
121
+ const rel = relative(process.cwd(), target);
145
122
 
146
- console.log(`\n Forge Architecture OS`);
147
- console.log(` ${"=".repeat(40)}`);
148
- console.log(` Destino: ${target}\n`);
123
+ const s = spinner();
149
124
 
125
+ s.start("Copiando skill a " + rel);
150
126
  copyRecursive(SKILL_SRC, target);
151
- console.log(` ✓ Skill copiada a ${relative(process.cwd(), target)}`);
127
+ s.stop("Skill copiada a " + rel);
128
+
129
+ s.start("Generando comandos /forge-*");
152
130
  generateCommands(configDir);
131
+ s.stop("Comandos generados en " + relative(process.cwd(), join(configDir, "commands")));
132
+
133
+ s.start("Instalando dependencias");
153
134
  ensureDependencies(configDir);
154
- console.log(`\n ✓ Forge instalado correctamente`);
155
- console.log(` ${"=".repeat(40)}\n`);
135
+ s.stop("Dependencias instaladas");
136
+
137
+ log.success("OpenCode configurado correctamente");
138
+ }
139
+
140
+ async function installCursor() {
141
+ const src = join(AGENTS_TEMPLATES, "cursor", ".cursorrules");
142
+ const dest = join(process.cwd(), ".cursorrules");
143
+
144
+ if (!existsSync(src)) {
145
+ log.error("Template .cursorrules no encontrado");
146
+ process.exit(1);
147
+ }
148
+
149
+ const s = spinner();
150
+ s.start("Creando .cursorrules");
151
+ copyFileSync(src, dest);
152
+ s.stop(".cursorrules creado");
153
+
154
+ log.success("Cursor configurado correctamente");
156
155
  }
157
156
 
158
- function main() {
157
+ async function installClaude() {
158
+ const claudeDir = join(process.cwd(), ".claude");
159
+ const src = join(AGENTS_TEMPLATES, "claude", "CLAUDE.md");
160
+ const dest = join(claudeDir, "CLAUDE.md");
161
+
162
+ if (!existsSync(src)) {
163
+ log.error("Template CLAUDE.md no encontrado");
164
+ process.exit(1);
165
+ }
166
+
167
+ const s = spinner();
168
+ s.start("Creando .claude/CLAUDE.md");
169
+ mkdirSync(claudeDir, { recursive: true });
170
+ copyFileSync(src, dest);
171
+ s.stop(".claude/CLAUDE.md creado");
172
+
173
+ log.success("Claude Code configurado correctamente");
174
+ }
175
+
176
+ async function installAgents(selected, isGlobal = false) {
177
+ for (const agent of selected) {
178
+ switch (agent) {
179
+ case "opencode":
180
+ await installOpenCode(isGlobal);
181
+ break;
182
+ case "cursor":
183
+ await installCursor();
184
+ break;
185
+ case "claude":
186
+ await installClaude();
187
+ break;
188
+ }
189
+ }
190
+ }
191
+
192
+ // --- CLI ---
193
+
194
+ function printHelp() {
195
+ console.log(`
196
+ ⚔️ Forge — Architecture OS
197
+
198
+ USO
199
+ forge install Mostrar wizard interactivo de instalación
200
+ forge install --opencode Instalar solo para OpenCode
201
+ forge install --cursor Instalar solo para Cursor
202
+ forge install --claude Instalar solo para Claude Code
203
+ forge install --all Instalar para todos los agentes
204
+ forge install --global Instalar OpenCode globalmente (~/.config/opencode/)
205
+ forge install --help Mostrar esta ayuda
206
+
207
+ OPCIONES
208
+ -g, --global Instalar OpenCode en ~/.config/opencode/skills/forge/
209
+ -h, --help Mostrar esta ayuda
210
+ `);
211
+ }
212
+
213
+ async function main() {
159
214
  const args = process.argv.slice(2);
160
215
  const isHelp = args.includes("-h") || args.includes("--help");
161
216
  const isGlobal = args.includes("-g") || args.includes("--global");
@@ -165,15 +220,33 @@ function main() {
165
220
  return;
166
221
  }
167
222
 
223
+ const hasOpenCode = args.includes("--opencode");
224
+ const hasCursor = args.includes("--cursor");
225
+ const hasClaude = args.includes("--claude");
226
+ const hasAll = args.includes("--all");
227
+
168
228
  const subcommandIndex = args.indexOf("skills");
169
229
  const command = subcommandIndex !== -1 ? args[subcommandIndex + 1] : args[0];
170
230
 
171
231
  if (command === "install") {
172
- install(isGlobal);
232
+ if (hasAll) {
233
+ await installAgents(["opencode", "cursor", "claude"], isGlobal);
234
+ } else if (hasOpenCode || hasCursor || hasClaude) {
235
+ const selected = [];
236
+ if (hasOpenCode) selected.push("opencode");
237
+ if (hasCursor) selected.push("cursor");
238
+ if (hasClaude) selected.push("claude");
239
+ await installAgents(selected, isGlobal);
240
+ } else {
241
+ await runWizard();
242
+ }
173
243
  } else {
174
244
  printHelp();
175
245
  process.exit(1);
176
246
  }
177
247
  }
178
248
 
179
- main();
249
+ main().catch((err) => {
250
+ console.error("[ERROR]", err.message);
251
+ process.exit(1);
252
+ });