apero-kit-cli 1.4.2 → 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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "apero-kit-cli",
3
- "version": "1.4.2",
3
+ "version": "1.5.0",
4
4
  "description": "CLI tool to scaffold AI agent projects with pre-configured kits (Claude, OpenCode, Codex)",
5
5
  "type": "module",
6
6
  "bin": {
@@ -33,44 +33,13 @@ if (!fs.existsSync(fullPlanPath)) {
33
33
  process.exit(1);
34
34
  }
35
35
 
36
- // Simple markdown to HTML converter
37
- function markdownToHtml(md) {
38
- return md
39
- // Code blocks with language
40
- .replace(/```(\w+)?\n([\s\S]*?)```/g, (_, lang, code) => {
41
- const langClass = lang ? `language-${lang}` : '';
42
- return `<pre class="${langClass}"><code>${escapeHtml(code.trim())}</code></pre>`;
43
- })
44
- // Inline code
45
- .replace(/`([^`]+)`/g, '<code class="inline">$1</code>')
46
- // Headers
47
- .replace(/^### (.*$)/gm, '<h3>$1</h3>')
48
- .replace(/^## (.*$)/gm, '<h2>$1</h2>')
49
- .replace(/^# (.*$)/gm, '<h1>$1</h1>')
50
- // Bold and italic
51
- .replace(/\*\*([^*]+)\*\*/g, '<strong>$1</strong>')
52
- .replace(/\*([^*]+)\*/g, '<em>$1</em>')
53
- // Links
54
- .replace(/\[([^\]]+)\]\(([^)]+)\)/g, '<a href="$2" target="_blank">$1</a>')
55
- // Lists
56
- .replace(/^\s*[-*] (.*$)/gm, '<li>$1</li>')
57
- .replace(/(<li>.*<\/li>)\n(?!<li>)/g, '$1</ul>\n')
58
- .replace(/(?<!<\/li>\n)(<li>)/g, '<ul>$1')
59
- // Numbered lists
60
- .replace(/^\s*\d+\. (.*$)/gm, '<li class="numbered">$1</li>')
61
- // Blockquotes
62
- .replace(/^> (.*$)/gm, '<blockquote>$1</blockquote>')
63
- // Horizontal rules
64
- .replace(/^---+$/gm, '<hr>')
65
- // Paragraphs
66
- .replace(/\n\n/g, '</p><p>')
67
- // Tables (simple)
68
- .replace(/\|(.+)\|/g, (match) => {
69
- const cells = match.split('|').filter(c => c.trim());
70
- if (cells.every(c => /^[-:]+$/.test(c.trim()))) return ''; // Skip separator
71
- const tag = 'td';
72
- return '<tr>' + cells.map(c => `<${tag}>${c.trim()}</${tag}>`).join('') + '</tr>';
73
- });
36
+ // Markdown to HTML - Uses marked.js on client-side for full GFM support
37
+ // Server just passes raw markdown, client renders with marked.js
38
+ function markdownToHtml(md, forEditor = false) {
39
+ // For editor preview pane, we'll render client-side with marked.js
40
+ // For static preview, we embed raw markdown and render on page load
41
+ const encodedMd = encodeURIComponent(md);
42
+ return `<div class="markdown-body" data-markdown="${encodedMd}"><noscript>${escapeHtml(md)}</noscript><div class="loading">Loading...</div></div>`;
74
43
  }
75
44
 
76
45
  function escapeHtml(text) {
@@ -134,6 +103,22 @@ function generatePage(files, currentFile, mode = 'preview') {
134
103
  <meta charset="UTF-8">
135
104
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
136
105
  <title>Plan Preview - ${path.basename(fullPlanPath)}</title>
106
+
107
+ <!-- marked.js - Markdown Parser -->
108
+ <script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
109
+
110
+ <!-- Prism.js - Syntax Highlighting -->
111
+ <link href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/themes/prism-tomorrow.min.css" rel="stylesheet" />
112
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/prism.min.js"></script>
113
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/components/prism-javascript.min.js"></script>
114
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/components/prism-typescript.min.js"></script>
115
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/components/prism-python.min.js"></script>
116
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/components/prism-bash.min.js"></script>
117
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/components/prism-json.min.js"></script>
118
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/components/prism-yaml.min.js"></script>
119
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/components/prism-css.min.js"></script>
120
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/components/prism-markdown.min.js"></script>
121
+
137
122
  <style>
138
123
  :root {
139
124
  --bg: #0d1117;
@@ -467,6 +452,171 @@ function generatePage(files, currentFile, mode = 'preview') {
467
452
  justify-content: space-between;
468
453
  }
469
454
 
455
+ /* GitHub-style Markdown Body */
456
+ .markdown-body {
457
+ line-height: 1.7;
458
+ }
459
+
460
+ .markdown-body .loading {
461
+ color: var(--text-muted);
462
+ font-style: italic;
463
+ }
464
+
465
+ .markdown-body h1 {
466
+ font-size: 2em;
467
+ border-bottom: 1px solid var(--border);
468
+ padding-bottom: 0.3em;
469
+ margin: 24px 0 16px;
470
+ }
471
+
472
+ .markdown-body h2 {
473
+ font-size: 1.5em;
474
+ border-bottom: 1px solid var(--border);
475
+ padding-bottom: 0.3em;
476
+ margin: 24px 0 16px;
477
+ }
478
+
479
+ .markdown-body h3 {
480
+ font-size: 1.25em;
481
+ margin: 24px 0 16px;
482
+ }
483
+
484
+ .markdown-body h4 {
485
+ font-size: 1em;
486
+ margin: 24px 0 16px;
487
+ }
488
+
489
+ .markdown-body p {
490
+ margin: 0 0 16px;
491
+ }
492
+
493
+ .markdown-body ul, .markdown-body ol {
494
+ margin: 0 0 16px;
495
+ padding-left: 2em;
496
+ }
497
+
498
+ .markdown-body li {
499
+ margin: 4px 0;
500
+ }
501
+
502
+ .markdown-body li > p {
503
+ margin: 0;
504
+ }
505
+
506
+ /* Task Lists */
507
+ .markdown-body input[type="checkbox"] {
508
+ margin-right: 8px;
509
+ vertical-align: middle;
510
+ }
511
+
512
+ .markdown-body li.task-list-item {
513
+ list-style: none;
514
+ margin-left: -1.5em;
515
+ }
516
+
517
+ /* Tables - GitHub style */
518
+ .markdown-body table {
519
+ width: 100%;
520
+ border-collapse: collapse;
521
+ margin: 16px 0;
522
+ display: block;
523
+ overflow-x: auto;
524
+ }
525
+
526
+ .markdown-body thead {
527
+ background: var(--bg-tertiary);
528
+ }
529
+
530
+ .markdown-body th, .markdown-body td {
531
+ padding: 12px 16px;
532
+ border: 1px solid var(--border);
533
+ text-align: left;
534
+ }
535
+
536
+ .markdown-body th {
537
+ font-weight: 600;
538
+ }
539
+
540
+ .markdown-body tbody tr:nth-child(even) {
541
+ background: var(--bg-secondary);
542
+ }
543
+
544
+ .markdown-body tbody tr:hover {
545
+ background: rgba(88, 166, 255, 0.05);
546
+ }
547
+
548
+ /* Code blocks with Prism */
549
+ .markdown-body pre {
550
+ background: var(--code-bg);
551
+ border: 1px solid var(--border);
552
+ border-radius: 8px;
553
+ padding: 16px;
554
+ overflow-x: auto;
555
+ margin: 16px 0;
556
+ }
557
+
558
+ .markdown-body pre code {
559
+ background: none;
560
+ padding: 0;
561
+ border-radius: 0;
562
+ font-size: 13px;
563
+ color: var(--text);
564
+ }
565
+
566
+ .markdown-body code {
567
+ font-family: 'SF Mono', Monaco, 'Cascadia Code', monospace;
568
+ font-size: 85%;
569
+ background: var(--code-bg);
570
+ padding: 0.2em 0.4em;
571
+ border-radius: 4px;
572
+ color: var(--accent);
573
+ }
574
+
575
+ /* Blockquotes */
576
+ .markdown-body blockquote {
577
+ border-left: 4px solid var(--accent);
578
+ padding: 0 16px;
579
+ margin: 16px 0;
580
+ color: var(--text-muted);
581
+ }
582
+
583
+ .markdown-body blockquote > :first-child {
584
+ margin-top: 0;
585
+ }
586
+
587
+ .markdown-body blockquote > :last-child {
588
+ margin-bottom: 0;
589
+ }
590
+
591
+ /* Images */
592
+ .markdown-body img {
593
+ max-width: 100%;
594
+ height: auto;
595
+ border-radius: 8px;
596
+ }
597
+
598
+ /* Horizontal Rule */
599
+ .markdown-body hr {
600
+ border: none;
601
+ border-top: 2px solid var(--border);
602
+ margin: 32px 0;
603
+ }
604
+
605
+ /* Links */
606
+ .markdown-body a {
607
+ color: var(--accent);
608
+ text-decoration: none;
609
+ }
610
+
611
+ .markdown-body a:hover {
612
+ text-decoration: underline;
613
+ }
614
+
615
+ /* Strikethrough */
616
+ .markdown-body del {
617
+ color: var(--text-muted);
618
+ }
619
+
470
620
  /* Responsive */
471
621
  @media (max-width: 768px) {
472
622
  .sidebar { width: 100%; height: auto; position: relative; }
@@ -476,6 +626,16 @@ function generatePage(files, currentFile, mode = 'preview') {
476
626
  .editor { min-height: 300px; }
477
627
  .preview-pane { min-height: 300px; }
478
628
  }
629
+
630
+ /* Print styles */
631
+ @media print {
632
+ .sidebar, .toolbar, .mode-toggle, .footer { display: none !important; }
633
+ .main { margin-left: 0; padding: 20px; }
634
+ body { background: white; color: black; }
635
+ .markdown-body pre { border: 1px solid #ddd; background: #f5f5f5; }
636
+ .markdown-body code { background: #f5f5f5; color: #333; }
637
+ .markdown-body a { color: #0366d6; }
638
+ }
479
639
  </style>
480
640
  </head>
481
641
  <body>
@@ -535,6 +695,36 @@ function generatePage(files, currentFile, mode = 'preview') {
535
695
 
536
696
  originalContent = editor.value;
537
697
 
698
+ // Configure marked.js for GitHub Flavored Markdown
699
+ marked.setOptions({
700
+ gfm: true,
701
+ breaks: true,
702
+ headerIds: true,
703
+ mangle: false
704
+ });
705
+
706
+ // Custom renderer for syntax highlighting with Prism
707
+ const renderer = new marked.Renderer();
708
+ renderer.code = function(code, language) {
709
+ const lang = language || '';
710
+ const validLang = Prism.languages[lang] ? lang : 'plaintext';
711
+ let highlighted;
712
+ try {
713
+ highlighted = Prism.languages[validLang]
714
+ ? Prism.highlight(code, Prism.languages[validLang], validLang)
715
+ : code;
716
+ } catch (e) {
717
+ highlighted = code;
718
+ }
719
+ return '<pre class="language-' + validLang + '"><code class="language-' + validLang + '">' + highlighted + '</code></pre>';
720
+ };
721
+ marked.setOptions({ renderer });
722
+
723
+ // Render markdown with marked.js
724
+ function renderMarkdown(md) {
725
+ return marked.parse(md);
726
+ }
727
+
538
728
  // Live preview update
539
729
  let updateTimeout;
540
730
  editor.addEventListener('input', () => {
@@ -543,26 +733,15 @@ function generatePage(files, currentFile, mode = 'preview') {
543
733
 
544
734
  clearTimeout(updateTimeout);
545
735
  updateTimeout = setTimeout(() => {
546
- preview.innerHTML = simpleMarkdown(editor.value);
547
- }, 300);
736
+ preview.innerHTML = renderMarkdown(editor.value);
737
+ // Re-run Prism highlighting for any code blocks
738
+ Prism.highlightAllUnder(preview);
739
+ }, 150);
548
740
  });
549
741
 
550
- // Simple client-side markdown (for live preview)
551
- function simpleMarkdown(md) {
552
- return md
553
- .replace(/\`\`\`(\\w+)?\\n([\\s\\S]*?)\`\`\`/g, '<pre><code>$2</code></pre>')
554
- .replace(/\`([^\`]+)\`/g, '<code class="inline">$1</code>')
555
- .replace(/^### (.*$)/gm, '<h3>$1</h3>')
556
- .replace(/^## (.*$)/gm, '<h2>$1</h2>')
557
- .replace(/^# (.*$)/gm, '<h1>$1</h1>')
558
- .replace(/\\*\\*([^*]+)\\*\\*/g, '<strong>$1</strong>')
559
- .replace(/\\*([^*]+)\\*/g, '<em>$1</em>')
560
- .replace(/\\[([^\\]]+)\\]\\(([^)]+)\\)/g, '<a href="$2">$1</a>')
561
- .replace(/^\\s*[-*] (.*$)/gm, '<li>$1</li>')
562
- .replace(/^> (.*$)/gm, '<blockquote>$1</blockquote>')
563
- .replace(/^---+$/gm, '<hr>')
564
- .replace(/\\n\\n/g, '</p><p>');
565
- }
742
+ // Initial render
743
+ preview.innerHTML = renderMarkdown(editor.value);
744
+ Prism.highlightAllUnder(preview);
566
745
 
567
746
  // Update status indicator
568
747
  function updateStatus() {
@@ -632,7 +811,42 @@ function generatePage(files, currentFile, mode = 'preview') {
632
811
  e.returnValue = '';
633
812
  }
634
813
  });
635
- ` : ''}
814
+ ` : `
815
+ // Preview mode: Render markdown on page load
816
+ document.addEventListener('DOMContentLoaded', () => {
817
+ // Configure marked.js
818
+ marked.setOptions({
819
+ gfm: true,
820
+ breaks: true,
821
+ headerIds: true,
822
+ mangle: false
823
+ });
824
+
825
+ // Custom renderer for syntax highlighting with Prism
826
+ const renderer = new marked.Renderer();
827
+ renderer.code = function(code, language) {
828
+ const lang = language || '';
829
+ const validLang = Prism.languages[lang] ? lang : 'plaintext';
830
+ let highlighted;
831
+ try {
832
+ highlighted = Prism.languages[validLang]
833
+ ? Prism.highlight(code, Prism.languages[validLang], validLang)
834
+ : code;
835
+ } catch (e) {
836
+ highlighted = code;
837
+ }
838
+ return '<pre class="language-' + validLang + '"><code class="language-' + validLang + '">' + highlighted + '</code></pre>';
839
+ };
840
+ marked.setOptions({ renderer });
841
+
842
+ // Find all markdown bodies and render them
843
+ document.querySelectorAll('.markdown-body[data-markdown]').forEach(el => {
844
+ const rawMd = decodeURIComponent(el.dataset.markdown);
845
+ el.innerHTML = marked.parse(rawMd);
846
+ Prism.highlightAllUnder(el);
847
+ });
848
+ });
849
+ `}
636
850
  </script>
637
851
  </body>
638
852
  </html>`;