ai-pdf-builder 0.2.0 → 0.3.1

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/dist/cli.js ADDED
@@ -0,0 +1,751 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ var __create = Object.create;
4
+ var __defProp = Object.defineProperty;
5
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
+ var __getOwnPropNames = Object.getOwnPropertyNames;
7
+ var __getProtoOf = Object.getPrototypeOf;
8
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
9
+ var __copyProps = (to, from, except, desc) => {
10
+ if (from && typeof from === "object" || typeof from === "function") {
11
+ for (let key of __getOwnPropNames(from))
12
+ if (!__hasOwnProp.call(to, key) && key !== except)
13
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
14
+ }
15
+ return to;
16
+ };
17
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
18
+ // If the importer is in node compatibility mode or this is not an ESM
19
+ // file that has been converted to a CommonJS file using a Babel-
20
+ // compatible transform (i.e. "__esModule" has not been set), then set
21
+ // "default" to the CommonJS "module.exports" for node compatibility.
22
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
23
+ mod
24
+ ));
25
+
26
+ // src/cli.ts
27
+ var fs4 = __toESM(require("fs"));
28
+ var path4 = __toESM(require("path"));
29
+
30
+ // src/generator.ts
31
+ var import_child_process2 = require("child_process");
32
+ var fs3 = __toESM(require("fs"));
33
+ var path3 = __toESM(require("path"));
34
+
35
+ // src/templates.ts
36
+ var fs = __toESM(require("fs"));
37
+ var path = __toESM(require("path"));
38
+ var TEMPLATES_DIR = path.join(__dirname, "..", "templates");
39
+ var BUILT_IN_TEMPLATES = {
40
+ default: {
41
+ name: "default",
42
+ path: path.join(TEMPLATES_DIR, "default.latex"),
43
+ description: "Clean, professional default template with modern styling",
44
+ supportedDocTypes: ["memo", "whitepaper", "report", "proposal", "generic"]
45
+ },
46
+ memo: {
47
+ name: "memo",
48
+ path: path.join(TEMPLATES_DIR, "memo.latex"),
49
+ description: "Business memo template with executive summary styling",
50
+ supportedDocTypes: ["memo", "report", "proposal"]
51
+ },
52
+ agreement: {
53
+ name: "agreement",
54
+ path: path.join(TEMPLATES_DIR, "agreement.latex"),
55
+ description: "Legal agreement template with formal structure",
56
+ supportedDocTypes: ["agreement", "generic"]
57
+ },
58
+ termsheet: {
59
+ name: "termsheet",
60
+ path: path.join(TEMPLATES_DIR, "termsheet.latex"),
61
+ description: "Term sheet template for investment documents",
62
+ supportedDocTypes: ["termsheet", "agreement"]
63
+ }
64
+ };
65
+ var customTemplates = /* @__PURE__ */ new Map();
66
+ var templateCache = /* @__PURE__ */ new Map();
67
+ function getTemplate(name) {
68
+ if (templateCache.has(name)) {
69
+ return templateCache.get(name) || null;
70
+ }
71
+ if (customTemplates.has(name)) {
72
+ const config = customTemplates.get(name);
73
+ return loadTemplateFile(config.path, name);
74
+ }
75
+ if (BUILT_IN_TEMPLATES[name]) {
76
+ return loadTemplateFile(BUILT_IN_TEMPLATES[name].path, name);
77
+ }
78
+ if (fs.existsSync(name)) {
79
+ return loadTemplateFile(name, name);
80
+ }
81
+ return null;
82
+ }
83
+ function getTemplatePath(name) {
84
+ if (customTemplates.has(name)) {
85
+ return customTemplates.get(name).path;
86
+ }
87
+ if (BUILT_IN_TEMPLATES[name]) {
88
+ return BUILT_IN_TEMPLATES[name].path;
89
+ }
90
+ if (fs.existsSync(name)) {
91
+ return name;
92
+ }
93
+ return null;
94
+ }
95
+ function loadTemplateFile(filePath, cacheKey) {
96
+ try {
97
+ if (!fs.existsSync(filePath)) {
98
+ console.warn(`Template file not found: ${filePath}`);
99
+ return null;
100
+ }
101
+ const content = fs.readFileSync(filePath, "utf-8");
102
+ templateCache.set(cacheKey, content);
103
+ return content;
104
+ } catch (error) {
105
+ console.error(`Error loading template ${filePath}:`, error);
106
+ return null;
107
+ }
108
+ }
109
+ function listTemplates() {
110
+ const builtIn = Object.values(BUILT_IN_TEMPLATES);
111
+ const custom = Array.from(customTemplates.values());
112
+ return [...builtIn, ...custom];
113
+ }
114
+
115
+ // src/utils.ts
116
+ var import_child_process = require("child_process");
117
+ var fs2 = __toESM(require("fs"));
118
+ var path2 = __toESM(require("path"));
119
+ var os = __toESM(require("os"));
120
+ function checkPandoc() {
121
+ try {
122
+ const version = (0, import_child_process.execSync)("pandoc --version", { encoding: "utf-8" });
123
+ const versionMatch = version.match(/pandoc\s+([\d.]+)/);
124
+ const pathResult = (0, import_child_process.execSync)("which pandoc", { encoding: "utf-8" }).trim();
125
+ return {
126
+ available: true,
127
+ version: versionMatch ? versionMatch[1] : "unknown",
128
+ path: pathResult
129
+ };
130
+ } catch (error) {
131
+ return {
132
+ available: false,
133
+ error: "Pandoc not found. Install with: brew install pandoc (macOS) or apt-get install pandoc (Linux)"
134
+ };
135
+ }
136
+ }
137
+ function checkLaTeX() {
138
+ try {
139
+ const version = (0, import_child_process.execSync)("pdflatex --version", { encoding: "utf-8" });
140
+ const versionMatch = version.match(/pdfTeX\s+([\d.-]+)/);
141
+ const pathResult = (0, import_child_process.execSync)("which pdflatex", { encoding: "utf-8" }).trim();
142
+ return {
143
+ available: true,
144
+ engine: "pdflatex",
145
+ version: versionMatch ? versionMatch[1] : "unknown",
146
+ path: pathResult
147
+ };
148
+ } catch (error) {
149
+ return {
150
+ available: false,
151
+ error: "pdflatex not found. Install BasicTeX (brew install --cask basictex) or TeX Live"
152
+ };
153
+ }
154
+ }
155
+ function checkSystem() {
156
+ const pandoc = checkPandoc();
157
+ const latex = checkLaTeX();
158
+ const ready = pandoc.available && latex.available;
159
+ let message = "";
160
+ if (ready) {
161
+ message = `Ready: Pandoc ${pandoc.version}, pdfTeX ${latex.version}`;
162
+ } else {
163
+ const missing = [];
164
+ if (!pandoc.available) missing.push("Pandoc");
165
+ if (!latex.available) missing.push("LaTeX/pdflatex");
166
+ message = `Missing dependencies: ${missing.join(", ")}`;
167
+ }
168
+ return { pandoc, latex, ready, message };
169
+ }
170
+ function createTempDir(prefix = "pdf-builder") {
171
+ const tempDir = path2.join(os.tmpdir(), `${prefix}-${Date.now()}`);
172
+ fs2.mkdirSync(tempDir, { recursive: true });
173
+ return tempDir;
174
+ }
175
+ function cleanupTempDir(dirPath) {
176
+ try {
177
+ if (fs2.existsSync(dirPath)) {
178
+ fs2.rmSync(dirPath, { recursive: true, force: true });
179
+ }
180
+ } catch (error) {
181
+ console.warn(`Warning: Could not clean up temp directory: ${dirPath}`);
182
+ }
183
+ }
184
+ function generateYAMLFrontMatter(metadata) {
185
+ const lines = ["---"];
186
+ for (const [key, value] of Object.entries(metadata)) {
187
+ if (value !== void 0 && value !== "") {
188
+ const escapedValue = String(value).replace(/"/g, '\\"');
189
+ lines.push(`${key}: "${escapedValue}"`);
190
+ }
191
+ }
192
+ lines.push("---");
193
+ return lines.join("\n");
194
+ }
195
+ function sanitizeContent(content) {
196
+ const dangerousCommands = [
197
+ "\\input",
198
+ "\\include",
199
+ "\\write18",
200
+ "\\immediate",
201
+ "\\openin",
202
+ "\\openout",
203
+ "\\read",
204
+ "\\write",
205
+ "\\newwrite",
206
+ "\\closeout",
207
+ "\\closein"
208
+ ];
209
+ let sanitized = content;
210
+ for (const cmd of dangerousCommands) {
211
+ const regex = new RegExp(cmd.replace("\\", "\\\\") + "[^\\s{]*({[^}]*})?", "gi");
212
+ sanitized = sanitized.replace(regex, "");
213
+ }
214
+ return sanitized;
215
+ }
216
+ function parseColor(color) {
217
+ if (/^\d+,\s*\d+,\s*\d+$/.test(color)) {
218
+ return color.replace(/\s/g, "");
219
+ }
220
+ const rgbMatch = color.match(/RGB\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)/i);
221
+ if (rgbMatch) {
222
+ return `${rgbMatch[1]},${rgbMatch[2]},${rgbMatch[3]}`;
223
+ }
224
+ const hexMatch = color.match(/^#?([A-Fa-f0-9]{2})([A-Fa-f0-9]{2})([A-Fa-f0-9]{2})$/);
225
+ if (hexMatch) {
226
+ const r = parseInt(hexMatch[1], 16);
227
+ const g = parseInt(hexMatch[2], 16);
228
+ const b = parseInt(hexMatch[3], 16);
229
+ return `${r},${g},${b}`;
230
+ }
231
+ return "59,130,246";
232
+ }
233
+
234
+ // src/generator.ts
235
+ var DEFAULT_OPTIONS = {
236
+ toc: true,
237
+ tocDepth: 2,
238
+ numberSections: true,
239
+ fontSize: 11,
240
+ margin: "1in",
241
+ paperSize: "letter",
242
+ timeout: 6e4
243
+ };
244
+ async function generatePDF(options) {
245
+ const startTime = Date.now();
246
+ const opts = { ...DEFAULT_OPTIONS, ...options };
247
+ const sysCheck = checkSystem();
248
+ if (!sysCheck.ready) {
249
+ return {
250
+ success: false,
251
+ error: `System requirements not met: ${sysCheck.message}`
252
+ };
253
+ }
254
+ if (!opts.content || opts.content.trim().length === 0) {
255
+ return {
256
+ success: false,
257
+ error: "Content is required"
258
+ };
259
+ }
260
+ let tempDir = null;
261
+ try {
262
+ tempDir = opts.workDir || createTempDir("pdf-builder");
263
+ const sanitizedContent = sanitizeContent(opts.content);
264
+ const fullMarkdown = buildMarkdownDocument(sanitizedContent, opts);
265
+ const inputPath = path3.join(tempDir, "input.md");
266
+ fs3.writeFileSync(inputPath, fullMarkdown, "utf-8");
267
+ const templatePath = await prepareTemplate(tempDir, opts);
268
+ const outputPath = opts.outputPath || path3.join(tempDir, "output.pdf");
269
+ const pandocOpts = {
270
+ inputPath,
271
+ outputPath,
272
+ templatePath,
273
+ toc: opts.toc ?? true,
274
+ tocDepth: opts.tocDepth ?? 2,
275
+ numberSections: opts.numberSections ?? true,
276
+ fontSize: opts.fontSize ?? 11,
277
+ margin: opts.margin ?? "1in",
278
+ paperSize: opts.paperSize ?? "letter",
279
+ timeout: opts.timeout ?? 6e4
280
+ };
281
+ await executePandoc(pandocOpts);
282
+ if (!fs3.existsSync(outputPath)) {
283
+ return {
284
+ success: false,
285
+ error: "PDF generation failed - output file not created"
286
+ };
287
+ }
288
+ const buffer = fs3.readFileSync(outputPath);
289
+ const stats = fs3.statSync(outputPath);
290
+ const generationTime = Date.now() - startTime;
291
+ const result = {
292
+ success: true,
293
+ buffer: opts.outputPath ? void 0 : buffer,
294
+ path: opts.outputPath || void 0,
295
+ fileSize: stats.size,
296
+ generationTime
297
+ };
298
+ result.pageCount = estimatePageCount(buffer);
299
+ return result;
300
+ } catch (error) {
301
+ const errorMessage = error instanceof Error ? error.message : String(error);
302
+ return {
303
+ success: false,
304
+ error: `PDF generation failed: ${errorMessage}`,
305
+ generationTime: Date.now() - startTime
306
+ };
307
+ } finally {
308
+ if (tempDir && !opts.workDir && !opts.outputPath) {
309
+ } else if (tempDir && !opts.workDir) {
310
+ cleanupTempDir(tempDir);
311
+ }
312
+ }
313
+ }
314
+ function buildMarkdownDocument(content, opts) {
315
+ const parts = [];
316
+ if (opts.metadata) {
317
+ const frontMatter = generateYAMLFrontMatter({
318
+ ...opts.metadata,
319
+ geometry: `margin=${opts.margin || "1in"}`,
320
+ fontsize: `${opts.fontSize || 11}pt`,
321
+ papersize: opts.paperSize || "letter"
322
+ });
323
+ parts.push(frontMatter);
324
+ parts.push("");
325
+ }
326
+ parts.push(content);
327
+ return parts.join("\n");
328
+ }
329
+ async function prepareTemplate(tempDir, opts) {
330
+ const templateName = opts.template || "default";
331
+ let templateContent = getTemplate(templateName);
332
+ if (!templateContent) {
333
+ const templatePath = getTemplatePath(templateName);
334
+ if (templatePath && fs3.existsSync(templatePath)) {
335
+ templateContent = fs3.readFileSync(templatePath, "utf-8");
336
+ } else {
337
+ templateContent = getTemplate("default");
338
+ }
339
+ }
340
+ if (!templateContent) {
341
+ throw new Error(`Template not found: ${templateName}`);
342
+ }
343
+ if (opts.customColors) {
344
+ templateContent = applyColorCustomizations(templateContent, opts.customColors);
345
+ }
346
+ const customTemplatePath = path3.join(tempDir, "template.latex");
347
+ fs3.writeFileSync(customTemplatePath, templateContent, "utf-8");
348
+ return customTemplatePath;
349
+ }
350
+ function applyColorCustomizations(template, colors) {
351
+ let result = template;
352
+ if (colors.primary) {
353
+ const rgb = parseColor(colors.primary);
354
+ result = result.replace(
355
+ /\\definecolor{(?:zeroBlue|primaryColor)}{RGB}{[^}]+}/g,
356
+ `\\definecolor{primaryColor}{RGB}{${rgb}}`
357
+ );
358
+ }
359
+ if (colors.secondary) {
360
+ const rgb = parseColor(colors.secondary);
361
+ result = result.replace(
362
+ /\\definecolor{(?:zeroGray|secondaryColor)}{RGB}{[^}]+}/g,
363
+ `\\definecolor{secondaryColor}{RGB}{${rgb}}`
364
+ );
365
+ }
366
+ if (colors.accent) {
367
+ const rgb = parseColor(colors.accent);
368
+ result = result.replace(
369
+ /\\definecolor{(?:zeroDark|accentColor)}{RGB}{[^}]+}/g,
370
+ `\\definecolor{accentColor}{RGB}{${rgb}}`
371
+ );
372
+ }
373
+ return result;
374
+ }
375
+ function executePandoc(opts) {
376
+ return new Promise((resolve3, reject) => {
377
+ const args = [
378
+ opts.inputPath,
379
+ "-o",
380
+ opts.outputPath,
381
+ "--pdf-engine=pdflatex",
382
+ `-V`,
383
+ `geometry:margin=${opts.margin}`,
384
+ `-V`,
385
+ `fontsize=${opts.fontSize}pt`,
386
+ `-V`,
387
+ `papersize=${opts.paperSize}`,
388
+ `-V`,
389
+ `documentclass=article`,
390
+ `-V`,
391
+ `colorlinks=true`,
392
+ `-V`,
393
+ `linkcolor=blue`,
394
+ `-V`,
395
+ `urlcolor=blue`
396
+ ];
397
+ if (opts.templatePath) {
398
+ args.push(`--template=${opts.templatePath}`);
399
+ }
400
+ if (opts.toc) {
401
+ args.push("--toc");
402
+ args.push(`--toc-depth=${opts.tocDepth}`);
403
+ }
404
+ if (opts.numberSections) {
405
+ args.push("--number-sections");
406
+ }
407
+ const pandoc = (0, import_child_process2.spawn)("pandoc", args, {
408
+ stdio: ["pipe", "pipe", "pipe"]
409
+ });
410
+ let stderr = "";
411
+ pandoc.stderr.on("data", (data) => {
412
+ stderr += data.toString();
413
+ });
414
+ const timeout = setTimeout(() => {
415
+ pandoc.kill("SIGKILL");
416
+ reject(new Error(`Pandoc timed out after ${opts.timeout}ms`));
417
+ }, opts.timeout);
418
+ pandoc.on("close", (code) => {
419
+ clearTimeout(timeout);
420
+ if (code === 0) {
421
+ resolve3();
422
+ } else {
423
+ reject(new Error(`Pandoc failed with code ${code}: ${stderr}`));
424
+ }
425
+ });
426
+ pandoc.on("error", (error) => {
427
+ clearTimeout(timeout);
428
+ reject(new Error(`Failed to execute Pandoc: ${error.message}`));
429
+ });
430
+ });
431
+ }
432
+ function estimatePageCount(buffer) {
433
+ const content = buffer.toString("binary");
434
+ const matches = content.match(/\/Type\s*\/Page[^s]/g);
435
+ return matches ? matches.length : 1;
436
+ }
437
+
438
+ // src/presets.ts
439
+ async function generateMemo(content, metadata, options) {
440
+ return generatePDF({
441
+ content,
442
+ metadata,
443
+ template: "memo",
444
+ toc: false,
445
+ numberSections: true,
446
+ fontSize: 11,
447
+ margin: "1in",
448
+ ...options
449
+ });
450
+ }
451
+ async function generateAgreement(content, metadata, options) {
452
+ return generatePDF({
453
+ content,
454
+ metadata,
455
+ template: "agreement",
456
+ toc: false,
457
+ numberSections: true,
458
+ fontSize: 10,
459
+ margin: "1in",
460
+ ...options
461
+ });
462
+ }
463
+ async function generateTermsheet(content, metadata, options) {
464
+ return generatePDF({
465
+ content,
466
+ metadata,
467
+ template: "termsheet",
468
+ toc: false,
469
+ numberSections: true,
470
+ fontSize: 10,
471
+ margin: "1in",
472
+ ...options
473
+ });
474
+ }
475
+ async function generateWhitepaper(content, metadata, options) {
476
+ return generatePDF({
477
+ content,
478
+ metadata,
479
+ template: "default",
480
+ toc: true,
481
+ tocDepth: 2,
482
+ numberSections: true,
483
+ fontSize: 11,
484
+ margin: "1in",
485
+ ...options
486
+ });
487
+ }
488
+ async function generateReport(content, metadata, options) {
489
+ return generatePDF({
490
+ content,
491
+ metadata,
492
+ template: "default",
493
+ toc: true,
494
+ tocDepth: 2,
495
+ numberSections: true,
496
+ fontSize: 11,
497
+ margin: "1in",
498
+ ...options
499
+ });
500
+ }
501
+ async function generateProposal(content, metadata, options) {
502
+ return generatePDF({
503
+ content,
504
+ metadata,
505
+ template: "default",
506
+ toc: false,
507
+ numberSections: true,
508
+ fontSize: 11,
509
+ margin: "1in",
510
+ ...options
511
+ });
512
+ }
513
+ async function generateSAFE(content, metadata, options) {
514
+ return generatePDF({
515
+ content,
516
+ metadata,
517
+ template: "agreement",
518
+ toc: false,
519
+ numberSections: true,
520
+ fontSize: 10,
521
+ margin: "1in",
522
+ ...options
523
+ });
524
+ }
525
+ async function generateNDA(content, metadata, options) {
526
+ return generatePDF({
527
+ content,
528
+ metadata,
529
+ template: "agreement",
530
+ toc: false,
531
+ numberSections: true,
532
+ fontSize: 10,
533
+ margin: "1in",
534
+ ...options
535
+ });
536
+ }
537
+
538
+ // src/cli.ts
539
+ var VERSION = "0.3.1";
540
+ var HELP = `
541
+ ai-pdf-builder v${VERSION}
542
+ Generate professional PDFs from Markdown
543
+
544
+ USAGE:
545
+ ai-pdf-builder <command> [options]
546
+
547
+ COMMANDS:
548
+ generate <type> <input> Generate a PDF from markdown file
549
+ check Check system requirements
550
+ templates List available templates
551
+ help Show this help message
552
+
553
+ DOCUMENT TYPES:
554
+ whitepaper Technical documentation, litepapers
555
+ memo Executive summaries, internal memos
556
+ agreement Legal contracts, general agreements
557
+ termsheet Investment term sheets
558
+ safe SAFE agreements
559
+ nda Non-disclosure agreements
560
+ report Business reports, analysis
561
+ proposal Business proposals, pitches
562
+
563
+ OPTIONS:
564
+ -o, --output <file> Output file path (default: output.pdf)
565
+ -t, --title <title> Document title
566
+ -s, --subtitle <text> Document subtitle
567
+ -a, --author <name> Author name
568
+ -d, --date <date> Document date
569
+ -v, --version <ver> Document version
570
+ --no-toc Disable table of contents
571
+ --color-primary <hex> Primary brand color
572
+ --color-secondary <hex> Secondary brand color
573
+
574
+ EXAMPLES:
575
+ # Generate a whitepaper
576
+ ai-pdf-builder generate whitepaper ./content.md -o whitepaper.pdf -t "My Project"
577
+
578
+ # Generate a term sheet
579
+ ai-pdf-builder generate termsheet ./terms.md -t "Series Seed" -s "Acme Inc."
580
+
581
+ # Generate a SAFE
582
+ ai-pdf-builder generate safe ./safe.md -o safe-agreement.pdf
583
+
584
+ # Check if Pandoc/LaTeX are installed
585
+ ai-pdf-builder check
586
+
587
+ # List available templates
588
+ ai-pdf-builder templates
589
+ `;
590
+ function parseArgs(args) {
591
+ const options = {
592
+ output: "output.pdf",
593
+ toc: true
594
+ };
595
+ let command = "help";
596
+ let type;
597
+ let input;
598
+ let i = 0;
599
+ while (i < args.length) {
600
+ const arg = args[i];
601
+ if (arg === "generate" || arg === "check" || arg === "templates" || arg === "help") {
602
+ command = arg;
603
+ if (arg === "generate") {
604
+ type = args[++i];
605
+ input = args[++i];
606
+ }
607
+ } else if (arg === "-o" || arg === "--output") {
608
+ options.output = args[++i];
609
+ } else if (arg === "-t" || arg === "--title") {
610
+ options.title = args[++i];
611
+ } else if (arg === "-s" || arg === "--subtitle") {
612
+ options.subtitle = args[++i];
613
+ } else if (arg === "-a" || arg === "--author") {
614
+ options.author = args[++i];
615
+ } else if (arg === "-d" || arg === "--date") {
616
+ options.date = args[++i];
617
+ } else if (arg === "-v" || arg === "--version") {
618
+ options.version = args[++i];
619
+ } else if (arg === "--no-toc") {
620
+ options.toc = false;
621
+ } else if (arg === "--color-primary") {
622
+ options.colorPrimary = args[++i];
623
+ } else if (arg === "--color-secondary") {
624
+ options.colorSecondary = args[++i];
625
+ } else if (arg === "--help" || arg === "-h") {
626
+ command = "help";
627
+ }
628
+ i++;
629
+ }
630
+ return { command, type, input, options };
631
+ }
632
+ async function runGenerate(type, input, options) {
633
+ if (!fs4.existsSync(input)) {
634
+ console.error(`Error: Input file not found: ${input}`);
635
+ process.exit(1);
636
+ }
637
+ const content = fs4.readFileSync(input, "utf-8");
638
+ const metadata = {
639
+ title: options.title || path4.basename(input, path4.extname(input)),
640
+ subtitle: options.subtitle || "",
641
+ author: options.author || "",
642
+ date: options.date || (/* @__PURE__ */ new Date()).toLocaleDateString("en-US", { month: "long", year: "numeric" }),
643
+ version: options.version || ""
644
+ };
645
+ const pdfOptions = {
646
+ outputPath: path4.resolve(options.output),
647
+ toc: options.toc,
648
+ customColors: options.colorPrimary || options.colorSecondary ? {
649
+ primary: options.colorPrimary,
650
+ secondary: options.colorSecondary
651
+ } : void 0
652
+ };
653
+ console.log(`Generating ${type}...`);
654
+ let result;
655
+ switch (type) {
656
+ case "whitepaper":
657
+ result = await generateWhitepaper(content, metadata, pdfOptions);
658
+ break;
659
+ case "memo":
660
+ result = await generateMemo(content, metadata, pdfOptions);
661
+ break;
662
+ case "agreement":
663
+ result = await generateAgreement(content, metadata, pdfOptions);
664
+ break;
665
+ case "termsheet":
666
+ result = await generateTermsheet(content, metadata, pdfOptions);
667
+ break;
668
+ case "safe":
669
+ result = await generateSAFE(content, metadata, pdfOptions);
670
+ break;
671
+ case "nda":
672
+ result = await generateNDA(content, metadata, pdfOptions);
673
+ break;
674
+ case "report":
675
+ result = await generateReport(content, metadata, pdfOptions);
676
+ break;
677
+ case "proposal":
678
+ result = await generateProposal(content, metadata, pdfOptions);
679
+ break;
680
+ default:
681
+ console.error(`Unknown document type: ${type}`);
682
+ console.log("Valid types: whitepaper, memo, agreement, termsheet, safe, nda, report, proposal");
683
+ process.exit(1);
684
+ }
685
+ if (result.success) {
686
+ console.log(`\u2713 Generated: ${options.output}`);
687
+ console.log(` Size: ${(result.fileSize / 1024).toFixed(1)} KB`);
688
+ console.log(` Time: ${result.generationTime}ms`);
689
+ if (result.pageCount) {
690
+ console.log(` Pages: ${result.pageCount}`);
691
+ }
692
+ } else {
693
+ console.error(`\u2717 Error: ${result.error}`);
694
+ process.exit(1);
695
+ }
696
+ }
697
+ function runCheck() {
698
+ console.log("Checking system requirements...\n");
699
+ const check = checkSystem();
700
+ if (check.ready) {
701
+ console.log("\u2713 System is ready for PDF generation\n");
702
+ console.log(` Pandoc: ${check.pandoc?.version || "installed"}`);
703
+ console.log(` LaTeX: ${check.latex?.version || "installed"}`);
704
+ } else {
705
+ console.log("\u2717 System requirements not met\n");
706
+ console.log(` ${check.message}
707
+ `);
708
+ console.log("Install instructions:");
709
+ console.log(" macOS: brew install pandoc && brew install --cask basictex");
710
+ console.log(" Linux: sudo apt-get install pandoc texlive-full");
711
+ process.exit(1);
712
+ }
713
+ }
714
+ function runTemplates() {
715
+ console.log("Available templates:\n");
716
+ const templates = listTemplates();
717
+ templates.forEach((t) => {
718
+ console.log(` ${t.name.padEnd(15)} - ${t.description || "No description"}`);
719
+ });
720
+ }
721
+ async function main() {
722
+ const args = process.argv.slice(2);
723
+ if (args.length === 0) {
724
+ console.log(HELP);
725
+ return;
726
+ }
727
+ const { command, type, input, options } = parseArgs(args);
728
+ switch (command) {
729
+ case "generate":
730
+ if (!type || !input) {
731
+ console.error("Error: generate requires <type> and <input> arguments");
732
+ console.log("Usage: ai-pdf-builder generate <type> <input> [options]");
733
+ process.exit(1);
734
+ }
735
+ await runGenerate(type, input, options);
736
+ break;
737
+ case "check":
738
+ runCheck();
739
+ break;
740
+ case "templates":
741
+ runTemplates();
742
+ break;
743
+ case "help":
744
+ default:
745
+ console.log(HELP);
746
+ }
747
+ }
748
+ main().catch((err) => {
749
+ console.error("Error:", err.message);
750
+ process.exit(1);
751
+ });