create-merlin-brain 3.6.3 → 3.7.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.
@@ -48,6 +48,10 @@ export function createServer() {
48
48
  description: 'Merlin Brain. MANDATORY BOOT: Call merlin_get_selected_repo then merlin_get_project_status BEFORE responding to user. Call merlin_get_context before every file edit. Never run claude --agent via Bash — use Skill("merlin:route") instead.',
49
49
  });
50
50
  const client = getClient();
51
+ // Tool loading mode: core (15 tools) vs all (67 tools)
52
+ // Core mode stays under Claude Code's 10% context threshold to avoid Tool Search lazy loading
53
+ // Set MERLIN_CORE_ONLY=true to load only 22 essential tools (if context is tight)
54
+ const allToolsMode = process.env.MERLIN_CORE_ONLY !== 'true';
51
55
  // Cache for resolved repo IDs to avoid repeated git/API calls
52
56
  // Key: url or 'auto' for auto-detected, Value: { repoId, expiresAt }
53
57
  const repoIdCache = new Map();
@@ -1186,51 +1190,76 @@ export function createServer() {
1186
1190
  throw error;
1187
1191
  }
1188
1192
  });
1189
- // Tool: merlin_check_repo_status
1190
- server.tool('merlin_check_repo_status', 'Check if a repository analysis is complete. Use after merlin_connect_repo to poll for completion. Returns status: pending, analyzing, completed, or failed.', {
1191
- repoUrl: z.string().optional().describe('GitHub URL of the repository (uses selected repo if omitted)'),
1192
- }, async ({ repoUrl }) => {
1193
- try {
1194
- // If repoUrl provided, check that specific repo
1195
- // Otherwise check the selected repo
1196
- const targetUrl = repoUrl || selectedRepoUrl;
1197
- if (!targetUrl) {
1198
- return {
1199
- content: [{
1200
- type: 'text',
1201
- text: 'No repository specified or selected. Provide repoUrl or select a repo first.',
1202
- }],
1203
- isError: true,
1204
- };
1205
- }
1206
- const repos = await client.getRepositories();
1207
- const repo = repos.find(r => r.url.includes(targetUrl.replace(/^https?:\/\//, '').replace(/\.git$/, '')));
1208
- if (!repo) {
1209
- return {
1210
- content: [{
1211
- type: 'text',
1212
- text: `Repository not found: ${targetUrl}\n\nIt may still be initializing. Try again in a moment or check merlin.build dashboard.`,
1213
- }],
1214
- };
1215
- }
1216
- const statusEmoji = {
1217
- pending: '⏳',
1218
- analyzing: '🔄',
1219
- completed: '✅',
1220
- failed: '❌',
1221
- locked: '🔒',
1222
- pending_tokens: '💳',
1223
- }[repo.status] || '❓';
1224
- // If repo is locked, attempt to unlock it first
1225
- if (repo.status === 'locked') {
1226
- console.log(`Repo ${repo.id} is locked, attempting auto-unlock...`);
1227
- const unlockResult = await client.unlockRepo(repo.id);
1228
- if (unlockResult.success) {
1229
- console.log(`Successfully unlocked repo ${repo.id}`);
1230
- let response = `# Repository Unlocked! ✅\n\n`;
1231
- response += `**Repository:** ${repo.fullName || repo.name}\n`;
1232
- response += `**Status:** Ready\n\n`;
1233
- response += `This repository was locked but has been unlocked with your available tokens. You can now use it.`;
1193
+ if (allToolsMode) { // Tool: merlin_check_repo_status
1194
+ server.tool('merlin_check_repo_status', 'Check if a repository analysis is complete. Use after merlin_connect_repo to poll for completion. Returns status: pending, analyzing, completed, or failed.', {
1195
+ repoUrl: z.string().optional().describe('GitHub URL of the repository (uses selected repo if omitted)'),
1196
+ }, async ({ repoUrl }) => {
1197
+ try {
1198
+ // If repoUrl provided, check that specific repo
1199
+ // Otherwise check the selected repo
1200
+ const targetUrl = repoUrl || selectedRepoUrl;
1201
+ if (!targetUrl) {
1202
+ return {
1203
+ content: [{
1204
+ type: 'text',
1205
+ text: 'No repository specified or selected. Provide repoUrl or select a repo first.',
1206
+ }],
1207
+ isError: true,
1208
+ };
1209
+ }
1210
+ const repos = await client.getRepositories();
1211
+ const repo = repos.find(r => r.url.includes(targetUrl.replace(/^https?:\/\//, '').replace(/\.git$/, '')));
1212
+ if (!repo) {
1213
+ return {
1214
+ content: [{
1215
+ type: 'text',
1216
+ text: `Repository not found: ${targetUrl}\n\nIt may still be initializing. Try again in a moment or check merlin.build dashboard.`,
1217
+ }],
1218
+ };
1219
+ }
1220
+ const statusEmoji = {
1221
+ pending: '⏳',
1222
+ analyzing: '🔄',
1223
+ completed: '✅',
1224
+ failed: '❌',
1225
+ locked: '🔒',
1226
+ pending_tokens: '💳',
1227
+ }[repo.status] || '❓';
1228
+ // If repo is locked, attempt to unlock it first
1229
+ if (repo.status === 'locked') {
1230
+ console.log(`Repo ${repo.id} is locked, attempting auto-unlock...`);
1231
+ const unlockResult = await client.unlockRepo(repo.id);
1232
+ if (unlockResult.success) {
1233
+ console.log(`Successfully unlocked repo ${repo.id}`);
1234
+ let response = `# Repository Unlocked! ✅\n\n`;
1235
+ response += `**Repository:** ${repo.fullName || repo.name}\n`;
1236
+ response += `**Status:** Ready\n\n`;
1237
+ response += `This repository was locked but has been unlocked with your available tokens. You can now use it.`;
1238
+ // Auto-select if not already selected
1239
+ if (!selectedRepoId) {
1240
+ selectedRepoId = repo.id;
1241
+ selectedRepoUrl = repo.url;
1242
+ selectedRepoName = repo.fullName || repo.name;
1243
+ response += `\n\n*Auto-selected this repository for the session.*`;
1244
+ }
1245
+ return { content: [{ type: 'text', text: response }] };
1246
+ }
1247
+ else {
1248
+ console.log(`Failed to unlock repo ${repo.id}: ${unlockResult.error}`);
1249
+ let response = `# Repository Locked 🔒\n\n`;
1250
+ response += `**Repository:** ${repo.fullName || repo.name}\n`;
1251
+ response += `**Status:** locked\n\n`;
1252
+ response += `${unlockResult.error || 'Insufficient tokens'}\n\n`;
1253
+ response += `You need to purchase more tokens to unlock this repository. Visit https://merlin.build/settings to purchase tokens.`;
1254
+ return { content: [{ type: 'text', text: response }] };
1255
+ }
1256
+ }
1257
+ let response = `# Repository Status\n\n`;
1258
+ response += `**Repository:** ${repo.fullName || repo.name}\n`;
1259
+ response += `**Status:** ${statusEmoji} ${repo.status}\n`;
1260
+ if (repo.status === 'completed') {
1261
+ response += `**Last Analyzed:** ${repo.lastAnalyzedAt}\n\n`;
1262
+ response += `Ready to use! Call \`merlin_get_context\` to start working.`;
1234
1263
  // Auto-select if not already selected
1235
1264
  if (!selectedRepoId) {
1236
1265
  selectedRepoId = repo.id;
@@ -1238,55 +1267,31 @@ export function createServer() {
1238
1267
  selectedRepoName = repo.fullName || repo.name;
1239
1268
  response += `\n\n*Auto-selected this repository for the session.*`;
1240
1269
  }
1241
- return { content: [{ type: 'text', text: response }] };
1270
+ }
1271
+ else if (repo.status === 'analyzing') {
1272
+ response += `\nAnalysis in progress. Check again in 30-60 seconds.`;
1273
+ }
1274
+ else if (repo.status === 'failed') {
1275
+ response += `\nAnalysis failed. Check merlin.build dashboard for details.`;
1242
1276
  }
1243
1277
  else {
1244
- console.log(`Failed to unlock repo ${repo.id}: ${unlockResult.error}`);
1245
- let response = `# Repository Locked 🔒\n\n`;
1246
- response += `**Repository:** ${repo.fullName || repo.name}\n`;
1247
- response += `**Status:** locked\n\n`;
1248
- response += `${unlockResult.error || 'Insufficient tokens'}\n\n`;
1249
- response += `You need to purchase more tokens to unlock this repository. Visit https://merlin.build/settings to purchase tokens.`;
1250
- return { content: [{ type: 'text', text: response }] };
1251
- }
1252
- }
1253
- let response = `# Repository Status\n\n`;
1254
- response += `**Repository:** ${repo.fullName || repo.name}\n`;
1255
- response += `**Status:** ${statusEmoji} ${repo.status}\n`;
1256
- if (repo.status === 'completed') {
1257
- response += `**Last Analyzed:** ${repo.lastAnalyzedAt}\n\n`;
1258
- response += `Ready to use! Call \`merlin_get_context\` to start working.`;
1259
- // Auto-select if not already selected
1260
- if (!selectedRepoId) {
1261
- selectedRepoId = repo.id;
1262
- selectedRepoUrl = repo.url;
1263
- selectedRepoName = repo.fullName || repo.name;
1264
- response += `\n\n*Auto-selected this repository for the session.*`;
1265
- }
1266
- }
1267
- else if (repo.status === 'analyzing') {
1268
- response += `\nAnalysis in progress. Check again in 30-60 seconds.`;
1269
- }
1270
- else if (repo.status === 'failed') {
1271
- response += `\nAnalysis failed. Check merlin.build dashboard for details.`;
1278
+ response += `\nWaiting to start. Check again in a moment.`;
1279
+ }
1280
+ return { content: [{ type: 'text', text: response }] };
1272
1281
  }
1273
- else {
1274
- response += `\nWaiting to start. Check again in a moment.`;
1282
+ catch (error) {
1283
+ if (isAuthError(error))
1284
+ return buildAuthErrorResponse();
1285
+ return {
1286
+ content: [{
1287
+ type: 'text',
1288
+ text: `Error checking status: ${error instanceof Error ? error.message : 'Unknown error'}`,
1289
+ }],
1290
+ isError: true,
1291
+ };
1275
1292
  }
1276
- return { content: [{ type: 'text', text: response }] };
1277
- }
1278
- catch (error) {
1279
- if (isAuthError(error))
1280
- return buildAuthErrorResponse();
1281
- return {
1282
- content: [{
1283
- type: 'text',
1284
- text: `Error checking status: ${error instanceof Error ? error.message : 'Unknown error'}`,
1285
- }],
1286
- isError: true,
1287
- };
1288
- }
1289
- });
1293
+ });
1294
+ } // end allToolsMode: merlin_check_repo_status
1290
1295
  // Tool: merlin_connect_repo
1291
1296
  server.tool('merlin_connect_repo', 'Connect a new repository to Merlin Sights for analysis. Use this when working on a repo that is not yet in Sights. Analysis takes 2-5 minutes.', {
1292
1297
  repoUrl: z.string().describe('GitHub URL of the repository (e.g., "https://github.com/user/repo")'),
@@ -1491,66 +1496,68 @@ export function createServer() {
1491
1496
  };
1492
1497
  }
1493
1498
  });
1494
- // Tool: merlin_similar_code
1495
- server.tool('merlin_similar_code', 'Find similar implementations in the codebase BEFORE writing new code. Prevents duplicating existing patterns. Use this when implementing anything — there is likely an existing pattern to follow.', {
1496
- description: z.string().describe('What you\'re trying to implement (e.g., "API endpoint with authentication", "database query with pagination", "React form with validation")'),
1497
- repoUrl: z.string().optional().describe('GitHub URL of the repository (auto-detected from git if omitted)'),
1498
- }, async ({ description, repoUrl }) => {
1499
- try {
1500
- const repoId = await resolveRepoId(repoUrl);
1501
- if (!repoId) {
1499
+ if (allToolsMode) { // Tool: merlin_similar_code
1500
+ server.tool('merlin_similar_code', 'Find similar implementations in the codebase BEFORE writing new code. Prevents duplicating existing patterns. Use this when implementing anything — there is likely an existing pattern to follow.', {
1501
+ description: z.string().describe('What you\'re trying to implement (e.g., "API endpoint with authentication", "database query with pagination", "React form with validation")'),
1502
+ repoUrl: z.string().optional().describe('GitHub URL of the repository (auto-detected from git if omitted)'),
1503
+ }, async ({ description, repoUrl }) => {
1504
+ try {
1505
+ const repoId = await resolveRepoId(repoUrl);
1506
+ if (!repoId) {
1507
+ return {
1508
+ content: [{
1509
+ type: 'text',
1510
+ text: 'Could not find repository. Make sure the repo is analyzed on merlin.build.',
1511
+ }],
1512
+ };
1513
+ }
1514
+ const similar = await client.getSimilarCode(repoId, description);
1515
+ return { content: [{ type: 'text', text: similar }] };
1516
+ }
1517
+ catch (error) {
1518
+ if (isAuthError(error))
1519
+ return buildAuthErrorResponse();
1502
1520
  return {
1503
1521
  content: [{
1504
1522
  type: 'text',
1505
- text: 'Could not find repository. Make sure the repo is analyzed on merlin.build.',
1523
+ text: `Error finding similar code: ${error instanceof Error ? error.message : 'Unknown error'}`,
1506
1524
  }],
1525
+ isError: true,
1507
1526
  };
1508
1527
  }
1509
- const similar = await client.getSimilarCode(repoId, description);
1510
- return { content: [{ type: 'text', text: similar }] };
1511
- }
1512
- catch (error) {
1513
- if (isAuthError(error))
1514
- return buildAuthErrorResponse();
1515
- return {
1516
- content: [{
1517
- type: 'text',
1518
- text: `Error finding similar code: ${error instanceof Error ? error.message : 'Unknown error'}`,
1519
- }],
1520
- isError: true,
1521
- };
1522
- }
1523
- });
1524
- // Tool: merlin_code_examples
1525
- server.tool('merlin_code_examples', 'Get real code examples from this codebase for common tasks. Shows actual patterns used in this project.', {
1526
- task: z.string().optional().describe('Specific task (e.g., "add endpoint", "add test", "error handling"). Leave empty for all examples.'),
1527
- repoUrl: z.string().optional().describe('GitHub URL of the repository (auto-detected from git if omitted)'),
1528
- }, async ({ task, repoUrl }) => {
1529
- try {
1530
- const repoId = await resolveRepoId(repoUrl);
1531
- if (!repoId) {
1528
+ });
1529
+ } // end allToolsMode: merlin_similar_code
1530
+ if (allToolsMode) { // Tool: merlin_code_examples
1531
+ server.tool('merlin_code_examples', 'Get real code examples from this codebase for common tasks. Shows actual patterns used in this project.', {
1532
+ task: z.string().optional().describe('Specific task (e.g., "add endpoint", "add test", "error handling"). Leave empty for all examples.'),
1533
+ repoUrl: z.string().optional().describe('GitHub URL of the repository (auto-detected from git if omitted)'),
1534
+ }, async ({ task, repoUrl }) => {
1535
+ try {
1536
+ const repoId = await resolveRepoId(repoUrl);
1537
+ if (!repoId) {
1538
+ return {
1539
+ content: [{
1540
+ type: 'text',
1541
+ text: 'Could not find repository. Make sure the repo is analyzed on merlin.build.',
1542
+ }],
1543
+ };
1544
+ }
1545
+ const examples = await client.getCodeExamples(repoId, task);
1546
+ return { content: [{ type: 'text', text: examples }] };
1547
+ }
1548
+ catch (error) {
1549
+ if (isAuthError(error))
1550
+ return buildAuthErrorResponse();
1532
1551
  return {
1533
1552
  content: [{
1534
1553
  type: 'text',
1535
- text: 'Could not find repository. Make sure the repo is analyzed on merlin.build.',
1554
+ text: `Error getting code examples: ${error instanceof Error ? error.message : 'Unknown error'}`,
1536
1555
  }],
1556
+ isError: true,
1537
1557
  };
1538
1558
  }
1539
- const examples = await client.getCodeExamples(repoId, task);
1540
- return { content: [{ type: 'text', text: examples }] };
1541
- }
1542
- catch (error) {
1543
- if (isAuthError(error))
1544
- return buildAuthErrorResponse();
1545
- return {
1546
- content: [{
1547
- type: 'text',
1548
- text: `Error getting code examples: ${error instanceof Error ? error.message : 'Unknown error'}`,
1549
- }],
1550
- isError: true,
1551
- };
1552
- }
1553
- });
1559
+ });
1560
+ } // end allToolsMode: merlin_code_examples
1554
1561
  // Tool: merlin_ask
1555
1562
  server.tool('merlin_ask', 'Ask ANY question about the codebase in natural language. Uses AI-powered semantic search + RAG to find relevant context and generate accurate answers with source citations. Perfect for questions like "How does authentication work?", "Where should I add a new API endpoint?", "Why is X structured this way?"', {
1556
1563
  question: z.string().describe('Your question in natural language (e.g., "How does the payment flow work?", "Where is user validation handled?", "What patterns are used for error handling?")'),
@@ -1765,671 +1772,682 @@ export function createServer() {
1765
1772
  };
1766
1773
  }
1767
1774
  });
1768
- // Tool: merlin_log_activity
1769
- server.tool('merlin_log_activity', 'Log an activity event to the team timeline. Use for tracking progress, milestones, and decisions.', {
1770
- eventType: z.string().describe('Event type (e.g., "phase_started", "task_completed", "blocker_hit", "decision_made")'),
1771
- eventData: z.any().optional().describe('Additional event data (JSON object)'),
1772
- agentId: z.string().describe('Agent identifier for tracking'),
1773
- sessionId: z.string().optional().describe('Session ID for grouping events'),
1774
- repoUrl: z.string().optional().describe('GitHub URL of the repository (auto-detected from git if omitted)'),
1775
- }, async ({ eventType, eventData, agentId, sessionId, repoUrl }) => {
1776
- try {
1777
- const repoId = await resolveRepoId(repoUrl);
1778
- if (!repoId) {
1775
+ if (allToolsMode) { // Tool: merlin_log_activity
1776
+ server.tool('merlin_log_activity', 'Log an activity event to the team timeline. Use for tracking progress, milestones, and decisions.', {
1777
+ eventType: z.string().describe('Event type (e.g., "phase_started", "task_completed", "blocker_hit", "decision_made")'),
1778
+ eventData: z.any().optional().describe('Additional event data (JSON object)'),
1779
+ agentId: z.string().describe('Agent identifier for tracking'),
1780
+ sessionId: z.string().optional().describe('Session ID for grouping events'),
1781
+ repoUrl: z.string().optional().describe('GitHub URL of the repository (auto-detected from git if omitted)'),
1782
+ }, async ({ eventType, eventData, agentId, sessionId, repoUrl }) => {
1783
+ try {
1784
+ const repoId = await resolveRepoId(repoUrl);
1785
+ if (!repoId) {
1786
+ return {
1787
+ content: [{
1788
+ type: 'text',
1789
+ text: 'Could not find repository. Make sure the repo is analyzed on merlin.build.',
1790
+ }],
1791
+ };
1792
+ }
1793
+ const result = await client.logActivity(repoId, eventType, {
1794
+ agentId,
1795
+ sessionId,
1796
+ eventData,
1797
+ });
1798
+ const notification = formatSaveNotification('activity', {
1799
+ key: eventType,
1800
+ timestamp: result.event?.timestamp,
1801
+ isSuccess: true,
1802
+ details: `Event ID: ${result.event?.id}`,
1803
+ });
1779
1804
  return {
1780
1805
  content: [{
1781
1806
  type: 'text',
1782
- text: 'Could not find repository. Make sure the repo is analyzed on merlin.build.',
1807
+ text: notification,
1783
1808
  }],
1784
1809
  };
1785
1810
  }
1786
- const result = await client.logActivity(repoId, eventType, {
1787
- agentId,
1788
- sessionId,
1789
- eventData,
1790
- });
1791
- const notification = formatSaveNotification('activity', {
1792
- key: eventType,
1793
- timestamp: result.event?.timestamp,
1794
- isSuccess: true,
1795
- details: `Event ID: ${result.event?.id}`,
1796
- });
1797
- return {
1798
- content: [{
1799
- type: 'text',
1800
- text: notification,
1801
- }],
1802
- };
1803
- }
1804
- catch (error) {
1805
- if (isAuthError(error))
1806
- return buildAuthErrorResponse();
1807
- return {
1808
- content: [{
1809
- type: 'text',
1810
- text: `Error logging activity: ${error instanceof Error ? error.message : 'Unknown error'}`,
1811
- }],
1812
- isError: true,
1813
- };
1814
- }
1815
- });
1816
- // Tool: merlin_sync_task
1817
- server.tool('merlin_sync_task', 'Sync a task to Merlin cloud. Use to track work items, their status, and dependencies for team visibility.', {
1818
- title: z.string().describe('Task title'),
1819
- status: z.enum(['pending', 'in_progress', 'completed', 'blocked', 'skipped']).optional().describe('Task status'),
1820
- description: z.string().optional().describe('Task description'),
1821
- externalId: z.string().optional().describe('External ID for linking to Claude Tasks or other systems'),
1822
- ownerAgent: z.string().optional().describe('Agent that owns this task'),
1823
- phase: z.string().optional().describe('Phase identifier'),
1824
- phaseName: z.string().optional().describe('Phase name'),
1825
- plan: z.string().optional().describe('Plan identifier'),
1826
- planName: z.string().optional().describe('Plan name'),
1827
- taskNumber: z.number().optional().describe('Task number within plan'),
1828
- wave: z.number().optional().describe('Execution wave'),
1829
- commitSha: z.string().optional().describe('Commit SHA if task resulted in a commit'),
1830
- filesChanged: z.array(z.string()).optional().describe('Files affected by this task'),
1831
- repoUrl: z.string().optional().describe('GitHub URL of the repository (auto-detected from git if omitted)'),
1832
- }, async ({ title, status, description, externalId, ownerAgent, phase, phaseName, plan, planName, taskNumber, wave, commitSha, filesChanged, repoUrl }) => {
1833
- try {
1834
- const repoId = await resolveRepoId(repoUrl);
1835
- if (!repoId) {
1811
+ catch (error) {
1812
+ if (isAuthError(error))
1813
+ return buildAuthErrorResponse();
1836
1814
  return {
1837
1815
  content: [{
1838
1816
  type: 'text',
1839
- text: 'Could not find repository. Make sure the repo is analyzed on merlin.build.',
1817
+ text: `Error logging activity: ${error instanceof Error ? error.message : 'Unknown error'}`,
1840
1818
  }],
1819
+ isError: true,
1841
1820
  };
1842
1821
  }
1843
- const result = await client.syncTask(repoId, {
1844
- title,
1845
- status,
1846
- description,
1847
- externalId,
1848
- ownerAgent,
1849
- phase,
1850
- phaseName,
1851
- plan,
1852
- planName,
1853
- taskNumber,
1854
- wave,
1855
- commitSha,
1856
- filesChanged,
1857
- });
1858
- const notification = formatSaveNotification('task', {
1859
- key: title,
1860
- isSuccess: true,
1861
- details: `ID: ${result.task?.id} Status: ${result.task?.status}`,
1862
- });
1863
- return {
1864
- content: [{
1865
- type: 'text',
1866
- text: notification,
1867
- }],
1868
- };
1869
- }
1870
- catch (error) {
1871
- if (isAuthError(error))
1872
- return buildAuthErrorResponse();
1873
- return {
1874
- content: [{
1875
- type: 'text',
1876
- text: `Error syncing task: ${error instanceof Error ? error.message : 'Unknown error'}`,
1877
- }],
1878
- isError: true,
1879
- };
1880
- }
1881
- });
1882
- // Tool: merlin_get_tasks
1883
- server.tool('merlin_get_tasks', 'Get tasks synced to Merlin. Shows work items from all agents for team coordination.', {
1884
- status: z.string().optional().describe('Filter by status (pending, in_progress, completed, blocked)'),
1885
- phase: z.string().optional().describe('Filter by phase'),
1886
- plan: z.string().optional().describe('Filter by plan'),
1887
- repoUrl: z.string().optional().describe('GitHub URL of the repository (auto-detected from git if omitted)'),
1888
- }, async ({ status, phase, plan, repoUrl }) => {
1889
- try {
1890
- const repoId = await resolveRepoId(repoUrl);
1891
- if (!repoId) {
1822
+ });
1823
+ } // end allToolsMode: merlin_log_activity
1824
+ if (allToolsMode) { // Tool: merlin_sync_task
1825
+ server.tool('merlin_sync_task', 'Sync a task to Merlin cloud. Use to track work items, their status, and dependencies for team visibility.', {
1826
+ title: z.string().describe('Task title'),
1827
+ status: z.enum(['pending', 'in_progress', 'completed', 'blocked', 'skipped']).optional().describe('Task status'),
1828
+ description: z.string().optional().describe('Task description'),
1829
+ externalId: z.string().optional().describe('External ID for linking to Claude Tasks or other systems'),
1830
+ ownerAgent: z.string().optional().describe('Agent that owns this task'),
1831
+ phase: z.string().optional().describe('Phase identifier'),
1832
+ phaseName: z.string().optional().describe('Phase name'),
1833
+ plan: z.string().optional().describe('Plan identifier'),
1834
+ planName: z.string().optional().describe('Plan name'),
1835
+ taskNumber: z.number().optional().describe('Task number within plan'),
1836
+ wave: z.number().optional().describe('Execution wave'),
1837
+ commitSha: z.string().optional().describe('Commit SHA if task resulted in a commit'),
1838
+ filesChanged: z.array(z.string()).optional().describe('Files affected by this task'),
1839
+ repoUrl: z.string().optional().describe('GitHub URL of the repository (auto-detected from git if omitted)'),
1840
+ }, async ({ title, status, description, externalId, ownerAgent, phase, phaseName, plan, planName, taskNumber, wave, commitSha, filesChanged, repoUrl }) => {
1841
+ try {
1842
+ const repoId = await resolveRepoId(repoUrl);
1843
+ if (!repoId) {
1844
+ return {
1845
+ content: [{
1846
+ type: 'text',
1847
+ text: 'Could not find repository. Make sure the repo is analyzed on merlin.build.',
1848
+ }],
1849
+ };
1850
+ }
1851
+ const result = await client.syncTask(repoId, {
1852
+ title,
1853
+ status,
1854
+ description,
1855
+ externalId,
1856
+ ownerAgent,
1857
+ phase,
1858
+ phaseName,
1859
+ plan,
1860
+ planName,
1861
+ taskNumber,
1862
+ wave,
1863
+ commitSha,
1864
+ filesChanged,
1865
+ });
1866
+ const notification = formatSaveNotification('task', {
1867
+ key: title,
1868
+ isSuccess: true,
1869
+ details: `ID: ${result.task?.id} Status: ${result.task?.status}`,
1870
+ });
1892
1871
  return {
1893
1872
  content: [{
1894
1873
  type: 'text',
1895
- text: 'Could not find repository. Make sure the repo is analyzed on merlin.build.',
1874
+ text: notification,
1896
1875
  }],
1897
1876
  };
1898
1877
  }
1899
- const result = await client.getTasks(repoId, { status, phase, plan });
1900
- if (result.tasks.length === 0) {
1878
+ catch (error) {
1879
+ if (isAuthError(error))
1880
+ return buildAuthErrorResponse();
1901
1881
  return {
1902
1882
  content: [{
1903
1883
  type: 'text',
1904
- text: 'No tasks found. Use merlin_sync_task to track work items.',
1884
+ text: `Error syncing task: ${error instanceof Error ? error.message : 'Unknown error'}`,
1905
1885
  }],
1886
+ isError: true,
1906
1887
  };
1907
1888
  }
1908
- let response = `# Tasks (${result.count})\n\n`;
1909
- response += `## Summary\n`;
1910
- Object.entries(result.byStatus).forEach(([s, count]) => {
1911
- const emoji = s === 'completed' ? '' : s === 'in_progress' ? '' : s === 'blocked' ? '' : '';
1912
- response += `- ${emoji} ${s}: ${count}\n`;
1913
- });
1914
- response += '\n';
1915
- result.tasks.forEach((t) => {
1916
- const statusEmoji = t.status === 'completed' ? '' : t.status === 'in_progress' ? '' : t.status === 'blocked' ? '' : '';
1917
- response += `### ${statusEmoji} ${t.title}\n`;
1918
- response += `- ID: ${t.id}\n`;
1919
- response += `- Status: ${t.status}\n`;
1920
- if (t.phase)
1921
- response += `- Phase: ${t.phase_name || t.phase}\n`;
1922
- if (t.plan)
1923
- response += `- Plan: ${t.plan_name || t.plan}\n`;
1924
- if (t.owner_agent)
1925
- response += `- Owner: ${t.owner_agent}\n`;
1926
- if (t.description)
1927
- response += `- Description: ${t.description.slice(0, 100)}...\n`;
1889
+ });
1890
+ } // end allToolsMode: merlin_sync_task
1891
+ if (allToolsMode) { // Tool: merlin_get_tasks
1892
+ server.tool('merlin_get_tasks', 'Get tasks synced to Merlin. Shows work items from all agents for team coordination.', {
1893
+ status: z.string().optional().describe('Filter by status (pending, in_progress, completed, blocked)'),
1894
+ phase: z.string().optional().describe('Filter by phase'),
1895
+ plan: z.string().optional().describe('Filter by plan'),
1896
+ repoUrl: z.string().optional().describe('GitHub URL of the repository (auto-detected from git if omitted)'),
1897
+ }, async ({ status, phase, plan, repoUrl }) => {
1898
+ try {
1899
+ const repoId = await resolveRepoId(repoUrl);
1900
+ if (!repoId) {
1901
+ return {
1902
+ content: [{
1903
+ type: 'text',
1904
+ text: 'Could not find repository. Make sure the repo is analyzed on merlin.build.',
1905
+ }],
1906
+ };
1907
+ }
1908
+ const result = await client.getTasks(repoId, { status, phase, plan });
1909
+ if (result.tasks.length === 0) {
1910
+ return {
1911
+ content: [{
1912
+ type: 'text',
1913
+ text: 'No tasks found. Use merlin_sync_task to track work items.',
1914
+ }],
1915
+ };
1916
+ }
1917
+ let response = `# Tasks (${result.count})\n\n`;
1918
+ response += `## Summary\n`;
1919
+ Object.entries(result.byStatus).forEach(([s, count]) => {
1920
+ const emoji = s === 'completed' ? '' : s === 'in_progress' ? '' : s === 'blocked' ? '' : '';
1921
+ response += `- ${emoji} ${s}: ${count}\n`;
1922
+ });
1928
1923
  response += '\n';
1929
- });
1930
- return { content: [{ type: 'text', text: response }] };
1931
- }
1932
- catch (error) {
1933
- if (isAuthError(error))
1934
- return buildAuthErrorResponse();
1935
- return {
1936
- content: [{
1937
- type: 'text',
1938
- text: `Error getting tasks: ${error instanceof Error ? error.message : 'Unknown error'}`,
1939
- }],
1940
- isError: true,
1941
- };
1942
- }
1943
- });
1944
- // Tool: merlin_report_blocker
1945
- server.tool('merlin_report_blocker', 'Report a blocker that requires human attention. The blocker will appear in the team dashboard for resolution.', {
1946
- title: z.string().describe('Brief description of the blocker'),
1947
- blockerType: z.enum(['human_verify', 'auth_required', 'clarification', 'decision_point', 'external', 'error', 'other']).describe('Type of blocker'),
1948
- description: z.string().optional().describe('Detailed description'),
1949
- severity: z.enum(['low', 'medium', 'high', 'critical']).optional().describe('Severity level'),
1950
- agentId: z.string().describe('Agent reporting the blocker'),
1951
- context: z.any().optional().describe('Additional context (files, errors, etc.)'),
1952
- repoUrl: z.string().optional().describe('GitHub URL of the repository (auto-detected from git if omitted)'),
1953
- }, async ({ title, blockerType, description, severity, agentId, context, repoUrl }) => {
1954
- try {
1955
- const repoId = await resolveRepoId(repoUrl);
1956
- if (!repoId) {
1924
+ result.tasks.forEach((t) => {
1925
+ const statusEmoji = t.status === 'completed' ? '' : t.status === 'in_progress' ? '' : t.status === 'blocked' ? '' : '';
1926
+ response += `### ${statusEmoji} ${t.title}\n`;
1927
+ response += `- ID: ${t.id}\n`;
1928
+ response += `- Status: ${t.status}\n`;
1929
+ if (t.phase)
1930
+ response += `- Phase: ${t.phase_name || t.phase}\n`;
1931
+ if (t.plan)
1932
+ response += `- Plan: ${t.plan_name || t.plan}\n`;
1933
+ if (t.owner_agent)
1934
+ response += `- Owner: ${t.owner_agent}\n`;
1935
+ if (t.description)
1936
+ response += `- Description: ${t.description.slice(0, 100)}...\n`;
1937
+ response += '\n';
1938
+ });
1939
+ return { content: [{ type: 'text', text: response }] };
1940
+ }
1941
+ catch (error) {
1942
+ if (isAuthError(error))
1943
+ return buildAuthErrorResponse();
1957
1944
  return {
1958
1945
  content: [{
1959
1946
  type: 'text',
1960
- text: 'Could not find repository. Make sure the repo is analyzed on merlin.build.',
1947
+ text: `Error getting tasks: ${error instanceof Error ? error.message : 'Unknown error'}`,
1961
1948
  }],
1949
+ isError: true,
1962
1950
  };
1963
1951
  }
1964
- const result = await client.reportBlocker(repoId, {
1965
- agentId,
1966
- blockerType,
1967
- severity,
1968
- title,
1969
- description,
1970
- context,
1971
- });
1972
- const notification = formatSaveNotification('blocker', {
1973
- key: title,
1974
- isSuccess: true,
1975
- details: `ID: ${result.blocker?.id} Type: ${blockerType} Severity: ${severity || 'medium'}\nThis blocker is now visible in the Merlin dashboard for human resolution.`,
1976
- });
1977
- return {
1978
- content: [{
1979
- type: 'text',
1980
- text: notification,
1981
- }],
1982
- };
1983
- }
1984
- catch (error) {
1985
- if (isAuthError(error))
1986
- return buildAuthErrorResponse();
1987
- return {
1988
- content: [{
1989
- type: 'text',
1990
- text: `Error reporting blocker: ${error instanceof Error ? error.message : 'Unknown error'}`,
1991
- }],
1992
- isError: true,
1993
- };
1994
- }
1995
- });
1996
- // Tool: merlin_get_blockers
1997
- server.tool('merlin_get_blockers', 'Get open blockers awaiting resolution. Check before starting work to see if there are issues to resolve.', {
1998
- status: z.string().optional().default('open').describe('Filter by status (open, resolved, all)'),
1999
- repoUrl: z.string().optional().describe('GitHub URL of the repository (auto-detected from git if omitted)'),
2000
- }, async ({ status, repoUrl }) => {
2001
- try {
2002
- const repoId = await resolveRepoId(repoUrl);
2003
- if (!repoId) {
2004
- return {
2005
- content: [{
1952
+ });
1953
+ } // end allToolsMode: merlin_get_tasks
1954
+ if (allToolsMode) { // Tool: merlin_report_blocker
1955
+ server.tool('merlin_report_blocker', 'Report a blocker that requires human attention. The blocker will appear in the team dashboard for resolution.', {
1956
+ title: z.string().describe('Brief description of the blocker'),
1957
+ blockerType: z.enum(['human_verify', 'auth_required', 'clarification', 'decision_point', 'external', 'error', 'other']).describe('Type of blocker'),
1958
+ description: z.string().optional().describe('Detailed description'),
1959
+ severity: z.enum(['low', 'medium', 'high', 'critical']).optional().describe('Severity level'),
1960
+ agentId: z.string().describe('Agent reporting the blocker'),
1961
+ context: z.any().optional().describe('Additional context (files, errors, etc.)'),
1962
+ repoUrl: z.string().optional().describe('GitHub URL of the repository (auto-detected from git if omitted)'),
1963
+ }, async ({ title, blockerType, description, severity, agentId, context, repoUrl }) => {
1964
+ try {
1965
+ const repoId = await resolveRepoId(repoUrl);
1966
+ if (!repoId) {
1967
+ return {
1968
+ content: [{
1969
+ type: 'text',
1970
+ text: 'Could not find repository. Make sure the repo is analyzed on merlin.build.',
1971
+ }],
1972
+ };
1973
+ }
1974
+ const result = await client.reportBlocker(repoId, {
1975
+ agentId,
1976
+ blockerType,
1977
+ severity,
1978
+ title,
1979
+ description,
1980
+ context,
1981
+ });
1982
+ const notification = formatSaveNotification('blocker', {
1983
+ key: title,
1984
+ isSuccess: true,
1985
+ details: `ID: ${result.blocker?.id} Type: ${blockerType} Severity: ${severity || 'medium'}\nThis blocker is now visible in the Merlin dashboard for human resolution.`,
1986
+ });
1987
+ return {
1988
+ content: [{
2006
1989
  type: 'text',
2007
- text: 'Could not find repository. Make sure the repo is analyzed on merlin.build.',
1990
+ text: notification,
2008
1991
  }],
2009
1992
  };
2010
1993
  }
2011
- const result = await client.getBlockers(repoId, status);
2012
- if (result.blockers.length === 0) {
1994
+ catch (error) {
1995
+ if (isAuthError(error))
1996
+ return buildAuthErrorResponse();
2013
1997
  return {
2014
1998
  content: [{
2015
1999
  type: 'text',
2016
- text: 'No open blockers. All clear to proceed!',
2000
+ text: `Error reporting blocker: ${error instanceof Error ? error.message : 'Unknown error'}`,
2017
2001
  }],
2002
+ isError: true,
2018
2003
  };
2019
2004
  }
2020
- let response = `# Open Blockers (${result.count})\n\n`;
2021
- result.blockers.forEach((b) => {
2022
- const severityEmoji = b.severity === 'critical' ? '' : b.severity === 'high' ? '' : '';
2023
- response += `## ${severityEmoji} ${b.title}\n`;
2024
- response += `- ID: ${b.id}\n`;
2025
- response += `- Type: ${b.blocker_type}\n`;
2026
- response += `- Severity: ${b.severity}\n`;
2027
- response += `- Reported by: ${b.reported_by_agent}\n`;
2028
- response += `- Created: ${b.created_at}\n`;
2029
- if (b.description)
2030
- response += `\n${b.description}\n`;
2031
- response += '\n';
2032
- });
2033
- return { content: [{ type: 'text', text: response }] };
2034
- }
2035
- catch (error) {
2036
- if (isAuthError(error))
2037
- return buildAuthErrorResponse();
2038
- return {
2039
- content: [{
2040
- type: 'text',
2041
- text: `Error getting blockers: ${error instanceof Error ? error.message : 'Unknown error'}`,
2042
- }],
2043
- isError: true,
2044
- };
2045
- }
2046
- });
2047
- // Tool: merlin_create_checkpoint
2048
- server.tool('merlin_create_checkpoint', 'Create a checkpoint requiring human verification or decision. Pauses agent work until human responds.', {
2049
- title: z.string().describe('Checkpoint title'),
2050
- checkpointType: z.enum(['human_verify', 'auth_gate', 'decision_point', 'approval', 'review']).describe('Type of checkpoint'),
2051
- description: z.string().optional().describe('Detailed description'),
2052
- agentId: z.string().describe('Agent creating the checkpoint'),
2053
- sessionId: z.string().optional().describe('Session ID for tracking'),
2054
- whatWasBuilt: z.string().optional().describe('Summary of work completed before checkpoint'),
2055
- verificationItems: z.array(z.string()).optional().describe('Checklist items for human to verify'),
2056
- options: z.any().optional().describe('If decision_point, the available options'),
2057
- resumeContext: z.any().optional().describe('Context needed to resume after checkpoint'),
2058
- repoUrl: z.string().optional().describe('GitHub URL of the repository (auto-detected from git if omitted)'),
2059
- }, async ({ title, checkpointType, description, agentId, sessionId, whatWasBuilt, verificationItems, options, resumeContext, repoUrl }) => {
2060
- try {
2061
- const repoId = await resolveRepoId(repoUrl);
2062
- if (!repoId) {
2005
+ });
2006
+ } // end allToolsMode: merlin_report_blocker
2007
+ if (allToolsMode) { // Tool: merlin_get_blockers
2008
+ server.tool('merlin_get_blockers', 'Get open blockers awaiting resolution. Check before starting work to see if there are issues to resolve.', {
2009
+ status: z.string().optional().default('open').describe('Filter by status (open, resolved, all)'),
2010
+ repoUrl: z.string().optional().describe('GitHub URL of the repository (auto-detected from git if omitted)'),
2011
+ }, async ({ status, repoUrl }) => {
2012
+ try {
2013
+ const repoId = await resolveRepoId(repoUrl);
2014
+ if (!repoId) {
2015
+ return {
2016
+ content: [{
2017
+ type: 'text',
2018
+ text: 'Could not find repository. Make sure the repo is analyzed on merlin.build.',
2019
+ }],
2020
+ };
2021
+ }
2022
+ const result = await client.getBlockers(repoId, status);
2023
+ if (result.blockers.length === 0) {
2024
+ return {
2025
+ content: [{
2026
+ type: 'text',
2027
+ text: 'No open blockers. All clear to proceed!',
2028
+ }],
2029
+ };
2030
+ }
2031
+ let response = `# Open Blockers (${result.count})\n\n`;
2032
+ result.blockers.forEach((b) => {
2033
+ const severityEmoji = b.severity === 'critical' ? '' : b.severity === 'high' ? '' : '';
2034
+ response += `## ${severityEmoji} ${b.title}\n`;
2035
+ response += `- ID: ${b.id}\n`;
2036
+ response += `- Type: ${b.blocker_type}\n`;
2037
+ response += `- Severity: ${b.severity}\n`;
2038
+ response += `- Reported by: ${b.reported_by_agent}\n`;
2039
+ response += `- Created: ${b.created_at}\n`;
2040
+ if (b.description)
2041
+ response += `\n${b.description}\n`;
2042
+ response += '\n';
2043
+ });
2044
+ return { content: [{ type: 'text', text: response }] };
2045
+ }
2046
+ catch (error) {
2047
+ if (isAuthError(error))
2048
+ return buildAuthErrorResponse();
2063
2049
  return {
2064
2050
  content: [{
2065
2051
  type: 'text',
2066
- text: 'Could not find repository. Make sure the repo is analyzed on merlin.build.',
2052
+ text: `Error getting blockers: ${error instanceof Error ? error.message : 'Unknown error'}`,
2067
2053
  }],
2054
+ isError: true,
2068
2055
  };
2069
2056
  }
2070
- const result = await client.createCheckpoint(repoId, {
2071
- agentId,
2072
- sessionId,
2073
- checkpointType,
2074
- title,
2075
- description,
2076
- whatWasBuilt,
2077
- verificationItems,
2078
- options,
2079
- resumeContext,
2080
- });
2081
- const notification = formatSaveNotification('checkpoint', {
2082
- key: title,
2083
- isSuccess: true,
2084
- details: `ID: ${result.checkpoint?.id} Type: ${checkpointType}\nThis checkpoint is now pending human response in the Merlin dashboard.`,
2085
- });
2086
- return {
2087
- content: [{
2088
- type: 'text',
2089
- text: notification,
2090
- }],
2091
- };
2092
- }
2093
- catch (error) {
2094
- if (isAuthError(error))
2095
- return buildAuthErrorResponse();
2096
- return {
2097
- content: [{
2098
- type: 'text',
2099
- text: `Error creating checkpoint: ${error instanceof Error ? error.message : 'Unknown error'}`,
2100
- }],
2101
- isError: true,
2102
- };
2103
- }
2104
- });
2105
- // Tool: merlin_get_team_state
2106
- server.tool('merlin_get_team_state', 'Get consolidated team state: active sessions, pending checkpoints, open blockers, task summary. Use at session start to see what others are working on.', {
2107
- repoUrl: z.string().optional().describe('GitHub URL of the repository (auto-detected from git if omitted)'),
2108
- }, async ({ repoUrl }) => {
2109
- try {
2110
- const repoId = await resolveRepoId(repoUrl);
2111
- if (!repoId) {
2057
+ });
2058
+ } // end allToolsMode: merlin_get_blockers
2059
+ if (allToolsMode) { // Tool: merlin_create_checkpoint
2060
+ server.tool('merlin_create_checkpoint', 'Create a checkpoint requiring human verification or decision. Pauses agent work until human responds.', {
2061
+ title: z.string().describe('Checkpoint title'),
2062
+ checkpointType: z.enum(['human_verify', 'auth_gate', 'decision_point', 'approval', 'review']).describe('Type of checkpoint'),
2063
+ description: z.string().optional().describe('Detailed description'),
2064
+ agentId: z.string().describe('Agent creating the checkpoint'),
2065
+ sessionId: z.string().optional().describe('Session ID for tracking'),
2066
+ whatWasBuilt: z.string().optional().describe('Summary of work completed before checkpoint'),
2067
+ verificationItems: z.array(z.string()).optional().describe('Checklist items for human to verify'),
2068
+ options: z.any().optional().describe('If decision_point, the available options'),
2069
+ resumeContext: z.any().optional().describe('Context needed to resume after checkpoint'),
2070
+ repoUrl: z.string().optional().describe('GitHub URL of the repository (auto-detected from git if omitted)'),
2071
+ }, async ({ title, checkpointType, description, agentId, sessionId, whatWasBuilt, verificationItems, options, resumeContext, repoUrl }) => {
2072
+ try {
2073
+ const repoId = await resolveRepoId(repoUrl);
2074
+ if (!repoId) {
2075
+ return {
2076
+ content: [{
2077
+ type: 'text',
2078
+ text: 'Could not find repository. Make sure the repo is analyzed on merlin.build.',
2079
+ }],
2080
+ };
2081
+ }
2082
+ const result = await client.createCheckpoint(repoId, {
2083
+ agentId,
2084
+ sessionId,
2085
+ checkpointType,
2086
+ title,
2087
+ description,
2088
+ whatWasBuilt,
2089
+ verificationItems,
2090
+ options,
2091
+ resumeContext,
2092
+ });
2093
+ const notification = formatSaveNotification('checkpoint', {
2094
+ key: title,
2095
+ isSuccess: true,
2096
+ details: `ID: ${result.checkpoint?.id} │ Type: ${checkpointType}\nThis checkpoint is now pending human response in the Merlin dashboard.`,
2097
+ });
2112
2098
  return {
2113
2099
  content: [{
2114
2100
  type: 'text',
2115
- text: 'Could not find repository. Make sure the repo is analyzed on merlin.build.',
2101
+ text: notification,
2116
2102
  }],
2117
2103
  };
2118
2104
  }
2119
- const team = await client.getTeamState(repoId);
2120
- // Safely default all arrays/objects that the API might not return
2121
- const activeSessions = team.activeSessions || [];
2122
- const pendingCheckpoints = team.pendingCheckpoints || [];
2123
- const openBlockers = team.openBlockers || [];
2124
- const taskSummary = team.taskSummary || { total: 0 };
2125
- const state = team.state || {};
2126
- let response = `# Team State\n\n`;
2127
- // Active sessions
2128
- response += `## Active Sessions (${activeSessions.length})\n`;
2129
- if (activeSessions.length === 0) {
2130
- response += 'No active sessions.\n\n';
2131
- }
2132
- else {
2133
- activeSessions.forEach((s) => {
2134
- response += `- **${s.agent_id}**: Working on ${s.current_phase || 'unknown phase'} / ${s.current_task || 'unknown task'}\n`;
2135
- response += ` Progress: ${s.tasks_completed}/${s.tasks_total} tasks\n`;
2136
- });
2137
- response += '\n';
2138
- }
2139
- // Pending checkpoints
2140
- response += `## Pending Checkpoints (${pendingCheckpoints.length})\n`;
2141
- if (pendingCheckpoints.length === 0) {
2142
- response += 'No pending checkpoints.\n\n';
2143
- }
2144
- else {
2145
- pendingCheckpoints.forEach((c) => {
2146
- response += `- **${c.title}** (${c.checkpoint_type})\n`;
2147
- response += ` From: ${c.agent_id} | Created: ${c.created_at}\n`;
2148
- });
2149
- response += '\n';
2150
- }
2151
- // Open blockers
2152
- response += `## Open Blockers (${openBlockers.length})\n`;
2153
- if (openBlockers.length === 0) {
2154
- response += 'No open blockers.\n\n';
2105
+ catch (error) {
2106
+ if (isAuthError(error))
2107
+ return buildAuthErrorResponse();
2108
+ return {
2109
+ content: [{
2110
+ type: 'text',
2111
+ text: `Error creating checkpoint: ${error instanceof Error ? error.message : 'Unknown error'}`,
2112
+ }],
2113
+ isError: true,
2114
+ };
2155
2115
  }
2156
- else {
2157
- openBlockers.forEach((b) => {
2158
- const emoji = b.severity === 'critical' ? '🔴' : b.severity === 'high' ? '🟠' : '🟡';
2159
- response += `- ${emoji} **${b.title}** (${b.blocker_type})\n`;
2160
- });
2116
+ });
2117
+ } // end allToolsMode: merlin_create_checkpoint
2118
+ if (allToolsMode) { // Tool: merlin_get_team_state
2119
+ server.tool('merlin_get_team_state', 'Get consolidated team state: active sessions, pending checkpoints, open blockers, task summary. Use at session start to see what others are working on.', {
2120
+ repoUrl: z.string().optional().describe('GitHub URL of the repository (auto-detected from git if omitted)'),
2121
+ }, async ({ repoUrl }) => {
2122
+ try {
2123
+ const repoId = await resolveRepoId(repoUrl);
2124
+ if (!repoId) {
2125
+ return {
2126
+ content: [{
2127
+ type: 'text',
2128
+ text: 'Could not find repository. Make sure the repo is analyzed on merlin.build.',
2129
+ }],
2130
+ };
2131
+ }
2132
+ const team = await client.getTeamState(repoId);
2133
+ // Safely default all arrays/objects that the API might not return
2134
+ const activeSessions = team.activeSessions || [];
2135
+ const pendingCheckpoints = team.pendingCheckpoints || [];
2136
+ const openBlockers = team.openBlockers || [];
2137
+ const taskSummary = team.taskSummary || { total: 0 };
2138
+ const state = team.state || {};
2139
+ let response = `# Team State\n\n`;
2140
+ // Active sessions
2141
+ response += `## Active Sessions (${activeSessions.length})\n`;
2142
+ if (activeSessions.length === 0) {
2143
+ response += 'No active sessions.\n\n';
2144
+ }
2145
+ else {
2146
+ activeSessions.forEach((s) => {
2147
+ response += `- **${s.agent_id}**: Working on ${s.current_phase || 'unknown phase'} / ${s.current_task || 'unknown task'}\n`;
2148
+ response += ` Progress: ${s.tasks_completed}/${s.tasks_total} tasks\n`;
2149
+ });
2150
+ response += '\n';
2151
+ }
2152
+ // Pending checkpoints
2153
+ response += `## Pending Checkpoints (${pendingCheckpoints.length})\n`;
2154
+ if (pendingCheckpoints.length === 0) {
2155
+ response += 'No pending checkpoints.\n\n';
2156
+ }
2157
+ else {
2158
+ pendingCheckpoints.forEach((c) => {
2159
+ response += `- **${c.title}** (${c.checkpoint_type})\n`;
2160
+ response += ` From: ${c.agent_id} | Created: ${c.created_at}\n`;
2161
+ });
2162
+ response += '\n';
2163
+ }
2164
+ // Open blockers
2165
+ response += `## Open Blockers (${openBlockers.length})\n`;
2166
+ if (openBlockers.length === 0) {
2167
+ response += 'No open blockers.\n\n';
2168
+ }
2169
+ else {
2170
+ openBlockers.forEach((b) => {
2171
+ const emoji = b.severity === 'critical' ? '🔴' : b.severity === 'high' ? '🟠' : '🟡';
2172
+ response += `- ${emoji} **${b.title}** (${b.blocker_type})\n`;
2173
+ });
2174
+ response += '\n';
2175
+ }
2176
+ // Task summary
2177
+ response += `## Task Summary\n`;
2178
+ response += `- Total: ${taskSummary.total}\n`;
2179
+ if (taskSummary.pending)
2180
+ response += `- Pending: ${taskSummary.pending}\n`;
2181
+ if (taskSummary.in_progress)
2182
+ response += `- In Progress: ${taskSummary.in_progress}\n`;
2183
+ if (taskSummary.completed)
2184
+ response += `- Completed: ${taskSummary.completed}\n`;
2161
2185
  response += '\n';
2186
+ // Stored state
2187
+ const stateKeys = Object.keys(state);
2188
+ response += `## Stored State (${stateKeys.length} keys)\n`;
2189
+ if (stateKeys.length === 0) {
2190
+ response += 'No state stored yet.\n';
2191
+ }
2192
+ else {
2193
+ stateKeys.forEach(key => {
2194
+ const s = state[key];
2195
+ response += `- **${key}** (v${s.version}) - Updated ${s.updatedAt} by ${s.updatedBy || 'unknown'}\n`;
2196
+ });
2197
+ }
2198
+ return { content: [{ type: 'text', text: response }] };
2162
2199
  }
2163
- // Task summary
2164
- response += `## Task Summary\n`;
2165
- response += `- Total: ${taskSummary.total}\n`;
2166
- if (taskSummary.pending)
2167
- response += `- Pending: ${taskSummary.pending}\n`;
2168
- if (taskSummary.in_progress)
2169
- response += `- In Progress: ${taskSummary.in_progress}\n`;
2170
- if (taskSummary.completed)
2171
- response += `- Completed: ${taskSummary.completed}\n`;
2172
- response += '\n';
2173
- // Stored state
2174
- const stateKeys = Object.keys(state);
2175
- response += `## Stored State (${stateKeys.length} keys)\n`;
2176
- if (stateKeys.length === 0) {
2177
- response += 'No state stored yet.\n';
2178
- }
2179
- else {
2180
- stateKeys.forEach(key => {
2181
- const s = state[key];
2182
- response += `- **${key}** (v${s.version}) - Updated ${s.updatedAt} by ${s.updatedBy || 'unknown'}\n`;
2183
- });
2184
- }
2185
- return { content: [{ type: 'text', text: response }] };
2186
- }
2187
- catch (error) {
2188
- if (isAuthError(error))
2189
- return buildAuthErrorResponse();
2190
- return {
2191
- content: [{
2192
- type: 'text',
2193
- text: `Error getting team state: ${error instanceof Error ? error.message : 'Unknown error'}`,
2194
- }],
2195
- isError: true,
2196
- };
2197
- }
2198
- });
2199
- // Tool: merlin_update_session
2200
- server.tool('merlin_update_session', 'Update your session status. Use to track your work progress and enable pause/resume across context resets.', {
2201
- sessionId: z.string().describe('Unique session identifier'),
2202
- agentId: z.string().describe('Agent identifier'),
2203
- status: z.enum(['active', 'paused', 'completed', 'abandoned']).optional().describe('Session status'),
2204
- currentPhase: z.string().optional().describe('Current phase being worked on'),
2205
- currentPlan: z.string().optional().describe('Current plan being executed'),
2206
- currentTask: z.string().optional().describe('Current task being worked on'),
2207
- tasksCompleted: z.number().optional().describe('Number of tasks completed'),
2208
- tasksTotal: z.number().optional().describe('Total number of tasks'),
2209
- pauseReason: z.string().optional().describe('Reason for pausing (if status is paused)'),
2210
- resumeContext: z.any().optional().describe('Context needed to resume later'),
2211
- repoUrl: z.string().optional().describe('GitHub URL of the repository (auto-detected from git if omitted)'),
2212
- }, async ({ sessionId, agentId, status, currentPhase, currentPlan, currentTask, tasksCompleted, tasksTotal, pauseReason, resumeContext, repoUrl }) => {
2213
- try {
2214
- const repoId = await resolveRepoId(repoUrl);
2215
- if (!repoId) {
2200
+ catch (error) {
2201
+ if (isAuthError(error))
2202
+ return buildAuthErrorResponse();
2216
2203
  return {
2217
2204
  content: [{
2218
2205
  type: 'text',
2219
- text: 'Could not find repository. Make sure the repo is analyzed on merlin.build.',
2206
+ text: `Error getting team state: ${error instanceof Error ? error.message : 'Unknown error'}`,
2220
2207
  }],
2208
+ isError: true,
2221
2209
  };
2222
2210
  }
2223
- const result = await client.updateSession(repoId, {
2224
- sessionId,
2225
- agentId,
2226
- status,
2227
- currentPhase,
2228
- currentPlan,
2229
- currentTask,
2230
- tasksCompleted,
2231
- tasksTotal,
2232
- pauseReason,
2233
- resumeContext,
2234
- });
2235
- const notification = formatSaveNotification('session', {
2236
- key: sessionId,
2237
- isSuccess: true,
2238
- details: `Status: ${result.session?.status} Progress: ${result.session?.tasks_completed}/${result.session?.tasks_total} tasks`,
2239
- });
2240
- return {
2241
- content: [{
2242
- type: 'text',
2243
- text: notification,
2244
- }],
2245
- };
2246
- }
2247
- catch (error) {
2248
- if (isAuthError(error))
2249
- return buildAuthErrorResponse();
2250
- return {
2251
- content: [{
2252
- type: 'text',
2253
- text: `Error updating session: ${error instanceof Error ? error.message : 'Unknown error'}`,
2254
- }],
2255
- isError: true,
2256
- };
2257
- }
2258
- });
2259
- // ============================================================
2260
- // CODING RULES
2261
- // ============================================================
2262
- // Tool: merlin_save_rule
2263
- server.tool('merlin_save_rule', 'Save a coding rule or preference to Merlin. Rules are returned with every context query and must be followed. Use when user expresses a preference that should persist.', {
2264
- rule: z.string().describe('The rule to save (e.g., "max 400 lines per file", "always use async/await", "no console.log in production")'),
2265
- category: z.enum(['file_size', 'testing', 'style', 'patterns', 'custom']).optional().default('custom').describe('Rule category for organization'),
2266
- repoUrl: z.string().optional().describe('GitHub URL of the repository (auto-detected from git if omitted)'),
2267
- }, async ({ rule, category, repoUrl }) => {
2268
- try {
2269
- const repoId = await resolveRepoId(repoUrl);
2270
- if (!repoId) {
2211
+ });
2212
+ } // end allToolsMode: merlin_get_team_state
2213
+ if (allToolsMode) { // Tool: merlin_update_session
2214
+ server.tool('merlin_update_session', 'Update your session status. Use to track your work progress and enable pause/resume across context resets.', {
2215
+ sessionId: z.string().describe('Unique session identifier'),
2216
+ agentId: z.string().describe('Agent identifier'),
2217
+ status: z.enum(['active', 'paused', 'completed', 'abandoned']).optional().describe('Session status'),
2218
+ currentPhase: z.string().optional().describe('Current phase being worked on'),
2219
+ currentPlan: z.string().optional().describe('Current plan being executed'),
2220
+ currentTask: z.string().optional().describe('Current task being worked on'),
2221
+ tasksCompleted: z.number().optional().describe('Number of tasks completed'),
2222
+ tasksTotal: z.number().optional().describe('Total number of tasks'),
2223
+ pauseReason: z.string().optional().describe('Reason for pausing (if status is paused)'),
2224
+ resumeContext: z.any().optional().describe('Context needed to resume later'),
2225
+ repoUrl: z.string().optional().describe('GitHub URL of the repository (auto-detected from git if omitted)'),
2226
+ }, async ({ sessionId, agentId, status, currentPhase, currentPlan, currentTask, tasksCompleted, tasksTotal, pauseReason, resumeContext, repoUrl }) => {
2227
+ try {
2228
+ const repoId = await resolveRepoId(repoUrl);
2229
+ if (!repoId) {
2230
+ return {
2231
+ content: [{
2232
+ type: 'text',
2233
+ text: 'Could not find repository. Make sure the repo is analyzed on merlin.build.',
2234
+ }],
2235
+ };
2236
+ }
2237
+ const result = await client.updateSession(repoId, {
2238
+ sessionId,
2239
+ agentId,
2240
+ status,
2241
+ currentPhase,
2242
+ currentPlan,
2243
+ currentTask,
2244
+ tasksCompleted,
2245
+ tasksTotal,
2246
+ pauseReason,
2247
+ resumeContext,
2248
+ });
2249
+ const notification = formatSaveNotification('session', {
2250
+ key: sessionId,
2251
+ isSuccess: true,
2252
+ details: `Status: ${result.session?.status} Progress: ${result.session?.tasks_completed}/${result.session?.tasks_total} tasks`,
2253
+ });
2271
2254
  return {
2272
2255
  content: [{
2273
2256
  type: 'text',
2274
- text: 'Could not find repository. Make sure the repo is analyzed on merlin.build.',
2257
+ text: notification,
2275
2258
  }],
2276
2259
  };
2277
2260
  }
2278
- // Read existing rules
2279
- const existingState = await client.readState(repoId, 'coding_rules');
2280
- const rules = existingState?.value || { rules: [], categories: {} };
2281
- // Check for duplicate
2282
- if (rules.rules.includes(rule)) {
2261
+ catch (error) {
2262
+ if (isAuthError(error))
2263
+ return buildAuthErrorResponse();
2283
2264
  return {
2284
2265
  content: [{
2285
2266
  type: 'text',
2286
- text: `Rule already exists: "${rule}"`,
2267
+ text: `Error updating session: ${error instanceof Error ? error.message : 'Unknown error'}`,
2287
2268
  }],
2269
+ isError: true,
2288
2270
  };
2289
2271
  }
2290
- // Add new rule
2291
- rules.rules.push(rule);
2292
- if (!rules.categories[category]) {
2293
- rules.categories[category] = [];
2294
- }
2295
- rules.categories[category].push(rule);
2296
- // Save updated rules
2297
- await client.writeState(repoId, 'coding_rules', rules, { agentId: 'user' });
2298
- const notification = formatSaveNotification('rule', {
2299
- key: rule,
2300
- isSuccess: true,
2301
- details: `Category: ${category}\nThis rule will be included with all future context queries for this project.`,
2302
- });
2303
- return {
2304
- content: [{
2305
- type: 'text',
2306
- text: notification,
2307
- }],
2308
- };
2309
- }
2310
- catch (error) {
2311
- if (isAuthError(error))
2312
- return buildAuthErrorResponse();
2313
- return {
2314
- content: [{
2315
- type: 'text',
2316
- text: `Error saving rule: ${error instanceof Error ? error.message : 'Unknown error'}`,
2317
- }],
2318
- isError: true,
2319
- };
2320
- }
2321
- });
2322
- // Tool: merlin_get_rules
2323
- server.tool('merlin_get_rules', 'Get all coding rules for this project. Returns rules that must be followed when writing code.', {
2324
- repoUrl: z.string().optional().describe('GitHub URL of the repository (auto-detected from git if omitted)'),
2325
- }, async ({ repoUrl }) => {
2326
- try {
2327
- const repoId = await resolveRepoId(repoUrl);
2328
- if (!repoId) {
2272
+ });
2273
+ } // end allToolsMode: merlin_update_session
2274
+ // ============================================================
2275
+ // CODING RULES
2276
+ // ============================================================
2277
+ if (allToolsMode) { // Tool: merlin_save_rule
2278
+ server.tool('merlin_save_rule', 'Save a coding rule or preference to Merlin. Rules are returned with every context query and must be followed. Use when user expresses a preference that should persist.', {
2279
+ rule: z.string().describe('The rule to save (e.g., "max 400 lines per file", "always use async/await", "no console.log in production")'),
2280
+ category: z.enum(['file_size', 'testing', 'style', 'patterns', 'custom']).optional().default('custom').describe('Rule category for organization'),
2281
+ repoUrl: z.string().optional().describe('GitHub URL of the repository (auto-detected from git if omitted)'),
2282
+ }, async ({ rule, category, repoUrl }) => {
2283
+ try {
2284
+ const repoId = await resolveRepoId(repoUrl);
2285
+ if (!repoId) {
2286
+ return {
2287
+ content: [{
2288
+ type: 'text',
2289
+ text: 'Could not find repository. Make sure the repo is analyzed on merlin.build.',
2290
+ }],
2291
+ };
2292
+ }
2293
+ // Read existing rules
2294
+ const existingState = await client.readState(repoId, 'coding_rules');
2295
+ const rules = existingState?.value || { rules: [], categories: {} };
2296
+ // Check for duplicate
2297
+ if (rules.rules.includes(rule)) {
2298
+ return {
2299
+ content: [{
2300
+ type: 'text',
2301
+ text: `Rule already exists: "${rule}"`,
2302
+ }],
2303
+ };
2304
+ }
2305
+ // Add new rule
2306
+ rules.rules.push(rule);
2307
+ if (!rules.categories[category]) {
2308
+ rules.categories[category] = [];
2309
+ }
2310
+ rules.categories[category].push(rule);
2311
+ // Save updated rules
2312
+ await client.writeState(repoId, 'coding_rules', rules, { agentId: 'user' });
2313
+ const notification = formatSaveNotification('rule', {
2314
+ key: rule,
2315
+ isSuccess: true,
2316
+ details: `Category: ${category}\nThis rule will be included with all future context queries for this project.`,
2317
+ });
2329
2318
  return {
2330
2319
  content: [{
2331
2320
  type: 'text',
2332
- text: 'Could not find repository. Make sure the repo is analyzed on merlin.build.',
2321
+ text: notification,
2333
2322
  }],
2334
2323
  };
2335
2324
  }
2336
- const state = await client.readState(repoId, 'coding_rules');
2337
- if (!state?.value || state.value.rules.length === 0) {
2325
+ catch (error) {
2326
+ if (isAuthError(error))
2327
+ return buildAuthErrorResponse();
2338
2328
  return {
2339
2329
  content: [{
2340
2330
  type: 'text',
2341
- text: 'No coding rules defined for this project yet.\n\nUse `merlin_save_rule` to add rules, or they will be collected during `/merlin:new-project`.',
2331
+ text: `Error saving rule: ${error instanceof Error ? error.message : 'Unknown error'}`,
2342
2332
  }],
2333
+ isError: true,
2343
2334
  };
2344
2335
  }
2345
- const rules = state.value;
2346
- let response = `# Coding Rules (${rules.rules.length})\n\n`;
2347
- response += `**These rules MUST be followed when writing code.**\n\n`;
2348
- // Group by category
2349
- Object.keys(rules.categories).forEach(cat => {
2350
- response += `## ${cat}\n`;
2351
- rules.categories[cat].forEach((r) => {
2352
- response += `- ${r}\n`;
2336
+ });
2337
+ } // end allToolsMode: merlin_save_rule
2338
+ if (allToolsMode) { // Tool: merlin_get_rules
2339
+ server.tool('merlin_get_rules', 'Get all coding rules for this project. Returns rules that must be followed when writing code.', {
2340
+ repoUrl: z.string().optional().describe('GitHub URL of the repository (auto-detected from git if omitted)'),
2341
+ }, async ({ repoUrl }) => {
2342
+ try {
2343
+ const repoId = await resolveRepoId(repoUrl);
2344
+ if (!repoId) {
2345
+ return {
2346
+ content: [{
2347
+ type: 'text',
2348
+ text: 'Could not find repository. Make sure the repo is analyzed on merlin.build.',
2349
+ }],
2350
+ };
2351
+ }
2352
+ const state = await client.readState(repoId, 'coding_rules');
2353
+ if (!state?.value || state.value.rules.length === 0) {
2354
+ return {
2355
+ content: [{
2356
+ type: 'text',
2357
+ text: 'No coding rules defined for this project yet.\n\nUse `merlin_save_rule` to add rules, or they will be collected during `/merlin:new-project`.',
2358
+ }],
2359
+ };
2360
+ }
2361
+ const rules = state.value;
2362
+ let response = `# Coding Rules (${rules.rules.length})\n\n`;
2363
+ response += `**These rules MUST be followed when writing code.**\n\n`;
2364
+ // Group by category
2365
+ Object.keys(rules.categories).forEach(cat => {
2366
+ response += `## ${cat}\n`;
2367
+ rules.categories[cat].forEach((r) => {
2368
+ response += `- ${r}\n`;
2369
+ });
2370
+ response += '\n';
2353
2371
  });
2354
- response += '\n';
2355
- });
2356
- return { content: [{ type: 'text', text: response }] };
2357
- }
2358
- catch (error) {
2359
- if (isAuthError(error))
2360
- return buildAuthErrorResponse();
2361
- return {
2362
- content: [{
2363
- type: 'text',
2364
- text: `Error getting rules: ${error instanceof Error ? error.message : 'Unknown error'}`,
2365
- }],
2366
- isError: true,
2367
- };
2368
- }
2369
- });
2370
- // Tool: merlin_remove_rule
2371
- server.tool('merlin_remove_rule', 'Remove a coding rule from this project.', {
2372
- rule: z.string().describe('The exact rule text to remove'),
2373
- repoUrl: z.string().optional().describe('GitHub URL of the repository (auto-detected from git if omitted)'),
2374
- }, async ({ rule, repoUrl }) => {
2375
- try {
2376
- const repoId = await resolveRepoId(repoUrl);
2377
- if (!repoId) {
2372
+ return { content: [{ type: 'text', text: response }] };
2373
+ }
2374
+ catch (error) {
2375
+ if (isAuthError(error))
2376
+ return buildAuthErrorResponse();
2378
2377
  return {
2379
2378
  content: [{
2380
2379
  type: 'text',
2381
- text: 'Could not find repository. Make sure the repo is analyzed on merlin.build.',
2380
+ text: `Error getting rules: ${error instanceof Error ? error.message : 'Unknown error'}`,
2382
2381
  }],
2382
+ isError: true,
2383
2383
  };
2384
2384
  }
2385
- const existingState = await client.readState(repoId, 'coding_rules');
2386
- if (!existingState?.value) {
2385
+ });
2386
+ } // end allToolsMode: merlin_get_rules
2387
+ if (allToolsMode) { // Tool: merlin_remove_rule
2388
+ server.tool('merlin_remove_rule', 'Remove a coding rule from this project.', {
2389
+ rule: z.string().describe('The exact rule text to remove'),
2390
+ repoUrl: z.string().optional().describe('GitHub URL of the repository (auto-detected from git if omitted)'),
2391
+ }, async ({ rule, repoUrl }) => {
2392
+ try {
2393
+ const repoId = await resolveRepoId(repoUrl);
2394
+ if (!repoId) {
2395
+ return {
2396
+ content: [{
2397
+ type: 'text',
2398
+ text: 'Could not find repository. Make sure the repo is analyzed on merlin.build.',
2399
+ }],
2400
+ };
2401
+ }
2402
+ const existingState = await client.readState(repoId, 'coding_rules');
2403
+ if (!existingState?.value) {
2404
+ return {
2405
+ content: [{
2406
+ type: 'text',
2407
+ text: 'No coding rules found for this project.',
2408
+ }],
2409
+ };
2410
+ }
2411
+ const rules = existingState.value;
2412
+ const index = rules.rules.indexOf(rule);
2413
+ if (index === -1) {
2414
+ return {
2415
+ content: [{
2416
+ type: 'text',
2417
+ text: `Rule not found: "${rule}"\n\nUse merlin_get_rules to see all current rules.`,
2418
+ }],
2419
+ };
2420
+ }
2421
+ // Remove from main list
2422
+ rules.rules.splice(index, 1);
2423
+ // Remove from categories
2424
+ Object.keys(rules.categories).forEach(cat => {
2425
+ const catIndex = rules.categories[cat].indexOf(rule);
2426
+ if (catIndex !== -1) {
2427
+ rules.categories[cat].splice(catIndex, 1);
2428
+ }
2429
+ });
2430
+ await client.writeState(repoId, 'coding_rules', rules, { agentId: 'user' });
2387
2431
  return {
2388
2432
  content: [{
2389
2433
  type: 'text',
2390
- text: 'No coding rules found for this project.',
2434
+ text: `Rule removed: "${rule}"`,
2391
2435
  }],
2392
2436
  };
2393
2437
  }
2394
- const rules = existingState.value;
2395
- const index = rules.rules.indexOf(rule);
2396
- if (index === -1) {
2438
+ catch (error) {
2439
+ if (isAuthError(error))
2440
+ return buildAuthErrorResponse();
2397
2441
  return {
2398
2442
  content: [{
2399
2443
  type: 'text',
2400
- text: `Rule not found: "${rule}"\n\nUse merlin_get_rules to see all current rules.`,
2444
+ text: `Error removing rule: ${error instanceof Error ? error.message : 'Unknown error'}`,
2401
2445
  }],
2446
+ isError: true,
2402
2447
  };
2403
2448
  }
2404
- // Remove from main list
2405
- rules.rules.splice(index, 1);
2406
- // Remove from categories
2407
- Object.keys(rules.categories).forEach(cat => {
2408
- const catIndex = rules.categories[cat].indexOf(rule);
2409
- if (catIndex !== -1) {
2410
- rules.categories[cat].splice(catIndex, 1);
2411
- }
2412
- });
2413
- await client.writeState(repoId, 'coding_rules', rules, { agentId: 'user' });
2414
- return {
2415
- content: [{
2416
- type: 'text',
2417
- text: `Rule removed: "${rule}"`,
2418
- }],
2419
- };
2420
- }
2421
- catch (error) {
2422
- if (isAuthError(error))
2423
- return buildAuthErrorResponse();
2424
- return {
2425
- content: [{
2426
- type: 'text',
2427
- text: `Error removing rule: ${error instanceof Error ? error.message : 'Unknown error'}`,
2428
- }],
2429
- isError: true,
2430
- };
2431
- }
2432
- });
2449
+ });
2450
+ } // end allToolsMode: merlin_remove_rule
2433
2451
  // ============================================================
2434
2452
  // RESOURCES
2435
2453
  // ============================================================
@@ -2452,101 +2470,123 @@ export function createServer() {
2452
2470
  // ============================================================
2453
2471
  registerProjectTools(server, () => client, resolveRepoId, getRepoRootPath);
2454
2472
  // ============================================================
2455
- // LEARNED BEHAVIORS TOOLS
2456
- // ============================================================
2457
- registerBehaviorTools({
2458
- server,
2459
- client,
2460
- resolveRepoId,
2461
- });
2462
- // ============================================================
2463
- // VERIFICATION CHECKPOINTS TOOLS
2473
+ if (allToolsMode) {
2474
+ // LEARNED BEHAVIORS TOOLS
2475
+ // ============================================================
2476
+ registerBehaviorTools({
2477
+ server,
2478
+ client,
2479
+ resolveRepoId,
2480
+ });
2481
+ } // end allToolsMode: registerBehaviorTools
2464
2482
  // ============================================================
2465
- registerVerificationTools({
2466
- server,
2467
- client,
2468
- resolveRepoId,
2469
- getRepoRootPath,
2470
- });
2471
- // ============================================================
2472
- // ADAPTIVE CONTEXT DISCOVERY TOOLS
2483
+ if (allToolsMode) {
2484
+ // VERIFICATION CHECKPOINTS TOOLS
2485
+ // ============================================================
2486
+ registerVerificationTools({
2487
+ server,
2488
+ client,
2489
+ resolveRepoId,
2490
+ getRepoRootPath,
2491
+ });
2492
+ } // end allToolsMode: registerVerificationTools
2473
2493
  // ============================================================
2474
- registerAdaptiveTools({
2475
- server,
2476
- client,
2477
- resolveRepoId,
2478
- });
2494
+ if (allToolsMode) {
2495
+ // ADAPTIVE CONTEXT DISCOVERY TOOLS
2496
+ // ============================================================
2497
+ registerAdaptiveTools({
2498
+ server,
2499
+ client,
2500
+ resolveRepoId,
2501
+ });
2502
+ } // end allToolsMode: registerAdaptiveTools
2479
2503
  // ============================================================
2480
- // STRUCTURED AGENT TOOLS
2504
+ if (allToolsMode) {
2505
+ // STRUCTURED AGENT TOOLS
2506
+ // ============================================================
2507
+ registerAgentTools({
2508
+ server,
2509
+ client,
2510
+ resolveRepoId,
2511
+ });
2512
+ } // end allToolsMode: registerAgentTools
2481
2513
  // ============================================================
2482
- registerAgentTools({
2483
- server,
2484
- client,
2485
- resolveRepoId,
2486
- });
2514
+ if (allToolsMode) {
2515
+ // DISCOVERY TOOLS - Teach Sights what Claude learns
2516
+ // ============================================================
2517
+ registerDiscoveryTools({
2518
+ server,
2519
+ client,
2520
+ resolveRepoId,
2521
+ });
2522
+ } // end allToolsMode: registerDiscoveryTools
2487
2523
  // ============================================================
2488
- // DISCOVERY TOOLS - Teach Sights what Claude learns
2524
+ if (allToolsMode) {
2525
+ // AUTO-TEACH TOOLS - Confidence detection + teach-back loop
2526
+ // ============================================================
2527
+ registerAutoTeachTools({
2528
+ server,
2529
+ client,
2530
+ resolveRepoId,
2531
+ });
2532
+ } // end allToolsMode: registerAutoTeachTools
2489
2533
  // ============================================================
2490
- registerDiscoveryTools({
2491
- server,
2492
- client,
2493
- resolveRepoId,
2494
- });
2534
+ if (allToolsMode) {
2535
+ // AGENT ROUTE TOOLS - Fresh process spawning for specialists
2536
+ // ============================================================
2537
+ registerRouteTools({
2538
+ server,
2539
+ client,
2540
+ resolveRepoId,
2541
+ getRepoRootPath,
2542
+ });
2543
+ } // end allToolsMode: registerRouteTools
2495
2544
  // ============================================================
2496
- // AUTO-TEACH TOOLS - Confidence detection + teach-back loop
2545
+ if (allToolsMode) {
2546
+ // PUBLIC SIGHTS INDEX - Browse open source analyzed codebases
2547
+ // ============================================================
2548
+ registerSightsIndexTools({
2549
+ server,
2550
+ client,
2551
+ resolveRepoId,
2552
+ });
2553
+ } // end allToolsMode: registerSightsIndexTools
2497
2554
  // ============================================================
2498
- registerAutoTeachTools({
2499
- server,
2500
- client,
2501
- resolveRepoId,
2502
- });
2555
+ if (allToolsMode) {
2556
+ // AGENTS INDEX - Browse discovered AI agents catalog
2557
+ // ============================================================
2558
+ registerAgentsIndexTools({
2559
+ server,
2560
+ client,
2561
+ resolveRepoId,
2562
+ });
2563
+ } // end allToolsMode: registerAgentsIndexTools
2503
2564
  // ============================================================
2504
- // AGENT ROUTE TOOLS - Fresh process spawning for specialists
2505
- // ============================================================
2506
- registerRouteTools({
2507
- server,
2508
- client,
2509
- resolveRepoId,
2510
- getRepoRootPath,
2511
- });
2512
- // ============================================================
2513
- // PUBLIC SIGHTS INDEX - Browse open source analyzed codebases
2514
- // ============================================================
2515
- registerSightsIndexTools({
2516
- server,
2517
- client,
2518
- resolveRepoId,
2519
- });
2565
+ if (allToolsMode) {
2566
+ // LITE MODE TOOLS - Local file-based context for small repos
2567
+ // ============================================================
2568
+ registerLiteTools({
2569
+ server,
2570
+ getRepoRootPath,
2571
+ getAuthToken: async () => {
2572
+ // Get token from saved config
2573
+ const config = loadSavedConfig();
2574
+ return config?.apiKey || null;
2575
+ },
2576
+ });
2577
+ } // end allToolsMode: registerLiteTools
2520
2578
  // ============================================================
2521
- // AGENTS INDEX - Browse discovered AI agents catalog
2522
- // ============================================================
2523
- registerAgentsIndexTools({
2524
- server,
2525
- client,
2526
- resolveRepoId,
2527
- });
2528
- // ============================================================
2529
- // LITE MODE TOOLS - Local file-based context for small repos
2530
- // ============================================================
2531
- registerLiteTools({
2532
- server,
2533
- getRepoRootPath,
2534
- getAuthToken: async () => {
2535
- // Get token from saved config
2536
- const config = loadSavedConfig();
2537
- return config?.apiKey || null;
2538
- },
2539
- });
2540
- // ============================================================
2541
- // CONFIG SYNC TOOLS - Sync CLAUDE.md across team
2542
- // ============================================================
2543
- registerConfigSyncTools({
2544
- server,
2545
- getAuthToken: async () => {
2546
- const config = loadSavedConfig();
2547
- return config?.apiKey || null;
2548
- },
2549
- });
2579
+ if (allToolsMode) {
2580
+ // CONFIG SYNC TOOLS - Sync CLAUDE.md across team
2581
+ // ============================================================
2582
+ registerConfigSyncTools({
2583
+ server,
2584
+ getAuthToken: async () => {
2585
+ const config = loadSavedConfig();
2586
+ return config?.apiKey || null;
2587
+ },
2588
+ });
2589
+ } // end allToolsMode: registerConfigSyncTools
2550
2590
  return server;
2551
2591
  }
2552
2592
  /** Start the MCP server with stdio transport */