Bladeren bron

feat: add label-aware behavior for Claude agent #25

- Extract issue labels in commandExecutor.js as {{issue_labels}} variable
- Pass labels to TypeScript client via commands.json
- Update TypeScript types to include labels field
- Implement label-specific behavior in Claude client prompt:
  - "question" label: prevents code implementation, provides info only
  - "documentation" label: focuses on docs, avoids implementation
  - "bug" label: focuses on bug fixes with minimal changes
  - "enhancement" label: allows feature implementation
- Update commands.json.example with {{issue_labels}} variable docs

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Claude 9 maanden geleden
bovenliggende
commit
19ba0c2680
6 gewijzigde bestanden met toevoegingen van 65 en 7 verwijderingen
  1. 48 1
      client/src/claude-client.ts
  2. 3 2
      client/src/index.ts
  3. 1 0
      client/src/types.ts
  4. 3 3
      commands.json
  5. 1 1
      commands.json.example
  6. 9 0
      src/commandExecutor.js

+ 48 - 1
client/src/claude-client.ts

@@ -47,9 +47,56 @@ export class ClaudeClient {
       ? `\n=== PROJECT MEMORY (CLAUDE.md) ===\n\nThe following is the project memory file that contains important context about this repository.\nYou MUST read and understand this information as it contains project-specific guidelines, architecture details, and development patterns.\n\n${memoryContent}\n\n`
       : '';
 
+    // Parse labels if provided
+    const labels = issue.labels ? issue.labels.split(',').filter(l => l.trim()) : [];
+    const hasQuestionLabel = labels.includes('question');
+    const hasDocumentationLabel = labels.includes('documentation');
+    const hasBugLabel = labels.includes('bug');
+    const hasEnhancementLabel = labels.includes('enhancement');
+
+    // Build label-specific instructions
+    let labelInstructions = '';
+    if (labels.length > 0) {
+      labelInstructions = `\n=== ISSUE LABELS AND BEHAVIOR ===\n\n`;
+      labelInstructions += `This issue has the following labels: ${labels.join(', ')}\n\n`;
+
+      if (hasQuestionLabel) {
+        labelInstructions += `⚠️ QUESTION LABEL DETECTED:\n`;
+        labelInstructions += `- This issue is labeled as "question"\n`;
+        labelInstructions += `- DO NOT implement any code changes or features\n`;
+        labelInstructions += `- Your role is to provide information, guidance, and answers only\n`;
+        labelInstructions += `- You may read code to understand context, but do not modify files\n`;
+        labelInstructions += `- Respond with helpful explanations, suggestions, or clarifications\n\n`;
+      }
+
+      if (hasDocumentationLabel) {
+        labelInstructions += `📚 DOCUMENTATION LABEL DETECTED:\n`;
+        labelInstructions += `- This issue is about documentation\n`;
+        labelInstructions += `- Focus on creating, updating, or improving documentation files\n`;
+        labelInstructions += `- Acceptable changes: README.md, CLAUDE.md, other .md files, code comments\n`;
+        labelInstructions += `- Avoid implementation changes unless specifically requested for documentation examples\n\n`;
+      }
+
+      if (hasBugLabel) {
+        labelInstructions += `🐛 BUG LABEL DETECTED:\n`;
+        labelInstructions += `- This issue reports a bug or defect\n`;
+        labelInstructions += `- Focus on identifying and fixing the root cause\n`;
+        labelInstructions += `- Implement minimal changes necessary to resolve the bug\n`;
+        labelInstructions += `- Add tests if appropriate to prevent regression\n\n`;
+      }
+
+      if (hasEnhancementLabel) {
+        labelInstructions += `✨ ENHANCEMENT LABEL DETECTED:\n`;
+        labelInstructions += `- This issue requests a new feature or improvement\n`;
+        labelInstructions += `- You may implement code changes as requested\n`;
+        labelInstructions += `- Follow existing code patterns and architecture\n`;
+        labelInstructions += `- Consider backward compatibility\n\n`;
+      }
+    }
+
     return `IMPORTANT: Issue #${issue.number} was assigned in repository ${issue.owner}/${issue.repo}.
 ${memorySection}
-
+${labelInstructions}
 
 === MANDATORY FIRST STEPS - READ ALL MESSAGES ===
 

+ 3 - 2
client/src/index.ts

@@ -29,11 +29,11 @@ if (import.meta.url === `file://${process.argv[1]}`) {
   const args = process.argv.slice(2);
 
   if (args.length < 7) {
-    console.error('Usage: node index.js <owner> <repo> <pusher> <issue_number> <issue_title> <issue_body> <issue_action> [comment_body] [assignee]');
+    console.error('Usage: node index.js <owner> <repo> <pusher> <issue_number> <issue_title> <issue_body> <issue_action> [comment_body] [assignee] [labels]');
     process.exit(1);
   }
 
-  const [owner, repo, pusher, issueNumber, issueTitle, issueBody, issueAction, commentBody, assignee] = args;
+  const [owner, repo, pusher, issueNumber, issueTitle, issueBody, issueAction, commentBody, assignee, labels] = args;
 
   // Exit if issue action is 'closed' (but allow 'reopened')
   if (issueAction === 'closed') {
@@ -129,6 +129,7 @@ if (import.meta.url === `file://${process.argv[1]}`) {
     action: issueAction,
     pusher,
     assignee,
+    labels,
     commentBody
   });
 

+ 1 - 0
client/src/types.ts

@@ -11,6 +11,7 @@ export interface IssueData {
   action: string;
   pusher: string;
   assignee?: string;
+  labels?: string; // Comma-separated list of label names
   commentBody?: string;
 }
 

+ 3 - 3
commands.json

@@ -1,6 +1,6 @@
 {
   "$schema": "https://json-schema.org/draft-07/schema#",
-  "description": "Command configuration for webhook events. Available variables: {{repo}}, {{full_repo}}, {{repo_owner}}, {{branch}}, {{pusher}}, {{event}}, {{commit}}, {{commit_msg}}, {{pr_number}}, {{pr_action}}, {{tag}}, {{ref_type}}, {{issue_number}}, {{issue_title}}, {{issue_action}}, {{issue_body}}, {{issue_assignee}}, {{comment_body}}",
+  "description": "Command configuration for webhook events. Available variables: {{repo}}, {{full_repo}}, {{repo_owner}}, {{branch}}, {{pusher}}, {{event}}, {{commit}}, {{commit_msg}}, {{pr_number}}, {{pr_action}}, {{tag}}, {{ref_type}}, {{issue_number}}, {{issue_title}}, {{issue_action}}, {{issue_body}}, {{issue_assignee}}, {{issue_labels}}, {{comment_body}}",
   "commands": {
     "push": [],
     "pull_request": [],
@@ -13,7 +13,7 @@
         "description": "Automatically handle issues assigned to Claude with Claude Agent SDK TypeScript client",
         "type": "node",
         "command": "${AGENT_MANAGER_PATH}/client/dist/index.js",
-        "args": ["{{repo_owner}}", "{{repo}}", "{{pusher}}", "{{issue_number}}", "{{issue_title}}", "{{issue_body}}", "{{issue_action}}", "", "{{issue_assignee}}"],
+        "args": ["{{repo_owner}}", "{{repo}}", "{{pusher}}", "{{issue_number}}", "{{issue_title}}", "{{issue_body}}", "{{issue_action}}", "", "{{issue_assignee}}", "{{issue_labels}}"],
         "cwd": "/home/claude",
         "timeout": 600000,
         "filterBranch": null,
@@ -27,7 +27,7 @@
         "description": "Automatically handle issue comments for issues assigned to Claude with Claude Agent SDK TypeScript client",
         "type": "node",
         "command": "${AGENT_MANAGER_PATH}/client/dist/index.js",
-        "args": ["{{repo_owner}}", "{{repo}}", "{{pusher}}", "{{issue_number}}", "{{issue_title}}", "{{issue_body}}", "{{issue_action}}", "{{comment_body}}", "{{issue_assignee}}"],
+        "args": ["{{repo_owner}}", "{{repo}}", "{{pusher}}", "{{issue_number}}", "{{issue_title}}", "{{issue_body}}", "{{issue_action}}", "{{comment_body}}", "{{issue_assignee}}", "{{issue_labels}}"],
         "cwd": "/home/claude",
         "timeout": 600000,
         "filterBranch": null,

+ 1 - 1
commands.json.example

@@ -1,6 +1,6 @@
 {
   "$schema": "https://json-schema.org/draft-07/schema#",
-  "description": "Command configuration for webhook events. Available variables: {{repo}}, {{full_repo}}, {{repo_owner}}, {{branch}}, {{pusher}}, {{event}}, {{commit}}, {{commit_msg}}, {{pr_number}}, {{pr_action}}, {{tag}}, {{ref_type}}, {{issue_number}}, {{issue_title}}, {{issue_action}}, {{issue_body}}, {{issue_assignee}}, {{comment_body}}",
+  "description": "Command configuration for webhook events. Available variables: {{repo}}, {{full_repo}}, {{repo_owner}}, {{branch}}, {{pusher}}, {{event}}, {{commit}}, {{commit_msg}}, {{pr_number}}, {{pr_action}}, {{tag}}, {{ref_type}}, {{issue_number}}, {{issue_title}}, {{issue_action}}, {{issue_body}}, {{issue_assignee}}, {{issue_labels}}, {{comment_body}}",
   "commands": {
     "push": [
       {

+ 9 - 0
src/commandExecutor.js

@@ -57,6 +57,7 @@ export class CommandExecutor {
       issue_action: '',
       issue_body: '',
       issue_assignee: '',
+      issue_labels: '',
       comment_body: ''
     };
 
@@ -127,6 +128,10 @@ export class CommandExecutor {
         variables.issue_body = payload.issue?.body || '';
         variables.issue_assignee = payload.issue?.assignee?.username ||
                                    payload.issue?.assignee?.login || '';
+        // Extract issue labels as comma-separated string
+        if (payload.issue?.labels && Array.isArray(payload.issue.labels)) {
+          variables.issue_labels = payload.issue.labels.map(label => label.name).join(',');
+        }
         break;
 
       case 'issue_comment':
@@ -136,6 +141,10 @@ export class CommandExecutor {
         variables.comment_body = payload.comment?.body || '';
         variables.issue_assignee = payload.issue?.assignee?.username ||
                                    payload.issue?.assignee?.login || '';
+        // Extract issue labels as comma-separated string
+        if (payload.issue?.labels && Array.isArray(payload.issue.labels)) {
+          variables.issue_labels = payload.issue.labels.map(label => label.name).join(',');
+        }
         break;
     }