openprompt-lang 0.3.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.
- package/LICENSE +21 -0
- package/README.md +663 -0
- package/bin/cli.js +110 -0
- package/bin/lint.js +50 -0
- package/docs/COMMANDS.md +229 -0
- package/docs/COMMITS/INDEX.md +11 -0
- package/docs/COMMITS/v0.1.0-existing.md +31 -0
- package/docs/COMMITS/v0.1.0-inicial.md +50 -0
- package/docs/COMMITS/v0.1.0-readme.md +24 -0
- package/docs/COMMITS/v0.2.0-strict-db-templates.md +50 -0
- package/docs/COMMITS/v0.3.0-parser-fixes-vscode.md +67 -0
- package/docs/COMMITS/v0.3.0-versioning-component.md +44 -0
- package/docs/DEPENDENCIES.md +45 -0
- package/docs/FRAMEWORK.md +1741 -0
- package/docs/SYNTAX.md +359 -0
- package/docs/VERSIONING.md +150 -0
- package/docs/referencia-metodologia/Anexos Finales Documentos de Respaldo y Estandarizaci/303/263n.md" +90 -0
- package/docs/referencia-metodologia/Cotizaciones.md +84 -0
- package/docs/referencia-metodologia/Example.md +1 -0
- package/docs/referencia-metodologia/ExtractorInformacion.py +78 -0
- package/docs/referencia-metodologia/Fase - 1 .- Desarrollo de la Metodolog/303/255a.md" +67 -0
- package/docs/referencia-metodologia/Fase - 2 .- Levantamiento de requisitos generales y traduccion a la IA.md +64 -0
- package/docs/referencia-metodologia/Fase - 3 .- Prototipado visual con IA (Figma Maker o equivalentes).md +64 -0
- package/docs/referencia-metodologia/Fase - 4 .- Especificacion de requisitos e iteracion con el cliente.md +58 -0
- package/docs/referencia-metodologia/Fase - 5 .- Estructuracion y maquetado de funciones (Scaffolding).md +118 -0
- package/docs/referencia-metodologia/Fase - 6 .- Estructuracion del backlog y division de tareas.md +48 -0
- package/docs/referencia-metodologia/Fase - 7 .- Desarrollo activo, pruebas y control de versiones.md +98 -0
- package/docs/referencia-metodologia/Fase - 8 .- Entrega, capacitaci/303/263n y mantenimiento.md" +55 -0
- package/docs/referencia-metodologia/Figma prompt template.md +130 -0
- package/docs/referencia-metodologia/Framework de Desarrollo Asistido por IA.md +1741 -0
- package/docs/referencia-metodologia/Indice General.md +83 -0
- package/docs/referencia-metodologia/Prompt refactorizar o creacion desde cero.md +50 -0
- package/docs/referencia-metodologia/docs/CONVENCIONES_DB.md +410 -0
- package/docs/referencia-metodologia/docs/CONVENCIONES_DOCUMENTACION.md +209 -0
- package/docs/referencia-metodologia/docs/PROMPTS/INDEX.md +73 -0
- package/docs/referencia-metodologia/docs/PROMPTS/PLANTILLAS/01-hook-supabase.md +79 -0
- package/docs/referencia-metodologia/docs/PROMPTS/PLANTILLAS/02-componente-ui.md +82 -0
- package/docs/referencia-metodologia/docs/PROMPTS/PLANTILLAS/03-pagina-feature.md +70 -0
- package/docs/referencia-metodologia/docs/PROMPTS/PLANTILLAS/04-comando-tauri.md +56 -0
- package/docs/referencia-metodologia/docs/PROMPTS/PLANTILLAS/05-store-zustand.md +74 -0
- package/docs/referencia-metodologia/docs/PROMPTS/PLANTILLAS/06-servicio-supabase.md +74 -0
- package/docs/referencia-metodologia/docs/PROMPTS/PLANTILLAS/07-formulario-validacion.md +63 -0
- package/docs/referencia-metodologia/docs/PROMPTS/PLANTILLAS/08-hook-capacitor.md +65 -0
- package/docs/referencia-metodologia/docs/PROMPTS/PLANTILLAS/09-refactor-division.md +51 -0
- package/docs/referencia-metodologia/docs/PROMPTS/PLANTILLAS/10-scaffolding-inicial.md +79 -0
- package/docs/referencia-metodologia/docs/PROMPTS/PLANTILLAS/11-supabase-crud-service.md +114 -0
- package/docs/referencia-metodologia/docs/PROMPTS/PLANTILLAS/12-supabase-hook-usetable.md +143 -0
- package/docs/referencia-metodologia/docs/PROMPTS/PLANTILLAS/13-tauri-command-rust.md +84 -0
- package/docs/referencia-metodologia/docs/PROMPTS/PLANTILLAS/14-tauri-wrapper-typescript.md +92 -0
- package/docs/referencia-metodologia/docs/PROMPTS/PLANTILLAS/15-documentar-tabla-db.md +50 -0
- package/docs/referencia-metodologia/docs/PROMPTS/PLANTILLAS/16-diagrama-arquitectura.md +60 -0
- package/docs/referencia-metodologia/docs/PROMPTS/PLANTILLAS/17-documentar-api-rpc.md +56 -0
- package/docs/referencia-metodologia/docs/PROMPTS/STACK/ionic-capacitor.md +52 -0
- package/docs/referencia-metodologia/docs/PROMPTS/STACK/react-web-puro.md +46 -0
- package/docs/referencia-metodologia/docs/PROMPTS/STACK/tauri-desktop.md +53 -0
- package/package.json +56 -0
- package/schemas/prompt-lang.json +98 -0
- package/src/commands/component.js +326 -0
- package/src/commands/context.js +206 -0
- package/src/commands/figma.js +63 -0
- package/src/commands/init.js +373 -0
- package/src/commands/suggest.js +31 -0
- package/src/commands/validate.js +183 -0
- package/src/generators/figma-prompt.js +56 -0
- package/src/utils/ai.js +143 -0
- package/src/utils/annotations.js +510 -0
- package/src/utils/config.js +60 -0
- package/vscode-extension/README.md +31 -0
- package/vscode-extension/language-configuration.json +7 -0
- package/vscode-extension/package.json +62 -0
- package/vscode-extension/snippets/promptlang.json +105 -0
- package/vscode-extension/syntaxes/annotations.tmGrammar.json +39 -0
- package/vscode-extension/syntaxes/promptlang.tmGrammar.json +14 -0
|
@@ -0,0 +1,373 @@
|
|
|
1
|
+
import { writeFileSync, readFileSync, existsSync, mkdirSync } from "fs";
|
|
2
|
+
import { join } from "path";
|
|
3
|
+
import chalk from "chalk";
|
|
4
|
+
import { detectStack } from "../utils/config.js";
|
|
5
|
+
|
|
6
|
+
const BASE_STRUCTURE = {
|
|
7
|
+
"src/components/ui": { desc: "Componentes atómicos reutilizables" },
|
|
8
|
+
"src/components/layout": { desc: "Componentes de layout (Navbar, Sidebar)" },
|
|
9
|
+
"src/components/shared": { desc: "Componentes compartidos del dominio" },
|
|
10
|
+
"src/features": { desc: "Módulos de features (auth, dashboard, etc.)" },
|
|
11
|
+
"src/hooks": { desc: "Custom hooks globales" },
|
|
12
|
+
"src/services": { desc: "APIs, clientes externos" },
|
|
13
|
+
"src/store": { desc: "Estado global (Zustand)" },
|
|
14
|
+
"src/types": { desc: "Interfaces globales" },
|
|
15
|
+
"src/utils": { desc: "Funciones utilitarias" },
|
|
16
|
+
"docs/COMMITS": { desc: "Logs de commits" },
|
|
17
|
+
"docs/LOGS/ERRORES": { desc: "Logs de errores por tipo" },
|
|
18
|
+
"docs/LOGS/ACTIVIDAD": { desc: "Logs de actividad diaria" },
|
|
19
|
+
"docs/BACKLOG": { desc: "Backlog por fases" },
|
|
20
|
+
"docs/PROMPTS/PLANTILLAS": { desc: "Prompts reutilizables" },
|
|
21
|
+
"docs/PROMPTS/STACK": { desc: "Contextos por tecnología" },
|
|
22
|
+
"scripts": { desc: "Scripts de automatización" },
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
const DESCRIPTIVE_SCRIPTS = {
|
|
26
|
+
"dev:start": "vite",
|
|
27
|
+
"dev:preview": "vite preview",
|
|
28
|
+
"build:prod": "vite build",
|
|
29
|
+
"build:analyze": "vite build --analyze",
|
|
30
|
+
"lint:check": "eslint src --ext .ts,.tsx",
|
|
31
|
+
"lint:fix": "eslint src --ext .ts,.tsx --fix",
|
|
32
|
+
"type:check": "tsc --noEmit",
|
|
33
|
+
"quality:doctor": "npx react-doctor check ./src",
|
|
34
|
+
"test:unit": "vitest run",
|
|
35
|
+
"test:watch": "vitest",
|
|
36
|
+
"test:coverage": "vitest run --coverage",
|
|
37
|
+
"validate:all": "npm run lint:check && npm run type:check && npm run quality:doctor && npm run test:unit",
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
export async function init(options) {
|
|
41
|
+
if (options.existing) {
|
|
42
|
+
return initExisting();
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const projectName = options.name || "mi-proyecto";
|
|
46
|
+
const baseDir = join(process.cwd(), projectName);
|
|
47
|
+
|
|
48
|
+
console.log(chalk.cyan(`\n🚀 Inicializando proyecto: ${projectName}\n`));
|
|
49
|
+
|
|
50
|
+
// Crear directorios
|
|
51
|
+
for (const [dir, meta] of Object.entries(BASE_STRUCTURE)) {
|
|
52
|
+
const fullPath = join(baseDir, dir);
|
|
53
|
+
mkdirSync(fullPath, { recursive: true });
|
|
54
|
+
console.log(` 📁 ${dir}/`);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Crear prompt-lang.json
|
|
58
|
+
const config = {
|
|
59
|
+
name: projectName,
|
|
60
|
+
version: "0.1.0",
|
|
61
|
+
stack: {
|
|
62
|
+
base: ["react", "typescript", "vite", "tailwind"],
|
|
63
|
+
extensions: options.stack ? [options.stack] : [],
|
|
64
|
+
},
|
|
65
|
+
profile: options.profile || "senior",
|
|
66
|
+
pipeline: {
|
|
67
|
+
"pre-commit": ["lint", "typecheck", "doctor", "test"],
|
|
68
|
+
scripts: {
|
|
69
|
+
"lint:check": "eslint src --ext .ts,.tsx",
|
|
70
|
+
"type:check": "tsc --noEmit",
|
|
71
|
+
"quality:doctor": "npx react-doctor check ./src",
|
|
72
|
+
"test:unit": "vitest run",
|
|
73
|
+
},
|
|
74
|
+
},
|
|
75
|
+
annotations: { enabled: true, strict: true },
|
|
76
|
+
components: {
|
|
77
|
+
source: "src/components/ui",
|
|
78
|
+
library: "",
|
|
79
|
+
},
|
|
80
|
+
docs: { commits: true, logs: true, backlog: true },
|
|
81
|
+
extractor: { ignore: [".env", "*.local", "dist"], output: "contexto.md" },
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
writeFileSync(join(baseDir, "prompt-lang.json"), JSON.stringify(config, null, 2), "utf-8");
|
|
85
|
+
console.log(chalk.green(" ✅ prompt-lang.json"));
|
|
86
|
+
|
|
87
|
+
// Crear AGENTS.md
|
|
88
|
+
const agentsContent = `# AGENTS.md — Contexto para IA
|
|
89
|
+
|
|
90
|
+
## Stack
|
|
91
|
+
- React 18 + TypeScript + Vite
|
|
92
|
+
- Tailwind CSS 3.4
|
|
93
|
+
${config.stack.extensions.includes("ionic") ? "- Ionic 8 + Capacitor 6 (mobile)" : ""}
|
|
94
|
+
${config.stack.extensions.includes("tauri") ? "- Tauri v2 (desktop)" : ""}
|
|
95
|
+
${config.stack.extensions.includes("supabase") ? "- Supabase (backend)" : ""}
|
|
96
|
+
- Zustand (estado global)
|
|
97
|
+
|
|
98
|
+
## Convenciones
|
|
99
|
+
- Un componente por archivo, máximo 120 líneas
|
|
100
|
+
- Un hook por archivo, máximo 80 líneas
|
|
101
|
+
- 100% Tailwind utility classes (sin CSS personalizado)
|
|
102
|
+
- Barrel exports en cada carpeta (index.ts)
|
|
103
|
+
- Commits: conventional commits + log en docs/COMMITS/
|
|
104
|
+
- Prompt library: docs/PROMPTS/INDEX.md
|
|
105
|
+
- Framework: openPrompt-Lang (anotaciones @kind, @contract, @limit, etc.)
|
|
106
|
+
|
|
107
|
+
## Perfil del desarrollador
|
|
108
|
+
${config.profile}
|
|
109
|
+
|
|
110
|
+
## Reglas críticas
|
|
111
|
+
- NO usar any
|
|
112
|
+
- NO superar límites de líneas sin refactorizar
|
|
113
|
+
- Ejecutar \`npm run validate:all\` antes de cada commit
|
|
114
|
+
- Las anotaciones PromptLang deben declarar @use() al inicio del archivo
|
|
115
|
+
|
|
116
|
+
## Referencias
|
|
117
|
+
- Framework: docs/FRAMEWORK.md
|
|
118
|
+
- Prompt library: docs/PROMPTS/INDEX.md
|
|
119
|
+
`;
|
|
120
|
+
|
|
121
|
+
writeFileSync(join(baseDir, "AGENTS.md"), agentsContent, "utf-8");
|
|
122
|
+
console.log(chalk.green(" ✅ AGENTS.md"));
|
|
123
|
+
|
|
124
|
+
// Crear .gitignore
|
|
125
|
+
const gitignore = `node_modules/
|
|
126
|
+
dist/
|
|
127
|
+
build/
|
|
128
|
+
.env
|
|
129
|
+
.env.local
|
|
130
|
+
*.log
|
|
131
|
+
.DS_Store
|
|
132
|
+
coverage/
|
|
133
|
+
contexto.md
|
|
134
|
+
`;
|
|
135
|
+
|
|
136
|
+
writeFileSync(join(baseDir, ".gitignore"), gitignore, "utf-8");
|
|
137
|
+
console.log(chalk.green(" ✅ .gitignore"));
|
|
138
|
+
|
|
139
|
+
// Crear package.json con scripts descriptivos
|
|
140
|
+
const packageJson = {
|
|
141
|
+
name: projectName,
|
|
142
|
+
version: "0.1.0",
|
|
143
|
+
private: true,
|
|
144
|
+
type: "module",
|
|
145
|
+
scripts: { ...DESCRIPTIVE_SCRIPTS },
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
writeFileSync(join(baseDir, "package.json"), JSON.stringify(packageJson, null, 2), "utf-8");
|
|
149
|
+
console.log(chalk.green(" ✅ package.json (scripts descriptivos)"));
|
|
150
|
+
|
|
151
|
+
// Crear scripts de validación
|
|
152
|
+
const validateScript = `#!/bin/bash
|
|
153
|
+
set -e
|
|
154
|
+
|
|
155
|
+
echo "=== Pipeline de Validación (openPrompt-Lang) ==="
|
|
156
|
+
|
|
157
|
+
echo "[1/4] Linting..."
|
|
158
|
+
npm run lint:check || { echo "❌ Lint falló"; exit 1; }
|
|
159
|
+
|
|
160
|
+
echo "[2/4] TypeScript check..."
|
|
161
|
+
npm run type:check || { echo "❌ TypeScript falló"; exit 1; }
|
|
162
|
+
|
|
163
|
+
echo "[3/4] react-doctor..."
|
|
164
|
+
npm run quality:doctor || { echo "❌ react-doctor encontró problemas"; exit 1; }
|
|
165
|
+
|
|
166
|
+
echo "[4/4] Tests..."
|
|
167
|
+
npm run test:unit || { echo "❌ Tests fallaron"; exit 1; }
|
|
168
|
+
|
|
169
|
+
echo "=== ✅ Pipeline completo — Código válido ==="
|
|
170
|
+
`;
|
|
171
|
+
|
|
172
|
+
writeFileSync(join(baseDir, "scripts/validate.sh"), validateScript, "utf-8");
|
|
173
|
+
console.log(chalk.green(" ✅ scripts/validate.sh"));
|
|
174
|
+
|
|
175
|
+
// Crear docs/BACKLOG/PHASE-1-core.md
|
|
176
|
+
const backlogPhase1 = `# PHASE-1 Core — MVP Funcional
|
|
177
|
+
|
|
178
|
+
> Prioridad: P0 (Crítico) | 50% del proyecto
|
|
179
|
+
|
|
180
|
+
## Tareas
|
|
181
|
+
|
|
182
|
+
### TASK-001: Setup inicial del proyecto
|
|
183
|
+
**Módulo:** infraestructura
|
|
184
|
+
**Estimación:** 2h
|
|
185
|
+
**Criterios de aceptación:**
|
|
186
|
+
- [ ] Proyecto Vite + React + TypeScript creado
|
|
187
|
+
- [ ] Tailwind configurado
|
|
188
|
+
- [ ] Estructura de carpetas según estándar
|
|
189
|
+
- [ ] Pipeline de validación funcional
|
|
190
|
+
|
|
191
|
+
### TASK-002: Sistema de autenticación
|
|
192
|
+
**Módulo:** auth
|
|
193
|
+
**Estimación:** 6h
|
|
194
|
+
**Dependencias:** TASK-001
|
|
195
|
+
**Criterios de aceptación:**
|
|
196
|
+
- [ ] Login con email/contraseña
|
|
197
|
+
- [ ] Registro de usuarios
|
|
198
|
+
- [ ] Protección de rutas
|
|
199
|
+
- [ ] Manejo de sesión con Zustand
|
|
200
|
+
`;
|
|
201
|
+
|
|
202
|
+
writeFileSync(join(baseDir, "docs/BACKLOG/PHASE-1-core.md"), backlogPhase1, "utf-8");
|
|
203
|
+
console.log(chalk.green(" ✅ docs/BACKLOG/PHASE-1-core.md"));
|
|
204
|
+
|
|
205
|
+
// Crear docs/COMMITS/INDEX.md
|
|
206
|
+
writeFileSync(
|
|
207
|
+
join(baseDir, "docs/COMMITS/INDEX.md"),
|
|
208
|
+
"# Índice de Commits\n\n| Fecha | Versión | Descripción |\n|---|---|---|\n",
|
|
209
|
+
"utf-8"
|
|
210
|
+
);
|
|
211
|
+
console.log(chalk.green(" ✅ docs/COMMITS/INDEX.md"));
|
|
212
|
+
|
|
213
|
+
console.log(chalk.cyan("\n📦 Estructura inicial creada exitosamente."));
|
|
214
|
+
console.log(chalk.cyan(`👉 cd ${projectName}`));
|
|
215
|
+
console.log(chalk.cyan(`👉 npx openPrompt-Lang context`));
|
|
216
|
+
console.log(chalk.cyan(`👉 npm run dev:start # Iniciar servidor de desarrollo`));
|
|
217
|
+
console.log(chalk.cyan(`👉 npm run validate:all # Pipeline completo de validación`));
|
|
218
|
+
console.log(chalk.cyan(`👉 Revisa docs/FRAMEWORK.md para las convenciones\n`));
|
|
219
|
+
|
|
220
|
+
return baseDir;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
async function initExisting() {
|
|
224
|
+
const cwd = process.cwd();
|
|
225
|
+
const projectName = existsSync(join(cwd, "package.json"))
|
|
226
|
+
? JSON.parse(readFileSync(join(cwd, "package.json"), "utf-8")).name || "mi-proyecto"
|
|
227
|
+
: "mi-proyecto";
|
|
228
|
+
|
|
229
|
+
console.log(chalk.cyan(`\n🔧 Configurando openPrompt-Lang en proyecto existente: ${projectName}\n`));
|
|
230
|
+
|
|
231
|
+
// Detectar stack
|
|
232
|
+
const stack = detectStack(cwd);
|
|
233
|
+
const detected = Object.entries(stack).filter(([, v]) => v).map(([k]) => k);
|
|
234
|
+
console.log(chalk.blue("🔍 Stack detectado:") + (detected.length ? detected.join(", ") : "ninguno detectado"));
|
|
235
|
+
|
|
236
|
+
const baseExtensions = [];
|
|
237
|
+
if (stack.ionic) baseExtensions.push("ionic");
|
|
238
|
+
if (stack.tauri) baseExtensions.push("tauri");
|
|
239
|
+
if (stack.supabase) baseExtensions.push("supabase");
|
|
240
|
+
|
|
241
|
+
// Crear prompt-lang.json
|
|
242
|
+
const config = {
|
|
243
|
+
name: projectName,
|
|
244
|
+
version: "0.1.0",
|
|
245
|
+
stack: {
|
|
246
|
+
base: detected.filter(t => ["react", "typescript", "vite", "tailwind"].includes(t)),
|
|
247
|
+
extensions: baseExtensions,
|
|
248
|
+
},
|
|
249
|
+
profile: "senior",
|
|
250
|
+
pipeline: {
|
|
251
|
+
"pre-commit": ["lint", "typecheck"],
|
|
252
|
+
scripts: {},
|
|
253
|
+
},
|
|
254
|
+
annotations: { enabled: true, strict: true },
|
|
255
|
+
components: {
|
|
256
|
+
source: "src/components/ui",
|
|
257
|
+
library: "",
|
|
258
|
+
},
|
|
259
|
+
docs: { commits: false, logs: false, backlog: false },
|
|
260
|
+
extractor: { ignore: [".env", "*.local", "dist", "node_modules"], output: "contexto.md" },
|
|
261
|
+
};
|
|
262
|
+
|
|
263
|
+
if (existsSync(join(cwd, "prompt-lang.json"))) {
|
|
264
|
+
console.log(chalk.yellow(" ⚠️ prompt-lang.json ya existe. Saltando."));
|
|
265
|
+
} else {
|
|
266
|
+
writeFileSync(join(cwd, "prompt-lang.json"), JSON.stringify(config, null, 2), "utf-8");
|
|
267
|
+
console.log(chalk.green(" ✅ prompt-lang.json"));
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
// Crear AGENTS.md
|
|
271
|
+
const agentsLines = [
|
|
272
|
+
"# AGENTS.md — Contexto para IA",
|
|
273
|
+
"",
|
|
274
|
+
"## Stack",
|
|
275
|
+
...(stack.react ? ["- React 18+"] : []),
|
|
276
|
+
...(stack.typescript ? ["- TypeScript"] : []),
|
|
277
|
+
...(stack.vite ? ["- Vite"] : []),
|
|
278
|
+
...(stack.tailwind ? ["- Tailwind CSS"] : []),
|
|
279
|
+
...(stack.ionic ? ["- Ionic + Capacitor (mobile)"] : []),
|
|
280
|
+
...(stack.tauri ? ["- Tauri (desktop)"] : []),
|
|
281
|
+
...(stack.supabase ? ["- Supabase (backend)"] : []),
|
|
282
|
+
"",
|
|
283
|
+
"## Convenciones",
|
|
284
|
+
"- Anotaciones PromptLang con @kind, @contract, @limit",
|
|
285
|
+
"- Framework: openPrompt-Lang",
|
|
286
|
+
"",
|
|
287
|
+
"## Perfil del desarrollador",
|
|
288
|
+
"senior",
|
|
289
|
+
"",
|
|
290
|
+
"## Reglas críticas",
|
|
291
|
+
"- NO usar any",
|
|
292
|
+
"- NO superar límites de líneas sin refactorizar",
|
|
293
|
+
"- Las anotaciones PromptLang deben declarar @use() al inicio del archivo",
|
|
294
|
+
"",
|
|
295
|
+
"## Referencias",
|
|
296
|
+
"- Framework: openPrompt-Lang",
|
|
297
|
+
"- Prompt library: docs/PROMPTS/INDEX.md (si aplica)",
|
|
298
|
+
"",
|
|
299
|
+
];
|
|
300
|
+
|
|
301
|
+
if (existsSync(join(cwd, "AGENTS.md"))) {
|
|
302
|
+
console.log(chalk.yellow(" ⚠️ AGENTS.md ya existe. Saltando."));
|
|
303
|
+
} else {
|
|
304
|
+
writeFileSync(join(cwd, "AGENTS.md"), agentsLines.join("\n"), "utf-8");
|
|
305
|
+
console.log(chalk.green(" ✅ AGENTS.md"));
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
// Preguntar: agregar scripts descriptivos al package.json
|
|
309
|
+
if (existsSync(join(cwd, "package.json"))) {
|
|
310
|
+
const { addScripts } = await import("inquirer").then(m =>
|
|
311
|
+
m.default.prompt([{
|
|
312
|
+
type: "confirm",
|
|
313
|
+
name: "addScripts",
|
|
314
|
+
message: "¿Agregar scripts descriptivos (dev:start, lint:check, validate:all...) al package.json?",
|
|
315
|
+
default: false,
|
|
316
|
+
}])
|
|
317
|
+
);
|
|
318
|
+
|
|
319
|
+
if (addScripts) {
|
|
320
|
+
const pkgPath = join(cwd, "package.json");
|
|
321
|
+
const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
|
|
322
|
+
pkg.scripts = pkg.scripts || {};
|
|
323
|
+
let added = 0;
|
|
324
|
+
for (const [name, cmd] of Object.entries(DESCRIPTIVE_SCRIPTS)) {
|
|
325
|
+
if (!pkg.scripts[name]) {
|
|
326
|
+
pkg.scripts[name] = cmd;
|
|
327
|
+
added++;
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
if (added > 0) {
|
|
331
|
+
writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + "\n", "utf-8");
|
|
332
|
+
console.log(chalk.green(` ✅ ${added} scripts agregados al package.json`));
|
|
333
|
+
} else {
|
|
334
|
+
console.log(chalk.yellow(" ⚠️ Todos los scripts descriptivos ya existen. Sin cambios."));
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
// Preguntar: crear docs/
|
|
340
|
+
const { createDocs } = await import("inquirer").then(m =>
|
|
341
|
+
m.default.prompt([{
|
|
342
|
+
type: "confirm",
|
|
343
|
+
name: "createDocs",
|
|
344
|
+
message: "¿Crear docs/BACKLOG, docs/COMMITS y docs/LOGS?",
|
|
345
|
+
default: false,
|
|
346
|
+
}])
|
|
347
|
+
);
|
|
348
|
+
|
|
349
|
+
if (createDocs) {
|
|
350
|
+
const docDirs = ["docs/BACKLOG", "docs/COMMITS", "docs/LOGS/ERRORES", "docs/LOGS/ACTIVIDAD", "docs/PROMPTS/PLANTILLAS", "docs/PROMPTS/STACK"];
|
|
351
|
+
for (const dir of docDirs) {
|
|
352
|
+
const fullPath = join(cwd, dir);
|
|
353
|
+
mkdirSync(fullPath, { recursive: true });
|
|
354
|
+
console.log(` 📁 ${dir}/`);
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
const backlogIndex = join(cwd, "docs/BACKLOG/PHASE-1-core.md");
|
|
358
|
+
if (!existsSync(backlogIndex)) {
|
|
359
|
+
writeFileSync(backlogIndex, `# PHASE-1 Core — MVP Funcional\n\n> Prioridad: P0 (Crítico) | 50% del proyecto\n\n## Tareas\n\n### TASK-001: Primera tarea\n**Módulo:** \n**Estimación:** \n**Criterios de aceptación:**\n- [ ] \n`, "utf-8");
|
|
360
|
+
console.log(chalk.green(" ✅ docs/BACKLOG/PHASE-1-core.md"));
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
const commitsIndex = join(cwd, "docs/COMMITS/INDEX.md");
|
|
364
|
+
if (!existsSync(commitsIndex)) {
|
|
365
|
+
writeFileSync(commitsIndex, "# Índice de Commits\n\n| Fecha | Versión | Descripción |\n|---|---|---|\n", "utf-8");
|
|
366
|
+
console.log(chalk.green(" ✅ docs/COMMITS/INDEX.md"));
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
console.log(chalk.cyan("\n✅ openPrompt-Lang configurado en proyecto existente."));
|
|
371
|
+
console.log(chalk.cyan(`👉 openPrompt-Lang context --output contexto.md`));
|
|
372
|
+
console.log(chalk.cyan(`👉 Empieza a anotar tu código con @kind, @contract, @limit...\n`));
|
|
373
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import chalk from "chalk";
|
|
2
|
+
import { suggestStack } from "../utils/ai.js";
|
|
3
|
+
|
|
4
|
+
export async function suggest(description) {
|
|
5
|
+
console.log(chalk.cyan(`\n🤖 Analizando: "${description}"\n`));
|
|
6
|
+
|
|
7
|
+
const result = await suggestStack(description);
|
|
8
|
+
|
|
9
|
+
if (result.source === "fallback" || !result.stack) {
|
|
10
|
+
console.log(result.plan);
|
|
11
|
+
console.log(chalk.yellow("\n📌 No se pudo conectar con IA. Usa el plan anterior con cualquier IA externa.\n"));
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
console.log(chalk.green("✅ Stack sugerido:"));
|
|
16
|
+
console.log(` ${result.stack.join(", ") || "React + TypeScript + Vite + Tailwind"}`);
|
|
17
|
+
|
|
18
|
+
if (result.modules) {
|
|
19
|
+
console.log(chalk.green("\n📦 Módulos sugeridos:"));
|
|
20
|
+
for (const mod of result.modules) {
|
|
21
|
+
console.log(` • ${mod}`);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (result.priorities) {
|
|
26
|
+
console.log(chalk.green("\n🎯 Prioridades:"));
|
|
27
|
+
for (const p of result.priorities) {
|
|
28
|
+
console.log(` ${p}`);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
import { readFileSync, existsSync, readdirSync, statSync } from "fs";
|
|
2
|
+
import { join, extname } from "path";
|
|
3
|
+
import { execSync } from "child_process";
|
|
4
|
+
import chalk from "chalk";
|
|
5
|
+
import { loadConfig, detectStack } from "../utils/config.js";
|
|
6
|
+
import { lintFile } from "../utils/annotations.js";
|
|
7
|
+
|
|
8
|
+
const CODE_EXTENSIONS = new Set([".ts", ".tsx", ".js", ".jsx"]);
|
|
9
|
+
|
|
10
|
+
export async function validate(options) {
|
|
11
|
+
const baseDir = process.cwd();
|
|
12
|
+
const config = loadConfig(baseDir);
|
|
13
|
+
let hasErrors = false;
|
|
14
|
+
|
|
15
|
+
console.log(chalk.cyan("\n🔍 openPrompt-Lang: Validación\n"));
|
|
16
|
+
|
|
17
|
+
// 1. Validar estructura
|
|
18
|
+
console.log(chalk.yellow("[1/5] Verificando estructura..."));
|
|
19
|
+
const requiredDirs = ["src", "docs"];
|
|
20
|
+
for (const dir of requiredDirs) {
|
|
21
|
+
if (!existsSync(join(baseDir, dir))) {
|
|
22
|
+
console.log(chalk.red(` ❌ Falta carpeta: ${dir}/`));
|
|
23
|
+
hasErrors = true;
|
|
24
|
+
} else {
|
|
25
|
+
console.log(chalk.green(` ✅ ${dir}/`));
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// 2. Validar config
|
|
30
|
+
console.log(chalk.yellow("\n[2/5] Verificando configuración..."));
|
|
31
|
+
if (existsSync(join(baseDir, "prompt-lang.json"))) {
|
|
32
|
+
console.log(chalk.green(" ✅ prompt-lang.json"));
|
|
33
|
+
} else {
|
|
34
|
+
console.log(chalk.yellow(" ⚠️ No hay prompt-lang.json (usando defaults)"));
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (existsSync(join(baseDir, "AGENTS.md"))) {
|
|
38
|
+
console.log(chalk.green(" ✅ AGENTS.md"));
|
|
39
|
+
} else {
|
|
40
|
+
console.log(chalk.yellow(" ⚠️ No hay AGENTS.md (recomendado)"));
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// 2b. Validar coherencia stack
|
|
44
|
+
console.log(chalk.yellow("\n[2b/5] Validando coherencia stack..."));
|
|
45
|
+
const stack = config.stack || {};
|
|
46
|
+
const base = (stack.base || []).map(s => s.toLowerCase());
|
|
47
|
+
const extensions = (stack.extensions || []).map(s => s.toLowerCase());
|
|
48
|
+
|
|
49
|
+
if (base.includes("tauri") && !extensions.includes("tauri")) {
|
|
50
|
+
console.log(chalk.red(" ❌ stack.base incluye 'tauri' pero stack.extensions no lo declara"));
|
|
51
|
+
hasErrors = true;
|
|
52
|
+
} else {
|
|
53
|
+
console.log(chalk.green(" ✅ stack.base / extensions coherente"));
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// 3. Validar anotaciones PromptLang en archivos
|
|
57
|
+
console.log(chalk.yellow("\n[3/5] Analizando anotaciones PromptLang..."));
|
|
58
|
+
let annotatedFiles = 0;
|
|
59
|
+
let annotationErrors = 0;
|
|
60
|
+
let annotationWarnings = 0;
|
|
61
|
+
let platformUsages = new Set();
|
|
62
|
+
|
|
63
|
+
const walkDir = (dirPath) => {
|
|
64
|
+
let entries;
|
|
65
|
+
try {
|
|
66
|
+
entries = readdirSync(dirPath);
|
|
67
|
+
} catch {
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
for (const entry of entries) {
|
|
71
|
+
const fullPath = join(dirPath, entry);
|
|
72
|
+
try {
|
|
73
|
+
const stat = statSync(fullPath);
|
|
74
|
+
if (stat.isDirectory()) {
|
|
75
|
+
if (!entry.startsWith(".") && entry !== "node_modules" && entry !== "dist") {
|
|
76
|
+
walkDir(fullPath);
|
|
77
|
+
}
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
} catch {
|
|
81
|
+
continue;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const ext = extname(entry).toLowerCase();
|
|
85
|
+
if (!CODE_EXTENSIONS.has(ext)) continue;
|
|
86
|
+
|
|
87
|
+
try {
|
|
88
|
+
const content = readFileSync(fullPath, "utf-8");
|
|
89
|
+
if (!content.includes("@use") && !content.includes("@kind")) continue;
|
|
90
|
+
|
|
91
|
+
annotatedFiles++;
|
|
92
|
+
const { annotations, errors, warnings } = lintFile(content, config.annotations.strict);
|
|
93
|
+
|
|
94
|
+
// Colectar @platform usages para validación de stack
|
|
95
|
+
for (const ann of annotations) {
|
|
96
|
+
if (ann.tag === "platform" && ann.args) {
|
|
97
|
+
ann.args.split(",").forEach(p => platformUsages.add(p.trim()));
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if (errors.length > 0 || warnings.length > 0) {
|
|
102
|
+
const relPath = entry;
|
|
103
|
+
for (const err of errors) {
|
|
104
|
+
annotationErrors++;
|
|
105
|
+
console.log(chalk.red(` ❌ ${relPath}: ${err}`));
|
|
106
|
+
}
|
|
107
|
+
for (const warn of warnings) {
|
|
108
|
+
annotationWarnings++;
|
|
109
|
+
console.log(chalk.yellow(` ⚠️ ${relPath}: ${warn}`));
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
} catch {
|
|
113
|
+
// skip
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
walkDir(join(baseDir, "src"));
|
|
119
|
+
|
|
120
|
+
if (annotatedFiles === 0) {
|
|
121
|
+
console.log(chalk.cyan(" 📭 No se encontraron archivos con anotaciones PromptLang"));
|
|
122
|
+
} else {
|
|
123
|
+
console.log(chalk.green(` 📄 ${annotatedFiles} archivos con anotaciones`));
|
|
124
|
+
if (annotationErrors > 0) {
|
|
125
|
+
hasErrors = true;
|
|
126
|
+
console.log(chalk.red(` ❌ ${annotationErrors} errores`));
|
|
127
|
+
}
|
|
128
|
+
if (annotationWarnings > 0) {
|
|
129
|
+
console.log(chalk.yellow(` ⚠️ ${annotationWarnings} advertencias`));
|
|
130
|
+
}
|
|
131
|
+
if (annotationErrors === 0 && annotationWarnings === 0) {
|
|
132
|
+
console.log(chalk.green(` ✅ Todas las anotaciones válidas`));
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Validación cruzada: @platform vs stack
|
|
137
|
+
if (platformUsages.size > 0) {
|
|
138
|
+
for (const plat of platformUsages) {
|
|
139
|
+
if (plat === "mobile" && !extensions.some(e => ["ionic", "capacitor"].includes(e))) {
|
|
140
|
+
console.log(chalk.yellow(` ⚠️ @platform(mobile) usado pero stack no incluye ionic/capacitor`));
|
|
141
|
+
}
|
|
142
|
+
if (plat === "desktop" && !extensions.includes("tauri")) {
|
|
143
|
+
console.log(chalk.yellow(` ⚠️ @platform(desktop) usado pero stack no incluye tauri`));
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// 4. Ejecutar pipeline configurado
|
|
149
|
+
console.log(chalk.yellow("\n[4/5] Ejecutando pipeline..."));
|
|
150
|
+
const scripts = config.pipeline?.scripts || {};
|
|
151
|
+
const pipelineSteps = config.pipeline?.["pre-commit"] || [];
|
|
152
|
+
|
|
153
|
+
if (pipelineSteps.length === 0) {
|
|
154
|
+
console.log(chalk.cyan(" 📭 No hay pipeline configurado"));
|
|
155
|
+
} else {
|
|
156
|
+
for (const step of pipelineSteps) {
|
|
157
|
+
const scriptCmd = scripts[step];
|
|
158
|
+
if (!scriptCmd) {
|
|
159
|
+
console.log(chalk.yellow(` ⚠️ Paso "${step}" sin script configurado`));
|
|
160
|
+
continue;
|
|
161
|
+
}
|
|
162
|
+
try {
|
|
163
|
+
process.stdout.write(` ▶️ ${step}... `);
|
|
164
|
+
execSync(scriptCmd, { cwd: baseDir, stdio: "pipe" });
|
|
165
|
+
console.log(chalk.green("✅"));
|
|
166
|
+
} catch (err) {
|
|
167
|
+
console.log(chalk.red("❌"));
|
|
168
|
+
hasErrors = true;
|
|
169
|
+
console.log(chalk.red(` ${err.stderr?.toString().split("\n")[0] || "Error"}`));
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// 5. Resumen
|
|
175
|
+
console.log(chalk.yellow("\n[5/5] Resumen\n"));
|
|
176
|
+
|
|
177
|
+
if (hasErrors) {
|
|
178
|
+
console.log(chalk.red("❌ Validación falló — revisa los errores arriba\n"));
|
|
179
|
+
process.exit(1);
|
|
180
|
+
} else {
|
|
181
|
+
console.log(chalk.green("✅ Validación completa — todo correcto\n"));
|
|
182
|
+
}
|
|
183
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
export function generateFigmaPrompt(answers) {
|
|
2
|
+
const { projectType, screens, mobileFirst, style, includeAntiPatterns } = answers;
|
|
3
|
+
const screenList = screens.split(",").map((s) => s.trim());
|
|
4
|
+
|
|
5
|
+
const styleMap = {
|
|
6
|
+
"Minimalista corporativo": "minimalista, profesional, colores neutros con acento azul oscuro, tipografía sans-serif limpia, espaciado generoso",
|
|
7
|
+
"Moderno vibrante": "moderno, atrevido, gradientes sutiles, colores vibrantes (púrpura/cian), micro-interacciones, glassmorphism ligero",
|
|
8
|
+
"Oscuro (dark mode)": "dark mode first, fondo gris oscuro (#0f0f0f), acentos en verde/azul neón, contrastes altos, sombras sutiles",
|
|
9
|
+
"Claro y limpio": "claro, fondos blancos/gris claro, mucho espacio negativo, bordes suaves, iconos lineales, tipografía elegante",
|
|
10
|
+
"Personalizado": style,
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
const prompt = `> [!example] Prompt generado por openPrompt-Lang
|
|
14
|
+
> \`\`\`markdown
|
|
15
|
+
> Eres un experto en diseño UI/UX y desarrollo Frontend con React y Tailwind CSS.
|
|
16
|
+
>
|
|
17
|
+
> ## Contexto
|
|
18
|
+
> - **Tipo de proyecto:** ${projectType}
|
|
19
|
+
> - **Pantallas a diseñar:** ${screenList.join(", ")}
|
|
20
|
+
> - **Enfoque:** ${mobileFirst ? "Mobile-first" : "Desktop-first"}
|
|
21
|
+
> - **Estilo visual:** ${styleMap[style] || style}
|
|
22
|
+
>
|
|
23
|
+
> ## Reglas de Diseño
|
|
24
|
+
> 1. **Modularidad React:** Cada elemento visual debe ser un componente independiente.
|
|
25
|
+
> - \`/components/ui/Button.tsx\`, \`/components/ui/Card.tsx\`, etc.
|
|
26
|
+
> - \`/components/layout/Navbar.tsx\`, \`/components/layout/Sidebar.tsx\`
|
|
27
|
+
> 2. **Tailwind 100%:** Sin CSS personalizado. Todo con clases de utilidad.
|
|
28
|
+
> 3. **Componentes atómicos:** Usar \`class-variance-authority\` para variantes.
|
|
29
|
+
> 4. **Composición:** Las páginas deben componer componentes, no ser monolíticas.
|
|
30
|
+
>
|
|
31
|
+
> ## Restricciones Técnicas
|
|
32
|
+
> - Navegación: ${mobileFirst ? "Bottom tabs en mobile, sidebar en desktop" : "Sidebar fija con header superior"}
|
|
33
|
+
> - Breakpoints: sm (640px), md (768px), lg (1024px), xl (1280px)
|
|
34
|
+
> - Zonas táctiles: mínimo \`h-10 w-10\` en mobile
|
|
35
|
+
> - Estados: loading, empty, error, success para cada vista
|
|
36
|
+
>
|
|
37
|
+
> ${includeAntiPatterns ? `## Anti-patrones (Prohibido)
|
|
38
|
+
> - ❌ Tamaños absolutos (h-[500px], w-[300px])
|
|
39
|
+
> - ❌ Posicionamiento absoluto para layout (usar flex/grid)
|
|
40
|
+
> - ❌ CSS personalizado o modules
|
|
41
|
+
> - ❌ Componentes de más de 120 líneas
|
|
42
|
+
> - ❌ Mezclar lógica de negocio en componentes de presentación` : ""}
|
|
43
|
+
>
|
|
44
|
+
> ## Lista de Pantallas
|
|
45
|
+
> ${screenList.map((s, i) => `${i + 1}. **${s}:** Describir componentes principales y flujo`).join("\n")}
|
|
46
|
+
>
|
|
47
|
+
> ## Entregables
|
|
48
|
+
> - Árbol de componentes con nombres y props principales
|
|
49
|
+
> - Layout de cada pantalla con estructura de grid/flex
|
|
50
|
+
> - Estados visuales (loading, empty, error) para cada componente crítico
|
|
51
|
+
> - Sugerencia de estructura de carpetas
|
|
52
|
+
> \`\`\`
|
|
53
|
+
`;
|
|
54
|
+
|
|
55
|
+
return prompt;
|
|
56
|
+
}
|