claude-code-templates 1.25.0 → 1.26.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-code-templates",
3
- "version": "1.25.0",
3
+ "version": "1.26.1",
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,59 @@
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: Start with context loaded</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: In interactive mode</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;">claude</code>
2179
+ <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; margin-top: 4px;">> @context-file.md continue with the task</code>
2180
+ </div>
2174
2181
  </div>
2175
2182
 
2176
2183
  <div style="background: rgba(100, 116, 139, 0.08); border-left: 3px solid #64748b; padding: 12px; border-radius: 6px;">
2177
2184
  <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.
2185
+ šŸ’¾ <strong>Tip:</strong> Save the downloaded file in your project directory for easy access
2179
2186
  </p>
2180
2187
  </div>
2181
2188
  </div>
2182
2189
 
2183
2190
  <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
2191
+ <button class="modal-btn secondary" onclick="closeDownloadModal()">Cancel</button>
2192
+ <button class="modal-btn primary" onclick="proceedWithDownload()" style="background: #3b82f6;">
2193
+ Download Context File
2187
2194
  </button>
2188
2195
  </div>
2189
2196
  </div>
2190
2197
  </div>
2191
2198
 
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
2199
  <!-- Import WebSocket and Data Services -->
2247
2200
  <script src="services/WebSocketService.js"></script>
2248
2201
  <script src="services/DataService.js"></script>
@@ -2647,9 +2600,9 @@
2647
2600
  actionButtonsGroup.style.display = 'inline-flex';
2648
2601
 
2649
2602
  const resumeBtn = document.getElementById('resumeConversation');
2650
- const shareBtn = document.getElementById('shareConversation');
2603
+ const downloadBtn = document.getElementById('downloadConversation');
2651
2604
  resumeBtn.setAttribute('data-conversation-id', conversationId);
2652
- shareBtn.setAttribute('data-conversation-id', conversationId);
2605
+ downloadBtn.setAttribute('data-conversation-id', conversationId);
2653
2606
 
2654
2607
  // Load messages (placeholder for now)
2655
2608
  this.loadChatMessages(conversationId);
@@ -4722,70 +4675,52 @@
4722
4675
  }
4723
4676
  }
4724
4677
 
4725
- // Share conversation functions
4726
- function shareConversation() {
4727
- const shareBtn = document.getElementById('shareConversation');
4728
- const conversationId = shareBtn.getAttribute('data-conversation-id');
4678
+ // Download conversation context functions
4679
+ function downloadConversation() {
4680
+ const downloadBtn = document.getElementById('downloadConversation');
4681
+ const conversationId = downloadBtn.getAttribute('data-conversation-id');
4729
4682
 
4730
4683
  if (!conversationId) {
4731
4684
  console.error('No conversation ID found');
4732
4685
  return;
4733
4686
  }
4734
4687
 
4735
- console.log('šŸ“¤ Opening share confirmation for conversation:', conversationId);
4688
+ console.log('šŸ“„ Opening download modal for conversation:', conversationId);
4736
4689
 
4737
- // Show confirmation modal first
4738
- const confirmModal = document.getElementById('confirmShareModal');
4739
- confirmModal.classList.add('show');
4690
+ // Show download modal
4691
+ const downloadModal = document.getElementById('downloadModal');
4692
+ downloadModal.classList.add('show');
4740
4693
 
4741
4694
  // Close modal when clicking outside
4742
- confirmModal.addEventListener('click', (e) => {
4743
- if (e.target === confirmModal) {
4744
- closeConfirmShareModal();
4695
+ downloadModal.addEventListener('click', (e) => {
4696
+ if (e.target === downloadModal) {
4697
+ closeDownloadModal();
4745
4698
  }
4746
4699
  });
4747
4700
  }
4748
4701
 
4749
- function closeConfirmShareModal() {
4750
- const confirmModal = document.getElementById('confirmShareModal');
4751
- confirmModal.classList.remove('show');
4702
+ function closeDownloadModal() {
4703
+ const downloadModal = document.getElementById('downloadModal');
4704
+ downloadModal.classList.remove('show');
4752
4705
  }
4753
4706
 
4754
- async function proceedWithShare() {
4755
- // Close confirmation modal
4756
- closeConfirmShareModal();
4707
+ async function proceedWithDownload() {
4708
+ // Close modal
4709
+ closeDownloadModal();
4757
4710
 
4758
- const shareBtn = document.getElementById('shareConversation');
4759
- const conversationId = shareBtn.getAttribute('data-conversation-id');
4711
+ const downloadBtn = document.getElementById('downloadConversation');
4712
+ const conversationId = downloadBtn.getAttribute('data-conversation-id');
4760
4713
 
4761
4714
  if (!conversationId) {
4762
4715
  console.error('No conversation ID found');
4763
4716
  return;
4764
4717
  }
4765
4718
 
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
- });
4719
+ console.log('šŸ“„ Downloading context for conversation:', conversationId);
4785
4720
 
4786
4721
  try {
4787
- // Call API to share the conversation
4788
- const response = await fetch(`/api/conversations/${conversationId}/share`, {
4722
+ // Call API to export the conversation as markdown
4723
+ const response = await fetch(`/api/conversations/${conversationId}/download`, {
4789
4724
  method: 'POST',
4790
4725
  headers: {
4791
4726
  'Content-Type': 'application/json'
@@ -4793,125 +4728,37 @@
4793
4728
  });
4794
4729
 
4795
4730
  if (!response.ok) {
4796
- throw new Error(`Failed to share session: ${response.statusText}`);
4731
+ throw new Error(`Failed to export session: ${response.statusText}`);
4797
4732
  }
4798
4733
 
4799
4734
  const data = await response.json();
4800
4735
 
4801
- console.log('āœ… Session shared successfully:', data);
4736
+ console.log('āœ… Session exported successfully:', data);
4802
4737
 
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
- }
4738
+ // Create a blob from the markdown content
4739
+ const blob = new Blob([data.markdown], { type: 'text/markdown;charset=utf-8' });
4815
4740
 
4816
- // Set command
4817
- commandDiv.textContent = data.shareCommand;
4818
- commandDiv.setAttribute('data-command', data.shareCommand);
4741
+ // Create a temporary download link
4742
+ const url = window.URL.createObjectURL(blob);
4743
+ const a = document.createElement('a');
4744
+ a.href = url;
4745
+ a.download = data.filename;
4746
+ document.body.appendChild(a);
4747
+ a.click();
4819
4748
 
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');
4749
+ // Cleanup
4750
+ window.URL.revokeObjectURL(url);
4751
+ document.body.removeChild(a);
4827
4752
 
4753
+ // Show success notification
4754
+ console.log(`šŸ“„ Downloaded: ${data.filename}`);
4828
4755
  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';
4756
+ console.log(`āš ļø Exported ${data.messageCount} of ${data.totalMessageCount} messages`);
4834
4757
  }
4835
4758
 
4836
- // Show content and hide loading
4837
- loadingDiv.style.display = 'none';
4838
- contentDiv.style.display = 'block';
4839
- copyBtn.style.display = 'block';
4840
-
4841
4759
  } 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);
4760
+ console.error('āŒ Failed to download context:', error);
4761
+ alert(`Failed to download context: ${error.message}`);
4915
4762
  }
4916
4763
  }
4917
4764
 
@@ -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`));