openprompt-lang 0.3.0 → 0.4.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 (90) hide show
  1. package/README.md +483 -32
  2. package/bin/cli.js +232 -41
  3. package/bin/create.js +135 -0
  4. package/bin/lint.js +20 -21
  5. package/docs/COMMANDS.md +200 -127
  6. package/docs/COMMITS/INDEX.md +1 -0
  7. package/docs/PROMPT_AI_CONTEXT.md +99 -0
  8. package/docs/langs/dotnet.md +36 -0
  9. package/docs/langs/java-spring.md +45 -0
  10. package/docs/langs/python-fastapi.md +35 -0
  11. package/docs/langs/unity.md +30 -0
  12. package/docs/langs/vue-nuxt.md +36 -0
  13. package/package.json +31 -3
  14. package/scaffolds/.cursorrules +6 -0
  15. package/scaffolds/AGENTS.md +27 -0
  16. package/scaffolds/Dockerfile +11 -0
  17. package/scaffolds/capacitor.config.ts +17 -0
  18. package/scaffolds/netlify.toml +8 -0
  19. package/scaffolds/prompt-lang.json +15 -0
  20. package/scaffolds/railway.json +12 -0
  21. package/scaffolds/tailwind.config.js +8 -0
  22. package/scaffolds/tauri.conf.json +26 -0
  23. package/schemas/language-module.json +116 -0
  24. package/schemas/prompt-lang.json +38 -3
  25. package/schemas/structures.json +145 -0
  26. package/src/ai/prompt-builder.js +184 -0
  27. package/src/ai/providers.js +247 -0
  28. package/src/annotations/registry.js +39 -0
  29. package/src/annotations/tags.json +24 -0
  30. package/src/commands/ai-gen.js +161 -0
  31. package/src/commands/component.js +242 -212
  32. package/src/commands/context.js +184 -109
  33. package/src/commands/extract.js +242 -0
  34. package/src/commands/figma.js +15 -15
  35. package/src/commands/init.js +197 -93
  36. package/src/commands/integrate.js +406 -0
  37. package/src/commands/lang.js +148 -0
  38. package/src/commands/qa-gen.js +139 -0
  39. package/src/commands/scaffold.js +127 -0
  40. package/src/commands/suggest.js +24 -14
  41. package/src/commands/teach.js +110 -0
  42. package/src/commands/validate.js +143 -83
  43. package/src/commands/wizard.js +456 -0
  44. package/src/generators/figma-prompt.js +20 -12
  45. package/src/language-service/plugin.cjs +94 -0
  46. package/src/language-service/plugin.d.ts +6 -0
  47. package/src/mcp-server.js +605 -0
  48. package/src/templates/langs/react/INDEX.json +262 -0
  49. package/src/templates/langs/react/MODULE.json +166 -0
  50. package/src/templates/langs/react/templates/hooks/useAuth.template.ts +134 -0
  51. package/src/templates/langs/react/templates/hooks/useDebounce.template.ts +45 -0
  52. package/src/templates/langs/react/templates/hooks/useForm.template.ts +146 -0
  53. package/src/templates/langs/react/templates/hooks/usePagination.template.ts +108 -0
  54. package/src/templates/langs/react/templates/services/apiService.template.ts +123 -0
  55. package/src/templates/langs/react/templates/ui/Button.template.tsx +87 -0
  56. package/src/templates/langs/react/templates/ui/Card.template.tsx +85 -0
  57. package/src/templates/langs/react/templates/ui/DataTable.template.tsx +163 -0
  58. package/src/templates/langs/react/templates/ui/Input.template.tsx +96 -0
  59. package/src/templates/langs/react/templates/ui/Modal.template.tsx +133 -0
  60. package/src/templates/langs/react/templates/ui/Select.template.tsx +99 -0
  61. package/src/templates/langs/vue/INDEX.json +246 -0
  62. package/src/templates/langs/vue/MODULE.json +105 -0
  63. package/src/templates/langs/vue/templates/composables/useAuth.template.ts +106 -0
  64. package/src/templates/langs/vue/templates/composables/useDebounce.template.ts +47 -0
  65. package/src/templates/langs/vue/templates/composables/useFetch.template.ts +54 -0
  66. package/src/templates/langs/vue/templates/composables/useForm.template.ts +127 -0
  67. package/src/templates/langs/vue/templates/composables/usePagination.template.ts +98 -0
  68. package/src/templates/langs/vue/templates/services/apiService.template.ts +116 -0
  69. package/src/templates/langs/vue/templates/ui/Button.template.vue +79 -0
  70. package/src/templates/langs/vue/templates/ui/Card.template.vue +73 -0
  71. package/src/templates/langs/vue/templates/ui/DataTable.template.vue +115 -0
  72. package/src/templates/langs/vue/templates/ui/Input.template.vue +70 -0
  73. package/src/templates/langs/vue/templates/ui/Modal.template.vue +112 -0
  74. package/src/templates/langs/vue/templates/ui/Select.template.vue +77 -0
  75. package/src/templates/scripts/log-actividad.sh +32 -0
  76. package/src/templates/scripts/log-commit.sh +35 -0
  77. package/src/templates/scripts/log-error.sh +45 -0
  78. package/src/templates/scripts/validate.sh +23 -0
  79. package/src/ts-transformer/index.cjs +86 -0
  80. package/src/utils/ai.js +35 -53
  81. package/src/utils/annotations.js +260 -214
  82. package/src/utils/config.js +61 -13
  83. package/src/utils/error-learner.js +203 -0
  84. package/src/utils/file-utils.js +119 -0
  85. package/src/utils/language-loader.js +167 -0
  86. package/src/utils/template-utils.js +45 -0
  87. package/src/vite-plugin/index.js +54 -0
  88. package/vscode-extension/package.json +23 -2
  89. package/vscode-extension/snippets/promptlang.json +1 -3
  90. package/vscode-extension/syntaxes/annotations-code.tmGrammar.json +15 -0
@@ -0,0 +1,456 @@
1
+ import { writeFileSync, existsSync, mkdirSync, readFileSync } from "fs"
2
+ import { join } from "path"
3
+ import chalk from "chalk"
4
+ import inquirer from "inquirer"
5
+ import {
6
+ getLanguages,
7
+ getProjectTypes,
8
+ getUiStyles,
9
+ getExtensions,
10
+ getStructure,
11
+ getLanguageIndex,
12
+ getLanguagePath,
13
+ getTemplateErrors,
14
+ } from "../utils/language-loader.js"
15
+
16
+ const PROFILES = [
17
+ { name: "Senior — max control, minimal boilerplate", value: "senior" },
18
+ { name: "Mid — balance between speed and structure", value: "mid" },
19
+ { name: "Junior — more guidance and restrictions", value: "junior" },
20
+ ]
21
+
22
+ const PROJECT_REQUIREMENTS = {
23
+ api: {
24
+ questions: [
25
+ {
26
+ name: "auth",
27
+ message: "Authentication?",
28
+ choices: ["none", "JWT", "Supabase", "Auth.js", "Clerk"],
29
+ default: "none",
30
+ },
31
+ {
32
+ name: "database",
33
+ message: "Database?",
34
+ choices: ["none", "SQLite", "PostgreSQL", "MongoDB", "Supabase"],
35
+ default: "none",
36
+ },
37
+ {
38
+ name: "orm",
39
+ message: "ORM / Query builder?",
40
+ choices: ["none", "Drizzle", "Prisma", "TypeORM", "Knex"],
41
+ default: "none",
42
+ },
43
+ {
44
+ name: "validation",
45
+ message: "Validation library?",
46
+ choices: ["none", "Zod", "Yup", "Joi"],
47
+ default: "none",
48
+ },
49
+ {
50
+ name: "testing",
51
+ message: "Testing framework?",
52
+ choices: ["Vitest", "Jest", "none"],
53
+ default: "Vitest",
54
+ },
55
+ ],
56
+ },
57
+ web: {
58
+ questions: [
59
+ {
60
+ name: "routing",
61
+ message: "Routing strategy?",
62
+ choices: ["React Router", "TanStack Router", "File-based (Next.js)", "none"],
63
+ default: "React Router",
64
+ },
65
+ {
66
+ name: "data-fetching",
67
+ message: "Data fetching?",
68
+ choices: ["TanStack Query", "SWR", "RTK Query", "none"],
69
+ default: "TanStack Query",
70
+ },
71
+ {
72
+ name: "state-management",
73
+ message: "State management?",
74
+ choices: ["Zustand", "Redux Toolkit", "Jotai", "Context only", "none"],
75
+ default: "Zustand",
76
+ },
77
+ {
78
+ name: "styling",
79
+ message: "Styling approach?",
80
+ choices: ["Tailwind", "CSS Modules", "Styled Components", "Vanilla CSS"],
81
+ default: "Tailwind",
82
+ },
83
+ ],
84
+ },
85
+ mobile: {
86
+ questions: [
87
+ {
88
+ name: "framework",
89
+ message: "Mobile framework?",
90
+ choices: ["React Native", "Capacitor", "Tauri"],
91
+ default: "Capacitor",
92
+ },
93
+ {
94
+ name: "navigation",
95
+ message: "Navigation?",
96
+ choices: ["React Navigation", "File-based (Expo)", "none"],
97
+ default: "React Navigation",
98
+ },
99
+ ],
100
+ },
101
+ desktop: {
102
+ questions: [
103
+ {
104
+ name: "framework",
105
+ message: "Desktop framework?",
106
+ choices: ["Tauri", "Electron"],
107
+ default: "Tauri",
108
+ },
109
+ ],
110
+ },
111
+ library: {
112
+ questions: [
113
+ {
114
+ name: "bundler",
115
+ message: "Bundler?",
116
+ choices: ["tsup", "Rollup", "Vite library mode"],
117
+ default: "tsup",
118
+ },
119
+ {
120
+ name: "publish",
121
+ message: "Publish target?",
122
+ choices: ["npm", "GitHub Packages", "none"],
123
+ default: "none",
124
+ },
125
+ ],
126
+ },
127
+ }
128
+
129
+ export async function wizard() {
130
+ console.log(chalk.cyan("\n🧙 openPrompt-Lang Project Wizard\n"))
131
+ console.log(chalk.gray("Answer the questions to generate the optimal configuration.\n"))
132
+
133
+ // Load languages dynamically from modules
134
+ const languages = getLanguages(true)
135
+ if (languages.length === 0) {
136
+ console.log(chalk.red("\n❌ No language modules found. Run: npx openPrompt-Lang lang list\n"))
137
+ return
138
+ }
139
+
140
+ const langChoices = languages.map((l) => ({
141
+ name: `${l.name} v${l.version}`,
142
+ value: l.id,
143
+ }))
144
+
145
+ // 1. Language
146
+ const { langId } = await inquirer.prompt([
147
+ {
148
+ type: "list",
149
+ name: "langId",
150
+ message: "Which language/framework will you use?",
151
+ choices: langChoices,
152
+ },
153
+ ])
154
+
155
+ // 2. Project type
156
+ const projectTypes = getProjectTypes(langId)
157
+ const typeChoices = projectTypes
158
+ ? Object.entries(projectTypes).map(([key, val]) => ({
159
+ name: `${val.name} — ${val.description || ""}`,
160
+ value: key,
161
+ }))
162
+ : [{ name: "Default", value: "default" }]
163
+
164
+ const { projectType } = await inquirer.prompt([
165
+ {
166
+ type: "list",
167
+ name: "projectType",
168
+ message: "What type of project is this?",
169
+ choices: typeChoices,
170
+ },
171
+ ])
172
+
173
+ // 2b. Requirements refinement
174
+ const refinement = await askRefinement(projectType)
175
+
176
+ // 3. UI Style (only if project type needs UI)
177
+ let uiStyle = "none"
178
+ const needsUI = projectTypes?.[projectType]?.ui !== false
179
+ if (needsUI) {
180
+ const styles = getUiStyles(langId)
181
+ const styleChoices = styles.map((s) => ({
182
+ name: s === "none" ? "None (no UI library)" : s,
183
+ value: s,
184
+ }))
185
+
186
+ const resp = await inquirer.prompt([
187
+ {
188
+ type: "list",
189
+ name: "uiStyle",
190
+ message: "Which UI library?",
191
+ choices: styleChoices,
192
+ },
193
+ ])
194
+ uiStyle = resp.uiStyle
195
+ }
196
+
197
+ // 4. Extensions available for this language
198
+ const availableExts = getExtensions(langId)
199
+ let extensions = []
200
+ if (availableExts.length > 0) {
201
+ const resp = await inquirer.prompt([
202
+ {
203
+ type: "checkbox",
204
+ name: "extensions",
205
+ message: "Which extensions do you need?",
206
+ choices: availableExts.map((e) => ({ name: e.name, value: e.value })),
207
+ },
208
+ ])
209
+ extensions = resp.extensions
210
+ }
211
+
212
+ // 5. Profile
213
+ const { profile } = await inquirer.prompt([
214
+ {
215
+ type: "list",
216
+ name: "profile",
217
+ message: "Developer profile?",
218
+ choices: PROFILES,
219
+ },
220
+ ])
221
+
222
+ // 6. Project name
223
+ const { projectName } = await inquirer.prompt([
224
+ {
225
+ type: "input",
226
+ name: "projectName",
227
+ message: "Project name:",
228
+ default: "mi-proyecto",
229
+ validate: (input) => input.trim().length > 0 || "Name cannot be empty",
230
+ },
231
+ ])
232
+
233
+ // Get structure info for the scaffold suggestion
234
+ const structure = getStructure(langId)
235
+ const index = getLanguageIndex(langId)
236
+ const templates = index?.templates || []
237
+
238
+ // Build config
239
+ const config = {
240
+ name: projectName,
241
+ version: "0.1.0",
242
+ lenguaje: langId,
243
+ projectType,
244
+ profile,
245
+ stack: {
246
+ base: structure?.folders
247
+ ?.filter((f) => f.startsWith("src/") || f.startsWith("app/"))
248
+ .map((f) => f.split("/")[1])
249
+ .filter(Boolean) || ["react"],
250
+ extensions,
251
+ },
252
+ annotations: { enabled: true, strict: profile === "senior" },
253
+ ui: uiStyle !== "none" ? { style: uiStyle } : undefined,
254
+ docs: { commits: true, logs: true, backlog: true },
255
+ extractor: {
256
+ ignore: [".env", "*.local", "dist", "node_modules"],
257
+ output: "contexto.md",
258
+ },
259
+ ...(Object.keys(refinement).length > 0 && { requirements: refinement }),
260
+ }
261
+
262
+ // Quality gating
263
+ const gateWarnings = qualityGate(config, structure, templates)
264
+
265
+ // Summary
266
+ console.log(chalk.cyan("\n📋 Configuration summary:\n"))
267
+ console.log(` ${chalk.bold("Name:")} ${projectName}`)
268
+ console.log(` ${chalk.bold("Language:")} ${langId}`)
269
+ console.log(` ${chalk.bold("Type:")} ${projectType}`)
270
+ console.log(` ${chalk.bold("UI:")} ${uiStyle}`)
271
+ console.log(
272
+ ` ${chalk.bold("Extensions:")} ${extensions.length > 0 ? extensions.join(", ") : "none"}`
273
+ )
274
+ console.log(` ${chalk.bold("Profile:")} ${profile}\n`)
275
+
276
+ for (const w of gateWarnings) {
277
+ console.log(chalk.yellow(` ⚠️ ${w}`))
278
+ }
279
+
280
+ if (gateWarnings.length > 0) console.log("")
281
+
282
+ const { confirm } = await inquirer.prompt([
283
+ {
284
+ type: "confirm",
285
+ name: "confirm",
286
+ message: "Generate configuration?",
287
+ default: true,
288
+ },
289
+ ])
290
+
291
+ if (!confirm) {
292
+ console.log(chalk.yellow("\n⏹️ Wizard cancelled.\n"))
293
+ return
294
+ }
295
+
296
+ // Write files
297
+ const targetDir = join(process.cwd(), projectName)
298
+ mkdirSync(targetDir, { recursive: true })
299
+
300
+ writeFileSync(join(targetDir, "prompt-lang.json"), JSON.stringify(config, null, 2), "utf-8")
301
+ console.log(chalk.green(` ✅ prompt-lang.json`))
302
+
303
+ const agentsContent = generateAgentsMd(config, structure)
304
+ writeFileSync(join(targetDir, "AGENTS.md"), agentsContent, "utf-8")
305
+ console.log(chalk.green(" ✅ AGENTS.md"))
306
+
307
+ // Post-wizard analyzer
308
+ postWizardReport(projectName, langId, config, templates)
309
+ }
310
+
311
+ function askRefinement(projectType) {
312
+ const specs = PROJECT_REQUIREMENTS[projectType]
313
+ if (!specs) return {}
314
+
315
+ return inquirer.prompt(
316
+ specs.questions.map((q) => ({
317
+ type: "list",
318
+ name: q.name,
319
+ message: q.message,
320
+ choices: q.choices,
321
+ default: q.default,
322
+ }))
323
+ )
324
+ }
325
+
326
+ export function qualityGate(config, structure, templates) {
327
+ const warnings = []
328
+
329
+ if (config.projectType === "api" && config.ui?.style && config.ui.style !== "none") {
330
+ warnings.push("UI library selected for an API project — you may not need it.")
331
+ }
332
+
333
+ if (config.profile === "senior" && config.projectType === "default") {
334
+ warnings.push(
335
+ "Senior profile with a default project type — consider specifying a type for better scaffolding."
336
+ )
337
+ }
338
+
339
+ if (config.projectType === "web" && !config.ui?.style) {
340
+ warnings.push(
341
+ "Web project without a UI library — consider adding one via component add --template."
342
+ )
343
+ }
344
+
345
+ if (config.projectType === "mobile" && config.ui?.style === "shadcn") {
346
+ warnings.push("shadcn/ui is not available for mobile projects.")
347
+ }
348
+
349
+ if (config.requirements?.testing === "none" && config.profile !== "junior") {
350
+ warnings.push("No testing framework selected — consider adding one.")
351
+ }
352
+
353
+ if (config.requirements?.database !== "none" && config.requirements?.orm === "none") {
354
+ warnings.push(
355
+ `Database (${config.requirements.database}) selected without an ORM — consider Drizzle or Prisma.`
356
+ )
357
+ }
358
+
359
+ if (templates && templates.length === 0) {
360
+ warnings.push(
361
+ "No templates available for this language module — teach command will have limited content."
362
+ )
363
+ }
364
+
365
+ return warnings
366
+ }
367
+
368
+ function postWizardReport(projectName, langId, config, templates) {
369
+ console.log(chalk.cyan("\n📊 Post-wizard analysis:\n"))
370
+
371
+ // Suggested templates to explore
372
+ const suggestedTemplates = templates
373
+ ? templates
374
+ .filter((t) => t.hasTeachMe)
375
+ .slice(0, 4)
376
+ .map((t) => ` • npx openPrompt-Lang teach ${t.id} --lang ${langId}`)
377
+ : []
378
+
379
+ console.log(chalk.bold(" 🎯 Commands to run next:\n"))
380
+ console.log(` cd ${projectName}`)
381
+ console.log(` npx openPrompt-Lang scaffold folders ${langId}`)
382
+ if (config.requirements?.testing && config.requirements.testing !== "none") {
383
+ console.log(` npx openPrompt-Lang qa-gen --lang ${langId} --output src/__tests__`)
384
+ }
385
+ console.log("")
386
+
387
+ if (suggestedTemplates.length > 0) {
388
+ console.log(chalk.bold(" 📚 Templates available with @teachMe:\n"))
389
+ for (const cmd of suggestedTemplates) console.log(cmd)
390
+ console.log("")
391
+ }
392
+
393
+ // Template coverage
394
+ if (templates) {
395
+ const practiceCounts = countPractices(langId, templates)
396
+ console.log(chalk.bold(" 📈 Module stats:\n"))
397
+ console.log(` Templates: ${templates.length}`)
398
+ console.log(` @teachMe: ${templates.filter((t) => t.hasTeachMe).length}`)
399
+ console.log(` @goodPractice: ${practiceCounts.good}`)
400
+ console.log(` @badPractice: ${practiceCounts.bad}`)
401
+ const errorCount = getTemplateErrors(langId).length
402
+ console.log(` Learned errors: ${errorCount}\n`)
403
+ }
404
+
405
+ console.log(chalk.cyan(` ✅ Done. Happy coding!\n`))
406
+ }
407
+
408
+ export function generateAgentsMd(config, structure) {
409
+ const uiNote =
410
+ config.ui?.style && config.ui.style !== "none" ? `- UI Library: ${config.ui.style}\n` : ""
411
+
412
+ const foldersNote = structure?.folders?.length
413
+ ? `\n## Structure\n${structure.folders.map((f) => `- ${f}/`).join("\n")}\n`
414
+ : ""
415
+
416
+ return `# AGENTS.md — Context for AI
417
+
418
+ ## Project: ${config.name}
419
+ ## Stack
420
+ - Language: ${config.lenguaje}
421
+ - Profile: ${config.profile}
422
+ ${uiNote}${config.stack.extensions.length > 0 ? config.stack.extensions.map((e) => `- ${e}`).join("\n") : ""}
423
+
424
+ ## Conventions
425
+ - Framework: openPrompt-Lang (@kind, @contract, @limit annotations)
426
+ - NO any types
427
+ - NO exceed line limits without refactoring
428
+ - Annotations must declare @use() at file start
429
+ - Run validation before every commit
430
+ - Search templates: npx openPrompt-Lang search <query>
431
+ - Learn from templates: npx openPrompt-Lang teach <template-id>
432
+
433
+ ## References
434
+ - PromptLang syntax: docs/SYNTAX.md
435
+ - Methodology: docs/referencia-metodologia/
436
+ - Templates: src/templates/langs/${config.lenguaje}/INDEX.json
437
+ ${foldersNote}`
438
+ }
439
+
440
+ export function countPractices(langId, templates) {
441
+ const langPath = getLanguagePath(langId)
442
+ let good = 0
443
+ let bad = 0
444
+
445
+ for (const t of templates) {
446
+ if (!t.file || !langPath) continue
447
+ try {
448
+ const filePath = join(langPath, t.file)
449
+ const content = readFileSync(filePath, "utf-8")
450
+ if (content.includes("@goodPractice")) good++
451
+ if (content.includes("@badPractice")) bad++
452
+ } catch {}
453
+ }
454
+
455
+ return { good, bad }
456
+ }
@@ -1,14 +1,18 @@
1
1
  export function generateFigmaPrompt(answers) {
2
- const { projectType, screens, mobileFirst, style, includeAntiPatterns } = answers;
3
- const screenList = screens.split(",").map((s) => s.trim());
2
+ const { projectType, screens, mobileFirst, style, includeAntiPatterns } = answers
3
+ const screenList = screens.split(",").map((s) => s.trim())
4
4
 
5
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
- };
6
+ "Minimalista corporativo":
7
+ "minimalista, profesional, colores neutros con acento azul oscuro, tipografía sans-serif limpia, espaciado generoso",
8
+ "Moderno vibrante":
9
+ "moderno, atrevido, gradientes sutiles, colores vibrantes (púrpura/cian), micro-interacciones, glassmorphism ligero",
10
+ "Oscuro (dark mode)":
11
+ "dark mode first, fondo gris oscuro (#0f0f0f), acentos en verde/azul neón, contrastes altos, sombras sutiles",
12
+ "Claro y limpio":
13
+ "claro, fondos blancos/gris claro, mucho espacio negativo, bordes suaves, iconos lineales, tipografía elegante",
14
+ Personalizado: style,
15
+ }
12
16
 
13
17
  const prompt = `> [!example] Prompt generado por openPrompt-Lang
14
18
  > \`\`\`markdown
@@ -34,12 +38,16 @@ export function generateFigmaPrompt(answers) {
34
38
  > - Zonas táctiles: mínimo \`h-10 w-10\` en mobile
35
39
  > - Estados: loading, empty, error, success para cada vista
36
40
  >
37
- > ${includeAntiPatterns ? `## Anti-patrones (Prohibido)
41
+ > ${
42
+ includeAntiPatterns
43
+ ? `## Anti-patrones (Prohibido)
38
44
  > - ❌ Tamaños absolutos (h-[500px], w-[300px])
39
45
  > - ❌ Posicionamiento absoluto para layout (usar flex/grid)
40
46
  > - ❌ CSS personalizado o modules
41
47
  > - ❌ Componentes de más de 120 líneas
42
- > - ❌ Mezclar lógica de negocio en componentes de presentación` : ""}
48
+ > - ❌ Mezclar lógica de negocio en componentes de presentación`
49
+ : ""
50
+ }
43
51
  >
44
52
  > ## Lista de Pantallas
45
53
  > ${screenList.map((s, i) => `${i + 1}. **${s}:** Describir componentes principales y flujo`).join("\n")}
@@ -50,7 +58,7 @@ export function generateFigmaPrompt(answers) {
50
58
  > - Estados visuales (loading, empty, error) para cada componente crítico
51
59
  > - Sugerencia de estructura de carpetas
52
60
  > \`\`\`
53
- `;
61
+ `
54
62
 
55
- return prompt;
63
+ return prompt
56
64
  }
@@ -0,0 +1,94 @@
1
+ var builtinTags = [
2
+ "use",
3
+ "kind",
4
+ "contract",
5
+ "props",
6
+ "state",
7
+ "limit",
8
+ "forbidden",
9
+ "compose",
10
+ "deps",
11
+ "platform",
12
+ "scope",
13
+ "test",
14
+ "meta",
15
+ "pattern",
16
+ "learn-error",
17
+ ]
18
+
19
+ function makeTagPattern(tags) {
20
+ var escaped = tags.map(function (t) {
21
+ return t.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")
22
+ })
23
+ return new RegExp("^\\s*@(" + escaped.join("|") + ")\\(")
24
+ }
25
+
26
+ function create(typescript) {
27
+ var tagPattern = makeTagPattern(builtinTags)
28
+
29
+ return {
30
+ create: function (info) {
31
+ var proxy = Object.create(null)
32
+ var oldLS = info.languageService
33
+
34
+ function isAnnotationLine(line) {
35
+ return tagPattern.test(line.trim())
36
+ }
37
+
38
+ proxy.getSemanticDiagnostics = function (fileName) {
39
+ var original = oldLS.getSemanticDiagnostics(fileName)
40
+ if (!original || !original.length) return original
41
+
42
+ var sourceFile = info.project.getSourceFile(fileName)
43
+ if (!sourceFile) return original
44
+
45
+ var text = sourceFile.text || ""
46
+ var lines = text.split("\n")
47
+
48
+ return original.filter(function (diag) {
49
+ if (diag.start === undefined) return true
50
+ var lineIndex = 0
51
+ var pos = 0
52
+ while (pos < diag.start && lineIndex < lines.length) {
53
+ pos += lines[lineIndex].length + 1
54
+ lineIndex++
55
+ }
56
+ return !isAnnotationLine(lines[lineIndex] || "")
57
+ })
58
+ }
59
+
60
+ proxy.getQuickInfoAtPosition = function (fileName, position) {
61
+ var sourceFile = info.project.getSourceFile(fileName)
62
+ if (!sourceFile) return undefined
63
+
64
+ var text = sourceFile.text || ""
65
+ var lines = text.split("\n")
66
+ var pos = 0
67
+ for (var i = 0; i < lines.length; i++) {
68
+ if (position >= pos && position < pos + lines[i].length + 1) {
69
+ var line = lines[i].trim()
70
+ var match = line.match(tagPattern)
71
+ if (match) {
72
+ return {
73
+ displayParts: [{ text: "promptlang annotation", kind: "keyword" }],
74
+ documentation: [
75
+ {
76
+ text: "@" + match[1] + " — annotation for AI-assisted development",
77
+ kind: "documentation",
78
+ },
79
+ ],
80
+ }
81
+ }
82
+ break
83
+ }
84
+ pos += lines[i].length + 1
85
+ }
86
+ return oldLS.getQuickInfoAtPosition(fileName, position)
87
+ }
88
+
89
+ return proxy
90
+ },
91
+ }
92
+ }
93
+
94
+ module.exports = { create }
@@ -0,0 +1,6 @@
1
+ export function create(typescript: {
2
+ createLanguageService: (host: any) => any
3
+ flattenDiagnosticMessageText: (text: any) => string
4
+ }): {
5
+ create: (info: { languageService: any; project: any }) => any
6
+ }