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.
- package/bin/fraim.js +52 -5
- package/dist/registry/scripts/cleanup-branch.js +62 -33
- package/dist/registry/scripts/generate-engagement-emails.js +119 -44
- package/dist/registry/scripts/newsletter-helpers.js +208 -268
- package/dist/registry/scripts/profile-server.js +387 -0
- package/dist/tests/test-chalk-regression.js +18 -2
- package/dist/tests/test-client-scripts-validation.js +133 -0
- package/dist/tests/test-markdown-to-pdf.js +454 -0
- package/dist/tests/test-script-location-independence.js +76 -28
- package/package.json +5 -2
- package/registry/agent-guardrails.md +62 -62
- package/registry/rules/communication.md +121 -121
- package/registry/rules/continuous-learning.md +54 -54
- package/registry/rules/hitl-ppe-record-analysis.md +302 -302
- package/registry/rules/software-development-lifecycle.md +104 -104
- package/registry/scripts/cleanup-branch.ts +341 -0
- package/registry/scripts/code-quality-check.sh +559 -559
- package/registry/scripts/detect-tautological-tests.sh +38 -38
- package/registry/scripts/generate-engagement-emails.ts +830 -0
- package/registry/scripts/markdown-to-pdf.js +395 -0
- package/registry/scripts/newsletter-helpers.ts +777 -0
- package/registry/scripts/profile-server.ts +424 -0
- package/registry/scripts/run-thank-you-workflow.ts +122 -0
- package/registry/scripts/send-newsletter-simple.ts +102 -0
- package/registry/scripts/send-thank-you-emails.ts +57 -0
- package/registry/scripts/validate-openapi-limits.ts +366 -366
- package/registry/scripts/validate-test-coverage.ts +280 -280
- package/registry/scripts/verify-pr-comments.sh +70 -70
- package/registry/templates/bootstrap/ARCHITECTURE-TEMPLATE.md +53 -53
- package/registry/templates/evidence/Implementation-BugEvidence.md +85 -85
- package/registry/templates/evidence/Implementation-FeatureEvidence.md +120 -120
- package/registry/workflows/convert-to-pdf.md +235 -0
- package/registry/workflows/customer-development/insight-analysis.md +156 -156
- package/registry/workflows/customer-development/interview-preparation.md +421 -421
- package/registry/workflows/customer-development/strategic-brainstorming.md +146 -146
- package/registry/workflows/quality-assurance/iterative-improvement-cycle.md +562 -562
- package/registry/workflows/reviewer/review-implementation-vs-feature-spec.md +669 -669
- package/dist/registry/scripts/build-scripts-generator.js +0 -205
- package/dist/registry/scripts/fraim-config.js +0 -61
- package/dist/registry/scripts/generic-issues-api.js +0 -100
- package/dist/registry/scripts/openapi-generator.js +0 -664
- package/dist/registry/scripts/performance/profile-server.js +0 -390
- package/dist/test-utils.js +0 -96
- package/dist/tests/esm-compat.js +0 -11
- package/dist/tests/test-chalk-esm-issue.js +0 -159
- package/dist/tests/test-chalk-real-world.js +0 -265
- package/dist/tests/test-chalk-resolution-issue.js +0 -304
- package/dist/tests/test-fraim-install-chalk-issue.js +0 -254
- 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 };
|