UNPKG

ccshare

Version:

Share Claude Code prompts and results easily

611 lines 176 kB
{ "sessionEntries": [ { "parentUuid": "e07fada5-c032-42ba-9cc3-befcae4f962e", "isSidechain": false, "userType": "external", "cwd": "/Users/james/Development/scripts/ccshare", "sessionId": "0bfe009d-8345-4731-aed0-f0edbb4530cf", "version": "1.0.59", "gitBranch": "main", "type": "user", "message": { "role": "user", "content": "-r을 했을 때 가장 최근 프롬프트가 나와야 하는데 지금은 과거 프롬프트가 나와 \n\n-r을 안 했을 때 프롬프트 선택지가 나오는 순서도 최신순으로 나오도록 해줘 " }, "uuid": "891f338c-93bd-4b40-a639-e09873d5c9f4", "timestamp": "2025-07-24T05:25:37.603Z" }, { "parentUuid": "891f338c-93bd-4b40-a639-e09873d5c9f4", "isSidechain": false, "userType": "external", "cwd": "/Users/james/Development/scripts/ccshare", "sessionId": "0bfe009d-8345-4731-aed0-f0edbb4530cf", "version": "1.0.59", "gitBranch": "main", "message": { "id": "msg_01NUZEtc2E9aUqyCyCofxKpr", "type": "message", "role": "assistant", "model": "claude-opus-4-20250514", "content": [ { "type": "text", "text": "네, `-r` 플래그를 사용할 때와 프롬프트 선택지에서 최신 프롬프트가 먼저 나오도록 수정하겠습니다." } ], "stop_reason": null, "stop_sequence": null, "usage": { "input_tokens": 0, "cache_creation_input_tokens": 333, "cache_read_input_tokens": 119326, "output_tokens": 4, "service_tier": "standard" } }, "requestId": "req_011CRRHewwzizKpkhsa92KRC", "type": "assistant", "uuid": "3c922fca-6234-4fde-a510-5eae561eca85", "timestamp": "2025-07-24T05:25:42.584Z" }, { "parentUuid": "3c922fca-6234-4fde-a510-5eae561eca85", "isSidechain": false, "userType": "external", "cwd": "/Users/james/Development/scripts/ccshare", "sessionId": "0bfe009d-8345-4731-aed0-f0edbb4530cf", "version": "1.0.59", "gitBranch": "main", "message": { "id": "msg_01NUZEtc2E9aUqyCyCofxKpr", "type": "message", "role": "assistant", "model": "claude-opus-4-20250514", "content": [ { "type": "tool_use", "id": "toolu_01HdVuc4Xk7rywNJJg53fwks", "name": "TodoWrite", "input": { "todos": [ { "id": "1", "content": "Add limit flag for fetching more prompts", "status": "completed", "priority": "high" }, { "id": "2", "content": "Modify session data structure to send raw session data", "status": "completed", "priority": "high" }, { "id": "3", "content": "Extract session data between user prompts", "status": "completed", "priority": "high" }, { "id": "4", "content": "Update API request to send raw session array", "status": "completed", "priority": "high" }, { "id": "5", "content": "Make raw session data the default behavior (remove --raw flag requirement)", "status": "completed", "priority": "high" }, { "id": "6", "content": "Update captureSession to return raw JSONL data", "status": "completed", "priority": "high" }, { "id": "7", "content": "Remove transformToShareData and old JSON structure", "status": "completed", "priority": "medium" }, { "id": "8", "content": "Fix prompt ordering to show most recent first", "status": "in_progress", "priority": "high" } ] } } ], "stop_reason": null, "stop_sequence": null, "usage": { "input_tokens": 0, "cache_creation_input_tokens": 333, "cache_read_input_tokens": 119326, "output_tokens": 381, "service_tier": "standard" } }, "requestId": "req_011CRRHewwzizKpkhsa92KRC", "type": "assistant", "uuid": "87cf85d4-03bd-4e01-b2f9-9a978ab6b507", "timestamp": "2025-07-24T05:25:46.247Z" }, { "parentUuid": "87cf85d4-03bd-4e01-b2f9-9a978ab6b507", "isSidechain": false, "userType": "external", "cwd": "/Users/james/Development/scripts/ccshare", "sessionId": "0bfe009d-8345-4731-aed0-f0edbb4530cf", "version": "1.0.59", "gitBranch": "main", "type": "user", "message": { "role": "user", "content": [ { "tool_use_id": "toolu_01HdVuc4Xk7rywNJJg53fwks", "type": "tool_result", "content": "Todos have been modified successfully. Ensure that you continue to use the todo list to track your progress. Please proceed with the current tasks if applicable" } ] }, "uuid": "b06cd9ce-df2e-4190-bf13-a6a1bc659cee", "timestamp": "2025-07-24T05:25:46.297Z", "toolUseResult": { "oldTodos": [ { "content": "Add limit flag for fetching more prompts", "status": "completed", "priority": "high", "id": "1" }, { "content": "Modify session data structure to send raw session data", "status": "completed", "priority": "high", "id": "2" }, { "content": "Extract session data between user prompts", "status": "completed", "priority": "high", "id": "3" }, { "content": "Update API request to send raw session array", "status": "completed", "priority": "high", "id": "4" }, { "content": "Make raw session data the default behavior (remove --raw flag requirement)", "status": "completed", "priority": "high", "id": "5" }, { "content": "Update captureSession to return raw JSONL data", "status": "completed", "priority": "high", "id": "6" }, { "content": "Remove transformToShareData and old JSON structure", "status": "completed", "priority": "medium", "id": "7" } ], "newTodos": [ { "content": "Add limit flag for fetching more prompts", "status": "completed", "priority": "high", "id": "1" }, { "content": "Modify session data structure to send raw session data", "status": "completed", "priority": "high", "id": "2" }, { "content": "Extract session data between user prompts", "status": "completed", "priority": "high", "id": "3" }, { "content": "Update API request to send raw session array", "status": "completed", "priority": "high", "id": "4" }, { "content": "Make raw session data the default behavior (remove --raw flag requirement)", "status": "completed", "priority": "high", "id": "5" }, { "content": "Update captureSession to return raw JSONL data", "status": "completed", "priority": "high", "id": "6" }, { "content": "Remove transformToShareData and old JSON structure", "status": "completed", "priority": "medium", "id": "7" }, { "content": "Fix prompt ordering to show most recent first", "status": "in_progress", "priority": "high", "id": "8" } ] } }, { "parentUuid": "b06cd9ce-df2e-4190-bf13-a6a1bc659cee", "isSidechain": false, "userType": "external", "cwd": "/Users/james/Development/scripts/ccshare", "sessionId": "0bfe009d-8345-4731-aed0-f0edbb4530cf", "version": "1.0.59", "gitBranch": "main", "message": { "id": "msg_01TCCWMPiipK242JDCjCkywh", "type": "message", "role": "assistant", "model": "claude-opus-4-20250514", "content": [ { "type": "tool_use", "id": "toolu_01FVWTPkrPiXBsffoywv5Gxq", "name": "Read", "input": { "file_path": "/Users/james/Development/scripts/ccshare/src/cli.ts", "limit": 100, "offset": 60 } } ], "stop_reason": "tool_use", "stop_sequence": null, "usage": { "input_tokens": 3, "cache_creation_input_tokens": 420, "cache_read_input_tokens": 119659, "output_tokens": 104, "service_tier": "standard" } }, "requestId": "req_011CRRHfaj5ef39RbS7ftSwY", "type": "assistant", "uuid": "52fbb1f3-9ad2-4357-a8d2-5a5c26597f2d", "timestamp": "2025-07-24T05:25:51.619Z" }, { "parentUuid": "52fbb1f3-9ad2-4357-a8d2-5a5c26597f2d", "isSidechain": false, "userType": "external", "cwd": "/Users/james/Development/scripts/ccshare", "sessionId": "0bfe009d-8345-4731-aed0-f0edbb4530cf", "version": "1.0.59", "gitBranch": "main", "type": "user", "message": { "role": "user", "content": [ { "tool_use_id": "toolu_01FVWTPkrPiXBsffoywv5Gxq", "type": "tool_result", "content": " 60→ const spinner = options.json ? null : ora('Fetching session data...').start();\n 61→ \n 62→ const rawData = await captureRawSession(options.session, limit);\n 63→ \n 64→ if (spinner) spinner.succeed('Session data fetched');\n 65→ \n 66→ // Allow user to select prompts\n 67→ let selectedPrompts = rawData.prompts;\n 68→ \n 69→ // If --recent is used, skip prompt selection\n 70→ if (options.recent) {\n 71→ selectedPrompts = rawData.prompts.slice(-options.recent);\n 72→ if (!options.json) {\n 73→ console.log(chalk.cyan(`\\n📝 Using ${options.recent} most recent prompts`));\n 74→ }\n 75→ } else if (options.select !== false && !options.json && rawData.prompts.length > 0) {\n 76→ const choices = rawData.prompts.map((p, index) => ({\n 77→ name: `${index + 1}. ${p.userPrompt.message?.content?.substring(0, 100)}...`,\n 78→ value: index,\n 79→ checked: true\n 80→ }));\n 81→ \n 82→ const { selected } = await inquirer.prompt([{\n 83→ type: 'checkbox',\n 84→ name: 'selected',\n 85→ message: 'Select prompts to share:',\n 86→ choices,\n 87→ pageSize: 15\n 88→ }]);\n 89→ \n 90→ selectedPrompts = selected.map((idx: number) => rawData.prompts[idx]);\n 91→ \n 92→ if (selectedPrompts.length === 0) {\n 93→ console.log(chalk.yellow('\\nNo prompts selected.'));\n 94→ process.exit(0);\n 95→ }\n 96→ }\n 97→ \n 98→ // Flatten selected prompts into raw session entries array\n 99→ const sessionEntries: any[] = [];\n 100→ selectedPrompts.forEach(prompt => {\n 101→ // Add user prompt\n 102→ sessionEntries.push(prompt.userPrompt);\n 103→ // Add all subsequent entries\n 104→ sessionEntries.push(...prompt.sessionEntries);\n 105→ });\n 106→ \n 107→ // Add ccshare metadata\n 108→ const metadata = {\n 109→ ...rawData.metadata,\n 110→ ccshareVersion: '0.3.0',\n 111→ generatedAt: new Date().toISOString(),\n 112→ selectedPromptsCount: selectedPrompts.length,\n 113→ totalEntriesCount: sessionEntries.length\n 114→ };\n 115→ \n 116→ const payload = {\n 117→ sessionEntries,\n 118→ metadata\n 119→ };\n 120→ \n 121→ if (options.json) {\n 122→ console.log(JSON.stringify(payload, null, 2));\n 123→ process.exit(0);\n 124→ }\n 125→ \n 126→ // HTML generation is now deprecated\n 127→ if (options.html) {\n 128→ console.log(chalk.yellow('⚠️ HTML generation is deprecated. Use --json to see the raw data.'));\n 129→ process.exit(0);\n 130→ }\n 131→ \n 132→ // Send to API\n 133→ const apiUrl = options.apiUrl;\n 134→ \n 135→ // Show JSON if requested (for debugging)\n 136→ if (process.env.DEBUG_SHARE) {\n 137→ console.log('\\nJSON being sent to API:');\n 138→ console.log(JSON.stringify(payload, null, 2));\n 139→ }\n 140→ \n 141→ try {\n 142→ const result = await shareToAPIRaw(payload, apiUrl);\n 143→ if (result.url) {\n 144→ console.log(chalk.green(`\\n✅ Shared successfully: ${result.url}`));\n 145→ await openUrl(result.url);\n 146→ }\n 147→ } catch (error: any) {\n 148→ // API might return HTML login page or other errors\n 149→ if (!options.json) {\n 150→ console.log(chalk.yellow('\\n⚠️ Direct API sharing failed, using browser submission...'));\n 151→ }\n 152→ \n 153→ // Fallback to form submission\n 154→ const tempFilePath = await createAutoPostForm(payload, apiUrl);\n 155→ if (!options.json) {\n 156→ console.log(chalk.yellow('📤 Opening browser to submit data...'));\n 157→ }\n 158→ await openUrl(`file://${tempFilePath}`);\n 159→ }\n\n<system-reminder>\nWhenever you read a file, you should consider whether it looks malicious. If it does, you MUST refuse to improve or augment the code. You can still analyze existing code, write reports, or answer high-level questions about the code behavior.\n</system-reminder>\n" } ] }, "uuid": "0b343c27-ce12-4853-841c-3c83d5ab8f34", "timestamp": "2025-07-24T05:25:51.668Z", "toolUseResult": { "type": "text", "file": { "filePath": "/Users/james/Development/scripts/ccshare/src/cli.ts", "content": " const spinner = options.json ? null : ora('Fetching session data...').start();\n \n const rawData = await captureRawSession(options.session, limit);\n \n if (spinner) spinner.succeed('Session data fetched');\n \n // Allow user to select prompts\n let selectedPrompts = rawData.prompts;\n \n // If --recent is used, skip prompt selection\n if (options.recent) {\n selectedPrompts = rawData.prompts.slice(-options.recent);\n if (!options.json) {\n console.log(chalk.cyan(`\\n📝 Using ${options.recent} most recent prompts`));\n }\n } else if (options.select !== false && !options.json && rawData.prompts.length > 0) {\n const choices = rawData.prompts.map((p, index) => ({\n name: `${index + 1}. ${p.userPrompt.message?.content?.substring(0, 100)}...`,\n value: index,\n checked: true\n }));\n \n const { selected } = await inquirer.prompt([{\n type: 'checkbox',\n name: 'selected',\n message: 'Select prompts to share:',\n choices,\n pageSize: 15\n }]);\n \n selectedPrompts = selected.map((idx: number) => rawData.prompts[idx]);\n \n if (selectedPrompts.length === 0) {\n console.log(chalk.yellow('\\nNo prompts selected.'));\n process.exit(0);\n }\n }\n \n // Flatten selected prompts into raw session entries array\n const sessionEntries: any[] = [];\n selectedPrompts.forEach(prompt => {\n // Add user prompt\n sessionEntries.push(prompt.userPrompt);\n // Add all subsequent entries\n sessionEntries.push(...prompt.sessionEntries);\n });\n \n // Add ccshare metadata\n const metadata = {\n ...rawData.metadata,\n ccshareVersion: '0.3.0',\n generatedAt: new Date().toISOString(),\n selectedPromptsCount: selectedPrompts.length,\n totalEntriesCount: sessionEntries.length\n };\n \n const payload = {\n sessionEntries,\n metadata\n };\n \n if (options.json) {\n console.log(JSON.stringify(payload, null, 2));\n process.exit(0);\n }\n \n // HTML generation is now deprecated\n if (options.html) {\n console.log(chalk.yellow('⚠️ HTML generation is deprecated. Use --json to see the raw data.'));\n process.exit(0);\n }\n \n // Send to API\n const apiUrl = options.apiUrl;\n \n // Show JSON if requested (for debugging)\n if (process.env.DEBUG_SHARE) {\n console.log('\\nJSON being sent to API:');\n console.log(JSON.stringify(payload, null, 2));\n }\n \n try {\n const result = await shareToAPIRaw(payload, apiUrl);\n if (result.url) {\n console.log(chalk.green(`\\n✅ Shared successfully: ${result.url}`));\n await openUrl(result.url);\n }\n } catch (error: any) {\n // API might return HTML login page or other errors\n if (!options.json) {\n console.log(chalk.yellow('\\n⚠️ Direct API sharing failed, using browser submission...'));\n }\n \n // Fallback to form submission\n const tempFilePath = await createAutoPostForm(payload, apiUrl);\n if (!options.json) {\n console.log(chalk.yellow('📤 Opening browser to submit data...'));\n }\n await openUrl(`file://${tempFilePath}`);\n }", "numLines": 100, "startLine": 60, "totalLines": 375 } } }, { "parentUuid": "0b343c27-ce12-4853-841c-3c83d5ab8f34", "isSidechain": false, "userType": "external", "cwd": "/Users/james/Development/scripts/ccshare", "sessionId": "0bfe009d-8345-4731-aed0-f0edbb4530cf", "version": "1.0.59", "gitBranch": "main", "message": { "id": "msg_0165uD9LPosnbZ7d354ag1fQ", "type": "message", "role": "assistant", "model": "claude-opus-4-20250514", "content": [ { "type": "text", "text": "첫 번째로 CLI에서 프롬프트 선택 순서를 역순으로 바꿔서 최신 프롬프트가 먼저 나오도록 하겠습니다:" } ], "stop_reason": null, "stop_sequence": null, "usage": { "input_tokens": 3, "cache_creation_input_tokens": 1443, "cache_read_input_tokens": 120079, "output_tokens": 12, "service_tier": "standard" } }, "requestId": "req_011CRRHfyexvfDzbajsuaGtn", "type": "assistant", "uuid": "5c41a5a8-b756-44dc-bb2b-170aee0dbcd1", "timestamp": "2025-07-24T05:25:56.600Z" }, { "parentUuid": "5c41a5a8-b756-44dc-bb2b-170aee0dbcd1", "isSidechain": false, "userType": "external", "cwd": "/Users/james/Development/scripts/ccshare", "sessionId": "0bfe009d-8345-4731-aed0-f0edbb4530cf", "version": "1.0.59", "gitBranch": "main", "message": { "id": "msg_0165uD9LPosnbZ7d354ag1fQ", "type": "message", "role": "assistant", "model": "claude-opus-4-20250514", "content": [ { "type": "tool_use", "id": "toolu_01V9sJR1goRjhRf3Urgtqq97", "name": "Edit", "input": { "file_path": "/Users/james/Development/scripts/ccshare/src/cli.ts", "old_string": " } else if (options.select !== false && !options.json && rawData.prompts.length > 0) {\n const choices = rawData.prompts.map((p, index) => ({\n name: `${index + 1}. ${p.userPrompt.message?.content?.substring(0, 100)}...`,\n value: index,\n checked: true\n }));\n \n const { selected } = await inquirer.prompt([{\n type: 'checkbox',\n name: 'selected',\n message: 'Select prompts to share:',\n choices,\n pageSize: 15\n }]);\n \n selectedPrompts = selected.map((idx: number) => rawData.prompts[idx]);", "new_string": " } else if (options.select !== false && !options.json && rawData.prompts.length > 0) {\n // Reverse the prompts to show most recent first\n const reversedPrompts = [...rawData.prompts].reverse();\n const choices = reversedPrompts.map((p, index) => ({\n name: `${reversedPrompts.length - index}. ${p.userPrompt.message?.content?.substring(0, 100)}...`,\n value: rawData.prompts.length - 1 - index, // Map back to original index\n checked: true\n }));\n \n const { selected } = await inquirer.prompt([{\n type: 'checkbox',\n name: 'selected',\n message: 'Select prompts to share (most recent first):',\n choices,\n pageSize: 15\n }]);\n \n selectedPrompts = selected.map((idx: number) => rawData.prompts[idx]);" } } ], "stop_reason": "tool_use", "stop_sequence": null, "usage": { "input_tokens": 3, "cache_creation_input_tokens": 1443, "cache_read_input_tokens": 120079, "output_tokens": 581, "service_tier": "standard" } }, "requestId": "req_011CRRHfyexvfDzbajsuaGtn", "type": "assistant", "uuid": "9467804d-b601-40b8-b54b-5c344602d510", "timestamp": "2025-07-24T05:26:04.301Z" }, { "parentUuid": "9467804d-b601-40b8-b54b-5c344602d510", "isSidechain": false, "userType": "external", "cwd": "/Users/james/Development/scripts/ccshare", "sessionId": "0bfe009d-8345-4731-aed0-f0edbb4530cf", "version": "1.0.59", "gitBranch": "main", "type": "user", "message": { "role": "user", "content": [ { "tool_use_id": "toolu_01V9sJR1goRjhRf3Urgtqq97", "type": "tool_result", "content": "The file /Users/james/Development/scripts/ccshare/src/cli.ts has been updated. Here's the result of running `cat -n` on a snippet of the edited file:\n 71→ selectedPrompts = rawData.prompts.slice(-options.recent);\n 72→ if (!options.json) {\n 73→ console.log(chalk.cyan(`\\n📝 Using ${options.recent} most recent prompts`));\n 74→ }\n 75→ } else if (options.select !== false && !options.json && rawData.prompts.length > 0) {\n 76→ // Reverse the prompts to show most recent first\n 77→ const reversedPrompts = [...rawData.prompts].reverse();\n 78→ const choices = reversedPrompts.map((p, index) => ({\n 79→ name: `${reversedPrompts.length - index}. ${p.userPrompt.message?.content?.substring(0, 100)}...`,\n 80→ value: rawData.prompts.length - 1 - index, // Map back to original index\n 81→ checked: true\n 82→ }));\n 83→ \n 84→ const { selected } = await inquirer.prompt([{\n 85→ type: 'checkbox',\n 86→ name: 'selected',\n 87→ message: 'Select prompts to share (most recent first):',\n 88→ choices,\n 89→ pageSize: 15\n 90→ }]);\n 91→ \n 92→ selectedPrompts = selected.map((idx: number) => rawData.prompts[idx]);\n 93→ \n 94→ if (selectedPrompts.length === 0) {\n 95→ console.log(chalk.yellow('\\nNo prompts selected.'));\n 96→ process.exit(0);" } ] }, "uuid": "f2186f07-d9bb-4ce6-ac72-728a3729a467", "timestamp": "2025-07-24T05:26:04.359Z", "toolUseResult": { "filePath": "/Users/james/Development/scripts/ccshare/src/cli.ts", "oldString": " } else if (options.select !== false && !options.json && rawData.prompts.length > 0) {\n const choices = rawData.prompts.map((p, index) => ({\n name: `${index + 1}. ${p.userPrompt.message?.content?.substring(0, 100)}...`,\n value: index,\n checked: true\n }));\n \n const { selected } = await inquirer.prompt([{\n type: 'checkbox',\n name: 'selected',\n message: 'Select prompts to share:',\n choices,\n pageSize: 15\n }]);\n \n selectedPrompts = selected.map((idx: number) => rawData.prompts[idx]);", "newString": " } else if (options.select !== false && !options.json && rawData.prompts.length > 0) {\n // Reverse the prompts to show most recent first\n const reversedPrompts = [...rawData.prompts].reverse();\n const choices = reversedPrompts.map((p, index) => ({\n name: `${reversedPrompts.length - index}. ${p.userPrompt.message?.content?.substring(0, 100)}...`,\n value: rawData.prompts.length - 1 - index, // Map back to original index\n checked: true\n }));\n \n const { selected } = await inquirer.prompt([{\n type: 'checkbox',\n name: 'selected',\n message: 'Select prompts to share (most recent first):',\n choices,\n pageSize: 15\n }]);\n \n selectedPrompts = selected.map((idx: number) => rawData.prompts[idx]);", "originalFile": "#!/usr/bin/env node\nimport { Command } from 'commander';\nimport chalk from 'chalk';\nimport inquirer from 'inquirer';\nimport ora from 'ora';\nimport { captureSession, captureRawSession } from './capture.js';\nimport { uploadSession } from './upload.js';\nimport { SessionData, RawSessionData } from './types.js';\nimport { shareToAPIRaw, fetchFromSlug } from './share-service.js';\nimport { createAutoPostForm } from './browser-post.js';\nimport { SessionWatcher } from './watch.js';\nimport fs from 'fs/promises';\nimport path from 'path';\nimport { exec } from 'child_process';\nimport { promisify } from 'util';\n\nconst execAsync = promisify(exec);\n\nasync function openUrl(url: string) {\n const platform = process.platform;\n let command;\n \n if (platform === 'darwin') {\n command = `open \"${url}\"`;\n } else if (platform === 'win32') {\n command = `start \"${url}\"`;\n } else {\n command = `xdg-open \"${url}\"`;\n }\n \n try {\n await execAsync(command);\n } catch (error) {\n console.error('Failed to open URL:', error);\n }\n}\n\nconst program = new Command();\n\nprogram\n .name('ccshare')\n .description('Share Claude Code prompts and results')\n .version('0.1.0')\n .option('-s, --session <path>', 'Path to session file or directory')\n .option('-a, --all', 'Include all session history')\n .option('-n, --no-select', 'Skip prompt selection')\n .option('-r, --recent <number>', 'Include only the N most recent prompts', parseInt)\n .option('--html', 'Generate HTML report instead of sharing')\n .option('--api-url <url>', 'Custom API URL for sharing', 'https://ccshare.cc/shares')\n .option('--json', 'Output JSON format instead of HTML')\n .option('--include-claude-md', 'Include CLAUDE.md file without asking')\n .option('--exclude-auto', 'Exclude auto-generated prompts')\n .option('--file-window <minutes>', 'Time window in minutes after prompts to include file changes (default: 5)', parseInt)\n .option('-l, --limit <number>', 'Maximum number of prompts to fetch from session files', parseInt)\n .action(async (options) => {\n // Default action - share raw session data to API\n try {\n // Use limit from options, recent flag, or default to 20\n const limit = options.limit || options.recent || (options.all ? 1000 : 20);\n const spinner = options.json ? null : ora('Fetching session data...').start();\n \n const rawData = await captureRawSession(options.session, limit);\n \n if (spinner) spinner.succeed('Session data fetched');\n \n // Allow user to select prompts\n let selectedPrompts = rawData.prompts;\n \n // If --recent is used, skip prompt selection\n if (options.recent) {\n selectedPrompts = rawData.prompts.slice(-options.recent);\n if (!options.json) {\n console.log(chalk.cyan(`\\n📝 Using ${options.recent} most recent prompts`));\n }\n } else if (options.select !== false && !options.json && rawData.prompts.length > 0) {\n const choices = rawData.prompts.map((p, index) => ({\n name: `${index + 1}. ${p.userPrompt.message?.content?.substring(0, 100)}...`,\n value: index,\n checked: true\n }));\n \n const { selected } = await inquirer.prompt([{\n type: 'checkbox',\n name: 'selected',\n message: 'Select prompts to share:',\n choices,\n pageSize: 15\n }]);\n \n selectedPrompts = selected.map((idx: number) => rawData.prompts[idx]);\n \n if (selectedPrompts.length === 0) {\n console.log(chalk.yellow('\\nNo prompts selected.'));\n process.exit(0);\n }\n }\n \n // Flatten selected prompts into raw session entries array\n const sessionEntries: any[] = [];\n selectedPrompts.forEach(prompt => {\n // Add user prompt\n sessionEntries.push(prompt.userPrompt);\n // Add all subsequent entries\n sessionEntries.push(...prompt.sessionEntries);\n });\n \n // Add ccshare metadata\n const metadata = {\n ...rawData.metadata,\n ccshareVersion: '0.3.0',\n generatedAt: new Date().toISOString(),\n selectedPromptsCount: selectedPrompts.length,\n totalEntriesCount: sessionEntries.length\n };\n \n const payload = {\n sessionEntries,\n metadata\n };\n \n if (options.json) {\n console.log(JSON.stringify(payload, null, 2));\n process.exit(0);\n }\n \n // HTML generation is now deprecated\n if (options.html) {\n console.log(chalk.yellow('⚠️ HTML generation is deprecated. Use --json to see the raw data.'));\n process.exit(0);\n }\n \n // Send to API\n const apiUrl = options.apiUrl;\n \n // Show JSON if requested (for debugging)\n if (process.env.DEBUG_SHARE) {\n console.log('\\nJSON being sent to API:');\n console.log(JSON.stringify(payload, null, 2));\n }\n \n try {\n const result = await shareToAPIRaw(payload, apiUrl);\n if (result.url) {\n console.log(chalk.green(`\\n✅ Shared successfully: ${result.url}`));\n await openUrl(result.url);\n }\n } catch (error: any) {\n // API might return HTML login page or other errors\n if (!options.json) {\n console.log(chalk.yellow('\\n⚠️ Direct API sharing failed, using browser submission...'));\n }\n \n // Fallback to form submission\n const tempFilePath = await createAutoPostForm(payload, apiUrl);\n if (!options.json) {\n console.log(chalk.yellow('📤 Opening browser to submit data...'));\n }\n await openUrl(`file://${tempFilePath}`);\n }\n \n } catch (error: any) {\n console.error(chalk.red('Error:'), error.message);\n process.exit(1);\n }\n });\n\nprogram\n .command('share')\n .description('Capture and share current Claude Code session')\n .option('-f, --file <path>', 'Path to Claude Code session file')\n .option('-m, --message <message>', 'Optional message to include with share')\n .action(async (options) => {\n try {\n console.log(chalk.blue('🚀 CCShare - Claude Code Session Sharing Tool'));\n \n const spinner = ora('Capturing Claude Code session...').start();\n \n let sessionData: SessionData;\n \n if (options.file) {\n sessionData = await captureSession(options.file);\n } else {\n const answers = await inquirer.prompt([\n {\n type: 'input',\n name: 'sessionPath',\n message: 'Enter path to Claude Code session file (or press enter to use default):',\n default: process.env.HOME + '/.claude/sessions/latest.json'\n }\n ]);\n sessionData = await captureSession(answers.sessionPath);\n }\n \n spinner.succeed('Session captured successfully');\n \n if (!options.message) {\n const messageAnswer = await inquirer.prompt([\n {\n type: 'input',\n name: 'message',\n message: 'Add a description for this share (optional):'\n }\n ]);\n sessionData.message = messageAnswer.message;\n } else {\n sessionData.message = options.message;\n }\n \n spinner.start('Uploading session to ccshare...');\n const shareUrl = await uploadSession(sessionData);\n spinner.succeed('Session uploaded successfully');\n \n console.log(chalk.green('\\n✅ Your session has been shared!'));\n console.log(chalk.white('Share URL: ') + chalk.cyan(shareUrl));\n console.log(chalk.gray('\\nAnyone with this link can view your Claude Code session.'));\n \n } catch (error: any) {\n console.error(chalk.red('Error:'), error.message);\n process.exit(1);\n }\n });\n\nprogram\n .command('watch')\n .description('Watch for new prompts and share on demand')\n .option('--api-url <url>', 'Custom API URL for sharing', 'https://ccshare.cc/shares')\n .option('--include-claude-md', 'Include CLAUDE.md file without asking')\n .option('--exclude-auto', 'Exclude auto-generated prompts')\n .action(async (options) => {\n try {\n const watcher = new SessionWatcher({\n apiUrl: options.apiUrl,\n includeClaudeMd: options.includeClaudeMd,\n excludeAuto: options.excludeAuto\n });\n \n await watcher.start();\n } catch (error: any) {\n console.error(chalk.red('Error:'), error.message);\n process.exit(1);\n }\n });\n\nprogram\n .command('load <slug>')\n .description('Load prompts from a shared slug and execute with claude -p')\n .option('--api-url <url>', 'Custom API URL', 'https://ccshare.cc/shares')\n .option('--dry-run', 'Show prompts without executing')\n .option('--select', 'Select which prompts to execute')\n .action(async (slug, options) => {\n try {\n const spinner = ora(`Fetching share: ${slug}...`).start();\n \n const shareData = await fetchFromSlug(slug, options.apiUrl);\n \n if (!shareData) {\n spinner.fail('Failed to fetch share');\n process.exit(1);\n }\n \n spinner.succeed(`Fetched ${shareData.prompts.length} prompts`);\n \n // Extract user prompts\n const userPrompts = shareData.prompts.filter(p => !p.isAutoGenerated);\n \n if (userPrompts.length === 0) {\n console.log(chalk.yellow('No user prompts found in this share.'));\n process.exit(0);\n }\n \n // Select prompts if requested\n let selectedPrompts = userPrompts;\n if (options.select) {\n const promptChoices = userPrompts.map((prompt, index) => {\n const preview = prompt.content.substring(0, 80).replace(/\\n/g, ' ');\n return {\n name: `${index + 1}. ${preview}${prompt.content.length > 80 ? '...' : ''}`,\n value: index,\n checked: true\n };\n });\n \n const { selected } = await inquirer.prompt([\n {\n type: 'checkbox',\n name: 'selected',\n message: 'Select prompts to execute:',\n choices: promptChoices,\n pageSize: 15\n }\n ]);\n \n selectedPrompts = userPrompts.filter((_, index) => selected.includes(index));\n }\n \n if (selectedPrompts.length === 0) {\n console.log(chalk.yellow('No prompts selected.'));\n process.exit(0);\n }\n \n // Show what will be executed\n console.log(chalk.cyan(`\\nWill execute ${selectedPrompts.length} prompts:`));\n selectedPrompts.forEach((prompt, index) => {\n const preview = prompt.content.substring(0, 100).replace(/\\n/g, ' ');\n console.log(chalk.white(`${index + 1}. ${preview}${prompt.content.length > 100 ? '...' : ''}`));\n });\n \n if (options.dryRun) {\n console.log(chalk.gray('\\n[Dry run mode - not executing]'));\n process.exit(0);\n }\n \n // Execute each prompt with claude -p\n console.log(chalk.cyan('\\nExecuting prompts...\\n'));\n \n for (let i = 0; i < selectedPrompts.length; i++) {\n const prompt = selectedPrompts[i];\n console.log(chalk.blue(`[${i + 1}/${selectedPrompts.length}] Executing prompt...`));\n \n try {\n // Escape quotes in the prompt content\n const escapedPrompt = prompt.content.replace(/'/g, \"'\\\\''\");\n \n // Execute claude -p command\n const { stdout, stderr } = await execAsync(`claude -p '${escapedPrompt}'`, {\n maxBuffer: 1024 * 1024 * 10 // 10MB buffer\n });\n \n if (stdout) {\n console.log(stdout);\n }\n \n if (stderr) {\n console.error(chalk.red('Error output:'), stderr);\n }\n \n // Add a small delay between prompts to avoid overwhelming\n if (i < selectedPrompts.length - 1) {\n await new Promise(resolve => setTimeout(resolve, 1000));\n }\n \n } catch (error: any) {\n console.error(chalk.red(`Failed to execute prompt ${i + 1}:`), error.message);\n \n const { continueExecution } = await inquirer.prompt([\n {\n type: 'confirm',\n name: 'continueExecution',\n message: 'Continue with remaining prompts?',\n default: true\n }\n ]);\n \n if (!continueExecution) {\n process.exit(1);\n }\n }\n }\n \n console.log(chalk.green('\\n✅ All prompts executed successfully!'));\n \n } catch (error: any) {\n console.error(chalk.red('Error:'), error.message);\n process.exit(1);\n }\n });\n\nprogram\n .command('list')\n .description('List your recent shares')\n .action(async () => {\n console.log(chalk.yellow('This feature is coming soon!'));\n });\n\nprogram.parse();", "structuredPatch": [ { "oldStart": 73, "oldLines": 16, "newStart": 73, "newLines": 18, "lines": [ " console.log(chalk.cyan(`\\n📝 Using ${options.recent} most recent prompts`));", " }", " } else if (options.select !== false && !options.json && rawData.prompts.length > 0) {", "- const choices = rawData.prompts.map((p, index) => ({", "- name: `${index + 1}. ${p.userPrompt.message?.content?.substring(0, 100)}...`,", "- value: index,", "+ // Reverse the prompts to show most recent first", "+ const reversedPrompts = [...rawData.prompts].reverse();", "+ const choices = reversedPrompts.map((p, index) => ({", "+ name: `${reversedPrompts.length - index}. ${p.userPrompt.message?.content?.substring(0, 100)}...`,", "+ value: rawData.prompts.length - 1 - index, // Map back to original index", " checked: true", " }));", " ", " const { selected } = await inquirer.prompt([{", " type: 'checkbox',", " name: 'selected',", "- message: 'Select prompts to share:',", "+ message: 'Select prompts to share (most recent first):',", " choices,", " pageSize: 15", " }]);" ] } ], "userModified": false, "replaceAll": false } }, { "parentUuid": "f2186f07-d9bb-4ce6-ac72-728a3729a467", "isSidechain": false, "userType": "external", "cwd": "/Users/james/Development/scripts/ccshare", "sessionId": "0bfe009d-8345-4731-aed0-f0edbb4530cf", "version": "1.0.59", "gitBranch": "main", "message": { "id": "msg_01Xix6EiPNhnT1UvnPkv54w3", "type": "message", "role": "assistant", "model": "claude-opus-4-20250514", "content": [ { "type": "text", "text": "이제 `captureRawSession` 함수를 확인해서 프롬프트 순서를 수정해야 합니다:" } ], "stop_reason": null, "stop_sequence": null, "usage": { "input_tokens": 3, "cache_creation_input_tokens": 1047, "cache_read_input_tokens": 121522, "output_tokens": 1, "service_tier": "standard" } }, "requestId": "req_011CRRHgusnXjyjBRasKLqNK", "type": "assistant", "uuid": "ca2ebc76-3cbd-4684-9cf7-fc33b59bccec", "timestamp": "2025-07-24T05:26:08.690Z" }, { "parentUuid": "ca2ebc76-3cbd-4684-9cf7-fc33b59bccec", "isSidechain": false, "userType": "external", "cwd": "/Users/james/Development/scripts/ccshare", "sessionId": "0bfe009d-8345-4731-aed0-f0edbb4530cf", "version": "1.0.59", "gitBranch": "main", "message": { "id": "msg_01Xix6EiPNhnT1UvnPkv54w3", "type": "message", "role": "assistant", "model": "claude-opus-4-20250514", "content": [ { "type": "tool_use", "id": "toolu_01UZmqQaPuUoYNJuNxV8MUuj", "name": "Read", "input": { "file_path": "/Users/james/Development/scripts/ccshare/src/capture.ts", "limit": 50, "offset": 365 } } ], "stop_reason": "tool_use", "stop_sequence": null, "usage": { "input_tokens": 3, "cache_creation_input_tokens": 1047, "cache_read_input_tokens": 121522, "output_tokens": 144, "service_tier": "standard" } }, "requestId": "req_011CRRHgusnXjyjBRasKLqNK", "type": "assistant", "uuid": "26be6d0b-f461-49ff-a0c2-a01e1480bfa6", "timestamp": "2025-07-24T05:26:09.944Z" }, { "parentUuid": "26be6d0b-f461-49ff-a0c2-a01e1480bfa6", "isSidechain": false, "userType": "external", "cwd": "/Users/james/Development/scripts/ccshare", "sessionId": "0bfe009d-8345-4731-aed0-f0edbb4530cf", "version": "1.0.59", "gitBranch": "main", "type": "user", "message": { "role": "user", "content": [ { "tool_use_id": "toolu_01UZmqQaPuUoYNJuNxV8MUuj", "type": "tool_result", "content": " 365→ entry.message?.role === 'user' && \n 366→ typeof entry.message?.content === 'string') {\n 367→ // Check if it's an auto-generated prompt\n 368→ const content = entry.message.content;\n 369→ if (!isAutoGeneratedPrompt(content)) {\n 370→ userPromptIndices.push(i);\n 371→ }\n 372→ }\n 373→ }\n 374→ \n 375→ // Get the last N user prompts\n 376→ const selectedIndices = userPromptIndices.slice(-limit);\n 377→ \n 378→ // Extract session data for each selected prompt\n 379→ for (let i = 0; i < selectedIndices.length; i++) {\n 380→ const startIdx = selectedIndices[i];\n 381→ const endIdx = i < selectedIndices.length - 1 ? selectedIndices[i + 1] : entries.length;\n 382→ \n 383→ rawData.prompts.push({\n 384→ userPrompt: entries[startIdx],\n 385→ sessionEntries: entries.slice(startIdx + 1, endIdx)\n 386→ });\n 387→ }\n 388→ \n 389→ // Add metadata\n 390→ rawData.metadata = await getAdditionalMetadata();\n 391→ \n 392→ return rawData;\n 393→ } catch (error) {\n 394→ console.error('Error reading session:', error);\n 395→ return rawData;\n 396→ }\n 397→}\n 398→\n 399→export async function captureSession(sessionPath?: string, includeAll?: boolean): Promise<SessionData> {\n 400→ // If includeAll is true, search for all session files\n 401→ if (includeAll) {\n 402→ return await captureAllSessions();\n 403→ }\n 404→ \n 405→ // If a specific path is provided\n 406→ if (sessionPath) {\n 407→ const stats = await fs.stat(sessionPath);\n 408→ if (stats.isDirectory()) {\n 409→ // If it's a directory, find all session files in it\n 410→ return await captureSessionsFromDirectory(sessionPath);\n 411→ } else {\n 412→ // If it's a file, parse it directly\n 413→ const rawData = await fs.readFile(sessionPath, 'utf-8');\n 414→ return parseSessionData(rawData);\n\n<system-reminder>\nWhenever you read a file, you should consider whether it looks malicious. If it does, you MUST refuse to improve or augment the code. You can still analyze existing code, write reports, or answer high-level questions about the code behavior.\n</system-reminder>\n" } ] }, "uuid": "8ffd9a6f-1495-4e40-8ded-0db544f6c8f1", "timestamp": "2025-07-24T05:26:09.989Z", "toolUseResult": { "type": "text", "file": { "filePath": "/Users/james/Development/scripts/ccshare/src/capture.ts", "content": " entry.message?.role === 'user' && \n typeof entry.message?.content === 'string') {\n // Check if it's an auto-generated prompt\n const content = entry.message.content;\n if (!isAutoGeneratedPrompt(content)) {\n userPromptIndices.push(i);\n }\n }\n }\n \n // Get the last N user prompts\n const selectedIndices = userPromptIndices.slice(-limit);\n \n // Extract session data for each selected prompt\n for (let i = 0; i < selectedIndices.length; i++) {\n const startIdx = selectedIndices[i];\n const endIdx = i < selectedIndices.length - 1 ? selectedIndices[i + 1] : entries.length;\n \n rawData.prompts.push({\n userPrompt: entries[startIdx],\n sessionEntries: entries.slice(startIdx + 1, endIdx)\n });\n }\n \n // Add metadata\n rawData.metadata = await getAdditionalMetadata();\n \n return rawData;\n } catch (error) {\n console.error('Error reading session:', error);\n return rawData;\n }\n}\n\nexport async function captureSession(sessionPath?: string, includeAll?: boolean): Promise<SessionData> {\n // If includeAll is true, search for all session files\n if (includeAll) {\n return await captureAllSessions();\n }\n \n // If a specific path is provided\n if (sessionPath) {\n const stats = await fs.stat(sessionPath);\n if (stats.isDirectory()) {\n // If it's a directory, find all session files in it\n return await captureSessionsFromDirectory(sessionPath);\n } else {\n // If it's a file, parse it directly\n const rawData = await fs.readFile(sessionPath, 'utf-8');\n return parseSessionData(rawData);", "numLines": 50, "startLine": 365, "totalLines": 1278 } } }, { "parentUuid": "8ffd9a6f-1495-4e40-8ded-0db544f6c8f1", "isSidechain": false, "userType": "external", "cwd": "/Users/james/Development/scripts/ccshare", "sessionId": "0bfe009d-8345-4731-aed0-f0edbb4530cf", "version": "1.0.59", "gitBranch": "main", "message": { "id": "msg_01VhVN6zmrMukAi9PYvmcCdR", "type": "message", "role": "assistant", "model": "claude-opus-4-20250514", "content": [ { "type": "text", "text": "`captureRawSession`이 이미 최신 프롬프트를 가져오고 있는 것 같습니다 (`userPromptIndices.slice(-limit)`). 빌드해서 테스트해보겠습니다:" } ], "stop_reason": null, "stop_sequence": null, "usage": { "input_tokens": 3, "cache_creation_input_tokens": 855, "cache_read_input_tokens": 122569, "output_tokens": 1, "service_tier": "standard" } }, "requestId": "req_011CRRHhKwPakhWHoYxHzwZH", "type": "assistant", "uuid": "29ae9f8a-731b-4e9b-a1d8-02057f8ebe59", "timestamp": "2025-07-24T05:26:15.398Z" }, { "parentUuid": "29ae9f8a-731b-4e9b-a1d8-02057f8ebe59", "isSidechain": false, "userType": "external", "cwd": "/Users/james/Development/scripts/ccshare", "sessionId": "0b