claude-multi-session 2.3.0 → 2.3.2
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/package.json +1 -1
- package/src/artifact-store.js +50 -0
- package/src/mcp-server.js +44 -0
- package/src/prompts.js +101 -4
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claude-multi-session",
|
|
3
|
-
"version": "2.3.
|
|
3
|
+
"version": "2.3.2",
|
|
4
4
|
"description": "Multi-session orchestrator for Claude Code CLI — spawn, control, pause, resume, and send multiple inputs to Claude Code sessions programmatically",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"bin": {
|
package/src/artifact-store.js
CHANGED
|
@@ -469,6 +469,45 @@ class ArtifactStore {
|
|
|
469
469
|
}
|
|
470
470
|
}
|
|
471
471
|
|
|
472
|
+
/**
|
|
473
|
+
* Track that a session read an artifact
|
|
474
|
+
* @param {string} artifactId - The artifact that was read
|
|
475
|
+
* @param {string} reader - The session name that read it
|
|
476
|
+
* @param {number} version - The version that was read
|
|
477
|
+
*/
|
|
478
|
+
trackRead(artifactId, reader, version) {
|
|
479
|
+
if (!reader) return; // Skip if no reader identified
|
|
480
|
+
|
|
481
|
+
const readsPath = path.join(this.dataDir, artifactId, 'reads.json');
|
|
482
|
+
|
|
483
|
+
// Read existing reads log
|
|
484
|
+
let reads = readJsonSafe(readsPath, []);
|
|
485
|
+
|
|
486
|
+
// Add this read event
|
|
487
|
+
reads.push({
|
|
488
|
+
reader,
|
|
489
|
+
version,
|
|
490
|
+
readAt: new Date().toISOString(),
|
|
491
|
+
});
|
|
492
|
+
|
|
493
|
+
// Write back (not immutable — reads are append-only log)
|
|
494
|
+
const dir = path.join(this.dataDir, artifactId);
|
|
495
|
+
if (!fs.existsSync(dir)) {
|
|
496
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
497
|
+
}
|
|
498
|
+
fs.writeFileSync(readsPath, JSON.stringify(reads, null, 2));
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
/**
|
|
502
|
+
* Get the read log for an artifact
|
|
503
|
+
* @param {string} artifactId - The artifact to check
|
|
504
|
+
* @returns {Array} Array of read events: [{ reader, version, readAt }]
|
|
505
|
+
*/
|
|
506
|
+
getReads(artifactId) {
|
|
507
|
+
const readsPath = path.join(this.dataDir, artifactId, 'reads.json');
|
|
508
|
+
return readJsonSafe(readsPath, []);
|
|
509
|
+
}
|
|
510
|
+
|
|
472
511
|
/**
|
|
473
512
|
* List artifacts with optional filtering
|
|
474
513
|
* @param {Object} [filters={}] - Filter criteria
|
|
@@ -497,6 +536,17 @@ class ArtifactStore {
|
|
|
497
536
|
results = results.filter(item => item.tags && item.tags.includes(tag));
|
|
498
537
|
}
|
|
499
538
|
|
|
539
|
+
// Enrich with read counts
|
|
540
|
+
results = results.map(item => {
|
|
541
|
+
const reads = this.getReads(item.artifactId);
|
|
542
|
+
const uniqueReaders = [...new Set(reads.map(r => r.reader))];
|
|
543
|
+
return {
|
|
544
|
+
...item,
|
|
545
|
+
readCount: reads.length,
|
|
546
|
+
uniqueReaders,
|
|
547
|
+
};
|
|
548
|
+
});
|
|
549
|
+
|
|
500
550
|
return results;
|
|
501
551
|
}
|
|
502
552
|
|
package/src/mcp-server.js
CHANGED
|
@@ -561,6 +561,7 @@ const TOOLS = [
|
|
|
561
561
|
properties: {
|
|
562
562
|
artifactId: { type: 'string', description: 'Artifact ID to retrieve' },
|
|
563
563
|
version: { type: 'number', description: 'Specific version number (omit for latest)' },
|
|
564
|
+
reader: { type: 'string', description: 'Session name reading this artifact (for tracking who consumed it)' },
|
|
564
565
|
team: { type: 'string', description: 'Team name (default: "default")' },
|
|
565
566
|
},
|
|
566
567
|
required: ['artifactId'],
|
|
@@ -582,6 +583,20 @@ const TOOLS = [
|
|
|
582
583
|
},
|
|
583
584
|
},
|
|
584
585
|
|
|
586
|
+
{
|
|
587
|
+
name: 'artifact_readers',
|
|
588
|
+
description:
|
|
589
|
+
'Check who has read a specific artifact. Use this to verify that workers actually consumed shared artifacts like conventions or schemas.',
|
|
590
|
+
inputSchema: {
|
|
591
|
+
type: 'object',
|
|
592
|
+
properties: {
|
|
593
|
+
artifactId: { type: 'string', description: 'Artifact ID to check readers for' },
|
|
594
|
+
team: { type: 'string', description: 'Team name (default: "default")' },
|
|
595
|
+
},
|
|
596
|
+
required: ['artifactId'],
|
|
597
|
+
},
|
|
598
|
+
},
|
|
599
|
+
|
|
585
600
|
{
|
|
586
601
|
name: 'artifact_history',
|
|
587
602
|
description:
|
|
@@ -1161,6 +1176,8 @@ async function executeTool(toolName, args) {
|
|
|
1161
1176
|
return handleArtifactGet(args);
|
|
1162
1177
|
case 'artifact_list':
|
|
1163
1178
|
return handleArtifactList(args);
|
|
1179
|
+
case 'artifact_readers':
|
|
1180
|
+
return handleArtifactReaders(args);
|
|
1164
1181
|
case 'artifact_history':
|
|
1165
1182
|
return handleArtifactHistory(args);
|
|
1166
1183
|
|
|
@@ -1867,6 +1884,11 @@ function handleArtifactGet(args) {
|
|
|
1867
1884
|
return errorResult(`Artifact ${args.artifactId} not found`);
|
|
1868
1885
|
}
|
|
1869
1886
|
|
|
1887
|
+
// Track this read if a reader was specified
|
|
1888
|
+
if (args.reader) {
|
|
1889
|
+
artifactStore.trackRead(args.artifactId, args.reader, artifact.version);
|
|
1890
|
+
}
|
|
1891
|
+
|
|
1870
1892
|
return textResult(JSON.stringify({
|
|
1871
1893
|
artifactId: artifact.artifactId,
|
|
1872
1894
|
version: artifact.version,
|
|
@@ -1878,6 +1900,7 @@ function handleArtifactGet(args) {
|
|
|
1878
1900
|
summary: artifact.summary,
|
|
1879
1901
|
lineage: artifact.lineage,
|
|
1880
1902
|
team: teamName,
|
|
1903
|
+
readBy: artifactStore.getReads(args.artifactId),
|
|
1881
1904
|
}, null, 2));
|
|
1882
1905
|
} catch (err) {
|
|
1883
1906
|
return errorResult(err.message);
|
|
@@ -1908,6 +1931,8 @@ function handleArtifactList(args) {
|
|
|
1908
1931
|
createdAt: a.createdAt,
|
|
1909
1932
|
updatedAt: a.updatedAt,
|
|
1910
1933
|
tags: a.tags,
|
|
1934
|
+
readCount: a.readCount,
|
|
1935
|
+
uniqueReaders: a.uniqueReaders,
|
|
1911
1936
|
})),
|
|
1912
1937
|
}, null, 2));
|
|
1913
1938
|
} catch (err) {
|
|
@@ -1915,6 +1940,25 @@ function handleArtifactList(args) {
|
|
|
1915
1940
|
}
|
|
1916
1941
|
}
|
|
1917
1942
|
|
|
1943
|
+
function handleArtifactReaders(args) {
|
|
1944
|
+
try {
|
|
1945
|
+
const teamName = args.team || 'default';
|
|
1946
|
+
const { artifactStore } = getTeamInstances(teamName);
|
|
1947
|
+
|
|
1948
|
+
const reads = artifactStore.getReads(args.artifactId);
|
|
1949
|
+
const uniqueReaders = [...new Set(reads.map(r => r.reader))];
|
|
1950
|
+
|
|
1951
|
+
return textResult(JSON.stringify({
|
|
1952
|
+
artifactId: args.artifactId,
|
|
1953
|
+
totalReads: reads.length,
|
|
1954
|
+
uniqueReaders,
|
|
1955
|
+
reads,
|
|
1956
|
+
}, null, 2));
|
|
1957
|
+
} catch (err) {
|
|
1958
|
+
return errorResult(err.message);
|
|
1959
|
+
}
|
|
1960
|
+
}
|
|
1961
|
+
|
|
1918
1962
|
function handleArtifactHistory(args) {
|
|
1919
1963
|
try {
|
|
1920
1964
|
const teamName = args.team || 'default';
|
package/src/prompts.js
CHANGED
|
@@ -66,6 +66,23 @@ Your inbox may contain:
|
|
|
66
66
|
- Dependency artifacts you need before starting
|
|
67
67
|
- Updated instructions from the orchestrator
|
|
68
68
|
|
|
69
|
+
### Step 1.5: CHECK FOR SHARED CONVENTIONS AND DEPENDENCY ARTIFACTS
|
|
70
|
+
Call \`mcp__multi-session__artifact_list\` and look for:
|
|
71
|
+
- A conventions or API contract artifact (e.g., "shared-conventions")
|
|
72
|
+
- Any dependency artifacts your task requires (e.g., "db-schema" if you're building routes)
|
|
73
|
+
|
|
74
|
+
If found, call \`mcp__multi-session__artifact_get\` with your session name as the \`reader\` parameter:
|
|
75
|
+
\`\`\`
|
|
76
|
+
mcp__multi-session__artifact_get({ artifactId: "shared-conventions", reader: "${name}" })
|
|
77
|
+
mcp__multi-session__artifact_get({ artifactId: "db-schema", reader: "${name}" })
|
|
78
|
+
\`\`\`
|
|
79
|
+
|
|
80
|
+
IMPORTANT: The \`reader\` parameter tracks that you consumed the artifact. This is how the orchestrator verifies workers actually read shared data. ALWAYS include your session name as \`reader\`.
|
|
81
|
+
|
|
82
|
+
Follow the conventions STRICTLY. If NO convention artifact exists AND your work must match other workers' output:
|
|
83
|
+
- Use \`team_ask\` to ask the relevant teammate about their format BEFORE writing code
|
|
84
|
+
- NEVER guess or assume format — mismatches cause test failures
|
|
85
|
+
|
|
69
86
|
### Step 2: UPDATE YOUR STATUS
|
|
70
87
|
Call \`mcp__multi-session__team_update_status\` to set yourself as "active" with your current task.
|
|
71
88
|
|
|
@@ -137,6 +154,8 @@ IMPORTANT: Follow these rules strictly. Violating them causes coordination failu
|
|
|
137
154
|
|
|
138
155
|
8. **If you are blocked, say so.** Update your status to "busy" with a note about what you're waiting for, and send a \`team_ask\` to the teammate who can unblock you.
|
|
139
156
|
|
|
157
|
+
9. **ALWAYS use relative file paths in code.** When creating database files, configs, or any path in generated code, use RELATIVE paths (e.g., \`path.join(__dirname, '..', 'data.db')\`). NEVER hardcode absolute paths like \`C:\\...\` or \`/home/...\` — they break portability.
|
|
158
|
+
|
|
140
159
|
<examples>
|
|
141
160
|
|
|
142
161
|
<example>
|
|
@@ -458,6 +477,8 @@ IMPORTANT: You are the ORCHESTRATOR. Your job is to PLAN, SPAWN, and MONITOR —
|
|
|
458
477
|
- Do NOT fix bugs found by one session yourself — tell that session to fix them
|
|
459
478
|
- Do NOT act as a message router between sessions — they can talk directly
|
|
460
479
|
- Do NOT do implementation work that should be delegated
|
|
480
|
+
- Do NOT fix code yourself when a worker's tests fail — send corrections to the worker that wrote the failing code
|
|
481
|
+
- Do NOT assume workers will agree on response formats — define shared conventions before spawning
|
|
461
482
|
|
|
462
483
|
=== CORRECT PATTERN ===
|
|
463
484
|
1. Plan the work and identify parallel tasks
|
|
@@ -474,6 +495,59 @@ Break the user's request into independent work units. Identify:
|
|
|
474
495
|
- What tasks are SEQUENTIAL (B needs output from A)
|
|
475
496
|
- What ROLES are needed (backend, frontend, testing, etc.)
|
|
476
497
|
|
|
498
|
+
### Phase 1.5: Define Shared Conventions
|
|
499
|
+
IMPORTANT: Before spawning workers, define shared conventions that ALL workers must follow. Either:
|
|
500
|
+
(a) Publish a conventions artifact that workers will read, OR
|
|
501
|
+
(b) Include the same convention rules in every worker's prompt
|
|
502
|
+
|
|
503
|
+
Conventions MUST cover:
|
|
504
|
+
- **Response format:** e.g., "All endpoints return { data: <result> } for success"
|
|
505
|
+
- **Error format:** e.g., "All errors return { error: <message> }"
|
|
506
|
+
- **Status codes:** e.g., "201 for create, 200 for update/delete/read, 404 for not found, 400 for validation"
|
|
507
|
+
- **Naming:** e.g., "snake_case for DB fields (in_progress not in-progress)"
|
|
508
|
+
- **File paths:** "Use relative paths only — never absolute"
|
|
509
|
+
|
|
510
|
+
\`\`\`
|
|
511
|
+
// Example: publish conventions before spawning workers
|
|
512
|
+
mcp__multi-session__artifact_publish({
|
|
513
|
+
artifactId: "shared-conventions",
|
|
514
|
+
type: "api-contract",
|
|
515
|
+
name: "Shared API Conventions",
|
|
516
|
+
data: {
|
|
517
|
+
responseFormat: "{ data: <result> }",
|
|
518
|
+
errorFormat: "{ error: <message> }",
|
|
519
|
+
statusCodes: { create: 201, read: 200, update: 200, delete: 200, notFound: 404, badRequest: 400 },
|
|
520
|
+
naming: "snake_case for DB fields",
|
|
521
|
+
paths: "relative only, never absolute"
|
|
522
|
+
}
|
|
523
|
+
})
|
|
524
|
+
\`\`\`
|
|
525
|
+
|
|
526
|
+
NEVER assume workers will independently agree on conventions. Define them explicitly.
|
|
527
|
+
|
|
528
|
+
### Phase Gate: VERIFY Before Spawning
|
|
529
|
+
=== CRITICAL: MANDATORY VERIFICATION STEP ===
|
|
530
|
+
Before spawning ANY worker that depends on a previous phase's output, you MUST:
|
|
531
|
+
1. Call \`artifact_list()\` to confirm the dependency artifact was published
|
|
532
|
+
2. Call \`artifact_get(artifactId)\` to confirm the artifact contains valid data
|
|
533
|
+
3. ONLY THEN spawn the dependent workers
|
|
534
|
+
|
|
535
|
+
Example phased workflow:
|
|
536
|
+
\`\`\`
|
|
537
|
+
// Phase 1: Spawn the database worker
|
|
538
|
+
team_spawn({ name: "db-worker", ... })
|
|
539
|
+
|
|
540
|
+
// PHASE GATE: Verify before Phase 2
|
|
541
|
+
artifact_list() // Confirm "db-schema" appears
|
|
542
|
+
artifact_get({ artifactId: "db-schema" }) // Confirm it has valid table definitions
|
|
543
|
+
|
|
544
|
+
// Phase 2: NOW spawn route workers (they depend on the schema)
|
|
545
|
+
team_spawn({ name: "api-worker", ... })
|
|
546
|
+
team_spawn({ name: "test-worker", ... })
|
|
547
|
+
\`\`\`
|
|
548
|
+
|
|
549
|
+
NEVER skip verification. NEVER rely on a worker's self-reported completion — verify the artifact exists yourself.
|
|
550
|
+
|
|
477
551
|
### Phase 2: Spawn Workers (use team_spawn, NOT delegate_task)
|
|
478
552
|
Spawn all independent workers at once. Example:
|
|
479
553
|
|
|
@@ -675,10 +749,15 @@ const ROLE_PROMPTS = {
|
|
|
675
749
|
|
|
676
750
|
'testing': `
|
|
677
751
|
### Role-Specific: Test Engineer
|
|
678
|
-
-
|
|
679
|
-
|
|
752
|
+
- CRITICAL: NEVER assume response format, status codes, or field names. Before writing ANY test:
|
|
753
|
+
1. Check \`artifact_list\` for a shared-conventions or api-contract artifact
|
|
754
|
+
2. If found, use \`artifact_get\` to read it and match your assertions to those conventions
|
|
755
|
+
3. If NOT found, use \`team_ask\` to ask route/API workers: "What response format and status codes do your endpoints use?"
|
|
756
|
+
4. Only write tests AFTER you know the exact response shape
|
|
680
757
|
- Publish test results as artifacts with type "test-results" including: passed, failed, skipped counts and failure details
|
|
681
|
-
- If tests fail due to bugs in another session's code, use team_send_message to notify them with the exact error and file path
|
|
758
|
+
- If tests fail due to bugs in another session's code, use \`team_send_message\` to notify them with the exact error and file path
|
|
759
|
+
- Use the correct status values as defined in the database schema (e.g., "in_progress" not "in-progress")
|
|
760
|
+
- IMPORTANT: When calling artifact_get, ALWAYS include reader: "<your-session-name>" so the orchestrator can verify you read the artifacts
|
|
682
761
|
`,
|
|
683
762
|
|
|
684
763
|
'database': `
|
|
@@ -762,6 +841,8 @@ IMPORTANT: You are the ORCHESTRATOR. Your job is to PLAN, SPAWN, and MONITOR —
|
|
|
762
841
|
|
|
763
842
|
1. **Plan** — Break the user's request into independent work units. Identify what can run in PARALLEL vs what is SEQUENTIAL.
|
|
764
843
|
|
|
844
|
+
1.5. **Define Conventions** — Before spawning, define shared conventions (response format, status codes, naming) either as a published artifact or in each worker's prompt. Workers CANNOT coordinate on conventions after they start — define them upfront.
|
|
845
|
+
|
|
765
846
|
2. **Spawn** — Use \`team_spawn\` to launch all independent workers in a SINGLE message with multiple tool calls. This makes them run in parallel. NEVER spawn workers one at a time sequentially.
|
|
766
847
|
|
|
767
848
|
3. **Tell workers** — Every worker prompt MUST include instructions to:
|
|
@@ -773,6 +854,8 @@ IMPORTANT: You are the ORCHESTRATOR. Your job is to PLAN, SPAWN, and MONITOR —
|
|
|
773
854
|
|
|
774
855
|
4. **Set up dependencies** — Use \`contract_create\` if Task B needs output from Task A.
|
|
775
856
|
|
|
857
|
+
4.5. **Phase Gate** — Before spawning workers that depend on previous workers' output, VERIFY the dependency artifact exists by calling \`artifact_list()\` and \`artifact_get()\`. Never trust self-reported completion — verify the artifact.
|
|
858
|
+
|
|
776
859
|
5. **Monitor** — Check progress with \`team_roster()\`, \`contract_list()\`, \`artifact_list()\`. Only intervene when a worker is BLOCKED or FAILED.
|
|
777
860
|
|
|
778
861
|
6. **Collect** — When all workers are idle, check \`artifact_list\` for published outputs and summarize results for the user.
|
|
@@ -819,6 +902,7 @@ IMPORTANT: Always tell workers in their prompt to:
|
|
|
819
902
|
3. Publish results as artifacts (\`artifact_publish\`)
|
|
820
903
|
4. Broadcast completion (\`team_broadcast\`)
|
|
821
904
|
5. Update their status when done (\`team_update_status\`)
|
|
905
|
+
6. Follow shared conventions you defined (include the convention rules in the prompt OR tell them to read the conventions artifact)
|
|
822
906
|
`;
|
|
823
907
|
|
|
824
908
|
const ORCHESTRATOR_DELEGATE = `
|
|
@@ -859,7 +943,7 @@ WHY: Specific file, exact error, expected behavior, and how to verify.
|
|
|
859
943
|
const ORCHESTRATOR_MONITORING = `
|
|
860
944
|
## How to Monitor Without Micromanaging
|
|
861
945
|
|
|
862
|
-
|
|
946
|
+
You MUST check progress using these tools after spawning workers — do NOT fall back to filesystem checks (Glob, Read) to verify worker output:
|
|
863
947
|
|
|
864
948
|
\`\`\`
|
|
865
949
|
mcp__multi-session__team_roster() — See who's active/idle/blocked
|
|
@@ -867,6 +951,19 @@ mcp__multi-session__contract_list() — See contract statuses
|
|
|
867
951
|
mcp__multi-session__artifact_list() — See published outputs
|
|
868
952
|
\`\`\`
|
|
869
953
|
|
|
954
|
+
### Phase Gate Verification
|
|
955
|
+
Between spawn phases, use these to verify dependencies:
|
|
956
|
+
\`\`\`
|
|
957
|
+
mcp__multi-session__artifact_get({ artifactId: "db-schema" }) — Verify artifact content
|
|
958
|
+
mcp__multi-session__artifact_readers({ artifactId: "db-schema" }) — Check who read it
|
|
959
|
+
\`\`\`
|
|
960
|
+
|
|
961
|
+
After all workers complete, verify every worker consumed the artifacts they needed:
|
|
962
|
+
\`\`\`
|
|
963
|
+
mcp__multi-session__artifact_readers({ artifactId: "shared-conventions" })
|
|
964
|
+
// Should list ALL route workers as readers
|
|
965
|
+
\`\`\`
|
|
966
|
+
|
|
870
967
|
### When to Intervene
|
|
871
968
|
|
|
872
969
|
| Worker Status | What to Do |
|