ccshare
Version:
Share Claude Code prompts and results easily
611 lines • 176 kB
JSON
{
"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