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 +31 -159
- package/dist/index.js +1 -1
- package/package.json +1 -4
- package/dist/__tests__/cli.test.js +0 -120
- package/dist/cli.js +0 -195
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
|
[](https://www.npmjs.com/package/claude-session-share)
|
|
8
6
|
[](https://opensource.org/licenses/MIT)
|
|
9
7
|
|
|
10
8
|
## Features
|
|
11
9
|
|
|
12
|
-
- **One-Click Sharing** - Export sessions to GitHub Gist
|
|
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
|
-
- **
|
|
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.
|
|
37
|
-
4.
|
|
38
|
-
5. Generate and copy the token
|
|
27
|
+
3. Check the **`gist`** scope
|
|
28
|
+
4. Generate and copy the token
|
|
39
29
|
|
|
40
|
-
|
|
30
|
+
### Configure MCP Server
|
|
41
31
|
|
|
42
|
-
Add
|
|
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
|
-
#
|
|
52
|
+
# Type: /mcp
|
|
71
53
|
# You should see "claude-session-share" in the list
|
|
72
54
|
```
|
|
73
55
|
|
|
74
56
|
## Usage
|
|
75
57
|
|
|
76
|
-
###
|
|
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
|
|
69
|
+
3. Upload to a secret GitHub Gist
|
|
88
70
|
4. Return a shareable link
|
|
89
71
|
|
|
90
|
-
|
|
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
|
-
###
|
|
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
|
|
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
|
-
###
|
|
137
|
-
|
|
138
|
-
-
|
|
139
|
-
-
|
|
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
|
|
148
|
-
-
|
|
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
|
-
|
|
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
|
-
|
|
115
|
+
### "Not authenticated" Error
|
|
116
|
+
Ensure `GITHUB_TOKEN` is set in MCP configuration.
|
|
173
117
|
|
|
174
|
-
|
|
175
|
-
|
|
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
|
-
|
|
179
|
-
|
|
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
package/package.json
CHANGED
|
@@ -1,12 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claude-session-share",
|
|
3
|
-
"version": "1.
|
|
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
|
-
}
|