popeye-cli 1.4.6 → 1.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.
Files changed (140) hide show
  1. package/README.md +222 -63
  2. package/dist/adapters/gemini.d.ts +1 -0
  3. package/dist/adapters/gemini.d.ts.map +1 -1
  4. package/dist/adapters/gemini.js +9 -4
  5. package/dist/adapters/gemini.js.map +1 -1
  6. package/dist/adapters/grok.d.ts +1 -0
  7. package/dist/adapters/grok.d.ts.map +1 -1
  8. package/dist/adapters/grok.js +9 -4
  9. package/dist/adapters/grok.js.map +1 -1
  10. package/dist/adapters/openai.d.ts +1 -1
  11. package/dist/adapters/openai.d.ts.map +1 -1
  12. package/dist/adapters/openai.js +35 -9
  13. package/dist/adapters/openai.js.map +1 -1
  14. package/dist/cli/interactive.d.ts.map +1 -1
  15. package/dist/cli/interactive.js +42 -0
  16. package/dist/cli/interactive.js.map +1 -1
  17. package/dist/generators/all.d.ts +4 -1
  18. package/dist/generators/all.d.ts.map +1 -1
  19. package/dist/generators/all.js +2 -1
  20. package/dist/generators/all.js.map +1 -1
  21. package/dist/generators/doc-parser.d.ts +49 -0
  22. package/dist/generators/doc-parser.d.ts.map +1 -0
  23. package/dist/generators/doc-parser.js +336 -0
  24. package/dist/generators/doc-parser.js.map +1 -0
  25. package/dist/generators/templates/index.d.ts +4 -0
  26. package/dist/generators/templates/index.d.ts.map +1 -1
  27. package/dist/generators/templates/index.js +4 -0
  28. package/dist/generators/templates/index.js.map +1 -1
  29. package/dist/generators/templates/website-components.d.ts +33 -0
  30. package/dist/generators/templates/website-components.d.ts.map +1 -0
  31. package/dist/generators/templates/website-components.js +278 -0
  32. package/dist/generators/templates/website-components.js.map +1 -0
  33. package/dist/generators/templates/website-config.d.ts +41 -0
  34. package/dist/generators/templates/website-config.d.ts.map +1 -0
  35. package/dist/generators/templates/website-config.js +283 -0
  36. package/dist/generators/templates/website-config.js.map +1 -0
  37. package/dist/generators/templates/website-conversion.d.ts +27 -0
  38. package/dist/generators/templates/website-conversion.d.ts.map +1 -0
  39. package/dist/generators/templates/website-conversion.js +326 -0
  40. package/dist/generators/templates/website-conversion.js.map +1 -0
  41. package/dist/generators/templates/website-seo.d.ts +76 -0
  42. package/dist/generators/templates/website-seo.d.ts.map +1 -0
  43. package/dist/generators/templates/website-seo.js +326 -0
  44. package/dist/generators/templates/website-seo.js.map +1 -0
  45. package/dist/generators/templates/website.d.ts +14 -47
  46. package/dist/generators/templates/website.d.ts.map +1 -1
  47. package/dist/generators/templates/website.js +412 -499
  48. package/dist/generators/templates/website.js.map +1 -1
  49. package/dist/generators/website-context.d.ts +83 -0
  50. package/dist/generators/website-context.d.ts.map +1 -0
  51. package/dist/generators/website-context.js +190 -0
  52. package/dist/generators/website-context.js.map +1 -0
  53. package/dist/generators/website.d.ts +3 -0
  54. package/dist/generators/website.d.ts.map +1 -1
  55. package/dist/generators/website.js +73 -10
  56. package/dist/generators/website.js.map +1 -1
  57. package/dist/state/index.d.ts +27 -0
  58. package/dist/state/index.d.ts.map +1 -1
  59. package/dist/state/index.js +30 -0
  60. package/dist/state/index.js.map +1 -1
  61. package/dist/types/consensus.d.ts +3 -0
  62. package/dist/types/consensus.d.ts.map +1 -1
  63. package/dist/types/consensus.js +1 -0
  64. package/dist/types/consensus.js.map +1 -1
  65. package/dist/types/website-strategy.d.ts +263 -0
  66. package/dist/types/website-strategy.d.ts.map +1 -0
  67. package/dist/types/website-strategy.js +105 -0
  68. package/dist/types/website-strategy.js.map +1 -0
  69. package/dist/types/workflow.d.ts +15 -0
  70. package/dist/types/workflow.d.ts.map +1 -1
  71. package/dist/types/workflow.js +6 -0
  72. package/dist/types/workflow.js.map +1 -1
  73. package/dist/workflow/auto-fix.d.ts +7 -1
  74. package/dist/workflow/auto-fix.d.ts.map +1 -1
  75. package/dist/workflow/auto-fix.js +55 -3
  76. package/dist/workflow/auto-fix.js.map +1 -1
  77. package/dist/workflow/consensus.d.ts.map +1 -1
  78. package/dist/workflow/consensus.js +2 -0
  79. package/dist/workflow/consensus.js.map +1 -1
  80. package/dist/workflow/execution-mode.d.ts.map +1 -1
  81. package/dist/workflow/execution-mode.js +18 -0
  82. package/dist/workflow/execution-mode.js.map +1 -1
  83. package/dist/workflow/index.d.ts +3 -0
  84. package/dist/workflow/index.d.ts.map +1 -1
  85. package/dist/workflow/index.js +25 -0
  86. package/dist/workflow/index.js.map +1 -1
  87. package/dist/workflow/overview.d.ts +89 -0
  88. package/dist/workflow/overview.d.ts.map +1 -0
  89. package/dist/workflow/overview.js +354 -0
  90. package/dist/workflow/overview.js.map +1 -0
  91. package/dist/workflow/plan-mode.d.ts +2 -1
  92. package/dist/workflow/plan-mode.d.ts.map +1 -1
  93. package/dist/workflow/plan-mode.js +83 -5
  94. package/dist/workflow/plan-mode.js.map +1 -1
  95. package/dist/workflow/website-strategy.d.ts +70 -0
  96. package/dist/workflow/website-strategy.d.ts.map +1 -0
  97. package/dist/workflow/website-strategy.js +238 -0
  98. package/dist/workflow/website-strategy.js.map +1 -0
  99. package/dist/workflow/website-updater.d.ts +17 -0
  100. package/dist/workflow/website-updater.d.ts.map +1 -0
  101. package/dist/workflow/website-updater.js +105 -0
  102. package/dist/workflow/website-updater.js.map +1 -0
  103. package/dist/workflow/workflow-logger.d.ts +1 -1
  104. package/dist/workflow/workflow-logger.d.ts.map +1 -1
  105. package/dist/workflow/workflow-logger.js.map +1 -1
  106. package/package.json +1 -1
  107. package/src/adapters/gemini.ts +10 -4
  108. package/src/adapters/grok.ts +10 -4
  109. package/src/adapters/openai.ts +38 -6
  110. package/src/cli/interactive.ts +47 -0
  111. package/src/generators/all.ts +6 -1
  112. package/src/generators/doc-parser.ts +372 -0
  113. package/src/generators/templates/index.ts +4 -0
  114. package/src/generators/templates/website-components.ts +305 -0
  115. package/src/generators/templates/website-config.ts +291 -0
  116. package/src/generators/templates/website-conversion.ts +341 -0
  117. package/src/generators/templates/website-seo.ts +370 -0
  118. package/src/generators/templates/website.ts +451 -505
  119. package/src/generators/website-context.ts +265 -0
  120. package/src/generators/website.ts +109 -19
  121. package/src/state/index.ts +42 -0
  122. package/src/types/consensus.ts +3 -0
  123. package/src/types/website-strategy.ts +243 -0
  124. package/src/types/workflow.ts +15 -0
  125. package/src/workflow/auto-fix.ts +57 -3
  126. package/src/workflow/consensus.ts +2 -0
  127. package/src/workflow/execution-mode.ts +21 -0
  128. package/src/workflow/index.ts +25 -0
  129. package/src/workflow/overview.ts +469 -0
  130. package/src/workflow/plan-mode.ts +115 -4
  131. package/src/workflow/website-strategy.ts +305 -0
  132. package/src/workflow/website-updater.ts +131 -0
  133. package/src/workflow/workflow-logger.ts +1 -0
  134. package/tests/adapters/persona-switching.test.ts +63 -0
  135. package/tests/generators/website-components.test.ts +159 -0
  136. package/tests/generators/website-context.test.ts +222 -0
  137. package/tests/generators/website-seo-quality.test.ts +246 -0
  138. package/tests/workflow/auto-fix-enhanced.test.ts +61 -1
  139. package/tests/workflow/overview.test.ts +392 -0
  140. package/tests/workflow/website-strategy.test.ts +191 -0
@@ -0,0 +1,370 @@
1
+ /**
2
+ * SEO infrastructure templates for Next.js marketing websites
3
+ * Generates JSON-LD components, enhanced sitemap, robots.txt,
4
+ * error pages, web manifest, and meta helpers
5
+ */
6
+
7
+ import type { WebsiteContentContext } from '../website-context.js';
8
+ import type { WebsiteStrategyDocument } from '../../types/website-strategy.js';
9
+
10
+ /**
11
+ * Escape a string for safe use inside JSX template literals
12
+ */
13
+ function escapeJsx(str: string): string {
14
+ return str
15
+ .replace(/\\/g, '\\\\')
16
+ .replace(/'/g, "\\'")
17
+ .replace(/`/g, '\\`')
18
+ .replace(/\$/g, '\\$');
19
+ }
20
+
21
+ /**
22
+ * Generate reusable JSON-LD component
23
+ *
24
+ * @returns JsonLd component source code
25
+ */
26
+ export function generateJsonLdComponent(): string {
27
+ return `/**
28
+ * Reusable JSON-LD structured data component
29
+ * Renders schema.org structured data as a script tag
30
+ */
31
+
32
+ interface JsonLdProps {
33
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
34
+ schema: Record<string, any>;
35
+ }
36
+
37
+ export default function JsonLd({ schema }: JsonLdProps) {
38
+ return (
39
+ <script
40
+ type="application/ld+json"
41
+ dangerouslySetInnerHTML={{ __html: JSON.stringify(schema) }}
42
+ />
43
+ );
44
+ }
45
+ `;
46
+ }
47
+
48
+ /**
49
+ * Generate Organization JSON-LD schema data
50
+ *
51
+ * @param projectName - Product name
52
+ * @param context - Optional content context
53
+ * @returns Organization schema as string constant
54
+ */
55
+ export function generateOrganizationJsonLd(
56
+ projectName: string,
57
+ context?: WebsiteContentContext
58
+ ): string {
59
+ const displayName = context?.productName || projectName;
60
+ const description = context?.description || `${displayName} - Modern web application`;
61
+
62
+ return `{
63
+ "@context": "https://schema.org",
64
+ "@type": "Organization",
65
+ "name": "${escapeJsx(displayName)}",
66
+ "description": "${escapeJsx(description)}",
67
+ "url": process.env.NEXT_PUBLIC_SITE_URL || "https://${projectName}.com"
68
+ }`;
69
+ }
70
+
71
+ /**
72
+ * Generate SoftwareApplication JSON-LD schema data
73
+ *
74
+ * @param projectName - Product name
75
+ * @param context - Optional content context
76
+ * @returns Software application schema as string constant
77
+ */
78
+ export function generateProductJsonLd(
79
+ projectName: string,
80
+ context?: WebsiteContentContext
81
+ ): string {
82
+ const displayName = context?.productName || projectName;
83
+ const description = context?.description || `${displayName} - Modern web application`;
84
+
85
+ return `{
86
+ "@context": "https://schema.org",
87
+ "@type": "SoftwareApplication",
88
+ "name": "${escapeJsx(displayName)}",
89
+ "description": "${escapeJsx(description)}",
90
+ "applicationCategory": "BusinessApplication",
91
+ "operatingSystem": "Web",
92
+ "url": process.env.NEXT_PUBLIC_SITE_URL || "https://${projectName}.com"
93
+ }`;
94
+ }
95
+
96
+ /**
97
+ * Generate enhanced sitemap with all strategy pages
98
+ *
99
+ * @param projectName - Project name for base URL fallback
100
+ * @param strategy - Optional strategy for page list
101
+ * @returns Enhanced sitemap.ts source code
102
+ */
103
+ export function generateEnhancedSitemap(
104
+ projectName: string,
105
+ strategy?: WebsiteStrategyDocument
106
+ ): string {
107
+ // Build page entries from strategy or defaults
108
+ const pages = strategy?.siteArchitecture.pages || [
109
+ { path: '/', pageType: 'landing' },
110
+ { path: '/pricing', pageType: 'pricing' },
111
+ { path: '/docs', pageType: 'docs' },
112
+ { path: '/blog', pageType: 'blog' },
113
+ ];
114
+
115
+ const priorityMap: Record<string, number> = {
116
+ landing: 1.0,
117
+ pricing: 0.9,
118
+ solution: 0.8,
119
+ 'use-cases': 0.8,
120
+ docs: 0.7,
121
+ blog: 0.7,
122
+ about: 0.6,
123
+ contact: 0.6,
124
+ legal: 0.3,
125
+ };
126
+
127
+ const frequencyMap: Record<string, string> = {
128
+ landing: 'weekly',
129
+ pricing: 'monthly',
130
+ solution: 'monthly',
131
+ 'use-cases': 'monthly',
132
+ docs: 'weekly',
133
+ blog: 'daily',
134
+ about: 'monthly',
135
+ contact: 'monthly',
136
+ legal: 'yearly',
137
+ };
138
+
139
+ const entries = pages.map(page => {
140
+ const priority = priorityMap[page.pageType] || 0.5;
141
+ const frequency = frequencyMap[page.pageType] || 'monthly';
142
+ const urlPath = page.path === '/' ? '' : page.path;
143
+ return ` {
144
+ url: \`\${baseUrl}${urlPath}\`,
145
+ lastModified: new Date(),
146
+ changeFrequency: '${frequency}' as const,
147
+ priority: ${priority},
148
+ }`;
149
+ });
150
+
151
+ return `import { MetadataRoute } from 'next';
152
+
153
+ export default function sitemap(): MetadataRoute.Sitemap {
154
+ const baseUrl = process.env.NEXT_PUBLIC_SITE_URL || 'https://${projectName}.com';
155
+
156
+ return [
157
+ ${entries.join(',\n')}
158
+ ];
159
+ }
160
+ `;
161
+ }
162
+
163
+ /**
164
+ * Generate enhanced robots.txt
165
+ *
166
+ * @param projectName - Project name for base URL fallback
167
+ * @returns robots.ts source code
168
+ */
169
+ export function generateEnhancedRobots(projectName: string): string {
170
+ return `import { MetadataRoute } from 'next';
171
+
172
+ export default function robots(): MetadataRoute.Robots {
173
+ const baseUrl = process.env.NEXT_PUBLIC_SITE_URL || 'https://${projectName}.com';
174
+
175
+ return {
176
+ rules: [
177
+ {
178
+ userAgent: '*',
179
+ allow: '/',
180
+ disallow: ['/api/', '/admin/', '/_next/'],
181
+ },
182
+ ],
183
+ sitemap: \`\${baseUrl}/sitemap.xml\`,
184
+ };
185
+ }
186
+ `;
187
+ }
188
+
189
+ /**
190
+ * Generate branded 404 Not Found page
191
+ *
192
+ * @param projectName - Product name for branding
193
+ * @param context - Optional content context
194
+ * @returns not-found.tsx source code
195
+ */
196
+ export function generate404Page(
197
+ projectName: string,
198
+ context?: WebsiteContentContext
199
+ ): string {
200
+ const displayName = context?.productName || projectName
201
+ .split('-')
202
+ .map((w) => w.charAt(0).toUpperCase() + w.slice(1))
203
+ .join(' ');
204
+
205
+ return `import Link from 'next/link';
206
+
207
+ export default function NotFound() {
208
+ return (
209
+ <main className="flex min-h-[60vh] flex-col items-center justify-center">
210
+ <div className="text-center">
211
+ <p className="text-7xl font-bold text-primary-600">404</p>
212
+ <h1 className="mt-4 text-3xl font-bold text-gray-900">Page not found</h1>
213
+ <p className="mt-4 text-lg text-gray-600">
214
+ Sorry, we couldn&apos;t find the page you&apos;re looking for.
215
+ </p>
216
+ <div className="mt-8">
217
+ <Link
218
+ href="/"
219
+ className="rounded-md bg-primary-600 px-6 py-3 text-sm font-semibold text-white shadow-sm hover:bg-primary-500"
220
+ >
221
+ Back to ${escapeJsx(displayName)}
222
+ </Link>
223
+ </div>
224
+ </div>
225
+ </main>
226
+ );
227
+ }
228
+ `;
229
+ }
230
+
231
+ /**
232
+ * Generate error boundary page (500)
233
+ *
234
+ * @param projectName - Product name for branding
235
+ * @returns error.tsx source code
236
+ */
237
+ export function generate500Page(_projectName: string): string {
238
+ return `'use client';
239
+
240
+ export default function Error({
241
+ reset,
242
+ }: {
243
+ error: Error & { digest?: string };
244
+ reset: () => void;
245
+ }) {
246
+ return (
247
+ <main className="flex min-h-[60vh] flex-col items-center justify-center">
248
+ <div className="text-center">
249
+ <p className="text-7xl font-bold text-red-600">500</p>
250
+ <h1 className="mt-4 text-3xl font-bold text-gray-900">Something went wrong</h1>
251
+ <p className="mt-4 text-lg text-gray-600">
252
+ An unexpected error occurred. Please try again.
253
+ </p>
254
+ <div className="mt-8">
255
+ <button
256
+ onClick={() => reset()}
257
+ className="rounded-md bg-primary-600 px-6 py-3 text-sm font-semibold text-white shadow-sm hover:bg-primary-500"
258
+ >
259
+ Try again
260
+ </button>
261
+ </div>
262
+ </div>
263
+ </main>
264
+ );
265
+ }
266
+ `;
267
+ }
268
+
269
+ /**
270
+ * Generate PWA web manifest
271
+ *
272
+ * @param projectName - Product name
273
+ * @param context - Optional content context
274
+ * @returns manifest.webmanifest JSON content
275
+ */
276
+ export function generateWebManifest(
277
+ projectName: string,
278
+ context?: WebsiteContentContext
279
+ ): string {
280
+ const displayName = context?.productName || projectName
281
+ .split('-')
282
+ .map((w) => w.charAt(0).toUpperCase() + w.slice(1))
283
+ .join(' ');
284
+
285
+ const primaryColor = context?.brand?.primaryColor || '#0ea5e9';
286
+ const description = context?.description || `${displayName} - Modern web application`;
287
+
288
+ return JSON.stringify(
289
+ {
290
+ name: displayName,
291
+ short_name: displayName,
292
+ description,
293
+ start_url: '/',
294
+ display: 'standalone',
295
+ background_color: '#ffffff',
296
+ theme_color: primaryColor,
297
+ icons: [
298
+ { src: '/favicon.ico', sizes: '48x48', type: 'image/x-icon' },
299
+ { src: '/icon-192.png', sizes: '192x192', type: 'image/png' },
300
+ { src: '/icon-512.png', sizes: '512x512', type: 'image/png' },
301
+ ],
302
+ },
303
+ null,
304
+ 2
305
+ );
306
+ }
307
+
308
+ /**
309
+ * Generate shared metadata helper utility
310
+ *
311
+ * @param projectName - Product name
312
+ * @param strategy - Optional strategy for SEO data
313
+ * @returns Metadata helper source code
314
+ */
315
+ export function generateMetaHelper(
316
+ projectName: string,
317
+ strategy?: WebsiteStrategyDocument
318
+ ): string {
319
+ const primaryKeywords = strategy?.seoStrategy.primaryKeywords || [projectName, 'web app'];
320
+ const keywordsStr = primaryKeywords.map(k => `'${escapeJsx(k)}'`).join(', ');
321
+
322
+ return `import type { Metadata } from 'next';
323
+
324
+ const BASE_URL = process.env.NEXT_PUBLIC_SITE_URL || 'https://${projectName}.com';
325
+
326
+ /**
327
+ * Build page-level metadata with site-wide defaults
328
+ *
329
+ * @param title - Page title (combined with site name via template)
330
+ * @param description - Page meta description
331
+ * @param keywords - Additional page-specific keywords
332
+ * @param path - Page path for canonical URL
333
+ * @returns Next.js Metadata object
334
+ */
335
+ export function buildMetadata({
336
+ title,
337
+ description,
338
+ keywords = [],
339
+ path = '/',
340
+ }: {
341
+ title: string;
342
+ description: string;
343
+ keywords?: string[];
344
+ path?: string;
345
+ }): Metadata {
346
+ const url = \`\${BASE_URL}\${path}\`;
347
+ const allKeywords = [...new Set([${keywordsStr}, ...keywords])];
348
+
349
+ return {
350
+ title,
351
+ description,
352
+ keywords: allKeywords,
353
+ alternates: {
354
+ canonical: url,
355
+ },
356
+ openGraph: {
357
+ title,
358
+ description,
359
+ url,
360
+ type: 'website',
361
+ },
362
+ twitter: {
363
+ card: 'summary_large_image',
364
+ title,
365
+ description,
366
+ },
367
+ };
368
+ }
369
+ `;
370
+ }