fraim-framework 2.0.34 → 2.0.36

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 (49) hide show
  1. package/bin/fraim.js +52 -5
  2. package/dist/registry/scripts/cleanup-branch.js +62 -33
  3. package/dist/registry/scripts/generate-engagement-emails.js +119 -44
  4. package/dist/registry/scripts/newsletter-helpers.js +208 -268
  5. package/dist/registry/scripts/profile-server.js +387 -0
  6. package/dist/tests/test-chalk-regression.js +18 -2
  7. package/dist/tests/test-client-scripts-validation.js +133 -0
  8. package/dist/tests/test-markdown-to-pdf.js +454 -0
  9. package/dist/tests/test-script-location-independence.js +76 -28
  10. package/package.json +5 -2
  11. package/registry/agent-guardrails.md +62 -62
  12. package/registry/rules/communication.md +121 -121
  13. package/registry/rules/continuous-learning.md +54 -54
  14. package/registry/rules/hitl-ppe-record-analysis.md +302 -302
  15. package/registry/rules/software-development-lifecycle.md +104 -104
  16. package/registry/scripts/cleanup-branch.ts +341 -0
  17. package/registry/scripts/code-quality-check.sh +559 -559
  18. package/registry/scripts/detect-tautological-tests.sh +38 -38
  19. package/registry/scripts/generate-engagement-emails.ts +830 -0
  20. package/registry/scripts/markdown-to-pdf.js +395 -0
  21. package/registry/scripts/newsletter-helpers.ts +777 -0
  22. package/registry/scripts/profile-server.ts +424 -0
  23. package/registry/scripts/run-thank-you-workflow.ts +122 -0
  24. package/registry/scripts/send-newsletter-simple.ts +102 -0
  25. package/registry/scripts/send-thank-you-emails.ts +57 -0
  26. package/registry/scripts/validate-openapi-limits.ts +366 -366
  27. package/registry/scripts/validate-test-coverage.ts +280 -280
  28. package/registry/scripts/verify-pr-comments.sh +70 -70
  29. package/registry/templates/bootstrap/ARCHITECTURE-TEMPLATE.md +53 -53
  30. package/registry/templates/evidence/Implementation-BugEvidence.md +85 -85
  31. package/registry/templates/evidence/Implementation-FeatureEvidence.md +120 -120
  32. package/registry/workflows/convert-to-pdf.md +235 -0
  33. package/registry/workflows/customer-development/insight-analysis.md +156 -156
  34. package/registry/workflows/customer-development/interview-preparation.md +421 -421
  35. package/registry/workflows/customer-development/strategic-brainstorming.md +146 -146
  36. package/registry/workflows/quality-assurance/iterative-improvement-cycle.md +562 -562
  37. package/registry/workflows/reviewer/review-implementation-vs-feature-spec.md +669 -669
  38. package/dist/registry/scripts/build-scripts-generator.js +0 -205
  39. package/dist/registry/scripts/fraim-config.js +0 -61
  40. package/dist/registry/scripts/generic-issues-api.js +0 -100
  41. package/dist/registry/scripts/openapi-generator.js +0 -664
  42. package/dist/registry/scripts/performance/profile-server.js +0 -390
  43. package/dist/test-utils.js +0 -96
  44. package/dist/tests/esm-compat.js +0 -11
  45. package/dist/tests/test-chalk-esm-issue.js +0 -159
  46. package/dist/tests/test-chalk-real-world.js +0 -265
  47. package/dist/tests/test-chalk-resolution-issue.js +0 -304
  48. package/dist/tests/test-fraim-install-chalk-issue.js +0 -254
  49. package/dist/tests/test-npm-resolution-diagnostic.js +0 -140
@@ -0,0 +1,395 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Markdown to PDF Converter Script
5
+ *
6
+ * This script converts markdown files to PDF using puppeteer and markdown-it.
7
+ * It supports various markdown features including tables, code blocks, and images.
8
+ *
9
+ * EXECUTION MODEL:
10
+ * - Script location: ~/.fraim/scripts/markdown-to-pdf.js
11
+ * - Working directory: Current project directory (process.cwd())
12
+ * - Input/output files: Relative to current project directory
13
+ * - Config: Reads from .fraim/config.json in current project directory
14
+ *
15
+ * Usage:
16
+ * node ~/.fraim/scripts/markdown-to-pdf.js <input.md> [output.pdf] [options]
17
+ *
18
+ * Options:
19
+ * --format <format> Paper format (A4, Letter, Legal, etc.) - default: A4
20
+ * --margin <margin> Page margins in inches - default: 0.5
21
+ * --css <file> Custom CSS file for styling
22
+ * --header <text> Header text
23
+ * --footer <text> Footer text
24
+ * --landscape Use landscape orientation
25
+ * --no-background Disable background graphics
26
+ *
27
+ * Examples:
28
+ * node ~/.fraim/scripts/markdown-to-pdf.js README.md
29
+ * node ~/.fraim/scripts/markdown-to-pdf.js docs/spec.md output/spec.pdf --format Letter
30
+ * node ~/.fraim/scripts/markdown-to-pdf.js report.md --css custom.css --header "Company Report"
31
+ */
32
+
33
+ const fs = require('fs');
34
+ const path = require('path');
35
+
36
+ // Get project directory (where the user is working)
37
+ const PROJECT_DIR = process.cwd();
38
+ const CONFIG_FILE = path.join(PROJECT_DIR, '.fraim', 'config.json');
39
+
40
+ // Load project configuration if available
41
+ function loadProjectConfig() {
42
+ try {
43
+ if (fs.existsSync(CONFIG_FILE)) {
44
+ const config = JSON.parse(fs.readFileSync(CONFIG_FILE, 'utf-8'));
45
+ return config;
46
+ }
47
+ } catch (error) {
48
+ console.warn('Warning: Could not load project config from .fraim/config.json');
49
+ }
50
+ return {};
51
+ }
52
+
53
+ // Check if required dependencies are available
54
+ function checkDependencies() {
55
+ const requiredPackages = ['puppeteer', 'markdown-it', 'markdown-it-highlightjs'];
56
+ const missingPackages = [];
57
+
58
+ for (const pkg of requiredPackages) {
59
+ try {
60
+ require.resolve(pkg);
61
+ } catch (error) {
62
+ missingPackages.push(pkg);
63
+ }
64
+ }
65
+
66
+ if (missingPackages.length > 0) {
67
+ console.error('Missing required packages:', missingPackages.join(', '));
68
+ console.error('Install them with: npm install', missingPackages.join(' '));
69
+ return false;
70
+ }
71
+ return true;
72
+ }
73
+
74
+ // Parse command line arguments
75
+ function parseArgs() {
76
+ const args = process.argv.slice(2);
77
+
78
+ if (args.length === 0 || args.includes('--help') || args.includes('-h')) {
79
+ console.log(`
80
+ Markdown to PDF Converter
81
+
82
+ Usage: node ~/.fraim/scripts/markdown-to-pdf.js <input.md> [output.pdf] [options]
83
+
84
+ Options:
85
+ --format <format> Paper format (A4, Letter, Legal, etc.) - default: A4
86
+ --margin <margin> Page margins in inches - default: 0.5
87
+ --css <file> Custom CSS file for styling
88
+ --header <text> Header text
89
+ --footer <text> Footer text
90
+ --landscape Use landscape orientation
91
+ --no-background Disable background graphics
92
+ --help, -h Show this help message
93
+
94
+ Examples:
95
+ node ~/.fraim/scripts/markdown-to-pdf.js README.md
96
+ node ~/.fraim/scripts/markdown-to-pdf.js docs/spec.md output/spec.pdf --format Letter
97
+ node ~/.fraim/scripts/markdown-to-pdf.js report.md --css custom.css --header "Company Report"
98
+
99
+ Working Directory: ${PROJECT_DIR}
100
+ Config File: ${CONFIG_FILE}
101
+ `);
102
+ process.exit(0);
103
+ }
104
+
105
+ const config = {
106
+ input: args[0],
107
+ output: null,
108
+ format: 'A4',
109
+ margin: '0.5in',
110
+ css: null,
111
+ header: null,
112
+ footer: null,
113
+ landscape: false,
114
+ background: true
115
+ };
116
+
117
+ // Resolve input path relative to project directory
118
+ if (!path.isAbsolute(config.input)) {
119
+ config.input = path.resolve(PROJECT_DIR, config.input);
120
+ }
121
+
122
+ // Check if second argument is output file or option
123
+ if (args[1] && !args[1].startsWith('--')) {
124
+ config.output = args[1];
125
+ }
126
+
127
+ // Parse options
128
+ for (let i = 0; i < args.length; i++) {
129
+ const arg = args[i];
130
+
131
+ if (arg === '--format' && args[i + 1]) {
132
+ config.format = args[i + 1];
133
+ i++;
134
+ } else if (arg === '--margin' && args[i + 1]) {
135
+ config.margin = args[i + 1];
136
+ i++;
137
+ } else if (arg === '--css' && args[i + 1]) {
138
+ config.css = args[i + 1];
139
+ i++;
140
+ } else if (arg === '--header' && args[i + 1]) {
141
+ config.header = args[i + 1];
142
+ i++;
143
+ } else if (arg === '--footer' && args[i + 1]) {
144
+ config.footer = args[i + 1];
145
+ i++;
146
+ } else if (arg === '--landscape') {
147
+ config.landscape = true;
148
+ } else if (arg === '--no-background') {
149
+ config.background = false;
150
+ }
151
+ }
152
+
153
+ // Set default output if not provided
154
+ if (!config.output) {
155
+ const inputPath = path.parse(config.input);
156
+ config.output = path.join(inputPath.dir, inputPath.name + '.pdf');
157
+ }
158
+
159
+ // Resolve output path relative to project directory
160
+ if (!path.isAbsolute(config.output)) {
161
+ config.output = path.resolve(PROJECT_DIR, config.output);
162
+ }
163
+
164
+ // Resolve CSS path relative to project directory if provided
165
+ if (config.css && !path.isAbsolute(config.css)) {
166
+ config.css = path.resolve(PROJECT_DIR, config.css);
167
+ }
168
+
169
+ return config;
170
+ }
171
+
172
+ // Convert markdown to HTML
173
+ async function markdownToHtml(markdownContent, customCss = null) {
174
+ const MarkdownIt = require('markdown-it');
175
+ const hljs = require('markdown-it-highlightjs');
176
+
177
+ const md = new MarkdownIt({
178
+ html: true,
179
+ linkify: true,
180
+ typographer: true,
181
+ breaks: false
182
+ }).use(hljs);
183
+
184
+ const htmlContent = md.render(markdownContent);
185
+
186
+ // Default CSS for better PDF rendering
187
+ const defaultCss = `
188
+ body {
189
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
190
+ line-height: 1.6;
191
+ color: #333;
192
+ max-width: none;
193
+ margin: 0;
194
+ padding: 20px;
195
+ }
196
+
197
+ h1, h2, h3, h4, h5, h6 {
198
+ color: #2c3e50;
199
+ margin-top: 2em;
200
+ margin-bottom: 1em;
201
+ }
202
+
203
+ h1 { font-size: 2.5em; border-bottom: 2px solid #3498db; padding-bottom: 0.3em; }
204
+ h2 { font-size: 2em; border-bottom: 1px solid #bdc3c7; padding-bottom: 0.3em; }
205
+ h3 { font-size: 1.5em; }
206
+
207
+ code {
208
+ background-color: #f8f9fa;
209
+ padding: 2px 4px;
210
+ border-radius: 3px;
211
+ font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
212
+ }
213
+
214
+ pre {
215
+ background-color: #f8f9fa;
216
+ border: 1px solid #e9ecef;
217
+ border-radius: 6px;
218
+ padding: 16px;
219
+ overflow-x: auto;
220
+ margin: 1em 0;
221
+ }
222
+
223
+ pre code {
224
+ background: none;
225
+ padding: 0;
226
+ }
227
+
228
+ table {
229
+ border-collapse: collapse;
230
+ width: 100%;
231
+ margin: 1em 0;
232
+ }
233
+
234
+ th, td {
235
+ border: 1px solid #ddd;
236
+ padding: 12px;
237
+ text-align: left;
238
+ }
239
+
240
+ th {
241
+ background-color: #f2f2f2;
242
+ font-weight: bold;
243
+ }
244
+
245
+ blockquote {
246
+ border-left: 4px solid #3498db;
247
+ margin: 1em 0;
248
+ padding-left: 1em;
249
+ color: #7f8c8d;
250
+ }
251
+
252
+ img {
253
+ max-width: 100%;
254
+ height: auto;
255
+ }
256
+
257
+ a {
258
+ color: #3498db;
259
+ text-decoration: none;
260
+ }
261
+
262
+ a:hover {
263
+ text-decoration: underline;
264
+ }
265
+
266
+ ul, ol {
267
+ padding-left: 2em;
268
+ }
269
+
270
+ li {
271
+ margin: 0.5em 0;
272
+ }
273
+
274
+ @media print {
275
+ body { margin: 0; }
276
+ h1, h2, h3, h4, h5, h6 { page-break-after: avoid; }
277
+ pre, blockquote { page-break-inside: avoid; }
278
+ img { page-break-inside: avoid; }
279
+ }
280
+ `;
281
+
282
+ let customCssContent = '';
283
+ if (customCss && fs.existsSync(customCss)) {
284
+ customCssContent = fs.readFileSync(customCss, 'utf8');
285
+ }
286
+
287
+ return `
288
+ <!DOCTYPE html>
289
+ <html>
290
+ <head>
291
+ <meta charset="utf-8">
292
+ <title>Converted from Markdown</title>
293
+ <style>${defaultCss}${customCssContent}</style>
294
+ </head>
295
+ <body>
296
+ ${htmlContent}
297
+ </body>
298
+ </html>
299
+ `;
300
+ }
301
+
302
+ // Convert HTML to PDF using Puppeteer
303
+ async function htmlToPdf(html, outputPath, config) {
304
+ const puppeteer = require('puppeteer');
305
+
306
+ const browser = await puppeteer.launch({
307
+ headless: 'new',
308
+ args: ['--no-sandbox', '--disable-setuid-sandbox']
309
+ });
310
+
311
+ try {
312
+ const page = await browser.newPage();
313
+ await page.setContent(html, { waitUntil: 'networkidle0' });
314
+
315
+ const pdfOptions = {
316
+ path: outputPath,
317
+ format: config.format,
318
+ margin: {
319
+ top: config.margin,
320
+ right: config.margin,
321
+ bottom: config.margin,
322
+ left: config.margin
323
+ },
324
+ landscape: config.landscape,
325
+ printBackground: config.background,
326
+ preferCSSPageSize: true
327
+ };
328
+
329
+ if (config.header) {
330
+ pdfOptions.displayHeaderFooter = true;
331
+ pdfOptions.headerTemplate = `<div style="font-size: 10px; width: 100%; text-align: center;">${config.header}</div>`;
332
+ }
333
+
334
+ if (config.footer) {
335
+ pdfOptions.displayHeaderFooter = true;
336
+ pdfOptions.footerTemplate = `<div style="font-size: 10px; width: 100%; text-align: center;">${config.footer}</div>`;
337
+ }
338
+
339
+ await page.pdf(pdfOptions);
340
+ console.log(`✅ PDF generated successfully: ${path.relative(PROJECT_DIR, outputPath)}`);
341
+
342
+ } finally {
343
+ await browser.close();
344
+ }
345
+ }
346
+
347
+ // Main function
348
+ async function main() {
349
+ try {
350
+ if (!checkDependencies()) {
351
+ process.exit(1);
352
+ }
353
+
354
+ const config = parseArgs();
355
+ const projectConfig = loadProjectConfig();
356
+
357
+ console.log(`📄 Converting markdown to PDF...`);
358
+ console.log(` Working directory: ${PROJECT_DIR}`);
359
+ console.log(` Input: ${path.relative(PROJECT_DIR, config.input)}`);
360
+ console.log(` Output: ${path.relative(PROJECT_DIR, config.output)}`);
361
+
362
+ // Validate input file
363
+ if (!fs.existsSync(config.input)) {
364
+ console.error(`❌ Input file not found: ${path.relative(PROJECT_DIR, config.input)}`);
365
+ process.exit(1);
366
+ }
367
+
368
+ // Ensure output directory exists
369
+ const outputDir = path.dirname(config.output);
370
+ if (!fs.existsSync(outputDir)) {
371
+ fs.mkdirSync(outputDir, { recursive: true });
372
+ console.log(`📁 Created output directory: ${path.relative(PROJECT_DIR, outputDir)}`);
373
+ }
374
+
375
+ // Read markdown file
376
+ const markdownContent = fs.readFileSync(config.input, 'utf8');
377
+
378
+ // Convert to HTML
379
+ const html = await markdownToHtml(markdownContent, config.css);
380
+
381
+ // Convert to PDF
382
+ await htmlToPdf(html, config.output, config);
383
+
384
+ } catch (error) {
385
+ console.error('❌ Error:', error.message);
386
+ process.exit(1);
387
+ }
388
+ }
389
+
390
+ // Run if called directly
391
+ if (require.main === module) {
392
+ main();
393
+ }
394
+
395
+ module.exports = { markdownToHtml, htmlToPdf, parseArgs, checkDependencies };