claude-session-share 1.1.0 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -2,18 +2,15 @@
2
2
 
3
3
  MCP server for sharing Claude Code sessions via GitHub Gist with automatic privacy protection.
4
4
 
5
- Share your Claude Code conversations while keeping private data safe. Export sessions to shareable GitHub Gist links and import them back—all through natural language or CLI.
6
-
7
5
  [![npm version](https://badge.fury.io/js/claude-session-share.svg)](https://www.npmjs.com/package/claude-session-share)
8
6
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
9
7
 
10
8
  ## Features
11
9
 
12
- - **One-Click Sharing** - Export sessions to GitHub Gist with a simple command
10
+ - **One-Click Sharing** - Export sessions to GitHub Gist through natural language
13
11
  - **Privacy First** - Automatically strips thinking blocks, sanitizes paths, and redacts secrets
14
12
  - **Seamless Import** - Import shared sessions that work exactly like native Claude Code sessions
15
- - **Natural Language** - Just ask Claude to "share my session" or "import from [link]"
16
- - **Full Compatibility** - Imported sessions appear in `claude --resume` and preserve conversation context
13
+ - **Full Compatibility** - Imported sessions appear in `claude --resume`
17
14
 
18
15
  ## Installation
19
16
 
@@ -23,27 +20,16 @@ Share your Claude Code conversations while keeping private data safe. Export ses
23
20
  - Claude Code CLI
24
21
  - GitHub Personal Access Token with `gist` scope
25
22
 
26
- ### Install via npm
27
-
28
- ```bash
29
- npm install -g claude-session-share
30
- ```
31
-
32
23
  ### Setup GitHub Token
33
24
 
34
25
  1. Go to [GitHub Settings > Personal Access Tokens](https://github.com/settings/tokens)
35
26
  2. Click "Generate new token (classic)"
36
- 3. Give it a name like "Claude Session Share"
37
- 4. Check the **`gist`** scope
38
- 5. Generate and copy the token
27
+ 3. Check the **`gist`** scope
28
+ 4. Generate and copy the token
39
29
 
40
- ## Configuration
30
+ ### Configure MCP Server
41
31
 
42
- Add the MCP server to your Claude Code configuration.
43
-
44
- ### User Config (Recommended)
45
-
46
- Create or edit `~/.claude/mcp.json`:
32
+ Add to `~/.claude/mcp.json`:
47
33
 
48
34
  ```json
49
35
  {
@@ -59,21 +45,17 @@ Create or edit `~/.claude/mcp.json`:
59
45
  }
60
46
  ```
61
47
 
62
- ### Project-Specific Config
63
-
64
- Create `.mcp.json` in your project directory with the same structure.
65
-
66
48
  ### Verify Installation
67
49
 
68
50
  ```bash
69
51
  claude # Start Claude Code
70
- # Then type: /mcp
52
+ # Type: /mcp
71
53
  # You should see "claude-session-share" in the list
72
54
  ```
73
55
 
74
56
  ## Usage
75
57
 
76
- ### Natural Language (via MCP)
58
+ ### Share a Session
77
59
 
78
60
  In any Claude Code conversation:
79
61
 
@@ -84,47 +66,23 @@ In any Claude Code conversation:
84
66
  Claude will:
85
67
  1. Find your current session
86
68
  2. Remove thinking blocks and sanitize paths/secrets
87
- 3. Upload to a secret (unlisted) GitHub Gist
69
+ 3. Upload to a secret GitHub Gist
88
70
  4. Return a shareable link
89
71
 
90
- To import a shared session:
72
+ ### Import a Session
91
73
 
92
74
  ```
93
- "Import this session: https://gist.github.com/username/abc123..."
94
- ```
95
-
96
- ### Command Line (Standalone CLI)
97
-
98
- #### Share a session
99
-
100
- ```bash
101
- # Share most recent session
102
- npx claude-session-share share
103
-
104
- # Share specific session file
105
- npx claude-session-share share --session-path ~/.claude/projects/abc/session.jsonl
106
- ```
107
-
108
- #### Import a session
109
-
110
- ```bash
111
- # Import to current directory
112
- npx claude-session-share import https://gist.github.com/user/abc123
113
-
114
- # Import to specific directory
115
- npx claude-session-share import abc123 --project-path /Users/name/project
75
+ "Import this session: https://gist.github.com/username/abc123"
116
76
  ```
117
77
 
118
- ### Resuming an Imported Session
78
+ ### Resume an Imported Session
119
79
 
120
80
  ```bash
121
- cd your-project-directory
122
81
  claude --resume
123
82
  # Select the imported session from the list
124
83
  ```
125
84
 
126
- Or resume directly with the session ID:
127
-
85
+ Or directly:
128
86
  ```bash
129
87
  claude --resume <session-id>
130
88
  ```
@@ -133,132 +91,46 @@ claude --resume <session-id>
133
91
 
134
92
  Every shared session is automatically sanitized:
135
93
 
136
- ### What Gets Removed/Sanitized
137
-
138
- - **Thinking Blocks** - Internal reasoning stripped completely
139
- - **Absolute Paths** - `/Users/you/project/file.ts` → `file.ts`
140
- - **API Keys** - `sk_test_abc123`, `ghp_token`, AWS keys → `[REDACTED]`
141
- - **Tokens** - Bearer tokens, OAuth tokens → `[REDACTED]`
142
- - **Secrets** - Environment variables, passwords (key=value format) → `[REDACTED]`
143
-
144
- ### What Gets Preserved
94
+ ### Removed/Sanitized
95
+ - Thinking blocks (internal reasoning)
96
+ - Absolute paths relative paths
97
+ - API keys, tokens, secrets → `[REDACTED]`
145
98
 
99
+ ### Preserved
146
100
  - Conversation flow and context
147
- - Code examples and explanations
148
- - File names and relative paths
101
+ - Code examples
102
+ - Relative file paths
149
103
  - Tool use history
150
- - UUIDs and message chains (remapped on import)
151
-
152
- ### Known Limitations
153
104
 
154
- - Passwords in connection strings (e.g., `postgresql://user:pass@host/db`) are not detected
155
- - Secrets in natural language (not key=value format) may not be redacted
156
-
157
- ## MCP Tools Reference
105
+ ## MCP Tools
158
106
 
159
107
  ### `share_session`
160
-
161
- Exports the current session to GitHub Gist.
162
-
163
- **Parameters:**
164
- - `sessionPath` (optional) - Path to session file (defaults to most recent)
165
-
166
- **Returns:**
167
- - `gistUrl` - Shareable GitHub Gist URL
168
- - `messageCount` - Number of messages exported
108
+ Exports current session to GitHub Gist.
169
109
 
170
110
  ### `import_session`
111
+ Imports session from GitHub Gist URL.
112
+
113
+ ## Troubleshooting
171
114
 
172
- Imports a session from a GitHub Gist.
115
+ ### "Not authenticated" Error
116
+ Ensure `GITHUB_TOKEN` is set in MCP configuration.
173
117
 
174
- **Parameters:**
175
- - `gistUrl` - GitHub Gist URL or bare gist ID
176
- - `projectPath` - Local project directory for import
118
+ ### Imported Session Doesn't Appear
119
+ Restart Claude Code to refresh the session list.
177
120
 
178
- **Returns:**
179
- - `sessionPath` - Path to imported session file
180
- - `sessionId` - New session ID
181
- - `messageCount` - Number of messages imported
121
+ ### MCP Server Not Listed
122
+ Verify `~/.claude/mcp.json` and restart Claude Code.
182
123
 
183
124
  ## Development
184
125
 
185
- ### Setup
186
-
187
126
  ```bash
188
127
  git clone https://github.com/OmkarKovvali/claude-session-share.git
189
128
  cd claude-session-share
190
129
  npm install
191
- ```
192
-
193
- ### Build
194
-
195
- ```bash
196
130
  npm run build
197
- ```
198
-
199
- ### Run Tests
200
-
201
- ```bash
202
131
  npm test
203
- # 420 tests
204
- ```
205
-
206
- ### Project Structure
207
-
208
- ```
209
- claude-session-share/
210
- ├── src/
211
- │ ├── index.ts # MCP server entry point
212
- │ ├── cli.ts # CLI entry point
213
- │ ├── gist/ # GitHub Gist integration
214
- │ ├── sanitization/ # Privacy protection
215
- │ ├── services/ # Share/import orchestration
216
- │ ├── session/ # Session read/write
217
- │ └── utils/ # UUID remapping, path encoding
218
- ├── dist/ # Compiled output
219
- └── package.json
220
- ```
221
-
222
- ## Troubleshooting
223
-
224
- ### "Not authenticated" Error
225
-
226
- Ensure `GITHUB_TOKEN` is set in MCP configuration:
227
- ```json
228
- "env": {
229
- "GITHUB_TOKEN": "ghp_your_token_here"
230
- }
231
132
  ```
232
133
 
233
- ### "No sessions found" Error
234
-
235
- Ensure you're in a directory with an active Claude Code session. Sessions are stored in `~/.claude/projects/`.
236
-
237
- ### Imported Session Doesn't Appear
238
-
239
- After importing, restart Claude Code to refresh the session list. The session file should be in `~/.claude/projects/-{encoded-path}/`.
240
-
241
- ### MCP Server Not Listed
242
-
243
- Verify your MCP configuration:
244
- ```bash
245
- cat ~/.claude/mcp.json
246
- ```
247
- Then restart Claude Code.
248
-
249
- ## Contributing
250
-
251
- 1. Fork the repository
252
- 2. Create a feature branch (`git checkout -b feature/amazing-feature`)
253
- 3. Commit your changes
254
- 4. Push to the branch
255
- 5. Open a Pull Request
256
-
257
134
  ## License
258
135
 
259
136
  MIT © Omkar Kovvali
260
-
261
- ## Support
262
-
263
- - **Issues**: [GitHub Issues](https://github.com/OmkarKovvali/claude-session-share/issues)
264
- - **Email**: okovvali5@gmail.com
package/dist/index.js CHANGED
@@ -18,7 +18,7 @@ import { fileURLToPath } from 'url';
18
18
  // Create the MCP server instance
19
19
  const server = new Server({
20
20
  name: "claude-session-share",
21
- version: "1.1.0",
21
+ version: "1.2.0",
22
22
  }, {
23
23
  capabilities: {
24
24
  tools: {},
package/package.json CHANGED
@@ -1,12 +1,9 @@
1
1
  {
2
2
  "name": "claude-session-share",
3
- "version": "1.1.0",
3
+ "version": "1.2.0",
4
4
  "description": "MCP server for sharing Claude Code sessions via GitHub Gist with privacy protection",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
7
- "bin": {
8
- "claude-session-share": "dist/cli.js"
9
- },
10
7
  "files": [
11
8
  "dist/**/*",
12
9
  "README.md",
@@ -1,120 +0,0 @@
1
- /**
2
- * Tests for CLI entry point
3
- *
4
- * Validates command parsing and integration with services
5
- */
6
- import { describe, it, expect, vi } from 'vitest';
7
- import { uploadSession } from '../services/session-uploader.js';
8
- import { importSession } from '../services/session-importer.js';
9
- // Mock the services at module level
10
- vi.mock('../services/session-uploader.js', () => ({
11
- uploadSession: vi.fn(),
12
- }));
13
- vi.mock('../services/session-importer.js', () => ({
14
- importSession: vi.fn(),
15
- }));
16
- // Mock fs/promises for findMostRecentSession
17
- vi.mock('fs/promises', async () => {
18
- const actual = await vi.importActual('fs/promises');
19
- return {
20
- ...actual,
21
- readdir: vi.fn(),
22
- stat: vi.fn(),
23
- };
24
- });
25
- describe('CLI argument parsing', () => {
26
- it('should parse share command with session path', () => {
27
- // Test that share command with --session-path flag works
28
- const args = ['share', '--session-path', '/path/to/session.jsonl'];
29
- // Find the session-path value
30
- const pathIndex = args.indexOf('--session-path');
31
- const sessionPath = pathIndex !== -1 && pathIndex + 1 < args.length
32
- ? args[pathIndex + 1]
33
- : null;
34
- expect(sessionPath).toBe('/path/to/session.jsonl');
35
- });
36
- it('should parse share command without session path', () => {
37
- const args = ['share'];
38
- const pathIndex = args.indexOf('--session-path');
39
- const sessionPath = pathIndex !== -1 && pathIndex + 1 < args.length
40
- ? args[pathIndex + 1]
41
- : null;
42
- expect(sessionPath).toBeNull();
43
- });
44
- it('should parse import command with gist URL and default project path', () => {
45
- // Simulate: node cli.js import https://gist...
46
- // After "import" is consumed, remaining args are:
47
- const argsAfterCommand = ['https://gist.github.com/user/abc123'];
48
- const gistUrl = argsAfterCommand[0];
49
- const pathIndex = argsAfterCommand.indexOf('--project-path');
50
- const projectPath = pathIndex !== -1 && pathIndex + 1 < argsAfterCommand.length
51
- ? argsAfterCommand[pathIndex + 1]
52
- : process.cwd();
53
- expect(gistUrl).toBe('https://gist.github.com/user/abc123');
54
- expect(projectPath).toBe(process.cwd());
55
- });
56
- it('should parse import command with project-path flag', () => {
57
- // Simulate: node cli.js import abc123 --project-path /tmp/test
58
- const argsAfterCommand = ['abc123', '--project-path', '/tmp/test'];
59
- const gistUrl = argsAfterCommand[0];
60
- const pathIndex = argsAfterCommand.indexOf('--project-path');
61
- const projectPath = pathIndex !== -1 && pathIndex + 1 < argsAfterCommand.length
62
- ? argsAfterCommand[pathIndex + 1]
63
- : process.cwd();
64
- expect(gistUrl).toBe('abc123');
65
- expect(projectPath).toBe('/tmp/test');
66
- });
67
- it('should handle missing gist URL in import command', () => {
68
- // Simulate: node cli.js import (no URL provided)
69
- const argsAfterCommand = [];
70
- const gistUrl = argsAfterCommand[0];
71
- expect(gistUrl).toBeUndefined();
72
- });
73
- });
74
- describe('CLI service integration', () => {
75
- it('uploadSession service should be importable', () => {
76
- expect(uploadSession).toBeDefined();
77
- expect(typeof uploadSession).toBe('function');
78
- });
79
- it('importSession service should be importable', () => {
80
- expect(importSession).toBeDefined();
81
- expect(typeof importSession).toBe('function');
82
- });
83
- it('uploadSession mock can be configured', () => {
84
- vi.mocked(uploadSession).mockResolvedValue('https://gist.github.com/test/123');
85
- expect(vi.mocked(uploadSession)).toBeDefined();
86
- });
87
- it('importSession mock can be configured', () => {
88
- vi.mocked(importSession).mockResolvedValue({
89
- sessionPath: '/path/to/session.jsonl',
90
- sessionId: 'test-id',
91
- messageCount: 10,
92
- projectPath: '/test',
93
- });
94
- expect(vi.mocked(importSession)).toBeDefined();
95
- });
96
- });
97
- describe('CLI usage validation', () => {
98
- it('should have share and import as valid commands', () => {
99
- const validCommands = ['share', 'import'];
100
- expect(validCommands).toContain('share');
101
- expect(validCommands).toContain('import');
102
- expect(validCommands).not.toContain('unknown');
103
- });
104
- it('should validate share command structure', () => {
105
- // share command can have optional --session-path
106
- const shareArgs1 = ['share'];
107
- const shareArgs2 = ['share', '--session-path', '/path'];
108
- expect(shareArgs1[0]).toBe('share');
109
- expect(shareArgs2[0]).toBe('share');
110
- expect(shareArgs2.includes('--session-path')).toBe(true);
111
- });
112
- it('should validate import command structure', () => {
113
- // import command requires gist URL, optional --project-path
114
- const importArgsAfterCommand1 = ['https://gist.github.com/user/id'];
115
- const importArgsAfterCommand2 = ['abc123', '--project-path', '/path'];
116
- expect(importArgsAfterCommand1[0]).toBe('https://gist.github.com/user/id');
117
- expect(importArgsAfterCommand2[0]).toBe('abc123');
118
- expect(importArgsAfterCommand2.includes('--project-path')).toBe(true);
119
- });
120
- });
package/dist/cli.js DELETED
@@ -1,195 +0,0 @@
1
- #!/usr/bin/env node
2
- /**
3
- * CLI entry point for Claude Session Share
4
- *
5
- * Provides standalone command-line interface:
6
- * - share: Upload session to Gist
7
- * - import: Download session from Gist
8
- *
9
- * Also detects MCP mode when stdin is piped (no TTY).
10
- */
11
- import { uploadSession } from './services/session-uploader.js';
12
- import { importSession } from './services/session-importer.js';
13
- import { stdin } from 'process';
14
- import { homedir } from 'os';
15
- import { join } from 'path';
16
- import { readdir, stat } from 'fs/promises';
17
- import { realpathSync } from 'fs';
18
- import { fileURLToPath } from 'url';
19
- /**
20
- * Find the most recent session file
21
- * Searches ~/.claude/projects/ for all session files and returns the most recently modified one
22
- */
23
- async function findMostRecentSession() {
24
- const projectsDir = join(homedir(), '.claude', 'projects');
25
- try {
26
- // Get all subdirectories in ~/.claude/projects/
27
- const entries = await readdir(projectsDir, { withFileTypes: true });
28
- const dirs = entries.filter(e => e.isDirectory()).map(e => e.name);
29
- let mostRecentFile = null;
30
- let mostRecentTime = 0;
31
- // Search each project directory for session files
32
- for (const dir of dirs) {
33
- const dirPath = join(projectsDir, dir);
34
- try {
35
- const files = await readdir(dirPath);
36
- for (const file of files) {
37
- if (file.endsWith('.jsonl')) {
38
- const filePath = join(dirPath, file);
39
- const stats = await stat(filePath);
40
- if (stats.mtimeMs > mostRecentTime) {
41
- mostRecentTime = stats.mtimeMs;
42
- mostRecentFile = filePath;
43
- }
44
- }
45
- }
46
- }
47
- catch (err) {
48
- // Skip directories we can't read
49
- continue;
50
- }
51
- }
52
- return mostRecentFile;
53
- }
54
- catch (err) {
55
- throw new Error(`Failed to find sessions: ${err instanceof Error ? err.message : String(err)}`);
56
- }
57
- }
58
- /**
59
- * Parse command-line arguments for share command
60
- * Returns session path or null for auto-detect
61
- */
62
- function parseShareArgs(args) {
63
- // Look for --session-path flag
64
- const pathIndex = args.indexOf('--session-path');
65
- if (pathIndex !== -1 && pathIndex + 1 < args.length) {
66
- return args[pathIndex + 1];
67
- }
68
- return null; // Auto-detect most recent
69
- }
70
- /**
71
- * Parse command-line arguments for import command
72
- * Returns { gistUrl, projectPath } or throws if invalid
73
- */
74
- function parseImportArgs(args) {
75
- // First positional arg after "import" is gist URL
76
- const gistUrl = args[0];
77
- if (!gistUrl) {
78
- throw new Error('Missing required argument: gist-url\nUsage: import <gist-url> [--project-path <path>]');
79
- }
80
- // Look for --project-path flag, default to cwd
81
- const pathIndex = args.indexOf('--project-path');
82
- const projectPath = pathIndex !== -1 && pathIndex + 1 < args.length
83
- ? args[pathIndex + 1]
84
- : process.cwd();
85
- return { gistUrl, projectPath };
86
- }
87
- /**
88
- * Display usage information
89
- */
90
- function showUsage() {
91
- console.log(`
92
- Claude Session Share - CLI for sharing Claude Code sessions
93
-
94
- USAGE:
95
- claude-session-share <command> [options]
96
-
97
- COMMANDS:
98
- share Share most recent session to GitHub Gist
99
- share --session-path PATH Share specific session file
100
- import <gist-url> Import session from GitHub Gist to current directory
101
- import <gist-url> --project-path PATH
102
- Import session to specific directory
103
-
104
- EXAMPLES:
105
- # Share most recent session
106
- claude-session-share share
107
-
108
- # Share specific session
109
- claude-session-share share --session-path ~/.claude/projects/abc/session.jsonl
110
-
111
- # Import session to current directory
112
- claude-session-share import https://gist.github.com/user/abc123
113
-
114
- # Import to specific directory
115
- claude-session-share import abc123 --project-path /Users/name/project
116
-
117
- ENVIRONMENT:
118
- GITHUB_TOKEN Required for both share and import operations
119
- Get token at https://github.com/settings/tokens
120
- Needs 'gist' scope for creating/reading gists
121
- `);
122
- }
123
- /**
124
- * Main CLI entry point
125
- */
126
- async function main() {
127
- const args = process.argv.slice(2);
128
- // If no args and stdin is not a TTY, assume MCP stdio mode
129
- if (args.length === 0 && !stdin.isTTY) {
130
- // Import and run MCP server
131
- const { default: runMCPServer } = await import('./index.js');
132
- return runMCPServer();
133
- }
134
- // CLI mode: require at least one argument
135
- if (args.length === 0) {
136
- showUsage();
137
- process.exit(1);
138
- }
139
- const command = args[0];
140
- try {
141
- if (command === 'share') {
142
- // Parse share arguments
143
- const sessionPath = parseShareArgs(args.slice(1));
144
- // Find session to share
145
- const pathToShare = sessionPath || await findMostRecentSession();
146
- if (!pathToShare) {
147
- console.error('Error: No session files found in ~/.claude/projects/');
148
- console.error('Please provide a session path with --session-path');
149
- process.exit(1);
150
- }
151
- // Upload session
152
- console.log(`Uploading session: ${pathToShare}`);
153
- const gistUrl = await uploadSession(pathToShare);
154
- console.log('\n✓ Session shared successfully!');
155
- console.log(`\nGist URL: ${gistUrl}`);
156
- console.log('\nShare this URL to give others access to the conversation.');
157
- }
158
- else if (command === 'import') {
159
- // Parse import arguments
160
- const { gistUrl, projectPath } = parseImportArgs(args.slice(1));
161
- // Import session
162
- console.log(`Importing session from: ${gistUrl}`);
163
- console.log(`Target directory: ${projectPath}`);
164
- const result = await importSession(gistUrl, projectPath);
165
- console.log('\n✓ Session imported successfully!');
166
- console.log(`\nSession ID: ${result.sessionId}`);
167
- console.log(`Messages: ${result.messageCount}`);
168
- console.log(`Location: ${result.sessionPath}`);
169
- console.log(`\nUse 'claude --resume' to see imported session.`);
170
- }
171
- else {
172
- console.error(`Error: Unknown command '${command}'`);
173
- console.error('');
174
- showUsage();
175
- process.exit(1);
176
- }
177
- }
178
- catch (error) {
179
- const errorMessage = error instanceof Error ? error.message : String(error);
180
- console.error(`\nError: ${errorMessage}`);
181
- process.exit(1);
182
- }
183
- }
184
- // Export main for MCP mode, but also run if this is the entry point
185
- export default main;
186
- // Run main if this file is executed directly (not imported)
187
- // Need to resolve symlinks because process.argv[1] might be a symlink
188
- const currentFile = fileURLToPath(import.meta.url);
189
- const scriptFile = realpathSync(process.argv[1]);
190
- if (currentFile === scriptFile) {
191
- main().catch((error) => {
192
- console.error('Fatal error:', error);
193
- process.exit(1);
194
- });
195
- }