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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-multi-session",
3
- "version": "2.3.0",
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": {
@@ -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
- - ALWAYS check artifact_list for API contracts and schemas before writing tests
679
- - If dependencies are not yet published, use team_ask to check when they'll be ready
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
- Check progress periodically — do NOT read full outputs from workers:
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 |