claude-code-templates 1.25.0 → 1.26.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": "claude-code-templates",
3
- "version": "1.25.0",
3
+ "version": "1.26.0",
4
4
  "description": "CLI tool to setup Claude Code configurations with framework-specific commands, automation hooks and MCP Servers for your projects",
5
5
  "main": "src/index.js",
6
6
  "bin": {
@@ -755,12 +755,12 @@
755
755
  stroke: rgba(63, 185, 80, 1);
756
756
  }
757
757
 
758
- .action-btn.share-btn:hover {
758
+ .action-btn.download-btn:hover {
759
759
  background: rgba(59, 130, 246, 0.15);
760
760
  color: rgba(59, 130, 246, 1);
761
761
  }
762
762
 
763
- .action-btn.share-btn:hover svg {
763
+ .action-btn.download-btn:hover svg {
764
764
  stroke: rgba(59, 130, 246, 1);
765
765
  }
766
766
 
@@ -2052,13 +2052,13 @@
2052
2052
  </svg>
2053
2053
  <span>Resume</span>
2054
2054
  </button>
2055
- <button class="action-btn share-btn" id="shareConversation" onclick="shareConversation()">
2055
+ <button class="action-btn download-btn" id="downloadConversation" onclick="downloadConversation()">
2056
2056
  <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
2057
- <path d="M4 12v8a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-8"></path>
2058
- <polyline points="16 6 12 2 8 6"></polyline>
2059
- <line x1="12" y1="2" x2="12" y2="15"></line>
2057
+ <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path>
2058
+ <polyline points="7 10 12 15 17 10"></polyline>
2059
+ <line x1="12" y1="15" x2="12" y2="3"></line>
2060
2060
  </svg>
2061
- <span>Share</span>
2061
+ <span>Download</span>
2062
2062
  </button>
2063
2063
  <button class="action-btn search-btn" id="chatSearchToggle">
2064
2064
  <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
@@ -2143,106 +2143,58 @@
2143
2143
  </div>
2144
2144
  </div>
2145
2145
 
2146
- <!-- Confirm Share Modal (Security Warning) -->
2147
- <div class="modal-overlay" id="confirmShareModal">
2146
+ <!-- Download Context Modal -->
2147
+ <div class="modal-overlay" id="downloadModal">
2148
2148
  <div class="modal">
2149
2149
  <div class="modal-header" style="background: linear-gradient(135deg, #3b82f6 0%, #2563eb 100%);">
2150
- <span class="modal-icon">šŸ“¤</span>
2151
- <h3 class="modal-title">Share Your Session</h3>
2150
+ <span class="modal-icon">šŸ“„</span>
2151
+ <h3 class="modal-title">Download Conversation Context</h3>
2152
2152
  </div>
2153
2153
  <div style="padding: 20px;">
2154
2154
  <p class="modal-description" style="margin-bottom: 16px; line-height: 1.5;">
2155
- Your conversation will be uploaded to <strong>x0.at</strong>, a temporary file hosting service.
2156
- The generated link can be shared with others to clone your session.
2155
+ Download this conversation as a <strong>Markdown context file</strong> that Claude Code can read to continue your work.
2157
2156
  </p>
2158
2157
 
2159
2158
  <div style="background: rgba(59, 130, 246, 0.08); border-left: 3px solid #3b82f6; padding: 12px; border-radius: 6px; margin-bottom: 16px;">
2160
- <h4 style="color: #3b82f6; margin: 0 0 8px 0; font-size: 14px;">šŸ“‹ What you're sharing</h4>
2159
+ <h4 style="color: #3b82f6; margin: 0 0 8px 0; font-size: 14px;">šŸ“‹ What's included</h4>
2161
2160
  <p style="margin: 0; font-size: 13px; color: var(--text-secondary); line-height: 1.4;">
2162
- Last <strong>100 messages</strong> from this conversation (or all messages if less than 100)
2161
+ Full conversation history (last <strong>100 messages</strong>) formatted for Claude Code to understand
2163
2162
  </p>
2164
2163
  </div>
2165
2164
 
2166
- <div style="background: rgba(234, 179, 8, 0.08); border-left: 3px solid #eab308; padding: 12px; border-radius: 6px; margin-bottom: 16px;">
2167
- <h4 style="color: #eab308; margin: 0 0 8px 0; font-size: 14px;">ā„¹ļø About x0.at</h4>
2168
- <ul style="margin: 0; padding-left: 20px; font-size: 13px; color: var(--text-secondary); line-height: 1.5;">
2169
- <li style="margin-bottom: 4px;">Simple temporary file hosting (open source)</li>
2170
- <li style="margin-bottom: 4px;">Files available for 3-100 days depending on size</li>
2171
- <li style="margin-bottom: 4px;">Anyone with the link can download your session</li>
2172
- <li style="margin-bottom: 4px;">No encryption - avoid sharing sensitive data</li>
2173
- </ul>
2165
+ <div style="background: rgba(16, 185, 129, 0.08); border-left: 3px solid #10b981; padding: 12px; border-radius: 6px; margin-bottom: 16px;">
2166
+ <h4 style="color: #10b981; margin: 0 0 8px 0; font-size: 14px;">šŸ’” How to continue the conversation</h4>
2167
+ <p style="margin: 0 0 12px 0; font-size: 13px; color: var(--text-secondary); line-height: 1.4;">
2168
+ After downloading, use one of these commands to load the context:
2169
+ </p>
2170
+
2171
+ <div style="margin-bottom: 10px;">
2172
+ <p style="margin: 0 0 4px 0; font-size: 12px; font-weight: 600; color: #10b981;">Option 1: Direct reference</p>
2173
+ <code style="display: block; background: rgba(0,0,0,0.5); padding: 8px; border-radius: 4px; font-size: 12px; color: #e5e7eb; overflow-x: auto; white-space: nowrap;">claude "read @context-file.md and continue"</code>
2174
+ </div>
2175
+
2176
+ <div>
2177
+ <p style="margin: 0 0 4px 0; font-size: 12px; font-weight: 600; color: #10b981;">Option 2: Using cat (pipe)</p>
2178
+ <code style="display: block; background: rgba(0,0,0,0.5); padding: 8px; border-radius: 4px; font-size: 12px; color: #e5e7eb; overflow-x: auto; white-space: nowrap;">cat context-file.md | claude</code>
2179
+ </div>
2174
2180
  </div>
2175
2181
 
2176
2182
  <div style="background: rgba(100, 116, 139, 0.08); border-left: 3px solid #64748b; padding: 12px; border-radius: 6px;">
2177
2183
  <p style="margin: 0; font-size: 12px; color: var(--text-secondary); line-height: 1.4;">
2178
- šŸ’” <strong>Tip:</strong> Only share links with people you trust. The session includes your conversation history and may contain project-specific information.
2184
+ šŸ’¾ <strong>Tip:</strong> Save the downloaded file in your project directory for easy access
2179
2185
  </p>
2180
2186
  </div>
2181
2187
  </div>
2182
2188
 
2183
2189
  <div class="modal-actions">
2184
- <button class="modal-btn secondary" onclick="closeConfirmShareModal()">Cancel</button>
2185
- <button class="modal-btn primary" onclick="proceedWithShare()" style="background: #3b82f6;">
2186
- Continue & Upload
2190
+ <button class="modal-btn secondary" onclick="closeDownloadModal()">Cancel</button>
2191
+ <button class="modal-btn primary" onclick="proceedWithDownload()" style="background: #3b82f6;">
2192
+ Download Context File
2187
2193
  </button>
2188
2194
  </div>
2189
2195
  </div>
2190
2196
  </div>
2191
2197
 
2192
- <!-- Share Modal -->
2193
- <div class="modal-overlay" id="shareModal">
2194
- <div class="modal">
2195
- <div class="modal-header">
2196
- <span class="modal-icon">šŸ“¤</span>
2197
- <h3 class="modal-title">Share Conversation</h3>
2198
- </div>
2199
- <div id="shareModalLoading" style="text-align: center; padding: 40px; display: none;">
2200
- <div class="loading-spinner"></div>
2201
- <p style="margin-top: 16px; color: var(--text-secondary);">Uploading session...</p>
2202
- </div>
2203
- <div id="shareModalContent" style="display: none;">
2204
- <p class="modal-description">
2205
- Your conversation has been uploaded to x0.at. Share the command or QR code below with others.
2206
- </p>
2207
- <div id="shareMessageInfo" style="display: none; padding: 12px; background: var(--bg-tertiary); border-radius: 6px; margin-bottom: 16px;">
2208
- <p style="font-size: 13px; color: var(--text-secondary); margin: 0;">
2209
- <span id="shareMessageCount"></span>
2210
- </p>
2211
- </div>
2212
- <div style="text-align: center; margin: 24px 0;">
2213
- <div style="margin-bottom: 16px; display: flex; flex-direction: column; align-items: center;">
2214
- <h4 style="margin-bottom: 12px; color: var(--text-primary);">šŸ“± Scan QR Code</h4>
2215
- <img id="shareQRCode" src="" alt="QR Code" style="max-width: 300px; border-radius: 8px; border: 2px solid var(--border-color); display: block; margin: 0 auto;">
2216
- <p style="font-size: 12px; color: var(--text-secondary); margin-top: 12px; margin-bottom: 0;">
2217
- Scan this QR code to get the share command
2218
- </p>
2219
- </div>
2220
- </div>
2221
- <div>
2222
- <h4 style="margin-bottom: 8px; color: var(--text-primary);">šŸ“‹ Share Command</h4>
2223
- <div class="modal-command" id="shareModalCommand">
2224
- <!-- Command will be inserted here -->
2225
- </div>
2226
- </div>
2227
- <div style="margin-top: 16px; padding: 12px; background: var(--bg-tertiary); border-radius: 6px;">
2228
- <p style="font-size: 12px; color: var(--text-secondary); margin: 0;">
2229
- šŸ”— Direct URL: <a id="shareDirectUrl" href="#" target="_blank" style="color: var(--text-accent); word-break: break-all;"></a>
2230
- </p>
2231
- <p style="font-size: 12px; color: var(--text-warning); margin: 8px 0 0 0;">
2232
- āš ļø Files kept for 3-100 days (based on size)
2233
- </p>
2234
- <p style="font-size: 12px; color: var(--text-secondary); margin: 4px 0 0 0;">
2235
- šŸ”“ Files are not encrypted by default
2236
- </p>
2237
- </div>
2238
- </div>
2239
- <div class="modal-actions">
2240
- <button class="modal-btn secondary" onclick="closeShareModal()">Close</button>
2241
- <button class="modal-btn primary" id="copyShareCommandBtn" onclick="copyShareCommand()" style="display: none;">Copy Command</button>
2242
- </div>
2243
- </div>
2244
- </div>
2245
-
2246
2198
  <!-- Import WebSocket and Data Services -->
2247
2199
  <script src="services/WebSocketService.js"></script>
2248
2200
  <script src="services/DataService.js"></script>
@@ -2647,9 +2599,9 @@
2647
2599
  actionButtonsGroup.style.display = 'inline-flex';
2648
2600
 
2649
2601
  const resumeBtn = document.getElementById('resumeConversation');
2650
- const shareBtn = document.getElementById('shareConversation');
2602
+ const downloadBtn = document.getElementById('downloadConversation');
2651
2603
  resumeBtn.setAttribute('data-conversation-id', conversationId);
2652
- shareBtn.setAttribute('data-conversation-id', conversationId);
2604
+ downloadBtn.setAttribute('data-conversation-id', conversationId);
2653
2605
 
2654
2606
  // Load messages (placeholder for now)
2655
2607
  this.loadChatMessages(conversationId);
@@ -4722,70 +4674,52 @@
4722
4674
  }
4723
4675
  }
4724
4676
 
4725
- // Share conversation functions
4726
- function shareConversation() {
4727
- const shareBtn = document.getElementById('shareConversation');
4728
- const conversationId = shareBtn.getAttribute('data-conversation-id');
4677
+ // Download conversation context functions
4678
+ function downloadConversation() {
4679
+ const downloadBtn = document.getElementById('downloadConversation');
4680
+ const conversationId = downloadBtn.getAttribute('data-conversation-id');
4729
4681
 
4730
4682
  if (!conversationId) {
4731
4683
  console.error('No conversation ID found');
4732
4684
  return;
4733
4685
  }
4734
4686
 
4735
- console.log('šŸ“¤ Opening share confirmation for conversation:', conversationId);
4687
+ console.log('šŸ“„ Opening download modal for conversation:', conversationId);
4736
4688
 
4737
- // Show confirmation modal first
4738
- const confirmModal = document.getElementById('confirmShareModal');
4739
- confirmModal.classList.add('show');
4689
+ // Show download modal
4690
+ const downloadModal = document.getElementById('downloadModal');
4691
+ downloadModal.classList.add('show');
4740
4692
 
4741
4693
  // Close modal when clicking outside
4742
- confirmModal.addEventListener('click', (e) => {
4743
- if (e.target === confirmModal) {
4744
- closeConfirmShareModal();
4694
+ downloadModal.addEventListener('click', (e) => {
4695
+ if (e.target === downloadModal) {
4696
+ closeDownloadModal();
4745
4697
  }
4746
4698
  });
4747
4699
  }
4748
4700
 
4749
- function closeConfirmShareModal() {
4750
- const confirmModal = document.getElementById('confirmShareModal');
4751
- confirmModal.classList.remove('show');
4701
+ function closeDownloadModal() {
4702
+ const downloadModal = document.getElementById('downloadModal');
4703
+ downloadModal.classList.remove('show');
4752
4704
  }
4753
4705
 
4754
- async function proceedWithShare() {
4755
- // Close confirmation modal
4756
- closeConfirmShareModal();
4706
+ async function proceedWithDownload() {
4707
+ // Close modal
4708
+ closeDownloadModal();
4757
4709
 
4758
- const shareBtn = document.getElementById('shareConversation');
4759
- const conversationId = shareBtn.getAttribute('data-conversation-id');
4710
+ const downloadBtn = document.getElementById('downloadConversation');
4711
+ const conversationId = downloadBtn.getAttribute('data-conversation-id');
4760
4712
 
4761
4713
  if (!conversationId) {
4762
4714
  console.error('No conversation ID found');
4763
4715
  return;
4764
4716
  }
4765
4717
 
4766
- console.log('šŸ“¤ User confirmed - Proceeding with share for conversation:', conversationId);
4767
-
4768
- // Show share modal with loading state
4769
- const modal = document.getElementById('shareModal');
4770
- const loadingDiv = document.getElementById('shareModalLoading');
4771
- const contentDiv = document.getElementById('shareModalContent');
4772
- const copyBtn = document.getElementById('copyShareCommandBtn');
4773
-
4774
- modal.classList.add('show');
4775
- loadingDiv.style.display = 'block';
4776
- contentDiv.style.display = 'none';
4777
- copyBtn.style.display = 'none';
4778
-
4779
- // Close modal when clicking outside
4780
- modal.addEventListener('click', (e) => {
4781
- if (e.target === modal) {
4782
- closeShareModal();
4783
- }
4784
- });
4718
+ console.log('šŸ“„ Downloading context for conversation:', conversationId);
4785
4719
 
4786
4720
  try {
4787
- // Call API to share the conversation
4788
- const response = await fetch(`/api/conversations/${conversationId}/share`, {
4721
+ // Call API to export the conversation as markdown
4722
+ const response = await fetch(`/api/conversations/${conversationId}/download`, {
4789
4723
  method: 'POST',
4790
4724
  headers: {
4791
4725
  'Content-Type': 'application/json'
@@ -4793,125 +4727,37 @@
4793
4727
  });
4794
4728
 
4795
4729
  if (!response.ok) {
4796
- throw new Error(`Failed to share session: ${response.statusText}`);
4730
+ throw new Error(`Failed to export session: ${response.statusText}`);
4797
4731
  }
4798
4732
 
4799
4733
  const data = await response.json();
4800
4734
 
4801
- console.log('āœ… Session shared successfully:', data);
4735
+ console.log('āœ… Session exported successfully:', data);
4802
4736
 
4803
- // Update modal with share data
4804
- const qrCodeImg = document.getElementById('shareQRCode');
4805
- const commandDiv = document.getElementById('shareModalCommand');
4806
- const directUrlLink = document.getElementById('shareDirectUrl');
4807
-
4808
- // Set QR code
4809
- if (data.qrCode && data.qrCode.dataUrl) {
4810
- qrCodeImg.src = data.qrCode.dataUrl;
4811
- qrCodeImg.style.display = 'block';
4812
- } else {
4813
- qrCodeImg.style.display = 'none';
4814
- }
4737
+ // Create a blob from the markdown content
4738
+ const blob = new Blob([data.markdown], { type: 'text/markdown;charset=utf-8' });
4815
4739
 
4816
- // Set command
4817
- commandDiv.textContent = data.shareCommand;
4818
- commandDiv.setAttribute('data-command', data.shareCommand);
4740
+ // Create a temporary download link
4741
+ const url = window.URL.createObjectURL(blob);
4742
+ const a = document.createElement('a');
4743
+ a.href = url;
4744
+ a.download = data.filename;
4745
+ document.body.appendChild(a);
4746
+ a.click();
4819
4747
 
4820
- // Set direct URL
4821
- directUrlLink.textContent = data.uploadUrl;
4822
- directUrlLink.href = data.uploadUrl;
4823
-
4824
- // Show message count information
4825
- const messageInfoDiv = document.getElementById('shareMessageInfo');
4826
- const messageCountSpan = document.getElementById('shareMessageCount');
4748
+ // Cleanup
4749
+ window.URL.revokeObjectURL(url);
4750
+ document.body.removeChild(a);
4827
4751
 
4752
+ // Show success notification
4753
+ console.log(`šŸ“„ Downloaded: ${data.filename}`);
4828
4754
  if (data.wasLimited) {
4829
- messageCountSpan.innerHTML = `āš ļø This session has <strong>${data.totalMessageCount}</strong> messages. Sharing last <strong>${data.messageCount}</strong> messages to keep file size manageable.`;
4830
- messageInfoDiv.style.display = 'block';
4831
- } else {
4832
- messageCountSpan.innerHTML = `āœ… Sharing <strong>${data.messageCount}</strong> messages from this conversation.`;
4833
- messageInfoDiv.style.display = 'block';
4755
+ console.log(`āš ļø Exported ${data.messageCount} of ${data.totalMessageCount} messages`);
4834
4756
  }
4835
4757
 
4836
- // Show content and hide loading
4837
- loadingDiv.style.display = 'none';
4838
- contentDiv.style.display = 'block';
4839
- copyBtn.style.display = 'block';
4840
-
4841
4758
  } catch (error) {
4842
- console.error('āŒ Failed to share session:', error);
4843
- alert(`Failed to share session: ${error.message}`);
4844
- closeShareModal();
4845
- }
4846
- }
4847
-
4848
- function closeShareModal() {
4849
- const modal = document.getElementById('shareModal');
4850
- modal.classList.remove('show');
4851
- }
4852
-
4853
- function copyShareCommand() {
4854
- const commandDiv = document.getElementById('shareModalCommand');
4855
- const copyBtn = document.getElementById('copyShareCommandBtn');
4856
- const command = commandDiv.getAttribute('data-command');
4857
-
4858
- if (!command) {
4859
- console.error('No command found to copy');
4860
- return;
4861
- }
4862
-
4863
- if (navigator.clipboard && navigator.clipboard.writeText) {
4864
- navigator.clipboard.writeText(command).then(() => {
4865
- // Show success feedback
4866
- const originalText = copyBtn.textContent;
4867
- copyBtn.textContent = 'āœ… Copied!';
4868
- copyBtn.style.backgroundColor = 'rgba(63, 185, 80, 0.8)';
4869
- copyBtn.style.borderColor = 'rgba(63, 185, 80, 1)';
4870
-
4871
- setTimeout(() => {
4872
- copyBtn.textContent = originalText;
4873
- copyBtn.style.backgroundColor = '';
4874
- copyBtn.style.borderColor = '';
4875
- }, 1500);
4876
-
4877
- console.log('šŸ“‹ Share command copied to clipboard:', command);
4878
- }).catch(err => {
4879
- console.error('Failed to copy to clipboard:', err);
4880
- fallbackCopyShare(command);
4881
- });
4882
- } else {
4883
- // Fallback for browsers without clipboard API
4884
- fallbackCopyShare(command);
4885
- }
4886
- }
4887
-
4888
- function fallbackCopyShare(command) {
4889
- // Create a temporary text area to select and copy
4890
- const tempTextArea = document.createElement('textarea');
4891
- tempTextArea.value = command;
4892
- tempTextArea.style.position = 'fixed';
4893
- tempTextArea.style.left = '-9999px';
4894
- document.body.appendChild(tempTextArea);
4895
- tempTextArea.select();
4896
-
4897
- try {
4898
- document.execCommand('copy');
4899
- const copyBtn = document.getElementById('copyShareCommandBtn');
4900
- const originalText = copyBtn.textContent;
4901
- copyBtn.textContent = 'āœ… Copied!';
4902
- copyBtn.style.backgroundColor = 'rgba(63, 185, 80, 0.8)';
4903
-
4904
- setTimeout(() => {
4905
- copyBtn.textContent = originalText;
4906
- copyBtn.style.backgroundColor = '';
4907
- }, 1500);
4908
-
4909
- console.log('šŸ“‹ Share command copied using fallback method:', command);
4910
- } catch (err) {
4911
- console.error('Fallback copy failed:', err);
4912
- alert(`Please copy this command manually:\n\n${command}`);
4913
- } finally {
4914
- document.body.removeChild(tempTextArea);
4759
+ console.error('āŒ Failed to download context:', error);
4760
+ alert(`Failed to download context: ${error.message}`);
4915
4761
  }
4916
4762
  }
4917
4763
 
@@ -478,8 +478,8 @@ class ChatsMobile {
478
478
  }
479
479
  });
480
480
 
481
- // API to share a conversation session
482
- this.app.post('/api/conversations/:id/share', async (req, res) => {
481
+ // API to download a conversation session as markdown
482
+ this.app.post('/api/conversations/:id/download', async (req, res) => {
483
483
  try {
484
484
  const conversationId = req.params.id;
485
485
  const conversation = this.data.conversations.find(conv => conv.id === conversationId);
@@ -488,24 +488,25 @@ class ChatsMobile {
488
488
  return res.status(404).json({ error: 'Conversation not found' });
489
489
  }
490
490
 
491
- console.log(chalk.cyan(`šŸ“¤ Sharing conversation ${conversationId}...`));
491
+ console.log(chalk.cyan(`šŸ“„ Exporting conversation ${conversationId} as markdown...`));
492
492
 
493
- // Share the session using SessionSharing module
494
- const shareResult = await this.sessionSharing.shareSession(conversationId, conversation);
493
+ // Export the session as markdown using SessionSharing module
494
+ const exportResult = await this.sessionSharing.exportSessionAsMarkdown(conversationId, conversation);
495
495
 
496
496
  res.json({
497
497
  success: true,
498
498
  conversationId: conversationId,
499
- uploadUrl: shareResult.uploadUrl,
500
- shareCommand: shareResult.shareCommand,
501
- expiresIn: shareResult.expiresIn,
502
- qrCode: shareResult.qrCode,
499
+ markdown: exportResult.markdown,
500
+ filename: exportResult.filename,
501
+ messageCount: exportResult.messageCount,
502
+ totalMessageCount: exportResult.totalMessageCount,
503
+ wasLimited: exportResult.wasLimited,
503
504
  timestamp: new Date().toISOString()
504
505
  });
505
506
  } catch (error) {
506
- console.error('Error sharing conversation:', error);
507
+ console.error('Error exporting conversation:', error);
507
508
  res.status(500).json({
508
- error: 'Failed to share session',
509
+ error: 'Failed to export session',
509
510
  message: error.message
510
511
  });
511
512
  }
@@ -8,62 +8,123 @@ const execAsync = promisify(exec);
8
8
  const QRCode = require('qrcode');
9
9
 
10
10
  /**
11
- * SessionSharing - Handles exporting and importing Claude Code sessions
12
- * Uses x0.at - a simple, reliable file hosting service
11
+ * SessionSharing - Handles exporting Claude Code sessions as downloadable context
13
12
  */
14
13
  class SessionSharing {
15
14
  constructor(conversationAnalyzer) {
16
15
  this.conversationAnalyzer = conversationAnalyzer;
17
- this.uploadService = 'x0.at';
18
- this.uploadUrl = 'https://x0.at';
19
16
  }
20
17
 
21
18
  /**
22
- * Export and share a conversation session
23
- * @param {string} conversationId - Conversation ID to share
19
+ * Export conversation session as downloadable markdown file
20
+ * @param {string} conversationId - Conversation ID to export
24
21
  * @param {Object} conversationData - Full conversation data object
25
- * @param {Object} options - Share options (messageLimit, etc.)
26
- * @returns {Promise<Object>} Share result with URL, command, and QR code
22
+ * @param {Object} options - Export options (messageLimit, etc.)
23
+ * @returns {Promise<Object>} Export result with markdown content and filename
27
24
  */
28
- async shareSession(conversationId, conversationData, options = {}) {
29
- console.log(chalk.blue(`šŸ“¤ Preparing session ${conversationId} for sharing...`));
25
+ async exportSessionAsMarkdown(conversationId, conversationData, options = {}) {
26
+ console.log(chalk.blue(`šŸ“„ Preparing session ${conversationId} for download...`));
30
27
 
31
28
  try {
32
- // 1. Export session to structured JSON format
33
- const sessionExport = await this.exportSessionData(conversationId, conversationData, options);
29
+ // 1. Get conversation messages
30
+ const allMessages = await this.conversationAnalyzer.getParsedConversation(conversationData.filePath);
34
31
 
35
- // 2. Upload to x0.at
36
- const uploadUrl = await this.uploadToX0(sessionExport, conversationId);
32
+ // Limit messages to avoid large file sizes (default: last 100 messages)
33
+ const messageLimit = options.messageLimit || 100;
34
+ const messages = allMessages.slice(-messageLimit);
37
35
 
38
- // 3. Generate share command
39
- const shareCommand = `npx claude-code-templates@latest --clone-session "${uploadUrl}"`;
36
+ // 2. Convert to markdown format
37
+ const markdown = this.convertToMarkdown(messages, conversationData, {
38
+ messageCount: messages.length,
39
+ totalMessageCount: allMessages.length,
40
+ wasLimited: allMessages.length > messageLimit
41
+ });
40
42
 
41
- // 4. Generate QR code
42
- const qrCode = await this.generateQRCode(shareCommand);
43
+ // 3. Generate filename
44
+ const projectName = (conversationData.project || 'session').replace(/[^a-zA-Z0-9-_]/g, '-');
45
+ const date = new Date().toISOString().split('T')[0];
46
+ const filename = `claude-context-${projectName}-${date}.md`;
43
47
 
44
- console.log(chalk.green(`āœ… Session shared successfully!`));
45
- console.log(chalk.cyan(`šŸ“‹ Share command: ${shareCommand}`));
46
- console.log(chalk.gray(`šŸ”— Direct URL: ${uploadUrl}`));
47
- console.log(chalk.yellow(`āš ļø Files kept for 3-100 days (based on size)`));
48
- console.log(chalk.gray(`šŸ”“ Note: Files are not encrypted by default`));
48
+ console.log(chalk.green(`āœ… Session exported successfully!`));
49
+ console.log(chalk.gray(`šŸ“Š Exported ${messages.length} messages`));
49
50
 
50
51
  return {
51
52
  success: true,
52
- uploadUrl,
53
- shareCommand,
54
- expiresIn: 'After first download',
55
- conversationId,
56
- qrCode,
57
- messageCount: sessionExport.conversation.messageCount,
58
- totalMessageCount: sessionExport.conversation.totalMessageCount,
59
- wasLimited: sessionExport.conversation.wasLimited
53
+ markdown,
54
+ filename,
55
+ messageCount: messages.length,
56
+ totalMessageCount: allMessages.length,
57
+ wasLimited: allMessages.length > messageLimit
60
58
  };
61
59
  } catch (error) {
62
- console.error(chalk.red('āŒ Failed to share session:'), error.message);
60
+ console.error(chalk.red('āŒ Failed to export session:'), error.message);
63
61
  throw error;
64
62
  }
65
63
  }
66
64
 
65
+ /**
66
+ * Convert conversation messages to markdown format optimized for Claude Code
67
+ * @param {Array} messages - Parsed conversation messages
68
+ * @param {Object} conversationData - Conversation metadata
69
+ * @param {Object} stats - Export statistics
70
+ * @returns {string} Markdown formatted content
71
+ */
72
+ convertToMarkdown(messages, conversationData, stats) {
73
+ const lines = [];
74
+
75
+ // Header for Claude Code
76
+ lines.push('# Previous Conversation Context\n');
77
+ lines.push('> **Note to Claude Code**: This file contains the complete conversation history from a previous session. Read and understand this context to continue helping the user with their task.\n');
78
+ lines.push(`**Project:** ${conversationData.project || 'Unknown'}`);
79
+ lines.push(`**Date:** ${new Date().toISOString().split('T')[0]}`);
80
+ lines.push(`**Messages in this export:** ${stats.messageCount}${stats.wasLimited ? ` (most recent from a total of ${stats.totalMessageCount})` : ''}`);
81
+ lines.push('');
82
+ lines.push('---');
83
+ lines.push('');
84
+
85
+ // Conversation
86
+ lines.push('## šŸ’¬ Conversation History\n');
87
+
88
+ messages.forEach((msg, index) => {
89
+ const role = msg.role === 'user' ? 'šŸ‘¤ User' : 'šŸ¤– Assistant';
90
+ const timestamp = new Date(msg.timestamp).toLocaleString();
91
+
92
+ lines.push(`### Message ${index + 1}: ${role}`);
93
+ lines.push(`*${timestamp}*\n`);
94
+
95
+ // Extract text content from message
96
+ if (Array.isArray(msg.content)) {
97
+ msg.content.forEach(block => {
98
+ if (block.type === 'text') {
99
+ lines.push(block.text);
100
+ } else if (block.type === 'tool_use') {
101
+ lines.push(`\`\`\`${block.name || 'tool'}`);
102
+ lines.push(JSON.stringify(block.input || {}, null, 2));
103
+ lines.push('```');
104
+ } else if (block.type === 'tool_result') {
105
+ lines.push('**Tool Result:**');
106
+ lines.push('```');
107
+ lines.push(typeof block.content === 'string' ? block.content : JSON.stringify(block.content, null, 2));
108
+ lines.push('```');
109
+ }
110
+ });
111
+ } else if (typeof msg.content === 'string') {
112
+ lines.push(msg.content);
113
+ }
114
+
115
+ lines.push('');
116
+ lines.push('---');
117
+ lines.push('');
118
+ });
119
+
120
+ // Footer
121
+ lines.push('\n---');
122
+ lines.push('');
123
+ lines.push('*Generated by Claude Code Templates - [aitmpl.com](https://aitmpl.com)*');
124
+
125
+ return lines.join('\n');
126
+ }
127
+
67
128
  /**
68
129
  * Export session data to standardized format
69
130
  * @param {string} conversationId - Conversation ID
@@ -117,14 +178,14 @@ class SessionSharing {
117
178
  exported_at: new Date().toISOString(),
118
179
  conversation: {
119
180
  id: conversationId,
120
- project: conversationData.project || 'Unknown',
181
+ project: conversationData.project || 'shared-session',
121
182
  created: conversationData.created,
122
183
  lastModified: conversationData.lastModified,
123
184
  messageCount: messages.length,
124
185
  totalMessageCount: allMessages.length,
125
186
  wasLimited: allMessages.length > messageLimit,
126
187
  tokens: conversationData.tokens,
127
- model: conversationData.modelInfo?.primaryModel || 'Unknown'
188
+ model: conversationData.modelInfo?.primaryModel || 'claude-sonnet-4-5-20250929'
128
189
  },
129
190
  messages: jsonlMessages,
130
191
  metadata: {
@@ -223,8 +284,8 @@ class SessionSharing {
223
284
  console.log(chalk.green(`\nāœ… Session installed successfully!`));
224
285
  console.log(chalk.cyan(`šŸ“‚ Location: ${installResult.sessionPath}`));
225
286
 
226
- // Show resume command
227
- const resumeCommand = `claude --resume ${installResult.projectPath} ${installResult.conversationId}`;
287
+ // Show resume command (only conversation ID needed)
288
+ const resumeCommand = `claude --resume ${installResult.conversationId}`;
228
289
  console.log(chalk.yellow(`\nšŸ’” To continue this conversation, run:`));
229
290
  console.log(chalk.white(`\n ${resumeCommand}\n`));
230
291
  console.log(chalk.gray(` Or open Claude Code to see it in your sessions list`));