claude-multi-session 1.0.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/bin/mcp.js ADDED
@@ -0,0 +1,27 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * claude-multi-session MCP Server entry point.
5
+ *
6
+ * This starts the MCP (Model Context Protocol) server that exposes
7
+ * multi-session tools to Claude Code as native tools.
8
+ *
9
+ * Claude Code connects to this server via stdio. Once connected,
10
+ * tools like spawn_session, send_message, delegate_task appear
11
+ * in Claude's tool list — just like Read, Edit, Grep, etc.
12
+ *
13
+ * Usage (in Claude Code MCP config):
14
+ * {
15
+ * "mcpServers": {
16
+ * "multi-session": {
17
+ * "command": "cms-mcp"
18
+ * }
19
+ * }
20
+ * }
21
+ *
22
+ * Or run directly:
23
+ * node bin/mcp.js
24
+ */
25
+
26
+ const { startServer } = require('../src/mcp-server');
27
+ startServer();
package/bin/setup.js ADDED
@@ -0,0 +1,469 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * ╔══════════════════════════════════════════════════════════════╗
5
+ * ║ claude-multi-session — Setup Wizard ║
6
+ * ╚══════════════════════════════════════════════════════════════╝
7
+ *
8
+ * Automatically registers the multi-session MCP server with Claude Code.
9
+ *
10
+ * Usage:
11
+ * cms setup Interactive setup wizard
12
+ * cms setup --global Skip prompt, install globally
13
+ * cms setup --local Skip prompt, install locally
14
+ * cms setup --uninstall Remove MCP entry + CLAUDE.md section
15
+ * cms setup --postinstall (Internal) Print hint after npm install
16
+ */
17
+
18
+ const fs = require('fs');
19
+ const path = require('path');
20
+ const os = require('os');
21
+ const readline = require('readline');
22
+
23
+ // ============================================================================
24
+ // Constants
25
+ // ============================================================================
26
+
27
+ // The name of the MCP server entry in settings.json
28
+ const MCP_SERVER_NAME = 'multi-session';
29
+
30
+ // Marker used to identify the strategy section in CLAUDE.md
31
+ // (so we can detect duplicates and remove on uninstall)
32
+ const CLAUDE_MD_START_MARKER = '<!-- claude-multi-session:start -->';
33
+ const CLAUDE_MD_END_MARKER = '<!-- claude-multi-session:end -->';
34
+
35
+ // ============================================================================
36
+ // Strategy Guide Content (concise version of STRATEGY.md)
37
+ // ============================================================================
38
+
39
+ // This gets appended to CLAUDE.md when the user opts in.
40
+ // It's a condensed version — just the decision framework and key rules.
41
+ const STRATEGY_CONTENT = `
42
+ ${CLAUDE_MD_START_MARKER}
43
+
44
+ ## Multi-Session Strategy (claude-multi-session)
45
+
46
+ ### When to Delegate
47
+ - **3+ independent parts** — run in parallel (e.g., login + signup + reset)
48
+ - **Task too large for one context** — full features with tests
49
+ - **Different models needed** — haiku for lookups, opus for architecture
50
+ - **Risky operations** — delegate with safety limits
51
+
52
+ ### When NOT to Delegate
53
+ - Simple tasks (< 5 min, < 3 files) — do it yourself
54
+ - Tightly coupled changes — must happen atomically
55
+ - Just reading/exploring — use Read, Grep, Glob directly
56
+
57
+ ### Decision Flow
58
+ 1. Simple task? → Do it yourself
59
+ 2. Can split into independent parts? → Delegate each in parallel
60
+ 3. Needs safety limits? → Use \`delegate_task\` with \`max_cost\` / \`max_turns\`
61
+ 4. Otherwise → \`spawn_session\` + \`send_message\` for direct control
62
+
63
+ ### Model Selection
64
+ | Task | Model |
65
+ |------|-------|
66
+ | Simple lookups, counting | haiku |
67
+ | Standard edits, bug fixes, tests | sonnet |
68
+ | Architecture, complex refactoring | opus |
69
+
70
+ ### The Correction Loop
71
+ 1. \`delegate_task\` → read result
72
+ 2. Status = completed? → evaluate quality
73
+ 3. Good? → \`finish_task\` | Bad? → \`continue_task\` with specific fixes
74
+ 4. Hopeless? → \`abort_task\`, try differently
75
+
76
+ ### Anti-Patterns
77
+ - Don't delegate trivial tasks (spawning overhead isn't worth it)
78
+ - Don't use \`full\` preset by default (use \`edit\`, escalate if needed)
79
+ - Don't send vague corrections ("fix it") — be specific
80
+ - Don't coordinate parallel sessions editing the SAME file (conflicts)
81
+ - Don't forget to \`finish_task\` — stopped sessions consume stored data
82
+
83
+ ${CLAUDE_MD_END_MARKER}
84
+ `;
85
+
86
+ // ============================================================================
87
+ // Helpers
88
+ // ============================================================================
89
+
90
+ /**
91
+ * Ask the user a question and return their answer.
92
+ * Uses Node's readline module — zero dependencies.
93
+ */
94
+ function ask(question) {
95
+ return new Promise((resolve) => {
96
+ const rl = readline.createInterface({
97
+ input: process.stdin,
98
+ output: process.stdout,
99
+ });
100
+ rl.question(question, (answer) => {
101
+ rl.close();
102
+ resolve(answer.trim());
103
+ });
104
+ });
105
+ }
106
+
107
+ /**
108
+ * Print a styled log line.
109
+ * @param {string} msg - The message to print
110
+ * @param {'info'|'success'|'warn'|'error'} [level] - Optional level
111
+ */
112
+ function log(msg, level) {
113
+ const prefix = {
114
+ info: ' ',
115
+ success: ' ✓ ',
116
+ warn: ' ⚠ ',
117
+ error: ' ✗ ',
118
+ }[level || 'info'];
119
+ console.log(prefix + msg);
120
+ }
121
+
122
+ /**
123
+ * Figure out whether this package was installed globally or locally,
124
+ * and return the right MCP server command config.
125
+ *
126
+ * Global install: the `cms-mcp` binary is on PATH, so we can just call it.
127
+ * Local install: we need an absolute path to bin/mcp.js.
128
+ */
129
+ function detectMcpCommand() {
130
+ // __dirname is the "bin/" folder where this script lives
131
+ // One level up is the package root
132
+ const packageRoot = path.resolve(__dirname, '..');
133
+ const mcpBin = path.join(packageRoot, 'bin', 'mcp.js');
134
+
135
+ // Check if cms-mcp is likely on PATH (global install)
136
+ // We do this by checking if the package is inside a global node_modules
137
+ const isGlobal = packageRoot.includes(path.join('node_modules', 'claude-multi-session'))
138
+ && (
139
+ packageRoot.includes(path.join('lib', 'node_modules')) // npm global on Linux/Mac
140
+ || packageRoot.includes(path.join('AppData', 'Roaming', 'npm')) // npm global on Windows
141
+ || packageRoot.includes('nvm') // nvm managed
142
+ || packageRoot.includes('.volta') // volta managed
143
+ );
144
+
145
+ if (isGlobal) {
146
+ // Global install — cms-mcp should be on PATH
147
+ return { command: 'cms-mcp' };
148
+ } else {
149
+ // Local or dev install — use absolute path to bin/mcp.js
150
+ return { command: 'node', args: [mcpBin] };
151
+ }
152
+ }
153
+
154
+ /**
155
+ * Read a JSON file safely. Returns empty object if file doesn't exist
156
+ * or has invalid JSON.
157
+ */
158
+ function readJsonSafe(filePath) {
159
+ try {
160
+ if (!fs.existsSync(filePath)) return {};
161
+ const raw = fs.readFileSync(filePath, 'utf-8');
162
+ return JSON.parse(raw);
163
+ } catch {
164
+ return {};
165
+ }
166
+ }
167
+
168
+ /**
169
+ * Write JSON to a file with nice formatting.
170
+ */
171
+ function writeJson(filePath, data) {
172
+ fs.writeFileSync(filePath, JSON.stringify(data, null, 2) + '\n', 'utf-8');
173
+ }
174
+
175
+ // ============================================================================
176
+ // Core Setup Logic
177
+ // ============================================================================
178
+
179
+ /**
180
+ * Add the multi-session MCP server to a Claude Code settings.json file.
181
+ *
182
+ * @param {string} settingsPath - Absolute path to settings.json
183
+ * @returns {{ created: boolean, existed: boolean, overwritten: boolean }}
184
+ */
185
+ async function addMcpServer(settingsPath) {
186
+ const result = { created: false, existed: false, overwritten: false };
187
+
188
+ // 1. Make sure the directory exists
189
+ const dir = path.dirname(settingsPath);
190
+ if (!fs.existsSync(dir)) {
191
+ fs.mkdirSync(dir, { recursive: true });
192
+ log(`Created ${dir}`, 'info');
193
+ }
194
+
195
+ // 2. Read existing settings (or start with empty object)
196
+ const settings = readJsonSafe(settingsPath);
197
+ const fileExists = fs.existsSync(settingsPath);
198
+
199
+ if (!fileExists) {
200
+ log(`Creating ${settingsPath} ...`, 'info');
201
+ result.created = true;
202
+ } else {
203
+ log(`Reading ${settingsPath} ...`, 'info');
204
+ }
205
+
206
+ // 3. Ensure mcpServers key exists
207
+ if (!settings.mcpServers) {
208
+ settings.mcpServers = {};
209
+ }
210
+
211
+ // 4. Check if multi-session already exists
212
+ if (settings.mcpServers[MCP_SERVER_NAME]) {
213
+ result.existed = true;
214
+ const answer = await ask(
215
+ ` MCP server "${MCP_SERVER_NAME}" already exists. Overwrite? (y/n) > `
216
+ );
217
+ if (answer.toLowerCase() !== 'y') {
218
+ log('Skipped — keeping existing MCP config.', 'warn');
219
+ return result;
220
+ }
221
+ result.overwritten = true;
222
+ }
223
+
224
+ // 5. Build the MCP config entry
225
+ const mcpConfig = detectMcpCommand();
226
+ settings.mcpServers[MCP_SERVER_NAME] = mcpConfig;
227
+
228
+ log('Adding multi-session MCP server ...', 'info');
229
+
230
+ // 6. Write back
231
+ writeJson(settingsPath, settings);
232
+ log(`Wrote ${settingsPath}`, 'success');
233
+
234
+ return result;
235
+ }
236
+
237
+ /**
238
+ * Append the strategy guide to a CLAUDE.md file.
239
+ *
240
+ * @param {string} claudeMdPath - Absolute path to CLAUDE.md
241
+ * @returns {{ created: boolean, skipped: boolean }}
242
+ */
243
+ function addStrategyGuide(claudeMdPath, skipPrompt) {
244
+ const result = { created: false, skipped: false };
245
+
246
+ // Check if file exists
247
+ let existing = '';
248
+ if (fs.existsSync(claudeMdPath)) {
249
+ existing = fs.readFileSync(claudeMdPath, 'utf-8');
250
+ }
251
+
252
+ // Check if strategy section already exists (avoid duplicates)
253
+ if (existing.includes(CLAUDE_MD_START_MARKER)) {
254
+ log('Strategy guide already in CLAUDE.md — skipping.', 'warn');
255
+ result.skipped = true;
256
+ return result;
257
+ }
258
+
259
+ // Create directory if needed
260
+ const dir = path.dirname(claudeMdPath);
261
+ if (!fs.existsSync(dir)) {
262
+ fs.mkdirSync(dir, { recursive: true });
263
+ }
264
+
265
+ // Append the strategy content
266
+ if (existing) {
267
+ // Append to existing file
268
+ fs.appendFileSync(claudeMdPath, '\n' + STRATEGY_CONTENT, 'utf-8');
269
+ log(`Appended strategy guide to ${claudeMdPath}`, 'success');
270
+ } else {
271
+ // Create new file with strategy content
272
+ fs.writeFileSync(claudeMdPath, STRATEGY_CONTENT.trim() + '\n', 'utf-8');
273
+ log(`Created ${claudeMdPath} with strategy guide`, 'success');
274
+ result.created = true;
275
+ }
276
+
277
+ return result;
278
+ }
279
+
280
+ /**
281
+ * Remove multi-session from settings.json and CLAUDE.md.
282
+ *
283
+ * @param {string} settingsPath - Path to settings.json
284
+ * @param {string} claudeMdPath - Path to CLAUDE.md
285
+ */
286
+ function uninstall(settingsPath, claudeMdPath) {
287
+ console.log('\n Uninstalling multi-session...\n');
288
+
289
+ // 1. Remove from settings.json
290
+ if (fs.existsSync(settingsPath)) {
291
+ const settings = readJsonSafe(settingsPath);
292
+ if (settings.mcpServers && settings.mcpServers[MCP_SERVER_NAME]) {
293
+ delete settings.mcpServers[MCP_SERVER_NAME];
294
+
295
+ // Clean up empty mcpServers object
296
+ if (Object.keys(settings.mcpServers).length === 0) {
297
+ delete settings.mcpServers;
298
+ }
299
+
300
+ writeJson(settingsPath, settings);
301
+ log(`Removed "${MCP_SERVER_NAME}" from ${settingsPath}`, 'success');
302
+ } else {
303
+ log(`"${MCP_SERVER_NAME}" not found in ${settingsPath}`, 'warn');
304
+ }
305
+ } else {
306
+ log(`${settingsPath} not found — nothing to remove`, 'warn');
307
+ }
308
+
309
+ // 2. Remove strategy section from CLAUDE.md
310
+ if (fs.existsSync(claudeMdPath)) {
311
+ let content = fs.readFileSync(claudeMdPath, 'utf-8');
312
+ if (content.includes(CLAUDE_MD_START_MARKER)) {
313
+ // Remove everything between (and including) the markers
314
+ const startIdx = content.indexOf(CLAUDE_MD_START_MARKER);
315
+ const endIdx = content.indexOf(CLAUDE_MD_END_MARKER);
316
+ if (startIdx !== -1 && endIdx !== -1) {
317
+ const before = content.slice(0, startIdx).trimEnd();
318
+ const after = content.slice(endIdx + CLAUDE_MD_END_MARKER.length).trimStart();
319
+ content = before + (after ? '\n\n' + after : '') + '\n';
320
+ fs.writeFileSync(claudeMdPath, content, 'utf-8');
321
+ log(`Removed strategy guide from ${claudeMdPath}`, 'success');
322
+ }
323
+ } else {
324
+ log('No strategy guide found in CLAUDE.md — nothing to remove', 'warn');
325
+ }
326
+ }
327
+
328
+ console.log('\n ✓ Uninstall complete. Restart Claude Code to apply.\n');
329
+ }
330
+
331
+ // ============================================================================
332
+ // Main Entry Point
333
+ // ============================================================================
334
+
335
+ /**
336
+ * Run the setup wizard.
337
+ *
338
+ * @param {object} flags - CLI flags passed from cli.js or direct invocation
339
+ */
340
+ async function run(flags = {}) {
341
+ // --postinstall mode: just print a hint and exit
342
+ if (flags.postinstall) {
343
+ console.log('');
344
+ console.log(' ╔═══════════════════════════════════════════════════════╗');
345
+ console.log(' ║ claude-multi-session installed successfully! ║');
346
+ console.log(' ║ ║');
347
+ console.log(' ║ Run setup to integrate with Claude Code: ║');
348
+ console.log(' ║ ║');
349
+ console.log(' ║ cms setup ║');
350
+ console.log(' ║ ║');
351
+ console.log(' ║ This registers the MCP server so Claude Code ║');
352
+ console.log(' ║ can use multi-session tools natively. ║');
353
+ console.log(' ╚═══════════════════════════════════════════════════════╝');
354
+ console.log('');
355
+ return;
356
+ }
357
+
358
+ // Determine paths based on scope (global vs local)
359
+ const homeDir = os.homedir();
360
+ const globalSettingsPath = path.join(homeDir, '.claude', 'settings.json');
361
+ const globalClaudeMdPath = path.join(homeDir, '.claude', 'CLAUDE.md');
362
+ const localSettingsPath = path.join(process.cwd(), '.claude', 'settings.json');
363
+ const localClaudeMdPath = path.join(process.cwd(), 'CLAUDE.md');
364
+
365
+ // --uninstall mode
366
+ if (flags.uninstall) {
367
+ // Ask which scope to uninstall from (or use --global/--local flags)
368
+ let scope = flags.global ? 'global' : flags.local ? 'local' : null;
369
+ if (!scope) {
370
+ const answer = await ask(
371
+ ' Uninstall from? (1 = Global, 2 = Local project) > '
372
+ );
373
+ scope = answer === '2' ? 'local' : 'global';
374
+ }
375
+
376
+ const settingsPath = scope === 'global' ? globalSettingsPath : localSettingsPath;
377
+ const claudeMdPath = scope === 'global' ? globalClaudeMdPath : localClaudeMdPath;
378
+ uninstall(settingsPath, claudeMdPath);
379
+ return;
380
+ }
381
+
382
+ // Print header
383
+ console.log('');
384
+ console.log(' ╔═══════════════════════════════════════════════════════╗');
385
+ console.log(' ║ claude-multi-session — Setup Wizard ║');
386
+ console.log(' ╚═══════════════════════════════════════════════════════╝');
387
+ console.log('');
388
+
389
+ // Step 1: Determine scope
390
+ let scope;
391
+ if (flags.global) {
392
+ scope = 'global';
393
+ log('Scope: Global (all projects)', 'info');
394
+ } else if (flags.local) {
395
+ scope = 'local';
396
+ log('Scope: Local (this project only)', 'info');
397
+ } else {
398
+ console.log(' Where should the MCP server be registered?\n');
399
+ console.log(' 1) Global — available in ALL projects');
400
+ console.log(' Writes to: ~/.claude/settings.json\n');
401
+ console.log(' 2) Local — only THIS project');
402
+ console.log(' Writes to: .claude/settings.json\n');
403
+ const answer = await ask(' Choose (1 or 2) > ');
404
+ scope = answer === '2' ? 'local' : 'global';
405
+ console.log('');
406
+ }
407
+
408
+ // Step 2: Set paths based on scope
409
+ const settingsPath = scope === 'global' ? globalSettingsPath : localSettingsPath;
410
+ const claudeMdPath = scope === 'global' ? globalClaudeMdPath : localClaudeMdPath;
411
+
412
+ // Step 3: Add MCP server to settings.json
413
+ const mcpResult = await addMcpServer(settingsPath);
414
+
415
+ // Step 4: Ask about strategy guide
416
+ console.log('');
417
+ let addGuide;
418
+ if (flags['no-guide']) {
419
+ addGuide = false;
420
+ } else if (flags.guide) {
421
+ addGuide = true;
422
+ } else {
423
+ const answer = await ask(
424
+ ' Add multi-session strategy guide to CLAUDE.md? (y/n) > '
425
+ );
426
+ addGuide = answer.toLowerCase() === 'y';
427
+ }
428
+
429
+ if (addGuide) {
430
+ addStrategyGuide(claudeMdPath);
431
+ }
432
+
433
+ // Step 5: Print summary
434
+ console.log('');
435
+ console.log(' ╔═══════════════════════════════════════════════════════╗');
436
+ console.log(' ║ ✓ Setup complete! ║');
437
+ console.log(' ╚═══════════════════════════════════════════════════════╝');
438
+ console.log('');
439
+ log('Restart Claude Code to load the multi-session tools.', 'info');
440
+ log('You\'ll see 17 new tools: spawn_session, delegate_task, etc.', 'info');
441
+ console.log('');
442
+ log('Test it: Ask Claude to "list all multi-session sessions"', 'info');
443
+ log('Learn more: cms help', 'info');
444
+ console.log('');
445
+ }
446
+
447
+ // ============================================================================
448
+ // Direct invocation support (node bin/setup.js)
449
+ // ============================================================================
450
+
451
+ // If this script is run directly (not required by cli.js),
452
+ // parse flags and run the wizard
453
+ if (require.main === module) {
454
+ // Parse simple flags from process.argv
455
+ const args = process.argv.slice(2);
456
+ const flags = {};
457
+ for (const arg of args) {
458
+ if (arg.startsWith('--')) {
459
+ const key = arg.slice(2);
460
+ flags[key] = true;
461
+ }
462
+ }
463
+ run(flags).catch((err) => {
464
+ console.error(`\n ✗ Setup failed: ${err.message}\n`);
465
+ process.exit(1);
466
+ });
467
+ }
468
+
469
+ module.exports = { run };
package/package.json ADDED
@@ -0,0 +1,42 @@
1
+ {
2
+ "name": "claude-multi-session",
3
+ "version": "1.0.0",
4
+ "description": "Multi-session orchestrator for Claude Code CLI — spawn, control, pause, resume, and send multiple inputs to Claude Code sessions programmatically",
5
+ "main": "src/index.js",
6
+ "bin": {
7
+ "cms": "bin/cli.js",
8
+ "claude-multi-session": "bin/cli.js",
9
+ "cms-mcp": "bin/mcp.js",
10
+ "cms-setup": "bin/setup.js"
11
+ },
12
+ "scripts": {
13
+ "test": "node test-stream.js",
14
+ "postinstall": "node bin/setup.js --postinstall"
15
+ },
16
+ "keywords": [
17
+ "claude",
18
+ "claude-code",
19
+ "session-manager",
20
+ "multi-session",
21
+ "orchestrator",
22
+ "ai",
23
+ "automation",
24
+ "cli"
25
+ ],
26
+ "author": "",
27
+ "license": "MIT",
28
+ "engines": {
29
+ "node": ">=18.0.0"
30
+ },
31
+ "files": [
32
+ "src/",
33
+ "bin/",
34
+ "README.md",
35
+ "STRATEGY.md",
36
+ "LICENSE"
37
+ ],
38
+ "repository": {
39
+ "type": "git",
40
+ "url": ""
41
+ }
42
+ }