index.ts 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  1. /**
  2. * Main entry point for the Agent Manager Client
  3. */
  4. export { ClaudeClient } from './claude-client.js';
  5. export { Config } from './config.js';
  6. export { McpConfigManager } from './mcpConfigManager.js';
  7. export { ToolDiscovery } from './toolDiscovery.js';
  8. export type {
  9. IssueData,
  10. ClaudeClientConfig,
  11. IssueHandlerResult,
  12. MCPServerConfig
  13. } from './types.js';
  14. // CLI entry point
  15. if (import.meta.url === `file://${process.argv[1]}`) {
  16. const { ClaudeClient } = await import('./claude-client.js');
  17. const { Config } = await import('./config.js');
  18. const { ToolDiscovery } = await import('./toolDiscovery.js');
  19. const { setupEnvironment, ensureRepository, detectAgentManagerPath } = await import('./repository-manager.js');
  20. const { fetchRepositorySshUrl, getExistingGitRemoteUrl } = await import('./gogs-helper.js');
  21. // Setup environment variables (HOME, PATH, XDG directories)
  22. console.log('Setting up environment...');
  23. setupEnvironment();
  24. // Load configuration early: global env vars are needed for Gogs API access and MCP config
  25. console.log('Loading configuration...');
  26. Config.loadGlobal(); // Load global .env from ~/.config/agent-manager/.env
  27. // Parse command line arguments
  28. const args = process.argv.slice(2);
  29. if (args.length < 7) {
  30. console.error('Usage: node index.js <owner> <repo> <pusher> <issue_number> <issue_title> <issue_body> <issue_action> [comment_body] [assignee] [labels]');
  31. process.exit(1);
  32. }
  33. const [owner, repo, pusher, issueNumber, issueTitle, issueBody, issueAction, commentBody, assignee, labels] = args;
  34. // Exit if issue action is 'closed' (but allow 'reopened')
  35. if (issueAction === 'closed') {
  36. console.log("Issue action is 'closed', skipping automation");
  37. process.exit(0);
  38. }
  39. // Validate assignee
  40. if (assignee && assignee !== 'claude') {
  41. console.log(`Issue not assigned to claude (assignee: ${assignee}), skipping`);
  42. process.exit(0);
  43. }
  44. // Skip if pusher is claude for issue_comment events (avoid comment loops)
  45. // commentBody is only populated for issue_comment events, not issues events
  46. if (pusher === 'claude' && commentBody) {
  47. console.log('Pusher is claude for issue_comment event, skipping automation to avoid loops');
  48. process.exit(0);
  49. }
  50. // Detect Agent Manager path
  51. const agentManagerPath = detectAgentManagerPath();
  52. console.log(`Agent Manager Path: ${agentManagerPath}`);
  53. // Determine repo path
  54. const home = process.env.HOME || '/root';
  55. const repoPath = `${home}/${repo}`;
  56. // Fetch SSH URL from Gogs API or use existing git remote
  57. let sshUrl: string;
  58. try {
  59. // First, try to get from existing repository
  60. const existingUrl = getExistingGitRemoteUrl(repoPath);
  61. if (existingUrl) {
  62. console.log(`Using existing git remote URL: ${existingUrl}`);
  63. sshUrl = existingUrl;
  64. } else {
  65. console.log('Fetching repository SSH URL from Gogs API...');
  66. sshUrl = await fetchRepositorySshUrl(owner, repo);
  67. console.log(`Fetched SSH URL: ${sshUrl}`);
  68. }
  69. } catch (error) {
  70. console.error('Failed to fetch SSH URL from Gogs API:', error);
  71. console.log('Falling back to existing repository or default URL format');
  72. const existingUrl = getExistingGitRemoteUrl(repoPath);
  73. if (existingUrl) {
  74. sshUrl = existingUrl;
  75. } else {
  76. // Fallback to default format
  77. sshUrl = `ssh://git@git.smartbotics.ai:10022/${owner}/${repo}.git`;
  78. console.log(`Using fallback SSH URL: ${sshUrl}`);
  79. }
  80. }
  81. // Ensure repository exists and is up to date
  82. console.log('Ensuring repository exists and is up to date...');
  83. ensureRepository(repoPath, sshUrl);
  84. // Change to repository directory
  85. process.chdir(repoPath);
  86. console.log(`Working directory: ${process.cwd()}`);
  87. // Load repository-specific .env (overrides global values)
  88. Config.load(repoPath);
  89. // Generate MCP configuration for this repository
  90. const mcpConfig = Config.generateMcpConfig(repoPath, owner, repo);
  91. // Discover available tools dynamically
  92. console.log('Discovering available tools from MCP servers...');
  93. const allowedTools = await ToolDiscovery.discoverTools(mcpConfig);
  94. console.log('\n=== ALLOWED TOOLS ===');
  95. console.log(`Total tools: ${allowedTools.length}`);
  96. console.log('Tools list:', JSON.stringify(allowedTools, null, 2));
  97. console.log('=====================\n');
  98. // Configure Claude client
  99. const client = new ClaudeClient({
  100. workingDirectory: repoPath,
  101. mcpConfigPath: `${repoPath}/.mcp-gogs.json`,
  102. allowedTools,
  103. dangerouslySkipPermissions: true
  104. });
  105. // Handle the issue
  106. const result = await client.handleIssue({
  107. owner,
  108. repo,
  109. number: parseInt(issueNumber, 10),
  110. title: issueTitle,
  111. body: issueBody,
  112. action: issueAction,
  113. pusher,
  114. assignee,
  115. labels,
  116. commentBody
  117. });
  118. // Output result
  119. console.log('---');
  120. console.log('Claude Code execution completed');
  121. if (result.success) {
  122. console.log('Session ID:', result.sessionId);
  123. console.log('Cost:', result.cost);
  124. console.log('Duration:', result.duration);
  125. process.exit(0);
  126. } else {
  127. console.error('Error:', result.error);
  128. process.exit(1);
  129. }
  130. }