openprompt-lang 0.3.0 → 0.5.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/README.md +553 -32
- package/bin/cli.js +343 -74
- package/bin/create.js +135 -0
- package/bin/lint.js +21 -22
- package/docs/AI-MANUAL.md +578 -0
- package/docs/COMMANDS.md +200 -127
- package/docs/COMMITS/INDEX.md +1 -0
- package/docs/PROMPT_AI_CONTEXT.md +99 -0
- package/docs/ROADMAP-DESARROLLO.md +274 -0
- package/docs/langs/dotnet.md +36 -0
- package/docs/langs/java-spring.md +45 -0
- package/docs/langs/python-fastapi.md +35 -0
- package/docs/langs/unity.md +30 -0
- package/docs/langs/vue-nuxt.md +36 -0
- package/package.json +33 -4
- package/scaffolds/.cursorrules +6 -0
- package/scaffolds/AGENTS.md +27 -0
- package/scaffolds/Dockerfile +11 -0
- package/scaffolds/capacitor.config.ts +17 -0
- package/scaffolds/netlify.toml +8 -0
- package/scaffolds/prompt-lang.json +15 -0
- package/scaffolds/railway.json +12 -0
- package/scaffolds/tailwind.config.js +8 -0
- package/scaffolds/tauri.conf.json +26 -0
- package/schemas/language-module.json +116 -0
- package/schemas/prompt-lang.json +38 -3
- package/schemas/structures.json +145 -0
- package/src/ai/learn-prompt.js +90 -0
- package/src/ai/prompt-builder.js +182 -0
- package/src/ai/providers.js +247 -0
- package/src/annotations/registry.js +39 -0
- package/src/annotations/tags.json +24 -0
- package/src/commands/ai-gen.js +161 -0
- package/src/commands/component.js +315 -227
- package/src/commands/context.js +351 -153
- package/src/commands/db-rules.js +117 -0
- package/src/commands/extract.js +562 -0
- package/src/commands/figma.js +42 -42
- package/src/commands/init.js +763 -246
- package/src/commands/integrate.js +544 -0
- package/src/commands/lang.js +148 -0
- package/src/commands/learn.js +182 -0
- package/src/commands/qa-gen.js +139 -0
- package/src/commands/scaffold.js +127 -0
- package/src/commands/suggest.js +37 -15
- package/src/commands/teach.js +109 -0
- package/src/commands/validate.js +189 -89
- package/src/commands/wizard.js +458 -0
- package/src/generators/figma-prompt.js +25 -17
- package/src/knowledge/db-rules.json +162 -0
- package/src/language-service/plugin.cjs +94 -0
- package/src/language-service/plugin.d.ts +6 -0
- package/src/mcp-server.js +710 -0
- package/src/templates/langs/react/INDEX.json +262 -0
- package/src/templates/langs/react/MODULE.json +166 -0
- package/src/templates/langs/react/templates/hooks/useAuth.template.ts +134 -0
- package/src/templates/langs/react/templates/hooks/useDebounce.template.ts +45 -0
- package/src/templates/langs/react/templates/hooks/useForm.template.ts +146 -0
- package/src/templates/langs/react/templates/hooks/usePagination.template.ts +108 -0
- package/src/templates/langs/react/templates/services/apiService.template.ts +123 -0
- package/src/templates/langs/react/templates/ui/Button.template.tsx +87 -0
- package/src/templates/langs/react/templates/ui/Card.template.tsx +85 -0
- package/src/templates/langs/react/templates/ui/DataTable.template.tsx +163 -0
- package/src/templates/langs/react/templates/ui/Input.template.tsx +96 -0
- package/src/templates/langs/react/templates/ui/Modal.template.tsx +133 -0
- package/src/templates/langs/react/templates/ui/Select.template.tsx +99 -0
- package/src/templates/langs/vue/INDEX.json +246 -0
- package/src/templates/langs/vue/MODULE.json +105 -0
- package/src/templates/langs/vue/templates/composables/useAuth.template.ts +106 -0
- package/src/templates/langs/vue/templates/composables/useDebounce.template.ts +47 -0
- package/src/templates/langs/vue/templates/composables/useFetch.template.ts +54 -0
- package/src/templates/langs/vue/templates/composables/useForm.template.ts +127 -0
- package/src/templates/langs/vue/templates/composables/usePagination.template.ts +98 -0
- package/src/templates/langs/vue/templates/services/apiService.template.ts +116 -0
- package/src/templates/langs/vue/templates/ui/Button.template.vue +79 -0
- package/src/templates/langs/vue/templates/ui/Card.template.vue +73 -0
- package/src/templates/langs/vue/templates/ui/DataTable.template.vue +115 -0
- package/src/templates/langs/vue/templates/ui/Input.template.vue +70 -0
- package/src/templates/langs/vue/templates/ui/Modal.template.vue +112 -0
- package/src/templates/langs/vue/templates/ui/Select.template.vue +77 -0
- package/src/templates/scripts/log-actividad.sh +32 -0
- package/src/templates/scripts/log-commit.sh +35 -0
- package/src/templates/scripts/log-error.sh +45 -0
- package/src/templates/scripts/validate.sh +23 -0
- package/src/templates/semantic-index.json +84 -0
- package/src/ts-transformer/index.cjs +86 -0
- package/src/utils/ai.js +66 -77
- package/src/utils/annotations.js +297 -251
- package/src/utils/config.js +85 -34
- package/src/utils/doc-ingest.js +101 -0
- package/src/utils/error-learner.js +205 -0
- package/src/utils/file-utils.js +119 -0
- package/src/utils/language-loader.js +167 -0
- package/src/utils/module-generator.js +235 -0
- package/src/utils/semantic-index.js +93 -0
- package/src/utils/template-utils.js +44 -0
- package/src/vite-plugin/index.js +54 -0
- package/vscode-extension/package.json +23 -2
- package/vscode-extension/snippets/promptlang.json +1 -3
- package/vscode-extension/syntaxes/annotations-code.tmGrammar.json +15 -0
package/bin/cli.js
CHANGED
|
@@ -1,110 +1,379 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
import { Command } from
|
|
4
|
-
import chalk from
|
|
5
|
-
import { readFileSync, existsSync } from
|
|
6
|
-
import { join, dirname } from
|
|
7
|
-
import { fileURLToPath } from
|
|
8
|
-
import { createRequire } from "module";
|
|
3
|
+
import { Command } from 'commander'
|
|
4
|
+
import chalk from 'chalk'
|
|
5
|
+
import { readFileSync, existsSync } from 'fs'
|
|
6
|
+
import { join, dirname } from 'path'
|
|
7
|
+
import { fileURLToPath } from 'url'
|
|
9
8
|
|
|
10
|
-
const __dirname = dirname(fileURLToPath(import.meta.url))
|
|
11
|
-
const
|
|
12
|
-
const pkg = JSON.parse(readFileSync(join(__dirname, "..", "package.json"), "utf-8"));
|
|
9
|
+
const __dirname = dirname(fileURLToPath(import.meta.url))
|
|
10
|
+
const pkg = JSON.parse(readFileSync(join(__dirname, '..', 'package.json'), 'utf-8'))
|
|
13
11
|
|
|
14
|
-
const program = new Command()
|
|
12
|
+
const program = new Command()
|
|
15
13
|
|
|
16
14
|
program
|
|
17
|
-
.name(
|
|
18
|
-
.description(chalk.cyan(
|
|
19
|
-
.version(pkg.version)
|
|
15
|
+
.name('openPrompt-Lang')
|
|
16
|
+
.description(chalk.cyan('Framework de anotaciones para desarrollo asistido por IA'))
|
|
17
|
+
.version(pkg.version)
|
|
20
18
|
|
|
21
19
|
program
|
|
22
|
-
.command(
|
|
23
|
-
.description(
|
|
24
|
-
.option(
|
|
25
|
-
.option(
|
|
26
|
-
.option(
|
|
20
|
+
.command('init')
|
|
21
|
+
.description('Inicializar proyecto con estructura PromptLang')
|
|
22
|
+
.option('--name <name>', 'Nombre del proyecto')
|
|
23
|
+
.option('--describe <text>', 'Descripción del proyecto para sugerencia IA')
|
|
24
|
+
.option('--existing', 'Configurar en proyecto existente (solo archivos de configuración)')
|
|
25
|
+
.option('--dry-run', 'Previsualizar sin crear archivos')
|
|
26
|
+
.option('--stack <items>', 'Stack base (separado por comas, ej: react,typescript,vite,tailwind)')
|
|
27
|
+
.option('--preset <name>', 'Usar preset pre-ensamblado (ej: react-supabase-stripe, vue-firebase-netlify)')
|
|
28
|
+
.option('--list-presets', 'Listar presets disponibles y salir')
|
|
29
|
+
.option('--list-stacks', 'Listar stacks disponibles y salir')
|
|
30
|
+
.option('--learn-from <file>', 'Ruta a perfil de aprendizaje (aprendizaje-perfil.json) para heredar patrones')
|
|
31
|
+
.option('--no-interactive', 'Saltar preguntas interactivas y usar defaults')
|
|
27
32
|
.action(async (options) => {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
33
|
+
if (options.listStacks) {
|
|
34
|
+
const { listStacks } = await import('../src/commands/init.js')
|
|
35
|
+
await listStacks()
|
|
36
|
+
return
|
|
37
|
+
}
|
|
38
|
+
if (options.listPresets) {
|
|
39
|
+
const { listPresets } = await import('../src/commands/init.js')
|
|
40
|
+
await listPresets()
|
|
41
|
+
return
|
|
42
|
+
}
|
|
43
|
+
const { init } = await import('../src/commands/init.js')
|
|
44
|
+
await init(options)
|
|
45
|
+
})
|
|
31
46
|
|
|
32
47
|
program
|
|
33
|
-
.command(
|
|
34
|
-
.description(
|
|
48
|
+
.command('figma')
|
|
49
|
+
.description('Generar prompt para diseño en Figma con convenciones React + Tailwind')
|
|
35
50
|
.action(async () => {
|
|
36
|
-
const { figma } = await import(
|
|
37
|
-
await figma()
|
|
38
|
-
})
|
|
51
|
+
const { figma } = await import('../src/commands/figma.js')
|
|
52
|
+
await figma()
|
|
53
|
+
})
|
|
39
54
|
|
|
40
55
|
program
|
|
41
|
-
.command(
|
|
42
|
-
.description(
|
|
43
|
-
.option(
|
|
44
|
-
.option(
|
|
45
|
-
.option(
|
|
56
|
+
.command('context')
|
|
57
|
+
.description('Extraer contexto del proyecto para la IA')
|
|
58
|
+
.option('--output <file>', 'Archivo de salida', 'contexto.md')
|
|
59
|
+
.option('--scope <module>', 'Filtrar por módulo (@scope)')
|
|
60
|
+
.option('--no-ai', 'No incluir anotaciones PromptLang en el output')
|
|
61
|
+
.option('--mode <mode>', 'Modo de extracción: raw (solo archivos), index (solo índice), full (ambos)', 'full')
|
|
62
|
+
.option('--query <text>', 'Consulta semántica para sugerencias de componentes/patrones')
|
|
63
|
+
.option('--domain <domain>', 'Filtrar por dominio de negocio (ecommerce, saas, mobile, api, admin, blog)')
|
|
64
|
+
.option('--projects <list>', 'Proyectos vecinos a referenciar (separados por coma)')
|
|
65
|
+
.option('--dir <path>', 'Directorio base del proyecto (default: cwd)')
|
|
46
66
|
.action(async (options) => {
|
|
47
|
-
const { context } = await import(
|
|
48
|
-
await context(options)
|
|
49
|
-
})
|
|
67
|
+
const { context } = await import('../src/commands/context.js')
|
|
68
|
+
await context(options)
|
|
69
|
+
})
|
|
50
70
|
|
|
51
71
|
program
|
|
52
|
-
.command(
|
|
53
|
-
.description(
|
|
54
|
-
.option(
|
|
72
|
+
.command('validate')
|
|
73
|
+
.description('Ejecutar pipeline de validación y verificar anotaciones')
|
|
74
|
+
.option('--fix', 'Auto-corregir errores cuando sea posible')
|
|
55
75
|
.action(async (options) => {
|
|
56
|
-
const { validate } = await import(
|
|
57
|
-
await validate(options)
|
|
58
|
-
})
|
|
76
|
+
const { validate } = await import('../src/commands/validate.js')
|
|
77
|
+
await validate(options)
|
|
78
|
+
})
|
|
59
79
|
|
|
60
80
|
program
|
|
61
|
-
.command(
|
|
62
|
-
.description(
|
|
63
|
-
.argument(
|
|
81
|
+
.command('suggest')
|
|
82
|
+
.description('IA sugiere estructura/stack según descripción del proyecto')
|
|
83
|
+
.argument('<description>', 'Descripción del proyecto')
|
|
64
84
|
.action(async (description) => {
|
|
65
|
-
const { suggest } = await import(
|
|
66
|
-
await suggest(description)
|
|
67
|
-
})
|
|
85
|
+
const { suggest } = await import('../src/commands/suggest.js')
|
|
86
|
+
await suggest(description)
|
|
87
|
+
})
|
|
68
88
|
|
|
69
89
|
const component = program
|
|
70
|
-
.command(
|
|
71
|
-
.description(
|
|
90
|
+
.command('component')
|
|
91
|
+
.description('Gestionar biblioteca de componentes reutilizables')
|
|
72
92
|
|
|
73
93
|
component
|
|
74
|
-
.command(
|
|
75
|
-
.description(
|
|
76
|
-
.option(
|
|
94
|
+
.command('list')
|
|
95
|
+
.description('Listar componentes disponibles')
|
|
96
|
+
.option('--library <path>', 'Ruta a la biblioteca de componentes')
|
|
97
|
+
.option('--style <style>', 'Filtrar por estilo UI (shadcn, mantine, chakra, headless, mui)')
|
|
98
|
+
.option('--tech <tech>', 'Filtrar por tecnología (react, vue, angular)')
|
|
99
|
+
.option('--validated', 'Mostrar solo componentes validados/aprendidos')
|
|
77
100
|
.action(async (options) => {
|
|
78
|
-
const { componentList } = await import(
|
|
79
|
-
await componentList(options)
|
|
80
|
-
})
|
|
101
|
+
const { componentList } = await import('../src/commands/component.js')
|
|
102
|
+
await componentList(options)
|
|
103
|
+
})
|
|
81
104
|
|
|
82
105
|
component
|
|
83
|
-
.command(
|
|
84
|
-
.description(
|
|
85
|
-
.option(
|
|
106
|
+
.command('add <name>')
|
|
107
|
+
.description('Agregar componente desde biblioteca o template del módulo de lenguaje')
|
|
108
|
+
.option('--library <path>', 'Ruta a la biblioteca de componentes')
|
|
109
|
+
.option('--template', 'Usar template del módulo de lenguaje (INDEX.json)')
|
|
110
|
+
.option('--lang <lang>', 'Lenguaje del template (default: de prompt-lang.json)')
|
|
111
|
+
.option('--variants <list>', 'Variantes a incluir (separadas por coma)')
|
|
112
|
+
.option('--output <name>', 'Nombre del archivo de salida (sin extensión)')
|
|
113
|
+
.option('--force', 'Sobrescribir archivo existente')
|
|
114
|
+
.option('--save', 'Marcar componente como validado/aprendido')
|
|
86
115
|
.action(async (name, options) => {
|
|
87
|
-
const { componentAdd } = await import(
|
|
88
|
-
await componentAdd(name, options)
|
|
89
|
-
})
|
|
116
|
+
const { componentAdd } = await import('../src/commands/component.js')
|
|
117
|
+
await componentAdd(name, options)
|
|
118
|
+
})
|
|
90
119
|
|
|
91
120
|
component
|
|
92
|
-
.command(
|
|
93
|
-
.description(
|
|
94
|
-
.option(
|
|
95
|
-
.option(
|
|
121
|
+
.command('init')
|
|
122
|
+
.description('Inicializar una biblioteca de componentes')
|
|
123
|
+
.option('--library <path>', 'Ruta donde crear la biblioteca')
|
|
124
|
+
.option('--name <name>', 'Nombre de la biblioteca')
|
|
96
125
|
.action(async (options) => {
|
|
97
|
-
const { componentInit } = await import(
|
|
98
|
-
await componentInit(options)
|
|
99
|
-
})
|
|
126
|
+
const { componentInit } = await import('../src/commands/component.js')
|
|
127
|
+
await componentInit(options)
|
|
128
|
+
})
|
|
100
129
|
|
|
101
130
|
component
|
|
102
|
-
.command(
|
|
103
|
-
.description(
|
|
104
|
-
.option(
|
|
131
|
+
.command('manifest')
|
|
132
|
+
.description('Mostrar el manifiesto de la biblioteca de componentes')
|
|
133
|
+
.option('--library <path>', 'Ruta a la biblioteca de componentes')
|
|
105
134
|
.action(async (options) => {
|
|
106
|
-
const { componentManifest } = await import(
|
|
107
|
-
await componentManifest(options)
|
|
108
|
-
})
|
|
135
|
+
const { componentManifest } = await import('../src/commands/component.js')
|
|
136
|
+
await componentManifest(options)
|
|
137
|
+
})
|
|
109
138
|
|
|
110
|
-
|
|
139
|
+
// ─── Wizard command ──────────────────────────────────────────────────────────
|
|
140
|
+
|
|
141
|
+
program
|
|
142
|
+
.command('wizard')
|
|
143
|
+
.description('Asistente interactivo para configurar proyecto openPrompt-Lang')
|
|
144
|
+
.action(async () => {
|
|
145
|
+
const { wizard } = await import('../src/commands/wizard.js')
|
|
146
|
+
await wizard()
|
|
147
|
+
})
|
|
148
|
+
|
|
149
|
+
// ─── Teach command ───────────────────────────────────────────────────────────
|
|
150
|
+
|
|
151
|
+
program
|
|
152
|
+
.command('teach <templateId>')
|
|
153
|
+
.description('Enseñanza estructurada desde template con @teachMe')
|
|
154
|
+
.option('--lang <lang>', 'Lenguaje del template (default: react)')
|
|
155
|
+
.option('--code', 'Mostrar código completo del template')
|
|
156
|
+
.option('--force', 'Mostrar template aunque no tenga @teachMe')
|
|
157
|
+
.action(async (templateId, options) => {
|
|
158
|
+
const { teach } = await import('../src/commands/teach.js')
|
|
159
|
+
await teach(templateId, options)
|
|
160
|
+
})
|
|
161
|
+
|
|
162
|
+
// ─── Learn command (Phase D.1) ────────────────────────────────────────────────
|
|
163
|
+
|
|
164
|
+
program
|
|
165
|
+
.command('learn')
|
|
166
|
+
.description('Aprender desde documentación: genera módulos de lenguaje usando IA')
|
|
167
|
+
.option('--docs <paths...>', 'Rutas a archivos/directorios de documentación')
|
|
168
|
+
.option('--from <paths...>', 'Proyectos reales para extraer patrones (opcional)')
|
|
169
|
+
.option('--name <name>', 'Nombre del lenguaje (si no se puede detectar automáticamente)')
|
|
170
|
+
.option('--output <dir>', 'Directorio de salida (default: ~/.openprompt/knowledge/langs/<name>)')
|
|
171
|
+
.option('--update <dir>', 'Actualizar módulo existente en lugar de crear nuevo')
|
|
172
|
+
.option('--integrate', 'Copiar el módulo generado a src/templates/langs/ (solo maintainers)')
|
|
173
|
+
.option('--dry-run', 'Analizar y mostrar preview sin escribir archivos')
|
|
174
|
+
.option('--provider <provider>', 'AI provider: openai | anthropic | ollama | mock')
|
|
175
|
+
.action(async (options) => {
|
|
176
|
+
const { learn } = await import('../src/commands/learn.js')
|
|
177
|
+
await learn(options)
|
|
178
|
+
})
|
|
179
|
+
|
|
180
|
+
// ─── QA commands ─────────────────────────────────────────────────────────────
|
|
181
|
+
|
|
182
|
+
program
|
|
183
|
+
.command('qa-gen')
|
|
184
|
+
.description('Generate tests from learned errors (@learn-error)')
|
|
185
|
+
.option('--lang <lang>', 'Language module (default: from INDEX.json)')
|
|
186
|
+
.option('--source <dir>', 'Source directory to scan for @learn-error')
|
|
187
|
+
.option('--output <dir>', 'Output directory for generated tests (default: src/__tests__)')
|
|
188
|
+
.option('--template <id>', 'Generate tests only for a specific template')
|
|
189
|
+
.option('--dry-run', 'Preview without writing files')
|
|
190
|
+
.option('--no-scan', 'Skip scanning source for new @learn-error annotations')
|
|
191
|
+
.action(async (options) => {
|
|
192
|
+
const { qaGen } = await import('../src/commands/qa-gen.js')
|
|
193
|
+
await qaGen(options)
|
|
194
|
+
})
|
|
195
|
+
|
|
196
|
+
program
|
|
197
|
+
.command('qa-learn')
|
|
198
|
+
.description('Parse @learn-error from a file and persist to language module')
|
|
199
|
+
.option('--file <path>', 'Source file to scan for @learn-error annotations')
|
|
200
|
+
.option('--lang <lang>', 'Language module (default: react)')
|
|
201
|
+
.action(async (options) => {
|
|
202
|
+
const { qaLearn } = await import('../src/commands/qa-gen.js')
|
|
203
|
+
await qaLearn(options)
|
|
204
|
+
})
|
|
205
|
+
|
|
206
|
+
// ─── Lang command ────────────────────────────────────────────────────────────
|
|
207
|
+
|
|
208
|
+
const lang = program.command('lang').description('Gestionar módulos de lenguaje plugables')
|
|
209
|
+
|
|
210
|
+
lang
|
|
211
|
+
.command('list')
|
|
212
|
+
.description('Listar lenguajes disponibles')
|
|
213
|
+
.action(async () => {
|
|
214
|
+
const { langList } = await import('../src/commands/lang.js')
|
|
215
|
+
await langList()
|
|
216
|
+
})
|
|
217
|
+
|
|
218
|
+
lang
|
|
219
|
+
.command('index <langId>')
|
|
220
|
+
.description('Mostrar índice de templates de un lenguaje')
|
|
221
|
+
.option('--category <category>', 'Filtrar por categoría')
|
|
222
|
+
.action(async (langId, options) => {
|
|
223
|
+
const { langIndex } = await import('../src/commands/lang.js')
|
|
224
|
+
await langIndex(langId, options)
|
|
225
|
+
})
|
|
226
|
+
|
|
227
|
+
lang
|
|
228
|
+
.command('search <query>')
|
|
229
|
+
.description('Buscar templates en todos los lenguajes')
|
|
230
|
+
.option('--lang <lang>', 'Filtrar por lenguaje')
|
|
231
|
+
.action(async (query, options) => {
|
|
232
|
+
const { langSearch } = await import('../src/commands/lang.js')
|
|
233
|
+
await langSearch(query, options)
|
|
234
|
+
})
|
|
235
|
+
|
|
236
|
+
lang
|
|
237
|
+
.command('errors [langId]')
|
|
238
|
+
.description('Mostrar errores aprendidos')
|
|
239
|
+
.option('--template <id>', 'Filtrar por template ID')
|
|
240
|
+
.action(async (langId, options) => {
|
|
241
|
+
const { langErrors } = await import('../src/commands/lang.js')
|
|
242
|
+
await langErrors(langId || 'react', options)
|
|
243
|
+
})
|
|
244
|
+
|
|
245
|
+
// ─── DB Rules command ────────────────────────────────────────────────────────
|
|
246
|
+
|
|
247
|
+
program
|
|
248
|
+
.command('db-rules')
|
|
249
|
+
.description('Consultar reglas de base de datos, anti-patrones y buenas prácticas')
|
|
250
|
+
.option('--list', 'Listar todas las reglas disponibles')
|
|
251
|
+
.option('--id <id>', 'Ver detalle de una regla específica (ej: db-001)')
|
|
252
|
+
.option('--tag <tag>', 'Filtrar por tag (ej: performance, security, supabase)')
|
|
253
|
+
.option('--category <cat>', 'Filtrar por categoría (ej: rendimiento, arquitectura)')
|
|
254
|
+
.action(async (options) => {
|
|
255
|
+
const { dbRules } = await import('../src/commands/db-rules.js')
|
|
256
|
+
await dbRules(options)
|
|
257
|
+
})
|
|
258
|
+
|
|
259
|
+
// ─── Extract command ─────────────────────────────────────────────────────────
|
|
260
|
+
|
|
261
|
+
program
|
|
262
|
+
.command('extract [sourceDir]')
|
|
263
|
+
.description('Analyze project and extract reusable components as templates')
|
|
264
|
+
.option('--lang <lang>', 'Language module to extract into (default: from prompt-lang.json)')
|
|
265
|
+
.option('--min-reuse <n>', 'Minimum imports to qualify (default: 2)')
|
|
266
|
+
.option('--dry-run', "Analyze only, don't write files")
|
|
267
|
+
.option('--learn', 'Deep project analysis: extract patterns, conventions and ways of working')
|
|
268
|
+
.option('--output <file>', 'Output file for --learn profile (default: aprendizaje-perfil.json)')
|
|
269
|
+
.action(async (sourceDir, options) => {
|
|
270
|
+
if (options.learn) {
|
|
271
|
+
const { extractLearn } = await import('../src/commands/extract.js')
|
|
272
|
+
await extractLearn(sourceDir || 'src', options)
|
|
273
|
+
return
|
|
274
|
+
}
|
|
275
|
+
const { extractTemplates } = await import('../src/commands/extract.js')
|
|
276
|
+
await extractTemplates(sourceDir || 'src', options)
|
|
277
|
+
})
|
|
278
|
+
|
|
279
|
+
program
|
|
280
|
+
.command('analyze [sourceDir]')
|
|
281
|
+
.description('Analyze project structure, deps, and code health')
|
|
282
|
+
.action(async (sourceDir) => {
|
|
283
|
+
const { extractAnalyze } = await import('../src/commands/extract.js')
|
|
284
|
+
await extractAnalyze(sourceDir || 'src', {})
|
|
285
|
+
})
|
|
286
|
+
|
|
287
|
+
// ─── Integrate command ────────────────────────────────────────────────────────
|
|
288
|
+
|
|
289
|
+
program
|
|
290
|
+
.command('integrate')
|
|
291
|
+
.description('Configure Opencode, Cursor, and VS Code for the project')
|
|
292
|
+
.option('--all', 'Setup all integrations without prompting')
|
|
293
|
+
.option('--lang <lang>', 'Language (auto-detects from prompt-lang.json)')
|
|
294
|
+
.option('--no-ask', 'Skip interactive prompts')
|
|
295
|
+
.option('--from <dir>', 'Transferir contexto desde otro proyecto (memoria estructurada)')
|
|
296
|
+
.option('--to <dir>', 'Proyecto destino para la transferencia de contexto')
|
|
297
|
+
.option('--migrate', 'Modo migración: copiar contexto de negocio entre proyectos')
|
|
298
|
+
.action(async (options) => {
|
|
299
|
+
if (options.migrate || options.from) {
|
|
300
|
+
const { migrateContext } = await import('../src/commands/integrate.js')
|
|
301
|
+
await migrateContext(options)
|
|
302
|
+
return
|
|
303
|
+
}
|
|
304
|
+
const { integrate } = await import('../src/commands/integrate.js')
|
|
305
|
+
await integrate(options)
|
|
306
|
+
})
|
|
307
|
+
|
|
308
|
+
// ─── AI commands ─────────────────────────────────────────────────────────────
|
|
309
|
+
|
|
310
|
+
program
|
|
311
|
+
.command('ai-gen')
|
|
312
|
+
.description('Generate components from natural language descriptions')
|
|
313
|
+
.option('--description <text>', 'Description of what to generate')
|
|
314
|
+
.option('--name <name>', 'Component name (auto-suggested if omitted)')
|
|
315
|
+
.option('--lang <lang>', 'Language module (default: react)')
|
|
316
|
+
.option('--profile <profile>', 'senior | mid | junior (default: mid)')
|
|
317
|
+
.option(
|
|
318
|
+
'--provider <provider>',
|
|
319
|
+
'AI provider: openai | anthropic | ollama | mock (default: auto-detect)'
|
|
320
|
+
)
|
|
321
|
+
.option('--output <dir>', 'Output directory (default: src/components)')
|
|
322
|
+
.option('--force', 'Overwrite existing files without asking')
|
|
323
|
+
.option('--validate', 'Run annotation validation after generation')
|
|
324
|
+
.option('--verbose', 'Show prompt and extended output')
|
|
325
|
+
.option('--temperature <num>', 'Temperature for generation (default: 0.3)')
|
|
326
|
+
.option('--max-tokens <num>', 'Max tokens for generation (default: 4096)')
|
|
327
|
+
.action(async (options) => {
|
|
328
|
+
const { aiGen } = await import('../src/commands/ai-gen.js')
|
|
329
|
+
await aiGen(options)
|
|
330
|
+
})
|
|
331
|
+
|
|
332
|
+
program
|
|
333
|
+
.command('ai-providers')
|
|
334
|
+
.description('List available AI providers and their status')
|
|
335
|
+
.action(async () => {
|
|
336
|
+
const { aiProviders } = await import('../src/commands/ai-gen.js')
|
|
337
|
+
await aiProviders()
|
|
338
|
+
})
|
|
339
|
+
|
|
340
|
+
// ─── Scaffold command ────────────────────────────────────────────────────────
|
|
341
|
+
|
|
342
|
+
const scaffold = program
|
|
343
|
+
.command('scaffold')
|
|
344
|
+
.description('Gestionar scafffolding de proyectos por framework')
|
|
345
|
+
|
|
346
|
+
scaffold
|
|
347
|
+
.command('folders <framework>')
|
|
348
|
+
.description('Crear estructura de carpetas según framework')
|
|
349
|
+
.option('--name <name>', 'Nombre del proyecto (directorio)')
|
|
350
|
+
.option('--force', 'Sobrescribir archivos existentes')
|
|
351
|
+
.action(async (framework, options) => {
|
|
352
|
+
const { scaffoldFolders } = await import('../src/commands/scaffold.js')
|
|
353
|
+
await scaffoldFolders(framework, options)
|
|
354
|
+
})
|
|
355
|
+
|
|
356
|
+
scaffold
|
|
357
|
+
.command('list')
|
|
358
|
+
.description('Listar frameworks disponibles en el catálogo')
|
|
359
|
+
.action(async () => {
|
|
360
|
+
const { scaffoldList } = await import('../src/commands/scaffold.js')
|
|
361
|
+
await scaffoldList()
|
|
362
|
+
})
|
|
363
|
+
|
|
364
|
+
// ─── MCP server command ───────────────────────────────────────────────────────
|
|
365
|
+
|
|
366
|
+
program
|
|
367
|
+
.command('mcp')
|
|
368
|
+
.description('Start MCP server for opencode integration via stdio')
|
|
369
|
+
.action(async () => {
|
|
370
|
+
const { startServer } = await import('../src/mcp-server.js')
|
|
371
|
+
await startServer()
|
|
372
|
+
})
|
|
373
|
+
|
|
374
|
+
try {
|
|
375
|
+
await program.parseAsync(process.argv)
|
|
376
|
+
} catch (err) {
|
|
377
|
+
console.error(chalk.red(`✘ Error: ${err.message}`))
|
|
378
|
+
process.exitCode = 1
|
|
379
|
+
}
|
package/bin/create.js
ADDED
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { execSync } from 'child_process'
|
|
4
|
+
import { readFileSync, writeFileSync, existsSync, mkdirSync, copyFileSync, rmSync } from 'fs'
|
|
5
|
+
import { join, dirname } from 'path'
|
|
6
|
+
import { fileURLToPath } from 'url'
|
|
7
|
+
import chalk from 'chalk'
|
|
8
|
+
|
|
9
|
+
const __dirname = dirname(fileURLToPath(import.meta.url))
|
|
10
|
+
|
|
11
|
+
async function main () {
|
|
12
|
+
const args = process.argv.slice(2)
|
|
13
|
+
const projectName = args[0] || 'mi-app'
|
|
14
|
+
const depsIndex = args.indexOf('--deps')
|
|
15
|
+
const deps = depsIndex !== -1 ? args[depsIndex + 1]?.split(',') || [] : []
|
|
16
|
+
const deployIndex = args.indexOf('--deploy')
|
|
17
|
+
const deploy = deployIndex !== -1 ? args[deployIndex + 1] : null
|
|
18
|
+
const hasIonic = args.includes('--ionic')
|
|
19
|
+
const hasTauri = args.includes('--tauri')
|
|
20
|
+
const force = args.includes('--force')
|
|
21
|
+
|
|
22
|
+
const projectDir = join(process.cwd(), projectName)
|
|
23
|
+
|
|
24
|
+
console.log(chalk.cyan(`\n🚀 Creando proyecto: ${projectName}\n`))
|
|
25
|
+
|
|
26
|
+
// 1. Crear con Vite
|
|
27
|
+
try {
|
|
28
|
+
console.log(chalk.blue(' [1/5] Creando base Vite + React + TS...'))
|
|
29
|
+
execSync(
|
|
30
|
+
`npx create-vite@latest ${projectName} --template react-ts ${force ? '--force' : ''}`,
|
|
31
|
+
{
|
|
32
|
+
stdio: 'pipe',
|
|
33
|
+
cwd: process.cwd()
|
|
34
|
+
}
|
|
35
|
+
)
|
|
36
|
+
console.log(chalk.green(' ✅ Base Vite creada'))
|
|
37
|
+
} catch {
|
|
38
|
+
console.log(chalk.yellow(' ⚠️ Vite base ya existe o falló. Continuando...'))
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// 2. Limpiar boilerplate predecible
|
|
42
|
+
console.log(chalk.blue(' [2/5] Limpiando boilerplate innecesario...'))
|
|
43
|
+
const toDelete = ['src/App.css', 'src/assets', 'public/vite.svg']
|
|
44
|
+
for (const file of toDelete) {
|
|
45
|
+
const fullPath = join(projectDir, file)
|
|
46
|
+
if (existsSync(fullPath)) {
|
|
47
|
+
rmSync(fullPath, { recursive: true, force: true })
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
// Reescribir App.tsx minimal
|
|
51
|
+
const appContent = `function App() {
|
|
52
|
+
return <div className="min-h-screen bg-gray-50" />
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export default App
|
|
56
|
+
`
|
|
57
|
+
writeFileSync(join(projectDir, 'src/App.tsx'), appContent, 'utf-8')
|
|
58
|
+
|
|
59
|
+
// 3. Instalar dependencias adicionales
|
|
60
|
+
if (deps.length > 0) {
|
|
61
|
+
console.log(chalk.blue(` [3/5] Instalando dependencias: ${deps.join(', ')}...`))
|
|
62
|
+
try {
|
|
63
|
+
execSync(`npm install ${deps.join(' ')}`, { stdio: 'pipe', cwd: projectDir })
|
|
64
|
+
console.log(chalk.green(' ✅ Dependencias instaladas'))
|
|
65
|
+
} catch {
|
|
66
|
+
console.log(
|
|
67
|
+
chalk.yellow(' ⚠️ Error instalando dependencias. Continúa con npm install manual.')
|
|
68
|
+
)
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// 4. Copiar scaffolds
|
|
73
|
+
console.log(chalk.blue(' [4/5] Copiando archivos de configuración...'))
|
|
74
|
+
const scaffoldsDir = join(__dirname, '..', 'scaffolds')
|
|
75
|
+
|
|
76
|
+
const scaffoldFiles = [
|
|
77
|
+
['tailwind.config.js', 'tailwind.config.js'],
|
|
78
|
+
['prompt-lang.json', 'prompt-lang.json']
|
|
79
|
+
]
|
|
80
|
+
|
|
81
|
+
if (deploy === 'railway') scaffoldFiles.push(['railway.json', 'railway.json'])
|
|
82
|
+
if (deploy === 'netlify') scaffoldFiles.push(['netlify.toml', 'netlify.toml'])
|
|
83
|
+
if (hasIonic) scaffoldFiles.push(['capacitor.config.ts', 'capacitor.config.ts'])
|
|
84
|
+
if (hasTauri) scaffoldFiles.push(['tauri.conf.json', 'tauri.conf.json'])
|
|
85
|
+
|
|
86
|
+
for (const [src, dest] of scaffoldFiles) {
|
|
87
|
+
const srcPath = join(scaffoldsDir, src)
|
|
88
|
+
const destPath = join(projectDir, dest)
|
|
89
|
+
if (existsSync(srcPath) && (!existsSync(destPath) || force)) {
|
|
90
|
+
copyFileSync(srcPath, destPath)
|
|
91
|
+
console.log(` ✅ ${dest}`)
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// 5. Configurar package.json con scripts descriptivos
|
|
96
|
+
console.log(chalk.blue(' [5/5] Agregando scripts descriptivos...'))
|
|
97
|
+
const pkgPath = join(projectDir, 'package.json')
|
|
98
|
+
if (existsSync(pkgPath)) {
|
|
99
|
+
const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'))
|
|
100
|
+
const descriptiveScripts = {
|
|
101
|
+
'dev:start': 'vite',
|
|
102
|
+
'dev:preview': 'vite preview',
|
|
103
|
+
'build:prod': 'vite build',
|
|
104
|
+
'build:analyze': 'vite build --analyze',
|
|
105
|
+
'lint:check': 'eslint src --ext .ts,.tsx',
|
|
106
|
+
'lint:fix': 'eslint src --ext .ts,.tsx --fix',
|
|
107
|
+
'type:check': 'tsc --noEmit',
|
|
108
|
+
'test:unit': 'vitest run',
|
|
109
|
+
'test:watch': 'vitest',
|
|
110
|
+
'test:coverage': 'vitest run --coverage',
|
|
111
|
+
'validate:all': 'npm run lint:check && npm run type:check && npm run test:unit'
|
|
112
|
+
}
|
|
113
|
+
pkg.scripts = { ...descriptiveScripts, ...pkg.scripts }
|
|
114
|
+
writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + '\n', 'utf-8')
|
|
115
|
+
console.log(chalk.green(' ✅ Scripts descriptivos agregados'))
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Ionic plug
|
|
119
|
+
if (hasIonic) {
|
|
120
|
+
console.log(chalk.blue(' 🔌 Integrando Ionic...'))
|
|
121
|
+
try {
|
|
122
|
+
execSync('npx cap init App com.app.app --web-dir=dist', { stdio: 'pipe', cwd: projectDir })
|
|
123
|
+
execSync('npx cap add android', { stdio: 'pipe', cwd: projectDir })
|
|
124
|
+
console.log(chalk.green(' ✅ Ionic/Capacitor configurado'))
|
|
125
|
+
} catch {
|
|
126
|
+
console.log(chalk.yellow(' ⚠️ Error en setup Ionic. Configura manualmente.'))
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
console.log(chalk.cyan(`\n✅ ${projectName} listo!`))
|
|
131
|
+
console.log(chalk.cyan(` cd ${projectName}`))
|
|
132
|
+
console.log(chalk.cyan(' npm run dev:start\n'))
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
main().catch(console.error)
|
package/bin/lint.js
CHANGED
|
@@ -1,50 +1,49 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
import { readFileSync, existsSync } from
|
|
4
|
-
import { join } from
|
|
5
|
-
import {
|
|
6
|
-
import
|
|
7
|
-
import chalk from "chalk";
|
|
3
|
+
import { readFileSync, existsSync } from 'fs'
|
|
4
|
+
import { join } from 'path'
|
|
5
|
+
import { lintFile } from '../src/utils/annotations.js'
|
|
6
|
+
import chalk from 'chalk'
|
|
8
7
|
|
|
9
|
-
const targetFile = join(process.cwd(), process.argv[2] ||
|
|
8
|
+
const targetFile = join(process.cwd(), process.argv[2] || '')
|
|
10
9
|
|
|
11
10
|
if (!targetFile || !existsSync(targetFile)) {
|
|
12
|
-
console.log(chalk.red(
|
|
13
|
-
console.log(
|
|
14
|
-
process.exit(1)
|
|
11
|
+
console.log(chalk.red('❌ Archivo no encontrado'))
|
|
12
|
+
console.log('Uso: node bin/lint.js <archivo>')
|
|
13
|
+
process.exit(1)
|
|
15
14
|
}
|
|
16
15
|
|
|
17
|
-
const content = readFileSync(targetFile,
|
|
18
|
-
const { annotations, errors, warnings } = lintFile(content)
|
|
16
|
+
const content = readFileSync(targetFile, 'utf-8')
|
|
17
|
+
const { annotations, errors, warnings } = lintFile(content)
|
|
19
18
|
|
|
20
19
|
if (annotations.length === 0) {
|
|
21
|
-
console.log(chalk.cyan(
|
|
22
|
-
process.exit(0)
|
|
20
|
+
console.log(chalk.cyan('📭 Sin anotaciones PromptLang en este archivo'))
|
|
21
|
+
process.exit(0)
|
|
23
22
|
}
|
|
24
23
|
|
|
25
|
-
console.log(chalk.cyan(`\n🔍 Anotaciones encontradas: ${annotations.length}\n`))
|
|
24
|
+
console.log(chalk.cyan(`\n🔍 Anotaciones encontradas: ${annotations.length}\n`))
|
|
26
25
|
|
|
27
26
|
for (const ann of annotations) {
|
|
28
27
|
const argsStr = ann.args.length
|
|
29
|
-
? ann.args.map((a) => (a.key ? `${a.key}: ${a.value}` : a.value)).join(
|
|
30
|
-
:
|
|
31
|
-
console.log(` @${ann.name}${argsStr ? `(${argsStr})` :
|
|
28
|
+
? ann.args.map((a) => (a.key ? `${a.key}: ${a.value}` : a.value)).join(', ')
|
|
29
|
+
: ''
|
|
30
|
+
console.log(` @${ann.name}${argsStr ? `(${argsStr})` : ''}`)
|
|
32
31
|
}
|
|
33
32
|
|
|
34
33
|
if (errors.length > 0) {
|
|
35
|
-
console.log(chalk.red(`\n❌ Errores (${errors.length}):\n`))
|
|
34
|
+
console.log(chalk.red(`\n❌ Errores (${errors.length}):\n`))
|
|
36
35
|
for (const err of errors) {
|
|
37
|
-
console.log(chalk.red(` • ${err}`))
|
|
36
|
+
console.log(chalk.red(` • ${err}`))
|
|
38
37
|
}
|
|
39
38
|
}
|
|
40
39
|
|
|
41
40
|
if (warnings.length > 0) {
|
|
42
|
-
console.log(chalk.yellow(`\n⚠️ Advertencias (${warnings.length}):\n`))
|
|
41
|
+
console.log(chalk.yellow(`\n⚠️ Advertencias (${warnings.length}):\n`))
|
|
43
42
|
for (const warn of warnings) {
|
|
44
|
-
console.log(chalk.yellow(` • ${warn}`))
|
|
43
|
+
console.log(chalk.yellow(` • ${warn}`))
|
|
45
44
|
}
|
|
46
45
|
}
|
|
47
46
|
|
|
48
47
|
if (errors.length === 0 && warnings.length === 0) {
|
|
49
|
-
console.log(chalk.green(
|
|
48
|
+
console.log(chalk.green('\n✅ Todas las anotaciones válidas\n'))
|
|
50
49
|
}
|