UNPKG

ccshare

Version:

Share Claude Code prompts and results easily

279 lines 10.7 kB
import axios from 'axios'; import { readFileSync } from 'fs'; import { join, dirname } from 'path'; import { fileURLToPath } from 'url'; const __dirname = dirname(fileURLToPath(import.meta.url)); const packageJson = JSON.parse(readFileSync(join(__dirname, '..', 'package.json'), 'utf-8')); const VERSION = packageJson.version; export function transformToShareData(htmlData, sessionData) { const { prompts, fileDiffs, assistantActions, toolExecutions, sessionInfo, techStack } = htmlData; // Count additions and deletions from diff function countDiffChanges(diff) { const lines = diff.split('\n'); let additions = 0; let deletions = 0; lines.forEach(line => { if (line.startsWith('+') && !line.startsWith('+++')) { additions++; } else if (line.startsWith('-') && !line.startsWith('---')) { deletions++; } }); return { additions, deletions }; } // Transform file diffs with additions/deletions count const transformedFileDiffs = fileDiffs.map(file => { const { additions, deletions } = countDiffChanges(file.diff); return { path: file.path, diff: file.diff, additions, deletions }; }); // Create mapping from prompt UUID to index const promptUuidToIndex = {}; prompts.forEach((prompt, index) => { if (prompt.uuid) { promptUuidToIndex[prompt.uuid] = index + 1; } }); const sharePrompts = prompts.map((item, index) => { // Find the next assistant response to get usage and response time const promptIndex = sessionData.prompts.findIndex(p => p.content === item.prompt && p.timestamp === item.timestamp); let usage = undefined; let responseTimeMs = undefined; let model = undefined; let toolCalls = undefined; // Find the corresponding prompt in sessionData const originalPrompt = sessionData.prompts[promptIndex]; const isAutoGenerated = originalPrompt?.isAutoGenerated; if (promptIndex !== -1 && promptIndex < sessionData.prompts.length - 1) { const nextPrompt = sessionData.prompts[promptIndex + 1]; if (nextPrompt.role === 'assistant') { usage = nextPrompt.usage; responseTimeMs = nextPrompt.responseTimeMs; model = nextPrompt.model; toolCalls = nextPrompt.toolCalls; } } return { id: index + 1, content: item.prompt, timestamp: item.timestamp || new Date().toISOString(), sourceFile: item.sourceFile, usage, responseTimeMs, isAutoGenerated, model, toolCalls }; }); // Calculate tool statistics let toolStats = undefined; if (sessionData.toolCalls && sessionData.toolCalls.length > 0) { const byTool = {}; let mcpCalls = 0; sessionData.toolCalls.forEach(call => { byTool[call.name] = (byTool[call.name] || 0) + 1; if (call.isMCP) mcpCalls++; }); toolStats = { totalCalls: sessionData.toolCalls.length, byTool, mcpCalls, regularCalls: sessionData.toolCalls.length - mcpCalls }; } // Transform MCP servers data const mcpServers = sessionData.metadata?.mcpServers?.map(server => ({ name: server.name, tools: server.tools, callCount: server.tools.reduce((sum, toolName) => sum + (toolStats?.byTool[toolName] || 0), 0) })); // Create workflow items from assistantActions and toolExecutions const workflow = []; // Add tool executions to workflow if (toolExecutions) { toolExecutions.forEach(exec => { const promptIndex = exec.promptId ? promptUuidToIndex[exec.promptId] : undefined; workflow.push({ type: 'tool_execution', timestamp: exec.timestamp, tool: exec.tool, parameters: exec.parameters, promptId: promptIndex ? promptIndex.toString() : undefined }); // Add tool result if exists if (exec.result) { workflow.push({ type: 'tool_result', timestamp: exec.timestamp, // Using same timestamp, though ideally should be slightly later tool: exec.tool, result: exec.result, status: exec.status, promptId: promptIndex ? promptIndex.toString() : undefined, fileChange: exec.fileChange }); } }); } // Add assistant actions to workflow if (assistantActions) { assistantActions.forEach(action => { const promptIndex = action.promptId ? promptUuidToIndex[action.promptId] : undefined; workflow.push({ type: 'assistant_action', timestamp: action.timestamp, description: action.description, actionType: action.type, promptId: promptIndex ? promptIndex.toString() : undefined }); }); } // Sort workflow by timestamp workflow.sort((a, b) => new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime()); // Group workflow items by prompt const workflowByPrompt = {}; workflow.forEach(item => { if (item.promptId) { if (!workflowByPrompt[item.promptId]) { workflowByPrompt[item.promptId] = []; } workflowByPrompt[item.promptId].push(item); } }); // Add workflow to each prompt sharePrompts.forEach(prompt => { const promptIdStr = prompt.id.toString(); if (workflowByPrompt[promptIdStr]) { prompt.workflow = workflowByPrompt[promptIdStr]; } }); const shareData = { title: 'Claude Code Prompts', createdAt: new Date().toISOString(), sessionInfo: { totalPrompts: sessionInfo?.totalPrompts || sharePrompts.length, timeRange: sessionInfo?.timeRange, sources: sessionInfo?.sources, projectPath: sessionInfo?.projectPath || sessionData.metadata?.workingDirectory, claudeProjectPath: sessionInfo?.claudeProjectPath || sessionData.metadata?.claudeProjectPath }, techStack: { languages: techStack?.languages || [], frameworks: techStack?.frameworks || [], tools: techStack?.tools || [], databases: techStack?.databases || [] }, prompts: sharePrompts, fileDiffs: transformedFileDiffs, // File diffs at share level metadata: { generatedBy: 'ccshare', version: VERSION, platform: sessionData.metadata?.platform || process.platform, nodeVersion: sessionData.metadata?.nodeVersion, claudeSettings: sessionData.metadata?.claudeSettings, sessionStats: sessionData.metadata?.sessionStats, workingDirectory: sessionData.metadata?.workingDirectory, claudeProjectPath: sessionData.metadata?.claudeProjectPath }, models: sessionData.metadata?.models, mcpServers: mcpServers && mcpServers.length > 0 ? mcpServers : undefined, toolStats }; return shareData; } export async function shareToAPIRaw(data, apiUrl) { try { const response = await axios.post(apiUrl, data, { headers: { 'Content-Type': 'application/json', 'Accept': 'application/json' }, maxBodyLength: Infinity, timeout: 10000 // 10 second timeout }); // Check if response is HTML (likely a login page or error page) if (typeof response.data === 'string' && response.data.includes('<!DOCTYPE html>')) { throw new Error('API returned HTML instead of JSON (possibly requires authentication)'); } // Check different possible response formats const url = response.data?.url || response.data?.share_url || response.data?.shareUrl || response.data?.slug ? `${apiUrl}/${response.data.slug}` : null; if (!url) { console.error('Unexpected API response:', response.data); throw new Error('No URL in API response'); } return { url }; } catch (error) { if (error.response?.status === 401) { throw new Error('Authentication required'); } if (error.response?.status === 404) { throw new Error('API endpoint not found'); } throw error; } } export async function shareToAPI(shareData, apiUrl = 'https://ccshare.cc/shares') { try { const response = await axios.post(apiUrl, shareData, { headers: { 'Content-Type': 'application/json' }, timeout: 10000 // 10 second timeout }); return response.data; } catch (error) { if (error.response) { // Server responded with error return { error: error.response.data?.message || error.response.data?.error || `Server error: ${error.response.status}` }; } else if (error.request) { // No response received return { error: 'No response from server. Make sure the API is running on localhost:3000' }; } else { // Request setup error return { error: error.message || 'Failed to send request' }; } } } export async function fetchFromSlug(slug, apiUrl = 'https://ccshare.cc/shares') { try { const response = await axios.get(`${apiUrl}/${slug}`, { headers: { 'Accept': 'application/json' }, timeout: 10000 // 10 second timeout }); return response.data; } catch (error) { if (error.response?.status === 404) { console.error(`Share not found: ${slug}`); } else if (error.response) { console.error(`Server error: ${error.response.status}`); } else { console.error(`Failed to fetch share: ${error.message}`); } return null; } } //# sourceMappingURL=share-service.js.map