Repository: dmitriz/issue-labeler
Pull request #9: New
Author: login: dmitriz
Labels: name: refactor
name: Review effort 3/5
name: size:L
description: This PR changes 100-499 lines, ignoring generated files
name: test-label-1746599460128
name: test-label-1746600585049
name: test-label-1746600733014
name: test-label-1746600949755
name: test-label-1746601128268
name: test-label-1746601770072
Created: 2025-05-07T06:16:47Z | Merged: 2025-05-07T07:09:39Z

## **User description**
### **User description**
This pull request introduces a new feature for automatically labeling GitHub issues based on their content using AI models. It includes the main script for the workflow, wrappers for GitHub API and AI model interactions, and updates to dependencies. Below are the key changes grouped by theme:

### New Feature Implementation:
* **`label-issue.js`**: Added the main script for automating issue labeling. The script fetches issue content, uses a GitHub AI model to determine urgency and importance, and applies the corresponding labels to the issue. It also includes error handling and command-line argument parsing.

### API Wrappers:
* **`lib/github-wrapper.js`**: Added functions for interacting with the GitHub API. These include `getIssueContent` for fetching issue details and `applyLabels` for applying labels to issues. Both functions include error handling and use an Axios instance with default configurations.
* **`lib/github-model.js`**: Added a wrapper for interacting with the GitHub AI model API. The `callModel` function sends a formatted prompt to the model, extracts the YAML response, and parses it to determine urgency and importance labels.

### Dependency Updates:
* **`package.json`**: Added the `js-yaml` package for parsing YAML responses from the AI model.


___

### **PR Type**
Enhancement


___

### **Description**
- Introduce automated GitHub issue labeling using AI models
  - Main script fetches issue content, analyzes with AI, applies labels
  - Command-line interface for specifying issue, owner, and repo

- Add wrappers for GitHub API and AI model interactions
  - Fetch issue details and apply labels via GitHub API
  - Query AI model and parse YAML response for label extraction

- Update dependencies to include YAML parsing support


___



### **Changes walkthrough** 📝
<table><thead><tr><th></th><th align="left">Relevant files</th></tr></thead><tbody><tr><td><strong>Enhancement</strong></td><td><table>
<tr>
  <td>
    <details>
      <summary><strong>label-issue.js</strong><dd><code>Main script for AI-powered GitHub issue labeling</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary>
<hr>

label-issue.js

<li>Implements main script for automated issue labeling<br> <li> Reads prompt template and prepares AI prompt<br> <li> Integrates with GitHub API and AI model wrappers<br> <li> Adds command-line argument parsing and error handling


</details>


  </td>
  <td><a href="https://github.com/dmitriz/issue-labeler/pull/9/files#diff-a08f0fee89a7629735079a9b5fe2a2c9dc2f992ff80d130b8784c4ffae5d1a0e">+81/-0</a>&nbsp; &nbsp; </td>

</tr>

<tr>
  <td>
    <details>
      <summary><strong>github-model.js</strong><dd><code>AI model API wrapper for label inference</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary>
<hr>

lib/github-model.js

<li>Adds wrapper for GitHub AI model API inference<br> <li> Sends prompt, extracts and parses YAML response<br> <li> Handles errors and malformed responses robustly


</details>


  </td>
  <td><a href="https://github.com/dmitriz/issue-labeler/pull/9/files#diff-5f8aa460a2d281acf7e49e043887bc1562e079bd70b3cbdd575b873c893e2bfd">+74/-0</a>&nbsp; &nbsp; </td>

</tr>

<tr>
  <td>
    <details>
      <summary><strong>github-wrapper.js</strong><dd><code>GitHub API wrapper for issue content and labeling</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary>
<hr>

lib/github-wrapper.js

<li>Adds functions to fetch issue content from GitHub<br> <li> Implements label application to issues via API<br> <li> Includes error handling for API requests


</details>


  </td>
  <td><a href="https://github.com/dmitriz/issue-labeler/pull/9/files#diff-09e8ff86ffa0113dcc5c155273affbacfa7bcfe337ade3371e36b55983abf61c">+78/-0</a>&nbsp; &nbsp; </td>

</tr>
</table></td></tr><tr><td><strong>Dependencies</strong></td><td><table>
<tr>
  <td>
    <details>
      <summary><strong>package.json</strong><dd><code>Add js-yaml dependency for model response parsing</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary>
<hr>

package.json

- Adds `js-yaml` dependency for YAML parsing


</details>


  </td>
  <td><a href="https://github.com/dmitriz/issue-labeler/pull/9/files#diff-7ae45ad102eab3b6d7e7896acd08c427a9b25b346470d7bc6507b6481575d519">+2/-1</a>&nbsp; &nbsp; &nbsp; </td>

</tr>
</table></td></tr></tr></tbody></table>

___

> <details> <summary>  Need help?</summary><li>Type <code>/help how to ...</code> in the comments thread for any questions about Qodo Merge usage.</li><li>Check out the <a href="https://qodo-merge-docs.qodo.ai/usage-guide/">documentation</a> for more information.</li></details>


___

## **CodeAnt-AI Description**
- Introduced a new script (`label-issue.js`) that automates the process of labeling GitHub issues by fetching their content, analyzing it with an AI model, and applying urgency/importance labels.
- Added a wrapper module (`lib/github-model.js`) to interact with the GitHub AI model API, including robust YAML response parsing and error handling.
- Implemented a GitHub API wrapper (`lib/github-wrapper.js`) to fetch issue details and apply labels programmatically.
- Updated `package.json` and `package-lock.json` to include the `js-yaml` package and its dependencies for YAML parsing.

This PR delivers an end-to-end workflow for AI-driven GitHub issue labeling, making it easy to automate label assignment based on issue content. The changes include all necessary API integrations, error handling, and dependency updates to support the new functionality.


___

## **Changes walkthrough**
<table><thead><tr><th></th><th align="left">Relevant files</th></tr></thead><tbody><tr><td><strong>Enhancement
</strong></td><td><table>
<tr>
  <td>
    <details>
      <summary><strong>label-issue.js</strong><dd><code>Introduce main script for automated AI-driven GitHub issue labeling</code></dd></summary>
<hr>

label-issue.js
<li>Added a new script to automate GitHub issue labeling using AI models.<br> <li> Implements fetching issue content, generating a prompt, calling an AI <br>model, and applying labels.<br> <li> Includes command-line argument parsing and error handling.<br> <li> Exports the main labeling function for external use.<br>


</details>
    

  </td>
  <td><a href="https://github.com/dmitriz/issue-labeler/pull/9/files#diff-a08f0fee89a7629735079a9b5fe2a2c9dc2f992ff80d130b8784c4ffae5d1a0e">+81/-0</a>&nbsp; &nbsp; </td>
</tr>                    

<tr>
  <td>
    <details>
      <summary><strong>github-model.js</strong><dd><code>Add GitHub AI model API wrapper for label inference</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary>
<hr>

lib/github-model.js
<li>Added a wrapper for interacting with the GitHub AI model API.<br> <li> Implements a function to send prompts, extract YAML responses, and <br>parse urgency/importance.<br> <li> Handles API errors and YAML parsing errors robustly.<br>


</details>
    

  </td>
  <td><a href="https://github.com/dmitriz/issue-labeler/pull/9/files#diff-5f8aa460a2d281acf7e49e043887bc1562e079bd70b3cbdd575b873c893e2bfd">+74/-0</a>&nbsp; &nbsp; </td>
</tr>                    

<tr>
  <td>
    <details>
      <summary><strong>github-wrapper.js</strong><dd><code>Add GitHub API wrapper for issue content and labeling</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary>
<hr>

lib/github-wrapper.js
<li>Added functions to fetch issue content and apply labels via the GitHub <br>API.<br> <li> Uses axios with custom configuration and error handling.<br> <li> Exports API wrapper functions for use in the main script.<br>


</details>
    

  </td>
  <td><a href="https://github.com/dmitriz/issue-labeler/pull/9/files#diff-09e8ff86ffa0113dcc5c155273affbacfa7bcfe337ade3371e36b55983abf61c">+78/-0</a>&nbsp; &nbsp; </td>
</tr>                    
</table></td></tr><tr><td><strong>Dependencies
</strong></td><td><table>
<tr>
  <td>
    <details>
      <summary><strong>package-lock.json</strong><dd><code>Update lockfile to include js-yaml and dependencies</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary>
<hr>

package-lock.json
<li>Added <code>js-yaml</code> and its dependency <code>argparse</code> to the lockfile.<br> <li> Updated dependency tree to reflect new package requirements.<br>


</details>
    

  </td>
  <td><a href="https://github.com/dmitriz/issue-labeler/pull/9/files#diff-053150b640a7ce75eff69d1a22cae7f0f94ad64ce9a855db544dda0929316519">+20/-1</a>&nbsp; &nbsp; </td>
</tr>                    

<tr>
  <td>
    <details>
      <summary><strong>package.json</strong><dd><code>Add js-yaml to project dependencies</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary>
<hr>

package.json
- Added `js-yaml` as a new dependency for YAML parsing.



</details>
    

  </td>
  <td><a href="https://github.com/dmitriz/issue-labeler/pull/9/files#diff-7ae45ad102eab3b6d7e7896acd08c427a9b25b346470d7bc6507b6481575d519">+2/-1</a>&nbsp; &nbsp; &nbsp; </td>
</tr>                    
</table></td></tr></tr></tbody></table><details>
<summary><strong>💡 Usage Guide</strong></summary>

### Checking Your Pull Request
Every time you make a pull request, our system automatically looks through it. We check for security issues, mistakes in how you're setting up your infrastructure, and common code problems. We do this to make sure your changes are solid and won't cause any trouble later.

### Talking to CodeAnt AI
Got a question or need a hand with something in your pull request? You can easily get in touch with CodeAnt AI right here. Just type the following in a comment on your pull request, and replace "Your question here" with whatever you want to ask:
<pre>
<code>@codeant-ai ask: Your question here</code>
</pre>
This lets you have a chat with CodeAnt AI about your pull request, making it easier to understand and improve your code.

### Retrigger review
Ask CodeAnt AI to review the PR again, by typing:
<pre>
<code>@codeant-ai: review</code>
</pre>

### Check Your Repository Health
To analyze the health of your code repository, visit our dashboard at [https://app.codeant.ai](https://app.codeant.ai). This tool helps you identify potential issues and areas for improvement in your codebase, ensuring your repository maintains high standards of code health.

</details>

<!-- Korbit AI PR Description Start -->
## Description by Korbit AI

### What change is being made?

Implement a script that automates the labeling of GitHub issues using AI to determine urgency and importance, and refactor the codebase to integrate this feature.

### Why are these changes being made?

These changes aim to streamline and enhance the efficiency of issue management by automatically assigning labels based on AI model predictions, replacing manual effort. The implementation uses GitHub's models to analyze issue content, providing consistent labeling to help prioritize issues effectively and ensuring scalability in handling large volumes of issues. A switch from YAML to JSON format for the label output increases compatibility and parsing reliability.

> Is this description stale? Ask me to generate a new description by commenting `/korbit-generate-pr-description`
<!-- Korbit AI PR Description End -->


Diff:
diff --git a/label-issue.js b/label-issue.js
new file mode 100644
index 0000000..65031b3
--- /dev/null
+++ b/label-issue.js
@@ -0,0 +1,104 @@
+/**
+ * GitHub Issue Automatic Labeling Script
+ * This script automates labeling of GitHub issues using GitHub's AI models
+ * It fetches issue content, uses a model to determine urgency and importance,
+ * and then applies those labels to the issue.
+ */
+const { getIssueContent, applyLabels } = require('./lib/github-wrapper');
+const { callModel } = require('./lib/github-model');
+const fs = require('fs');
+const path = require('path');
+const { Command } = require('commander');
+
+// Read the prompt template
+// Remove the synchronous file read at the top of the file.
+// Inside the async main function (at its start), add:
+const promptTemplate = await fs.promises.readFile(path.join(__dirname, 'prompt-template.txt'), 'utf8');
+let promptTemplate;
+try {
+  promptTemplate = fs.readFileSync(promptTemplatePath, 'utf8');
+} catch (error) {
+  console.error(`Error reading prompt template: ${error.message}`);
+  process.exit(1);
+}
+// Inside the async main function (at its start), add:
+promptTemplate = await fs.promises.readFile(path.join(__dirname, 'prompt-template.txt'), 'utf8');
+
+/**
+ * Main function to process and label an issue
+ * @param {Object} options - Options object
+ * @param {number} options.issueNumber - The issue number to process
+ * @param {string} options.owner - Repository owner
+ const prompt = promptTemplate
+   .replace(/\{\{title\}\}/g, issue.title)
+   .replace(/\{\{body\}\}/g, issue.body || '');
+  console.log(`Processing issue #${issueNumber} from ${owner}/${repo}...`);
+  
+  try {
+    // Step 1: Fetch issue content from GitHub
+    console.log(`Fetching content for issue #${issueNumber}...`);
+    const issue = await getIssueContent({ issueNumber, owner, repo });
+    console.log(`Issue #${issue.number} fetched: "${issue.title}"`);
+
+    // Step 2: Prepare the prompt by replacing the placeholders with actual issue content
+    const prompt = promptTemplate
+      .replace('{{title}}', issue.title)
+      .replace('{{body}}', issue.body || '');
+    
+    // Step 3: Call the GitHub Model to determine urgency and importance
+    console.log('Analyzing issue content with GitHub Model...');
+    const { urgency, importance } = await callModel({ prompt });
+    console.log(`Model inference complete: urgency=${urgency}, importance=${importance}`);
+
+    // Step 4: Create and apply labels
+    const labels = [];
+    if (urgency) labels.push(urgency);
+    if (importance) labels.push(importance);
+
+    if (labels.length === 0) {
+      console.warn(`No labels determined for issue #${issueNumber}.`);
+    }
+
+    if (labels.length > 0) {
+      console.log(`Applying labels to issue #${issueNumber}: ${labels.join(', ')}...`);
+      await applyLabels({ issueNumber, owner, repo, labels });
+      console.log(`Successfully labeled issue #${issueNumber} with: ${labels.join(', ')}`);
+    } else {
+      console.warn('No valid labels were determined. No labels applied to the issue.');
+    }
+  } catch (error) {
+    console.error(`Error processing issue #${issueNumber}:`, error);
+    process.exit(1);
+  }
+}
+
+// Parse command-line arguments
+if (require.main === module) {
+  const program = new Command();
+
+  program
+    .name('label-issue')
+    .description('Automatically label GitHub issues based on their content')
+    .argument('<issue-number>', 'The issue number to process')
+    .option('-o, --owner <owner>', 'Repository owner', 'dmitriz')
+    .option('-r, --repo <repo>', 'Repository name', 'issue-labeler')
+    .action(async (issueNumber, options) => {
+      const issueNum = parseInt(issueNumber, 10);
+      if (isNaN(issueNum)) {
+        console.error('Error: <issue-number> must be a valid number');
+        process.exit(1);
+      }
+
+      try {
+        await main({ issueNumber: issueNum, owner: options.owner, repo: options.repo });
+        console.log('Issue labeling process completed successfully');
+      } catch (err) {
+        console.error('Fatal error:', err.message);
+        process.exit(1);
+      }
+    });
+
+  program.parse(process.argv);
+}
+
+module.exports = { main };
\ No newline at end of file
diff --git a/lib/github-model.js b/lib/github-model.js
new file mode 100644
index 0000000..2253781
--- /dev/null
+++ b/lib/github-model.js
@@ -0,0 +1,79 @@
+/**
+ * GitHub Model API wrapper for the issue labeling workflow
+ * Provides a function for model inference to determine issue labels
+ */
+const axios = require('axios');
+const token = process.env.GITHUB_MODEL_TOKEN || require('../.secrets/gh-model').token;
+
+if (!token) {
+  console.error('GITHUB_MODEL_TOKEN environment variable is not set');
+  process.exit(1);
+}
+const config = require('../config/github-model');
+
+// Create a reusable axios instance with keepAlive enabled
+const https = require('https');
+const axiosInstance = axios.create({
+  httpsAgent: new https.Agent({ keepAlive: true }),
+  timeout: 10000
+});
+
+/**
+ * Calls the GitHub model API for inference.
+ * @param {string} prompt - The input prompt for the model.
+ * @returns {Object} - Object containing urgency and importance values.
+ */
+async function callModel(prompt) {
+  try {
+    const response = await axiosInstance.post(config.apiEndpoint, {
+      messages: [
+      { role: "user", content: prompt }
+      ],
+      model: config.model,
+      temperature: config.temperature,
+      max_tokens: config.max_tokens
+    }, {
+      headers: {
+      "Authorization": `Bearer ${token}`,
+      "Content-Type": "application/json"
+      }
+    });
+
+    // Extract content from response
+    if (!response.data.choices || !response.data.choices[0]?.message?.content) {
+      throw new Error('Invalid response format from GitHub model API');
+    }
+
+    const rawContent = response.data.choices[0].message.content.trim();
+    
+    // Extract JSON object from the response (handling possible markdown code blocks)
+    let jsonContent = rawContent;
+    const jsonMatch = rawContent.match(/```(?:json|js)?\s*([\s\S]*?)```/);
+    if (jsonMatch && jsonMatch[1]) {
+      jsonContent = jsonMatch[1].trim();
+    }
+
+    try {
+      const parsedJson = JSON.parse(jsonContent);
+      return {
+        urgency: parsedJson.urgency && parsedJson.urgency.trim() !== '' ? parsedJson.urgency.trim() : null,
+        importance: parsedJson.importance && parsedJson.importance.trim() !== '' ? parsedJson.importance.trim() : null
+      };
+    } catch (parseError) {
+      console.error('Failed to parse JSON response:', parseError.message);
+      console.error('Raw response:', rawContent);
+      throw new Error('Failed to parse model response');
+    }
+  } catch (error) {
+    console.error('Error calling GitHub model:', error.message);
+    if (error.response) {
+      console.error('API Error:', error.response.status, error.response.statusText);
+      console.error('Error details:', error.response.data);
+    } else {
+      console.error('Error stack:', error.stack);
+    }
+    throw error;
+  }
+}
+
+module.exports = { callModel };
\ No newline at end of file
diff --git a/lib/github-wrapper.js b/lib/github-wrapper.js
new file mode 100644
index 0000000..e927540
--- /dev/null
+++ b/lib/github-wrapper.js
@@ -0,0 +1,112 @@
+/**
+ * GitHub API wrapper for the issue labeling workflow
+ * Centralizes error handling and API configuration for reliable GitHub interactions.
+ * Use this wrapper instead of direct API calls to ensure consistent error handling and rate limit management.
+ */
+const axios = require('axios');
+const https = require('https');
+
+// Use environment variables for sensitive credentials
+const token = process.env.GITHUB_TOKEN;
+
+const github = axios.create({
+  baseURL: BASE_URL,
+  headers: {
+    Authorization: `Bearer ${token}`,
+    Accept: 'application/vnd.github.v3+json',
+    'User-Agent': 'issue-labeler-app'
+  },
+  timeout: 5000,
+  httpsAgent: new https.Agent({ keepAlive: true })
+});
+  if (!/^[\w.-]+$/.test(segment)) {
+    throw new Error('Invalid path segment');
+  }
+  return segment;
+}
+
+const github = axios.create({
+  baseURL: BASE_URL,
+  headers: {
+    Authorization: `token ${token}`,
+    Accept: 'application/vnd.github.v3+json',
+    'User-Agent': 'issue-labeler-app'
+  },
+  timeout: GITHUB_API_TIMEOUT_MS,
+  httpsAgent: new https.Agent({
+    keepAlive: true,
+    maxSockets: 100 // Limit the maximum concurrent connections
+  })
+});
+
+// Centralized error handling function
+function handleGitHubError(error, context) {
+  console.error(`Error ${context}: ${error.message}`);
+  if (error.response) {
+    console.error('API Error:', error.response.status, error.response.statusText);
+    console.error('Error details:', error.response.data);
+  }
+  throw error;
+}
+
+// Wrap the URL construction with validation
+function constructIssueUrl({ owner, repo, issueNumber }) {
+  return `/repos/${validatePathSegment(owner)}/${validatePathSegment(repo)}/issues/${Number(issueNumber)}`;
+}
+
+/**
+ * Fetch issue content from GitHub
+ * @param {Object} options - Options object
+ * @param {number} options.issueNumber - The issue number to fetch
+ * @param {string} options.owner - Repository owner
+ * @param {string} options.repo - Repository name
+ * @returns {Promise<Object>} - Issue object with title and body
+ */
+async function getIssueContent({ issueNumber, owner, repo }) {
+  try {
+    const response = await github.get(`/repos/${owner}/${repo}/issues/${issueNumber}`);
+    return {
+      title: response.data.title || '',
+      body: response.data.body || '',
+      number: response.data.number
+    };
+  } catch (error) {
+    console.error(`Error fetching issue #${issueNumber}: ${error.message}`);
+    if (error.response) {
+      console.error('API Error:', error.response.status, error.response.statusText);
+      console.error('Error details:', error.response.data);
+    }
+    throw error;
+  }
+}
+
+/**
+ * Apply labels to a GitHub issue
+ * @param {Object} options - Options object
+ * @param {number} options.issueNumber - The issue number to add labels to
+ * @param {string} options.owner - Repository owner
+ * @param {string} options.repo - Repository name
+ * @param {Array<string>} options.labels - Array of label names to add
+ * @returns {Promise<Array>} - Array of labels on the issue after the operation
+ */
+async function applyLabels({ issueNumber, owner, repo, labels }) {
+  try {
+    const response = await github.post(
+      `/repos/${owner}/${repo}/issues/${issueNumber}/labels`,
+      { labels }
+    );
+    return response.data;
+  } catch (error) {
+    console.error(`Error applying labels to issue #${issueNumber}: ${error.message}`);
+    if (error.response) {
+      console.error('API Error:', error.response.status, error.response.statusText);
+      // Detailed error logging removed to prevent exposure of sensitive information.
+    }
+    throw error;
+  }
+}
+
+module.exports = {
+  getIssueContent,
+  applyLabels
+};
\ No newline at end of file
diff --git a/package-lock.json b/package-lock.json
index 50b4a10..74db3fa 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -9,182 +9,10 @@
       "version": "1.0.0",
       "license": "ISC",
       "dependencies": {
-        "@octokit/rest": "^19.0.0",
         "axios": "^1.9.0",
         "dotenv": "^16.0.0"
       }
     },
-    "node_modules/@octokit/auth-token": {
-      "version": "3.0.4",
-      "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-3.0.4.tgz",
-      "integrity": "sha512-TWFX7cZF2LXoCvdmJWY7XVPi74aSY0+FfBZNSXEXFkMpjcqsQwDSYVv5FhRFaI0V1ECnwbz4j59T/G+rXNWaIQ==",
-      "license": "MIT",
-      "engines": {
-        "node": ">= 14"
-      }
-    },
-    "node_modules/@octokit/core": {
-      "version": "4.2.4",
-      "resolved": "https://registry.npmjs.org/@octokit/core/-/core-4.2.4.tgz",
-      "integrity": "sha512-rYKilwgzQ7/imScn3M9/pFfUf4I1AZEH3KhyJmtPdE2zfaXAn2mFfUy4FbKewzc2We5y/LlKLj36fWJLKC2SIQ==",
-      "license": "MIT",
-      "dependencies": {
-        "@octokit/auth-token": "^3.0.0",
-        "@octokit/graphql": "^5.0.0",
-        "@octokit/request": "^6.0.0",
-        "@octokit/request-error": "^3.0.0",
-        "@octokit/types": "^9.0.0",
-        "before-after-hook": "^2.2.0",
-        "universal-user-agent": "^6.0.0"
-      },
-      "engines": {
-        "node": ">= 14"
-      }
-    },
-    "node_modules/@octokit/endpoint": {
-      "version": "7.0.6",
-      "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-7.0.6.tgz",
-      "integrity": "sha512-5L4fseVRUsDFGR00tMWD/Trdeeihn999rTMGRMC1G/Ldi1uWlWJzI98H4Iak5DB/RVvQuyMYKqSK/R6mbSOQyg==",
-      "license": "MIT",
-      "dependencies": {
-        "@octokit/types": "^9.0.0",
-        "is-plain-object": "^5.0.0",
-        "universal-user-agent": "^6.0.0"
-      },
-      "engines": {
-        "node": ">= 14"
-      }
-    },
-    "node_modules/@octokit/graphql": {
-      "version": "5.0.6",
-      "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-5.0.6.tgz",
-      "integrity": "sha512-Fxyxdy/JH0MnIB5h+UQ3yCoh1FG4kWXfFKkpWqjZHw/p+Kc8Y44Hu/kCgNBT6nU1shNumEchmW/sUO1JuQnPcw==",
-      "license": "MIT",
-      "dependencies": {
-        "@octokit/request": "^6.0.0",
-        "@octokit/types": "^9.0.0",
-        "universal-user-agent": "^6.0.0"
-      },
-      "engines": {
-        "node": ">= 14"
-      }
-    },
-    "node_modules/@octokit/openapi-types": {
-      "version": "18.1.1",
-      "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-18.1.1.tgz",
-      "integrity": "sha512-VRaeH8nCDtF5aXWnjPuEMIYf1itK/s3JYyJcWFJT8X9pSNnBtriDf7wlEWsGuhPLl4QIH4xM8fqTXDwJ3Mu6sw==",
-      "license": "MIT"
-    },
-    "node_modules/@octokit/plugin-paginate-rest": {
-      "version": "6.1.2",
-      "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-6.1.2.tgz",
-      "integrity": "sha512-qhrmtQeHU/IivxucOV1bbI/xZyC/iOBhclokv7Sut5vnejAIAEXVcGQeRpQlU39E0WwK9lNvJHphHri/DB6lbQ==",
-      "license": "MIT",
-      "dependencies": {
-        "@octokit/tsconfig": "^1.0.2",
-        "@octokit/types": "^9.2.3"
-      },
-      "engines": {
-        "node": ">= 14"
-      },
-      "peerDependencies": {
-        "@octokit/core": ">=4"
-      }
-    },
-    "node_modules/@octokit/plugin-request-log": {
-      "version": "1.0.4",
-      "resolved": "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-1.0.4.tgz",
-      "integrity": "sha512-mLUsMkgP7K/cnFEw07kWqXGF5LKrOkD+lhCrKvPHXWDywAwuDUeDwWBpc69XK3pNX0uKiVt8g5z96PJ6z9xCFA==",
-      "license": "MIT",
-      "peerDependencies": {
-        "@octokit/core": ">=3"
-      }
-    },
-    "node_modules/@octokit/plugin-rest-endpoint-methods": {
-      "version": "7.2.3",
-      "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-7.2.3.tgz",
-      "integrity": "sha512-I5Gml6kTAkzVlN7KCtjOM+Ruwe/rQppp0QU372K1GP7kNOYEKe8Xn5BW4sE62JAHdwpq95OQK/qGNyKQMUzVgA==",
-      "license": "MIT",
-      "dependencies": {
-        "@octokit/types": "^10.0.0"
-      },
-      "engines": {
-        "node": ">= 14"
-      },
-      "peerDependencies": {
-        "@octokit/core": ">=3"
-      }
-    },
-    "node_modules/@octokit/plugin-rest-endpoint-methods/node_modules/@octokit/types": {
-      "version": "10.0.0",
-      "resolved": "https://registry.npmjs.org/@octokit/types/-/types-10.0.0.tgz",
-      "integrity": "sha512-Vm8IddVmhCgU1fxC1eyinpwqzXPEYu0NrYzD3YZjlGjyftdLBTeqNblRC0jmJmgxbJIsQlyogVeGnrNaaMVzIg==",
-      "license": "MIT",
-      "dependencies": {
-        "@octokit/openapi-types": "^18.0.0"
-      }
-    },
-    "node_modules/@octokit/request": {
-      "version": "6.2.8",
-      "resolved": "https://registry.npmjs.org/@octokit/request/-/request-6.2.8.tgz",
-      "integrity": "sha512-ow4+pkVQ+6XVVsekSYBzJC0VTVvh/FCTUUgTsboGq+DTeWdyIFV8WSCdo0RIxk6wSkBTHqIK1mYuY7nOBXOchw==",
-      "license": "MIT",
-      "dependencies": {
-        "@octokit/endpoint": "^7.0.0",
-        "@octokit/request-error": "^3.0.0",
-        "@octokit/types": "^9.0.0",
-        "is-plain-object": "^5.0.0",
-        "node-fetch": "^2.6.7",
-        "universal-user-agent": "^6.0.0"
-      },
-      "engines": {
-        "node": ">= 14"
-      }
-    },
-    "node_modules/@octokit/request-error": {
-      "version": "3.0.3",
-      "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-3.0.3.tgz",
-      "integrity": "sha512-crqw3V5Iy2uOU5Np+8M/YexTlT8zxCfI+qu+LxUB7SZpje4Qmx3mub5DfEKSO8Ylyk0aogi6TYdf6kxzh2BguQ==",
-      "license": "MIT",
-      "dependencies": {
-        "@octokit/types": "^9.0.0",
-        "deprecation": "^2.0.0",
-        "once": "^1.4.0"
-      },
-      "engines": {
-        "node": ">= 14"
-      }
-    },
-    "node_modules/@octokit/rest": {
-      "version": "19.0.13",
-      "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-19.0.13.tgz",
-      "integrity": "sha512-/EzVox5V9gYGdbAI+ovYj3nXQT1TtTHRT+0eZPcuC05UFSWO3mdO9UY1C0i2eLF9Un1ONJkAk+IEtYGAC+TahA==",
-      "license": "MIT",
-      "dependencies": {
-        "@octokit/core": "^4.2.1",
-        "@octokit/plugin-paginate-rest": "^6.1.2",
-        "@octokit/plugin-request-log": "^1.0.4",
-        "@octokit/plugin-rest-endpoint-methods": "^7.1.2"
-      },
-      "engines": {
-        "node": ">= 14"
-      }
-    },
-    "node_modules/@octokit/tsconfig": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/@octokit/tsconfig/-/tsconfig-1.0.2.tgz",
-      "integrity": "sha512-I0vDR0rdtP8p2lGMzvsJzbhdOWy405HcGovrspJ8RRibHnyRgggUSNO5AIox5LmqiwmatHKYsvj6VGFHkqS7lA==",
-      "license": "MIT"
-    },
-    "node_modules/@octokit/types": {
-      "version": "9.3.2",
-      "resolved": "https://registry.npmjs.org/@octokit/types/-/types-9.3.2.tgz",
-      "integrity": "sha512-D4iHGTdAnEEVsB8fl95m1hiz7D5YiRdQ9b/OEb3BYRVwbLsGHcRVPz+u+BgRLNk0Q0/4iZCBqDN96j2XNxfXrA==",
-      "license": "MIT",
-      "dependencies": {
-        "@octokit/openapi-types": "^18.0.0"
-      }
-    },
     "node_modules/asynckit": {
       "version": "0.4.0",
       "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
@@ -202,12 +30,6 @@
         "proxy-from-env": "^1.1.0"
       }
     },
-    "node_modules/before-after-hook": {
-      "version": "2.2.3",
-      "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.3.tgz",
-      "integrity": "sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ==",
-      "license": "Apache-2.0"
-    },
     "node_modules/call-bind-apply-helpers": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
@@ -242,12 +64,6 @@
         "node": ">=0.4.0"
       }
     },
-    "node_modules/deprecation": {
-      "version": "2.3.1",
-      "resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz",
-      "integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==",
-      "license": "ISC"
-    },
     "node_modules/dotenv": {
       "version": "16.5.0",
       "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.5.0.tgz",
@@ -451,15 +267,6 @@
         "node": ">= 0.4"
       }
     },
-    "node_modules/is-plain-object": {
-      "version": "5.0.0",
-      "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz",
-      "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==",
-      "license": "MIT",
-      "engines": {
-        "node": ">=0.10.0"
-      }
-    },
     "node_modules/math-intrinsics": {
       "version": "1.1.0",
       "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
@@ -490,74 +297,11 @@
         "node": ">= 0.6"
       }
     },
-    "node_modules/node-fetch": {
-      "version": "2.7.0",
-      "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz",
-      "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==",
-      "license": "MIT",
-      "dependencies": {
-        "whatwg-url": "^5.0.0"
-      },
-      "engines": {
-        "node": "4.x || >=6.0.0"
-      },
-      "peerDependencies": {
-        "encoding": "^0.1.0"
-      },
-      "peerDependenciesMeta": {
-        "encoding": {
-          "optional": true
-        }
-      }
-    },
-    "node_modules/once": {
-      "version": "1.4.0",
-      "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
-      "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
-      "license": "ISC",
-      "dependencies": {
-        "wrappy": "1"
-      }
-    },
     "node_modules/proxy-from-env": {
       "version": "1.1.0",
       "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
       "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==",
       "license": "MIT"
-    },
-    "node_modules/tr46": {
-      "version": "0.0.3",
-      "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
-      "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==",
-      "license": "MIT"
-    },
-    "node_modules/universal-user-agent": {
-      "version": "6.0.1",
-      "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.1.tgz",
-      "integrity": "sha512-yCzhz6FN2wU1NiiQRogkTQszlQSlpWaw8SvVegAc+bDxbzHgh1vX8uIe8OYyMH6DwH+sdTJsgMl36+mSMdRJIQ==",
-      "license": "ISC"
-    },
-    "node_modules/webidl-conversions": {
-      "version": "3.0.1",
-      "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
-      "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==",
-      "license": "BSD-2-Clause"
-    },
-    "node_modules/whatwg-url": {
-      "version": "5.0.0",
-      "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
-      "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
-      "license": "MIT",
-      "dependencies": {
-        "tr46": "~0.0.3",
-        "webidl-conversions": "^3.0.0"
-      }
-    },
-    "node_modules/wrappy": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
-      "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
-      "license": "ISC"
     }
   }
 }
diff --git a/package.json b/package.json
index 6c0612b..25b5c01 100644
--- a/package.json
+++ b/package.json
@@ -29,7 +29,6 @@
   },
   "homepage": "https://github.com/dmitriz/issue-labeler#readme",
   "dependencies": {
-    "@octokit/rest": "^19.0.0",
     "axios": "^1.9.0",
     "dotenv": "^16.0.0"
   }
diff --git a/prompt-template.txt b/prompt-template.txt
index 3df7128..b5e0641 100644
--- a/prompt-template.txt
+++ b/prompt-template.txt
@@ -10,10 +10,12 @@ For urgency, consider:
 For importance, consider:
 - "high": Issues impacting core functionality or user experience
 - "low": Nice-to-have improvements or minor bugs with workarounds
-Return a YAML block in this format:
+Return a JSON object in this format:
 
-urgency: <urgent | not urgent>
-importance: <high | low>
+{
+  "urgency": "<urgent | not urgent>",
+  "importance": "<high | low>"
+}
 
 Issue:
 Title: {{title}}


Reviews:
user: login: gemini-code-assist[bot]
body: Hello @dmitriz, I'm Gemini Code Assist[^1]! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

## Summary of Changes
This pull request introduces a new feature to automatically label GitHub issues based on their content using AI models. It includes a main script (`label-issue.js`) that orchestrates the labeling process, wrappers for interacting with the GitHub API (`lib/github-wrapper.js`) and the AI model (`lib/github-model.js`), and updates to the project's dependencies by adding `js-yaml` to parse YAML responses from the AI model.

### Highlights
* **Main Script**: The `label-issue.js` script fetches issue content, calls the GitHub AI model to determine urgency and importance, and applies corresponding labels to the issue. It also includes error handling and command-line argument parsing, and reads a prompt template from `prompt-template.txt`.
* **GitHub API Wrapper**: The `lib/github-wrapper.js` file provides functions `getIssueContent` and `applyLabels` for interacting with the GitHub API. These functions handle fetching issue details and applying labels, respectively, and include error handling using Axios.
* **GitHub Model API Wrapper**: The `lib/github-model.js` file wraps the GitHub AI model API. The `callModel` function sends a formatted prompt to the model, extracts the YAML response, and parses it to determine urgency and importance labels. It uses `js-yaml` for parsing the YAML content.
* **Dependency Update**: The `package.json` file has been updated to include the `js-yaml` package, which is used for parsing YAML responses from the AI model.

### Changelog
<details>
<summary>Click here to see the changelog</summary>

* **label-issue.js**
  * Added the main script for automating issue labeling using GitHub's AI models.
  * Fetches issue content, determines urgency and importance using a model, and applies labels.
  * Includes error handling and command-line argument parsing.
  * Reads the prompt template from `prompt-template.txt`.
  * The main function processes and labels an issue based on its number, owner, and repository.
* **lib/github-model.js**
  * Added a wrapper for interacting with the GitHub AI model API.
  * The `callModel` function sends a formatted prompt to the model and parses the YAML response to determine urgency and importance labels.
  * Includes error handling for API calls and YAML parsing.
  * Uses `js-yaml` to parse the YAML content extracted from the model's response.
* **lib/github-wrapper.js**
  * Added functions for interacting with the GitHub API.
  * `getIssueContent` fetches issue details, and `applyLabels` applies labels to issues.
  * Includes error handling and uses an Axios instance with default configurations.
  * Uses a base URL of 'https://api.github.com' for GitHub API requests.
* **package-lock.json**
  * Updated to include `js-yaml` and its dependencies.
* **package.json**
  * Added the `js-yaml` package as a dependency for parsing YAML responses from the AI model.

</details>


<details>

<summary><b>Using Gemini Code Assist</b></summary>
<br>

The full guide for Gemini Code Assist can be found on our [documentation page](https://developers.google.com/gemini-code-assist/docs/review-github-code), here are some quick tips.

<b>Invoking Gemini</b>

You can request assistance from Gemini at any point in your pull request via creating an issue comment (i.e. comment on the pull request page) using either `/gemini <command>` or `@gemini-code-assist <command>`. Below is a summary of the supported commands.

Feature | Command | Description
--- | --- | ---
Code Review | `/gemini review` | Performs a code review for the current pull request in its current state.
Pull Request Summary | `/gemini summary` | Provides a summary of the current pull request in its current state.
Comment | @gemini-code-assist | Responds in comments when explicitly tagged, both in issue comments and review comments.
Help | `/gemini help` | Displays a list of available commands.

<b>Customization</b>

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a `.gemini/` folder in the base of the repository. Detailed instructions can be found [here](https://developers.google.com/gemini-code-assist/docs/customize-gemini-behavior-github).

<b>Limitations & Feedback</b>

Gemini Code Assist is currently in preview and may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with :thumbsup: and :thumbsdown: on @gemini-code-assist comments to provide feedback.

</details>

<b>You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the [Gemini Code Assist IDE Extension](https://cloud.google.com/products/gemini/code-assist).</b>


[^1]: Review the [Privacy Notices](https://policies.google.com/privacy), [Generative AI Prohibited Use Policy](https://policies.google.com/terms/generative-ai/use-policy), [Terms of Service](https://policies.google.com/terms), and learn how to configure Gemini Code Assist in GitHub [here](https://developers.google.com/gemini-code-assist/docs/review-github-code). Gemini can make mistakes, so double check it and [use code with caution](https://support.google.com/legal/answer/13505487).

---

> A YAML's tale,
> Labels from the model sail,
> Issues now defined.


state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-07T06:16:56Z
user: login: gemini-code-assist[bot]
body: ## Code Review
The pull request introduces a new feature for automatically labeling GitHub issues using AI models. The code is well-structured and includes necessary API wrappers and dependency updates. The use of logging and error handling is commendable. However, there are a few areas where improvements can be made to enhance maintainability and robustness.

### Summary of Findings
* **Error Handling in `callModel`**: The `callModel` function in `lib/github-model.js` includes detailed error logging, which is beneficial for debugging. However, consider adding more context to the error messages to facilitate easier troubleshooting.
* **Configuration Management**: The configuration for the GitHub model (e.g., model name, temperature, max tokens) is currently hardcoded in `config/github-model.js`. Consider making these configurable via environment variables to allow for easier adjustments without modifying the code.
* **Command-Line Argument Parsing**: The command-line argument parsing in `label-issue.js` could be improved by using a dedicated library like `argparse` or `commander` for better handling of arguments and options.

### Merge Readiness
The pull request is almost ready for merging. Addressing the identified issues, particularly enhancing error context and improving command-line argument parsing, would significantly improve the code's robustness and maintainability. I am unable to approve the pull request, and other reviewers should review and approve this code before merging.
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-07T06:17:06Z
user: login: korbit-ai[bot]
body: ### Review by Korbit AI

###### Korbit automatically attempts to detect when you fix issues in new commits.

| Category | Issue | Status |
|----------|-------|----------|
| <sub>![Performance](https://img.shields.io/badge/Performance-4f46e5)</sub> | Unbounded Connection Pool ▹ [view]( https://github.com/dmitriz/issue-labeler/pull/9#discussion_r2076873450 ) | ✅ Fix detected |
| <sub>![Error Handling](https://img.shields.io/badge/Error%20Handling-ea580c)</sub> | Insufficient Top-level Error Handling ▹ [view]( https://github.com/dmitriz/issue-labeler/pull/9#discussion_r2076873473 ) ||
| <sub>![Documentation](https://img.shields.io/badge/Documentation-7c3aed)</sub> | Unclear wrapper benefits ▹ [view]( https://github.com/dmitriz/issue-labeler/pull/9#discussion_r2076873461 ) | ✅ Fix detected |
| <sub>![Design](https://img.shields.io/badge/Design-0d9488)</sub> | Duplicated Error Handling Logic ▹ [view]( https://github.com/dmitriz/issue-labeler/pull/9#discussion_r2076873467 ) | ✅ Fix detected |
| <sub>![Security](https://img.shields.io/badge/Security-e11d48)</sub> | Insecure credentials storage ▹ [view]( https://github.com/dmitriz/issue-labeler/pull/9#discussion_r2076873457 ) | ✅ Fix detected |
| <sub>![Security](https://img.shields.io/badge/Security-e11d48)</sub> | Unvalidated URL parameter interpolation ▹ [view]( https://github.com/dmitriz/issue-labeler/pull/9#discussion_r2076873459 ) | ✅ Fix detected |
| <sub>![Error Handling](https://img.shields.io/badge/Error%20Handling-ea580c)</sub> | Incomplete Error Logging ▹ [view]( https://github.com/dmitriz/issue-labeler/pull/9#discussion_r2076873470 ) | ✅ Fix detected |
| <sub>![Readability](https://img.shields.io/badge/Readability-0284c7)</sub> | Unexplained Magic Number ▹ [view]( https://github.com/dmitriz/issue-labeler/pull/9#discussion_r2076873446 ) | ✅ Fix detected |
| <sub>![Functionality](https://img.shields.io/badge/Functionality-0284c7)</sub> | Missing Labels Array Validation ▹ [view]( https://github.com/dmitriz/issue-labeler/pull/9#discussion_r2076873454 ) ||
| <sub>![Logging](https://img.shields.io/badge/Logging-4f46e5)</sub> | Fragmented Error Logging ▹ [view]( https://github.com/dmitriz/issue-labeler/pull/9#discussion_r2076873464 ) ||






<details>
  <summary>Files scanned</summary>
&#8203;



| File Path | Reviewed |
| ------------------ | -------------- |
| lib/github-model.js | ✅ |
| lib/github-wrapper.js | ✅ |
| label-issue.js | ✅ |


> Explore our [documentation](https://docs.korbit.ai) to understand the languages and file types [we support](https://docs.korbit.ai/configuration/supported-languages) and the [files we ignore](https://docs.korbit.ai/configuration/ignoring-files).

</details>





> Check out our [docs](https://docs.korbit.ai) on how you can make Korbit work best for you and your team.
>
> Loving Korbit!? Share us on [LinkedIn](https://www.linkedin.com/sharing/share-offsite/?url=https://www.korbit.ai&text=Korbit%20AI-powered%20Code%20Reviews%20are%20awesome...) [Reddit](https://www.reddit.com/submit?url=https://www.korbit.ai&title=Korbit%20AI-powered%20Code%20Reviews%20are%20awesome...) and [X](https://x.com/intent/tweet?text=Korbit%20AI-powered%20Code%20Reviews%20are%20awesome...%0A%0Ahttps%3A%2F%2Fwww.korbit.ai%0A%0A@KorbitAi)


state: COMMENTED
author_association: NONE
submitted_at: 2025-05-07T06:19:38Z
user: login: coderabbitai[bot]
body: **Actionable comments posted: 4**

<details>
<summary>🧹 Nitpick comments (7)</summary><blockquote>

<details>
<summary>label-issue.js (2)</summary><blockquote>

`31-35`: **Handle potentially undefined issue.body more robustly**

The current approach uses the logical OR operator, but a more explicit condition would be clearer.

```diff
// Step 2: Prepare the prompt by replacing the placeholders with actual issue content
const prompt = promptTemplate
  .replace('{{title}}', issue.title)
-  .replace('{{body}}', issue.body || '');
+  .replace('{{body}}', issue.body === null || issue.body === undefined ? '' : issue.body);
```

---

`53-56`: **Consider more granular error handling**

The error handler terminates the process without distinguishing between different error types.

Consider categorizing errors to allow for different handling strategies:
```diff
} catch (error) {
  console.error(`Error processing issue #${issueNumber}: ${error.message}`);
+  if (error.code === 'ENOTFOUND' || error.code === 'ETIMEDOUT') {
+    console.error('Network error occurred. Please check your connection and try again.');
+  } else if (error.response && error.response.status === 404) {
+    console.error(`Issue #${issueNumber} not found in ${owner}/${repo}`);
+  } else if (error.response && error.response.status === 401) {
+    console.error('Authentication failed. Please check your API token.');
+  }
  process.exit(1);
}
```

</blockquote></details>
<details>
<summary>lib/github-wrapper.js (2)</summary><blockquote>

`19-21`: **Configure request retries for improved reliability**

The current setup doesn't handle API rate limits or transient network failures.

Consider adding retry logic for improved reliability:
```diff
  timeout: 5000,
-  httpsAgent: new https.Agent({ keepAlive: true })
+  httpsAgent: new https.Agent({ keepAlive: true }),
+  // Add retry configuration
+  maxRetries: 3,
+  retryDelay: 1000,
+  retryCondition: (error) => {
+    return error.response && (error.response.status === 429 || error.response.status >= 500);
+  }
```

Note: You'll need to add axios-retry package to implement this functionality.

---

`40-45`: **Enhance error logging with error codes**

The error logging could be improved to include error codes for better debugging.

```diff
try {
  const response = await github.get(`/repos/${owner}/${repo}/issues/${issueNumber}`);
  // ...
} catch (error) {
-  console.error(`Error fetching issue #${issueNumber}: ${error.message}`);
+  console.error(`Error fetching issue #${issueNumber}: ${error.message} (${error.code || 'no code'})`);
  if (error.response) {
    console.error('API Error:', error.response.status, error.response.statusText);
    console.error('Error details:', error.response.data);
  }
  throw error;
}
```

</blockquote></details>
<details>
<summary>lib/github-model.js (3)</summary><blockquote>

`24-36`: **Extract API endpoint to configuration**

The GitHub model endpoint URL is hardcoded, which reduces maintainability.

```diff
-const response = await axiosInstance.post("https://models.github.ai/inference/chat/completions", {
+const MODEL_API_ENDPOINT = config.endpoint || "https://models.github.ai/inference/chat/completions";
+const response = await axiosInstance.post(MODEL_API_ENDPOINT, {
  messages: [
    { role: "user", content: prompt }
  ],
  model: config.model,
  temperature: config.temperature,
  max_tokens: config.max_tokens
}, {
  headers: {
    "Authorization": `Bearer ${token}`,
    "Content-Type": "application/json"
  }
});
```

---

`47-51`: **Use optional chaining as suggested by static analysis**

The static analysis tool correctly suggests using optional chaining in the YAML regex match.

```diff
// Extract YAML block from the response
let yamlContent = rawContent;
const yamlMatch = rawContent.match(/```(?:yaml)?\s*([\s\S]*?)```/);
-if (yamlMatch && yamlMatch[1]) {
+if (yamlMatch?.[1]) {
  yamlContent = yamlMatch[1].trim();
}
```

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 Biome (1.9.4)</summary>

[error] 48-48: Change to an optional chain.

Unsafe fix: Change to an optional chain.


(lint/complexity/useOptionalChain)

</details>

</details>

---

`52-63`: **Improve YAML parsing error handling**

Current error handling for YAML parsing could be more informative.

```diff
// Parse the YAML content to get urgency and importance values
try {
  const parsedYaml = yaml.load(yamlContent);
+  if (!parsedYaml || typeof parsedYaml !== 'object') {
+    throw new Error('YAML response is not a valid object');
+  }
  return {
    urgency: parsedYaml.urgency || null,
    importance: parsedYaml.importance || null
  };
} catch (parseError) {
  console.error('Failed to parse YAML response:', parseError.message);
  console.error('Raw response:', rawContent);
+  console.error('Attempted to parse content:', yamlContent);
  throw new Error('Failed to parse model response');
}
```

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: CHILL**
**Plan: Pro**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between 0aefb79af1d79914fde50a046d1fcb46835b4e90 and ae06d7fa3b965cec2f1b4c3c2897b3d042c5b9b8.

</details>

<details>
<summary>⛔ Files ignored due to path filters (1)</summary>

* `package-lock.json` is excluded by `!**/package-lock.json`

</details>

<details>
<summary>📒 Files selected for processing (4)</summary>

* `label-issue.js` (1 hunks)
* `lib/github-model.js` (1 hunks)
* `lib/github-wrapper.js` (1 hunks)
* `package.json` (1 hunks)

</details>

<details>
<summary>🧰 Additional context used</summary>

<details>
<summary>🧬 Code Graph Analysis (2)</summary>

<details>
<summary>label-issue.js (2)</summary><blockquote>

<details>
<summary>lib/github-wrapper.js (1)</summary>

* `require` (6-6)

</details>
<details>
<summary>lib/github-model.js (1)</summary>

* `require` (6-6)

</details>

</blockquote></details>
<details>
<summary>lib/github-model.js (2)</summary><blockquote>

<details>
<summary>lib/github-wrapper.js (5)</summary>

* `axios` (5-5)
* `require` (6-6)
* `https` (11-11)
* `response` (33-33)
* `response` (60-63)

</details>
<details>
<summary>label-issue.js (4)</summary>

* `require` (7-7)
* `require` (8-8)
* `callModel` (38-38)
* `prompt` (32-34)

</details>

</blockquote></details>

</details><details>
<summary>🪛 Biome (1.9.4)</summary>

<details>
<summary>lib/github-model.js</summary>

[error] 48-48: Change to an optional chain.

Unsafe fix: Change to an optional chain.


(lint/complexity/useOptionalChain)

</details>

</details>

</details>

<details>
<summary>🔇 Additional comments (1)</summary><blockquote>

<details>
<summary>package.json (1)</summary>

`34-35`: <details>
<summary>✅ Verification successful</summary>

**Verify js-yaml version for security vulnerabilities**

The addition of js-yaml v4.1.0 supports the YAML parsing in github-model.js.

---

🏁 Script executed:

```shell
#!/bin/bash
# Check if the js-yaml version is the latest and if there are any security vulnerabilities

echo "Checking latest version of js-yaml..."
npm view js-yaml version

echo "Checking for vulnerabilities in js-yaml@4.1.0..."
npm audit --json | jq '.vulnerabilities | select(.["js-yaml"])'
```

Length of output: 290

---



</details>

**No action required: js-yaml@4.1.0 is up-to-date and has no reported vulnerabilities**

Evidence:
- `npm view js-yaml version` → 4.1.0 (latest)
- `npm audit --json | jq '.vulnerabilities | select(.["js-yaml"])'` → no output (no vulnerabilities)

package.json (lines 34–35):
```json
    "dotenv": "^16.0.0",
    "js-yaml": "^4.1.0"
```

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-07T06:20:45Z
user: login: codeant-ai[bot]
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-07T06:21:11Z
user: login: llamapreview[bot]
body: # Auto Pull Request Review from LlamaPReview

## 1. Overview
### 1.1 Core Changes
- Primary purpose and scope: Introduce a new feature for automatically labeling GitHub issues based on their content using AI models.
- Key components modified:
  - `label-issue.js`: Main script for automating issue labeling.
  - `lib/github-wrapper.js`: Functions for interacting with the GitHub API.
  - `lib/github-model.js`: Wrapper for interacting with the GitHub AI model API.
  - `package.json`: Added the `js-yaml` package for parsing YAML responses from the AI model.
- Cross-component impacts: Integration points impact between GitHub API interactions and AI model responses.
- Business value alignment: Enhances issue management by automating the labeling process, improving efficiency and consistency.

### 1.2 Technical Architecture
- System design modifications: Introduces a new AI-powered labeling subsystem with direct dependency on GitHub API endpoints and coupling to specific AI model response formats (YAML).
- Component interaction changes: New interactions between the main script (`label-issue.js`), GitHub API wrapper (`lib/github-wrapper.js`), and AI model wrapper (`lib/github-model.js`).
- Integration points impact: Direct integration with GitHub API and AI model API.
- Dependency changes and implications: Added `js-yaml` for YAML parsing, which is crucial for processing AI model responses.

## 2. Critical Findings
### 2.1 Must Fix (P0🔴)
**Issue**: Uncaught File Read Failure
- **Analysis Confidence**: High
- **Impact**: Potential data loss and uncaught failures leading to application crashes.
- **Resolution**: Implement try-catch block for file read operations to handle missing or inaccessible files gracefully.

**Issue**: Label Validation Missing
- **Analysis Confidence**: High
- **Impact**: Incorrect or malicious labels may be applied to issues, leading to misclassification and potential security risks.
- **Resolution**: Add validation for labels returned by the AI model to ensure they conform to allowed values.

**Issue**: Unhandled Rate Limiting
- **Analysis Confidence**: High
- **Impact**: Potential service disruption due to API rate limiting, leading to failed issue labeling.
- **Resolution**: Implement rate limiting handling and retry logic for API requests.

### 2.2 Should Fix (P1🟡)
**Issue**: No input sanitization for issue content used in prompts
- **Analysis Confidence**: High
- **Impact**: Potential security risks due to prompt injection vulnerabilities.
- **Suggested Solution**: Implement content sanitization to prevent prompt escaping and enforce length limits.

**Issue**: Duplicate error handling patterns across modules
- **Analysis Confidence**: Medium
- **Impact**: Redundant code and potential maintenance challenges.
- **Suggested Solution**: Centralize error handling logic to ensure consistency and reduce redundancy.

**Issue**: Missing retry logic for transient API failures
- **Analysis Confidence**: High
- **Impact**: Potential service disruptions due to transient API failures.
- **Suggested Solution**: Implement retry logic with exponential backoff for API requests.

### 2.3 Consider (P2🟢)
**Area**: Hardcoded API timeout values
- **Analysis Confidence**: Medium
- **Improvement Opportunity**: Enhance flexibility and maintainability by using configurable timeout values.

**Area**: No dependency injection for API clients
- **Analysis Confidence**: Medium
- **Improvement Opportunity**: Improve testability and maintainability by introducing dependency injection for API clients.

**Area**: Mixed error handling strategies
- **Analysis Confidence**: Medium
- **Improvement Opportunity**: Standardize error handling strategies to improve code consistency and maintainability.

### 2.4 Summary of Action Items
- **P0🔴**: Address uncaught file read failures, implement label validation, and handle rate limiting.
- **P1🟡**: Implement input sanitization, centralize error handling, and add retry logic for transient API failures.
- **P2🟢**: Consider using configurable timeout values, introduce dependency injection, and standardize error handling strategies.

## 3. Technical Analysis
### 3.1 Code Logic Analysis
📁 **`label-issue.js`** - **Main function to process and label an issue**
  - Submitted PR Code:
```javascript
    const promptTemplate = fs.readFileSync(path.join(__dirname, 'prompt-template.txt'), 'utf8');
```
  - Analysis:
    * Current logic and potential issues: The file read operation is not handled gracefully, leading to potential uncaught exceptions.
    * Edge cases and error handling: Missing file or inaccessible file scenarios are not handled.
    * Cross-component impact: Failure in reading the prompt template will cause the entire script to fail.
    * Business logic considerations: Ensuring the prompt template is available is critical for the script's functionality.
  - LlamaPReview Suggested Improvements:
```javascript
    let promptTemplate;
    try {
      promptTemplate = fs.readFileSync(path.join(__dirname, 'prompt-template.txt'), 'utf8');
    } catch (e) {
      throw new Error(`Missing prompt template: ${e.message}`);
    }
```
  - Improvement rationale:
    * Technical benefits: Graceful handling of file read errors prevents application crashes.
    * Business value: Ensures the script can handle missing or inaccessible files without failing.
    * Risk assessment: Low risk, as it improves error handling without changing the core functionality.

📁 **`lib/github-model.js`** - **Call GitHub model to determine issue labels**
  - Submitted PR Code:
```javascript
    return {
      urgency: parsedYaml.urgency || null,
      importance: parsedYaml.importance || null
    };
```
  - Analysis:
    * Current logic and potential issues: No validation of labels returned by the AI model, leading to potential misclassification.
    * Edge cases and error handling: Invalid or malicious labels may be applied to issues.
    * Cross-component impact: Incorrect labels can affect issue management and prioritization.
    * Business logic considerations: Ensuring labels are valid is critical for accurate issue classification.
  - LlamaPReview Suggested Improvements:
```javascript
    const ALLOWED_LABELS = {
      urgency: new Set(['critical', 'high', 'medium', 'low']),
      importance: new Set(['p0', 'p1', 'p2'])
    };

    function validateLabel(type, value) {
      return ALLOWED_LABELS[type].has(value.toLowerCase()) ? value : null;
    }

    return {
      urgency: validateLabel('urgency', parsedYaml.urgency),
      importance: validateLabel('importance', parsedYaml.importance)
    };
```
  - Improvement rationale:
    * Technical benefits: Ensures labels are validated against allowed values, preventing misclassification.
    * Business value: Improves the accuracy and reliability of issue labeling.
    * Risk assessment: Low risk, as it adds a layer of validation without changing the core functionality.

📁 **`lib/github-wrapper.js`** - **API Error Enrichment**
  - Submitted PR Code:
```javascript
    if (error.response) {
      console.error('API Error:', error.response.status, error.response.statusText);
      console.error('Error details:', error.response.data);
    }
```
  - Analysis:
    * Current logic and potential issues: No enrichment of error messages for rate limiting scenarios.
    * Edge cases and error handling: Rate limiting errors are not handled gracefully.
    * Cross-component impact: Failure to handle rate limiting can lead to service disruptions.
    * Business logic considerations: Ensuring rate limiting is handled is critical for reliable service operation.
  - LlamaPReview Suggested Improvements:
```javascript
    if (error.response?.status === 403) {
      error.message += `. Rate limit remaining: ${error.response.headers['x-ratelimit-remaining']}`;
    }
```
  - Improvement rationale:
    * Technical benefits: Enriches error messages with rate limiting information, aiding in debugging and monitoring.
    * Business value: Improves the reliability and maintainability of the service.
    * Risk assessment: Low risk, as it enhances error handling without changing the core functionality.

### 3.2 Key Quality Aspects
- System scalability considerations: Implement retry logic and rate limiting handling to ensure the system can scale with increasing API usage.
- Performance bottlenecks and optimizations: Optimize API timeout values and introduce dependency injection for better performance and maintainability.
- Testing strategy and coverage: Add contract tests for GitHub API interactions, implement model response fuzzing, and add integration tests for the full happy path.
- Documentation needs: Document the new features and API interactions to ensure maintainability and ease of use.

## 4. Overall Evaluation
- Technical assessment: The implementation shows good separation of concerns but requires hardening for production use. The core AI integration needs validation layers and operational safeguards before deployment.
- Business impact: Enhances issue management by automating the labeling process, improving efficiency and consistency.
- Risk evaluation: There are critical issues that need to be addressed before merging, including uncaught file read failures, label validation, and rate limiting handling.
- Notable positive aspects and good practices: The PR introduces a well-structured approach to AI-driven issue labeling, with clear separation of concerns and good error handling practices.
- Implementation quality: The code is well-organized and follows good practices, but there are areas for improvement in error handling, input sanitization, and validation.
- Final recommendation: **Request Changes** - Address the critical findings and consider the suggested improvements before merging.

---
💡 **LlamaPReview Community**
Have feedback on this AI Code review tool? Join our [GitHub Discussions](https://github.com/JetXu-LLM/LlamaPReview-site/discussions) to share your thoughts and help shape the future of LlamaPReview.
state: COMMENTED
author_association: NONE
submitted_at: 2025-05-07T06:21:29Z
user: login: coderabbitai[bot]
body: **Actionable comments posted: 1**

<details>
<summary>♻️ Duplicate comments (5)</summary><blockquote>

<details>
<summary>lib/github-model.js (5)</summary><blockquote>

`5-8`: **Use environment variables for sensitive credentials**

Similar to `github-wrapper.js`, sensitive credentials are loaded directly from a file. This creates inconsistent security patterns across your codebase.


```diff
const axios = require('axios');
-const { token } = require('../.secrets/gh-model');
+// Use environment variables for sensitive credentials
+const token = process.env.GITHUB_MODEL_TOKEN;
+
+if (!token) {
+  console.error('GITHUB_MODEL_TOKEN environment variable is not set');
+  process.exit(1);
+}
const config = require('../config/github-model');
const yaml = require('js-yaml');
```

---

`18-22`: **Move axios instance creation outside the function**

Creating a new axios instance on every function call is inefficient. Move this to the module level for better performance.


```diff
const axios = require('axios');
// ...other imports
+const https = require('https');
+const axiosInstance = axios.create({
+  httpsAgent: new https.Agent({ keepAlive: true }),
+  timeout: 10000
+});

// Then inside callModel function:
async function callModel({ prompt }) {
  try {
-    const https = require('https');
-    const axiosInstance = axios.create({
-      httpsAgent: new https.Agent({ keepAlive: true }),
-      timeout: 10000
-    });
```

---

`54-54`: **Use a safe YAML schema while parsing**

Using `yaml.load()` without a schema specification can lead to security vulnerabilities. Restrict parsing to JSON schema to prevent code execution.


```diff
-      const parsedYaml = yaml.load(yamlContent);
+      const parsedYaml = yaml.load(yamlContent, { schema: yaml.JSON_SCHEMA });
```

---

`55-58`: **Validate and trim parsed YAML values**

The parsed YAML values should be trimmed and validated to avoid empty or whitespace-only entries.


```diff
      return {
-        urgency: parsedYaml.urgency || null,
-        importance: parsedYaml.importance || null
+        urgency: parsedYaml.urgency && parsedYaml.urgency.trim() !== '' ? parsedYaml.urgency.trim() : null,
+        importance: parsedYaml.importance && parsedYaml.importance.trim() !== '' ? parsedYaml.importance.trim() : null
      };
```

---

`66-69`: **Limit detailed error logging to prevent data leakage**

Detailed error logging could expose sensitive information. Limit what you log, especially in production environments.


```diff
    if (error.response) {
      console.error('API Error:', error.response.status, error.response.statusText);
-      console.error('Error details:', error.response.data, error);
+      // Detailed error logging removed to prevent exposure of sensitive information.
+      // Consider enabling detailed logging only in development environments
    } else {
```

</blockquote></details>

</blockquote></details>

<details>
<summary>🧹 Nitpick comments (2)</summary><blockquote>

<details>
<summary>lib/github-model.js (2)</summary><blockquote>

`47-50`: **Use optional chaining for safer property access**

The static analysis tool correctly identifies an opportunity to use optional chaining for safer property access.


```diff
-    const yamlMatch = rawContent.match(/```(?:`yaml`)?\s*([\s\S]*?)```/);
-    if (yamlMatch && yamlMatch[1]) {
+    const yamlMatch = rawContent.match(/```(?:`yaml`)?\s*([\s\S]*?)```/);
+    if (yamlMatch?.[1]) {
       yamlContent = yamlMatch[1].trim();
     }
```

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 Biome (1.9.4)</summary>

[error] 48-48: Change to an optional chain.

Unsafe fix: Change to an optional chain.


(lint/complexity/useOptionalChain)

</details>

</details>

---

`1-76`: **Add retry logic for API requests**

The current implementation doesn't handle transient network failures or rate limiting. Consider adding retry logic for better reliability.


```diff
const axios = require('axios');
const { token } = require('../.secrets/gh-model');
const config = require('../config/github-model');
const yaml = require('js-yaml');
+
+/**
+ * Retry a function with exponential backoff
+ * @param {Function} fn - Function to retry
+ * @param {number} maxRetries - Maximum number of retries
+ * @param {number} initialDelay - Initial delay in ms
+ * @returns {Promise<any>} - Result of the function
+ */
+async function withRetry(fn, maxRetries = 3, initialDelay = 500) {
+  let retries = 0;
+  let lastError;
+
+  while (retries <= maxRetries) {
+    try {
+      return await fn();
+    } catch (error) {
+      lastError = error;
+      
+      // Don't retry for certain status codes
+      if (error.response && (error.response.status === 400 || error.response.status === 401)) {
+        throw error;
+      }
+      
+      retries++;
+      if (retries > maxRetries) break;
+      
+      // Exponential backoff with jitter
+      const delay = initialDelay * Math.pow(2, retries - 1) * (0.5 + Math.random() * 0.5);
+      console.warn(`Retrying after ${Math.round(delay)}ms (${retries}/${maxRetries})...`);
+      await new Promise(resolve => setTimeout(resolve, delay));
+    }
+  }
+  
+  throw lastError;
+}

// Inside callModel function:
  try {
    // ...existing code
-    const response = await axiosInstance.post("https://models.github.ai/inference/chat/completions", {
+    const response = await withRetry(() => axiosInstance.post("https://models.github.ai/inference/chat/completions", {
      messages: [
        { role: "user", content: prompt }
      ],
      model: config.model,
      temperature: config.temperature,
      max_tokens: config.max_tokens
    }, {
      headers: {
        "Authorization": `Bearer ${token}`,
        "Content-Type": "application/json"
      }
-    });
+    }));
```

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 Biome (1.9.4)</summary>

[error] 48-48: Change to an optional chain.

Unsafe fix: Change to an optional chain.


(lint/complexity/useOptionalChain)

</details>

</details>

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: CHILL**
**Plan: Pro**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between ae06d7fa3b965cec2f1b4c3c2897b3d042c5b9b8 and be7a8cb046664d7ccc865e15f47875e60f5a03d3.

</details>

<details>
<summary>📒 Files selected for processing (2)</summary>

* `label-issue.js` (1 hunks)
* `lib/github-model.js` (1 hunks)

</details>

<details>
<summary>🚧 Files skipped from review as they are similar to previous changes (1)</summary>

* label-issue.js

</details>

<details>
<summary>🧰 Additional context used</summary>

<details>
<summary>🧬 Code Graph Analysis (1)</summary>

<details>
<summary>lib/github-model.js (2)</summary><blockquote>

<details>
<summary>lib/github-wrapper.js (5)</summary>

* `axios` (5-5)
* `require` (6-6)
* `https` (11-11)
* `response` (33-33)
* `response` (60-63)

</details>
<details>
<summary>label-issue.js (5)</summary>

* `require` (7-7)
* `require` (8-8)
* `require` (11-11)
* `callModel` (39-39)
* `prompt` (33-35)

</details>

</blockquote></details>

</details><details>
<summary>🪛 Biome (1.9.4)</summary>

<details>
<summary>lib/github-model.js</summary>

[error] 48-48: Change to an optional chain.

Unsafe fix: Change to an optional chain.


(lint/complexity/useOptionalChain)

</details>

</details>

</details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-07T06:35:58Z
user: login: coderabbitai[bot]
body: **Actionable comments posted: 6**

<details>
<summary>♻️ Duplicate comments (4)</summary><blockquote>

<details>
<summary>lib/github-wrapper.js (3)</summary><blockquote>

`92-107`: _⚠️ Potential issue_

**Missing validation and URL security in applyLabels method**

The method lacks input validation for the labels array and uses unvalidated URL construction.

Apply this diff to add validation and use secure URL construction:

```diff
async function applyLabels({ issueNumber, owner, repo, labels }) {
+  if (!Array.isArray(labels) || labels.length === 0) {
+    throw new Error('Labels must be a non-empty array of strings');
+  }
+  if (!labels.every(label => typeof label === 'string' && label.trim())) {
+    throw new Error('All labels must be non-empty strings');
+  }
  try {
    const response = await github.post(
-      `/repos/${owner}/${repo}/issues/${issueNumber}/labels`,
+      `${constructIssueUrl({ owner, repo, issueNumber })}/labels`,
      { labels }
    );
    return response.data;
  } catch (error) {
-    console.error(`Error applying labels to issue #${issueNumber}: ${error.message}`);
-    if (error.response) {
-      console.error('API Error:', error.response.status, error.response.statusText);
-      // Detailed error logging removed to prevent exposure of sensitive information.
-    }
-    throw error;
+    handleGitHubError(error, `applying labels to issue #${issueNumber}`);
  }
}
```

---

`65-81`: _⚠️ Potential issue_

**Missing URL validation in getIssueContent method**

The method constructs URLs with user-provided inputs without validation, which could lead to security issues.

Apply this diff to use the secure URL construction:

```diff
async function getIssueContent({ issueNumber, owner, repo }) {
  try {
-    const response = await github.get(`/repos/${owner}/${repo}/issues/${issueNumber}`);
+    const response = await github.get(constructIssueUrl({ owner, repo, issueNumber }));
    return {
      title: response.data.title || '',
      body: response.data.body || '',
      number: response.data.number
    };
  } catch (error) {
-    console.error(`Error fetching issue #${issueNumber}: ${error.message}`);
-    if (error.response) {
-      console.error('API Error:', error.response.status, error.response.statusText);
-      console.error('Error details:', error.response.data);
-    }
-    throw error;
+    handleGitHubError(error, `fetching issue #${issueNumber}`);
  }
}
```

---

`42-50`: _🛠️ Refactor suggestion_

**Centralized error handling function available but unused**

You've created a good centralized error handling function, but it's not being used in your API methods. This leads to code duplication in error handling logic.

Apply this diff to use the centralized error handler in your API methods:

```diff
async function getIssueContent({ issueNumber, owner, repo }) {
  try {
    const response = await github.get(`/repos/${owner}/${repo}/issues/${issueNumber}`);
    return {
      title: response.data.title || '',
      body: response.data.body || '',
      number: response.data.number
    };
  } catch (error) {
-    console.error(`Error fetching issue #${issueNumber}: ${error.message}`);
-    if (error.response) {
-      console.error('API Error:', error.response.status, error.response.statusText);
-      console.error('Error details:', error.response.data);
-    }
-    throw error;
+    handleGitHubError(error, `fetching issue #${issueNumber}`);
  }
}
```

</blockquote></details>
<details>
<summary>lib/github-model.js (1)</summary><blockquote>

`5-11`: _🛠️ Refactor suggestion_

**Improve security by using only environment variables for credentials**

The current approach falls back to a local secret file, which is less secure than exclusively using environment variables.

Apply this diff to remove the fallback to a local file:

```diff
-const token = process.env.GITHUB_MODEL_TOKEN || require('../.secrets/gh-model').token;
+const token = process.env.GITHUB_MODEL_TOKEN;
```

</blockquote></details>

</blockquote></details>

<details>
<summary>🧹 Nitpick comments (1)</summary><blockquote>

<details>
<summary>label-issue.js (1)</summary><blockquote>

`83-84`: **Hard-coded repository defaults limit flexibility**

Hard-coding repository values may cause issues when the script is used in different contexts.

Consider using environment variables as defaults:

```diff
-    .option('-o, --owner <owner>', 'Repository owner', 'dmitriz')
-    .option('-r, --repo <repo>', 'Repository name', 'issue-labeler')
+    .option('-o, --owner <owner>', 'Repository owner', process.env.GITHUB_REPOSITORY_OWNER || 'dmitriz')
+    .option('-r, --repo <repo>', 'Repository name', process.env.GITHUB_REPOSITORY?.split('/')[1] || 'issue-labeler')
```

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: CHILL**
**Plan: Pro**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between be7a8cb046664d7ccc865e15f47875e60f5a03d3 and 91189d41032ba80fdd627288c2a4b7807cfaae04.

</details>

<details>
<summary>📒 Files selected for processing (3)</summary>

* `label-issue.js` (1 hunks)
* `lib/github-model.js` (1 hunks)
* `lib/github-wrapper.js` (1 hunks)

</details>

<details>
<summary>🧰 Additional context used</summary>

<details>
<summary>🧬 Code Graph Analysis (1)</summary>

<details>
<summary>lib/github-model.js (2)</summary><blockquote>

<details>
<summary>lib/github-wrapper.js (5)</summary>

* `axios` (6-6)
* `token` (10-10)
* `https` (7-7)
* `response` (67-67)
* `response` (94-97)

</details>
<details>
<summary>label-issue.js (5)</summary>

* `require` (7-7)
* `require` (8-8)
* `require` (11-11)
* `callModel` (50-50)
* `prompt` (44-46)

</details>

</blockquote></details>

</details><details>
<summary>🪛 Biome (1.9.4)</summary>

<details>
<summary>lib/github-model.js</summary>

[error] 59-69: This code is unreachable

... because either this statement will throw an exception ...

... or this statement will return from the function beforehand


(lint/correctness/noUnreachable)

---

[error] 17-17: Shouldn't redeclare 'https'. Consider to delete it or rename it.

'https' is defined here:


(lint/suspicious/noRedeclare)

</details>
<details>
<summary>label-issue.js</summary>

[error] 17-17: Shouldn't redeclare 'promptTemplate'. Consider to delete it or rename it.

'promptTemplate' is defined here:


(lint/suspicious/noRedeclare)

</details>

</details>

</details>

<details>
<summary>🔇 Additional comments (8)</summary><blockquote>

<details>
<summary>lib/github-wrapper.js (5)</summary>

`1-5`: **Good documentation - clear purpose and benefits**

The file-level documentation clearly explains the purpose and benefits of using this wrapper, which helps other developers understand when to use it instead of direct API calls.

---

`9-15`: **Good security practice: using environment variables for credentials**

Using environment variables for sensitive credentials instead of hardcoded values or local files is a security best practice. The early validation and exit if the token is missing ensures the application fails fast rather than at runtime.

---

`17-19`: **Well-defined constants instead of magic numbers**

Defining `BASE_URL` and `GITHUB_API_TIMEOUT_MS` as named constants improves code maintainability by giving meaning to these values and centralizing them for easier updates.

---

`21-26`: **Good security practice: URL path validation**

The `validatePathSegment` function prevents path traversal attacks by validating URL segments before using them in API calls.

---

`28-40`: **Well-configured axios instance with performance optimizations**

The axios instance is properly configured with base URL, authentication, proper headers, timeout, and HTTPS agent with keepAlive and maxSockets limit to prevent connection pool exhaustion.

</details>
<details>
<summary>label-issue.js (2)</summary>

`34-73`: **Well-structured main function with clear execution flow**

The main function has a clear step-by-step approach with good error handling and logging at each step. The function properly handles the case when no labels are determined.

---

`77-101`: **Good CLI implementation with commander**

Using Commander for argument parsing is a good practice. The validation of the issue number is appropriate, and error handling is in place.


However, consider adding validation to ensure the issue number is positive:

```diff
      const issueNum = parseInt(issueNumber, 10);
-      if (isNaN(issueNum)) {
+      if (isNaN(issueNum) || issueNum <= 0) {
        console.error('Error: <issue-number> must be a valid number');
        process.exit(1);
      }
```

</details>
<details>
<summary>lib/github-model.js (1)</summary>

`70-79`: **Comprehensive error handling with detailed logging**

The error handling is thorough, including logging of error message, status, response data, and stack trace, which is good for debugging.

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-07T06:54:24Z
user: login: coderabbitai[bot]
body: **Actionable comments posted: 1**

<details>
<summary>♻️ Duplicate comments (6)</summary><blockquote>

<details>
<summary>lib/github-model.js (6)</summary><blockquote>

`5-13`: _🛠️ Refactor suggestion_

**Improve token handling logic for better security**

The current approach falls back to a local secrets file if the environment variable is not set. This is not ideal for security.

```diff
const axios = require('axios');
-const token = process.env.GITHUB_MODEL_TOKEN || require('../.secrets/gh-model').token;
+// Use environment variables for sensitive credentials
+const token = process.env.GITHUB_MODEL_TOKEN;

if (!token) {
  console.error('GITHUB_MODEL_TOKEN environment variable is not set');
  process.exit(1);
}
const config = require('../config/github-model');
```

---

`28-28`: _⚠️ Potential issue_

**Function parameter does not match usage pattern**

The function signature doesn't match how it's called in label-issue.js. The parameter should be an object with a prompt property.

```diff
-async function callModel(prompt) {
+async function callModel({ prompt }) {
```

---

`17-17`: _⚠️ Potential issue_

**Fix duplicate https import**

There is a duplicate import of the https module on line 17 which should be removed as it's already imported on line 16.

```diff
// Create a reusable axios instance with keepAlive enabled
const https = require('https');
-const https = require('https');
const axiosInstance = axios.create({
```

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 Biome (1.9.4)</summary>

[error] 17-17: Shouldn't redeclare 'https'. Consider to delete it or rename it.

'https' is defined here:


(lint/suspicious/noRedeclare)

</details>

</details>

---

`18-21`: _🛠️ Refactor suggestion_

**Improve axios instance configuration**

The axios instance should include the Authorization and Content-Type headers to avoid repeating them in each request.

```diff
const axiosInstance = axios.create({
  httpsAgent: new https.Agent({ keepAlive: true }),
-  timeout: 10000
+  timeout: 10000,
+  headers: {
+    "Authorization": `Bearer ${token}`,
+    "Content-Type": "application/json"
+  }
});
```

---

`51-69`: _⚠️ Potential issue_

**Fix unreachable code and YAML parsing logic**

There's unreachable code due to an early return after the YAML parsing on line 54, and inconsistent YAML parsing logic.

```diff
    // Extract YAML block from the response
    let yamlContent = rawContent;
    const yamlMatch = rawContent.match(/```(?:`yaml`)?\s*([\s\S]*?)```/);
-      const parsedYaml = yaml.load(yamlContent, { schema: yaml.JSON_SCHEMA });
-      return {
-        urgency: parsedYaml.urgency && parsedYaml.urgency.trim() !== '' ? parsedYaml.urgency.trim() : null,
-        importance: parsedYaml.importance && parsedYaml.importance.trim() !== '' ? parsedYaml.importance.trim() : null
-      };
+    
+    // If there's a YAML code block, extract just that content
+    if (yamlMatch && yamlMatch[1]) {
+      yamlContent = yamlMatch[1].trim();
+    }
+    
    try {
-      const parsedYaml = yaml.load(yamlContent);
+      const parsedYaml = yaml.load(yamlContent, { schema: yaml.JSON_SCHEMA });
      return {
-        urgency: parsedYaml.urgency || null,
-        importance: parsedYaml.importance || null
+        urgency: parsedYaml.urgency && parsedYaml.urgency.trim() !== '' ? parsedYaml.urgency.trim() : null,
+        importance: parsedYaml.importance && parsedYaml.importance.trim() !== '' ? parsedYaml.importance.trim() : null
      };
    } catch (parseError) {
```

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 Biome (1.9.4)</summary>

[error] 59-69: This code is unreachable

... because either this statement will throw an exception ...

... or this statement will return from the function beforehand


(lint/correctness/noUnreachable)

</details>

</details>

---

`37-42`: _🛠️ Refactor suggestion_

**Remove duplicate headers configuration**

If headers are properly configured in the axios instance, they don't need to be repeated in each request.

```diff
    }, {
-      headers: {
-      "Authorization": `Bearer ${token}`,
-      "Content-Type": "application/json"
-      }
    });
```

</blockquote></details>

</blockquote></details>

<details>
<summary>🧹 Nitpick comments (1)</summary><blockquote>

<details>
<summary>lib/github-model.js (1)</summary><blockquote>

`71-77`: **Limit sensitive information in error logs**

The current error logging might expose sensitive information. Consider limiting the details logged, especially for API errors.

```diff
    console.error('Error calling GitHub model:', error.message, error);
    if (error.response) {
      console.error('API Error:', error.response.status, error.response.statusText);
-      console.error('Error details:', error.response.data, error);
+      // Log limited error information to avoid exposing sensitive data
+      console.error('Error type:', error.name);
    } else {
      console.error('Error stack:', error.stack);
    }
```

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: CHILL**
**Plan: Pro**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between 91189d41032ba80fdd627288c2a4b7807cfaae04 and 2b081d82fb2cf6000a621fbd17144fa1d8d76555.

</details>

<details>
<summary>📒 Files selected for processing (1)</summary>

* `lib/github-model.js` (1 hunks)

</details>

<details>
<summary>🧰 Additional context used</summary>

<details>
<summary>🧬 Code Graph Analysis (1)</summary>

<details>
<summary>lib/github-model.js (2)</summary><blockquote>

<details>
<summary>lib/github-wrapper.js (5)</summary>

* `axios` (6-6)
* `token` (10-10)
* `https` (7-7)
* `response` (67-67)
* `response` (94-97)

</details>
<details>
<summary>label-issue.js (5)</summary>

* `require` (7-7)
* `require` (8-8)
* `require` (11-11)
* `callModel` (50-50)
* `prompt` (44-46)

</details>

</blockquote></details>

</details><details>
<summary>🪛 Biome (1.9.4)</summary>

<details>
<summary>lib/github-model.js</summary>

[error] 59-69: This code is unreachable

... because either this statement will throw an exception ...

... or this statement will return from the function beforehand


(lint/correctness/noUnreachable)

---

[error] 17-17: Shouldn't redeclare 'https'. Consider to delete it or rename it.

'https' is defined here:


(lint/suspicious/noRedeclare)

</details>

</details>

</details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-07T06:56:46Z
user: login: codiumai-pr-agent-free[bot]
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-07T07:05:57Z
user: login: qodo-code-review[bot]
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-07T07:05:57Z
user: login: qodo-code-review[bot]
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-07T07:06:11Z
user: login: codiumai-pr-agent-free[bot]
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-07T07:06:11Z

Review comments:
diff_hunk: @@ -0,0 +1,74 @@
+/**
+ * GitHub Model API wrapper for the issue labeling workflow
+ * Provides a function for model inference to determine issue labels
+ */
+const axios = require('axios');
+const { token } = require('../.secrets/gh-model');
+const config = require('../config/github-model');
+const yaml = require('js-yaml');
+
+/**
+ * Call GitHub model to determine issue labels
+ * @param {Object} options - Options object
+ * @param {string} options.prompt - The formatted prompt to send to the model
+ * @returns {Promise<Object>} - Object containing urgency and importance labels
+ */
+async function callModel({ prompt }) {
+  try {
+    const https = require('https');
+    const axiosInstance = axios.create({
+      httpsAgent: new https.Agent({ keepAlive: true }),
+      timeout: 10000
+    });
+
+    const response = await axiosInstance.post("https://models.github.ai/inference/chat/completions", {
+      messages: [
+        { role: "user", content: prompt }
+      ],
+      model: config.model,
+      temperature: config.temperature,
+      max_tokens: config.max_tokens
+    }, {
+      headers: {
+        "Authorization": `Bearer ${token}`,
+        "Content-Type": "application/json"
+      }
+    });
+
+    // Extract content from response
+    if (!response.data.choices || !response.data.choices[0]?.message?.content) {
+      throw new Error('Invalid response format from GitHub model API');
+    }
+
+    const rawContent = response.data.choices[0].message.content.trim();
+    
+    // Extract YAML block from the response
+    let yamlContent = rawContent;
+    const yamlMatch = rawContent.match(/```(?:yaml)?\s*([\s\S]*?)```/);
+    if (yamlMatch && yamlMatch[1]) {
+      yamlContent = yamlMatch[1].trim();
+    }
+    
+    // Parse the YAML content to get urgency and importance values
+    try {
+      const parsedYaml = yaml.load(yamlContent);
+      return {
+        urgency: parsedYaml.urgency || null,
+        importance: parsedYaml.importance || null
+      };
+    } catch (parseError) {
+      console.error('Failed to parse YAML response:', parseError.message);
+      console.error('Raw response:', rawContent);
+      throw new Error('Failed to parse model response');
+    }
+  } catch (error) {
+    console.error('Error calling GitHub model:', error.message);
+    if (error.response) {
+      console.error('API Error:', error.response.status, error.response.statusText);
+      console.error('Error details:', error.response.data);
+    }
path: lib/github-model.js
user: login: gemini-code-assist[bot]
body: ![medium](https://img.shields.io/badge/priority-medium-yellow)

Consider including the original error object in the `console.error` output to provide more context for debugging. This can help in identifying the root cause of the error more quickly.

For example, you can log `error.stack` to get the stack trace.

```javascript
      console.error('API Error:', error.response.status, error.response.statusText);
      console.error('Error details:', error.response.data, error);
```
created_at: 2025-05-07T06:17:05Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,81 @@
+/**
+ * GitHub Issue Automatic Labeling Script
+ * This script automates labeling of GitHub issues using GitHub's AI models
+ * It fetches issue content, uses a model to determine urgency and importance,
+ * and then applies those labels to the issue.
+ */
+const { getIssueContent, applyLabels } = require('./lib/github-wrapper');
+const { callModel } = require('./lib/github-model');
+const fs = require('fs');
+const path = require('path');
+
+// Read the prompt template
+const promptTemplate = fs.readFileSync(path.join(__dirname, 'prompt-template.txt'), 'utf8');
+
+/**
+ * Main function to process and label an issue
+ * @param {Object} options - Options object
+ * @param {number} options.issueNumber - The issue number to process
+ * @param {string} options.owner - Repository owner
+ * @param {string} options.repo - Repository name
+ */
+async function main({ issueNumber, owner, repo }) {
+  console.log(`Processing issue #${issueNumber} from ${owner}/${repo}...`);
+  
+  try {
+    // Step 1: Fetch issue content from GitHub
+    console.log(`Fetching content for issue #${issueNumber}...`);
+    const issue = await getIssueContent({ issueNumber, owner, repo });
+    console.log(`Issue #${issue.number} fetched: "${issue.title}"`);
+
+    // Step 2: Prepare the prompt by replacing the placeholders with actual issue content
+    const prompt = promptTemplate
+      .replace('{{title}}', issue.title)
+      .replace('{{body}}', issue.body || '');
+    
+    // Step 3: Call the GitHub Model to determine urgency and importance
+    console.log('Analyzing issue content with GitHub Model...');
+    const { urgency, importance } = await callModel({ prompt });
+    console.log(`Model inference complete: urgency=${urgency}, importance=${importance}`);
+
+    // Step 4: Create and apply labels
+    const labels = [];
+    if (urgency) labels.push(urgency);
+    if (importance) labels.push(importance);
+
+    if (labels.length > 0) {
+      console.log(`Applying labels to issue #${issueNumber}: ${labels.join(', ')}...`);
+      await applyLabels({ issueNumber, owner, repo, labels });
+      console.log(`Successfully labeled issue #${issueNumber} with: ${labels.join(', ')}`);
+    } else {
+      console.warn('No valid labels were determined. No labels applied to the issue.');
+    }
+  } catch (error) {
+    console.error(`Error processing issue #${issueNumber}: ${error.message}`);
+    process.exit(1);
+  }
+}
+
+// Parse command-line arguments
+if (require.main === module) {
+  const args = process.argv.slice(2);
+  const issueNumber = parseInt(args[0], 10);
+  
+  // Set default repository info or get from command line if provided
+  const owner = args[1] || 'dmitriz';
+  const repo = args[2] || 'issue-labeler';
+
+  if (!issueNumber) {
+    console.error('Usage: node label-issue.js <issue-number> [owner] [repo]');
+    process.exit(1);
+  }
path: label-issue.js
user: login: gemini-code-assist[bot]
body: ![medium](https://img.shields.io/badge/priority-medium-yellow)

Consider using a dedicated library for parsing command-line arguments, such as `argparse` or `commander`. This would provide better handling of arguments, options, and help messages. It also improves the readability and maintainability of the code.
created_at: 2025-05-07T06:17:06Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,78 @@
+/**
+ * GitHub API wrapper for the issue labeling workflow
+ * Provides functions for fetching issue content and applying labels
+ */
+const axios = require('axios');
+const { token } = require('../.secrets/github');
+
+const BASE_URL = 'https://api.github.com';
+
+// Create an axios instance with default configuration
+const https = require('https');
+const github = axios.create({
+  baseURL: BASE_URL,
+  headers: {
+    Authorization: `token ${token}`,
+    Accept: 'application/vnd.github.v3+json',
+    'User-Agent': 'issue-labeler-app'
+  },
+  timeout: 5000,
path: lib/github-wrapper.js
user: login: korbit-ai[bot]
body: ### Unexplained Magic Number <sub>![category Readability](https://img.shields.io/badge/Readability-0284c7)</sub>

<details>
  <summary>Tell me more</summary>

###### What is the issue?
Magic number 5000 is used for timeout configuration without explanation or constant definition.

###### Why this matters
Magic numbers reduce code maintainability and make it harder to understand the significance of the value.

###### Suggested change ∙ *Feature Preview*
```javascript
const GITHUB_API_TIMEOUT_MS = 5000;

// In configuration:
timeout: GITHUB_API_TIMEOUT_MS,
```


###### Provide feedback to improve future suggestions
[![Nice Catch](https://img.shields.io/badge/👍%20Nice%20Catch-71BC78)](https://app.korbit.ai/feedback/9edd23fc-12be-4bab-b20e-f5a30f0430ae/827c2a7a-96d2-4368-b9e5-80901a0763dd/upvote) [![Incorrect](https://img.shields.io/badge/👎%20Incorrect-white)](https://app.korbit.ai/feedback/9edd23fc-12be-4bab-b20e-f5a30f0430ae/827c2a7a-96d2-4368-b9e5-80901a0763dd?what_not_true=true)  [![Not in Scope](https://img.shields.io/badge/👎%20Out%20of%20PR%20scope-white)](https://app.korbit.ai/feedback/9edd23fc-12be-4bab-b20e-f5a30f0430ae/827c2a7a-96d2-4368-b9e5-80901a0763dd?what_out_of_scope=true) [![Not in coding standard](https://img.shields.io/badge/👎%20Not%20in%20our%20standards-white)](https://app.korbit.ai/feedback/9edd23fc-12be-4bab-b20e-f5a30f0430ae/827c2a7a-96d2-4368-b9e5-80901a0763dd?what_not_in_standard=true) [![Other](https://img.shields.io/badge/👎%20Other-white)](https://app.korbit.ai/feedback/9edd23fc-12be-4bab-b20e-f5a30f0430ae/827c2a7a-96d2-4368-b9e5-80901a0763dd)
</details>

<sub>

💬 Looking for more details? Reply to this comment to chat with Korbit.
</sub>

<!--- korbi internal id:406a992e-ce3c-4247-9d07-abfb1ba51a19 -->


[](406a992e-ce3c-4247-9d07-abfb1ba51a19)
created_at: 2025-05-07T06:19:37Z
author_association: NONE
diff_hunk: @@ -0,0 +1,78 @@
+/**
+ * GitHub API wrapper for the issue labeling workflow
+ * Provides functions for fetching issue content and applying labels
+ */
+const axios = require('axios');
+const { token } = require('../.secrets/github');
+
+const BASE_URL = 'https://api.github.com';
+
+// Create an axios instance with default configuration
+const https = require('https');
+const github = axios.create({
+  baseURL: BASE_URL,
+  headers: {
+    Authorization: `token ${token}`,
+    Accept: 'application/vnd.github.v3+json',
+    'User-Agent': 'issue-labeler-app'
+  },
+  timeout: 5000,
+  httpsAgent: new https.Agent({ keepAlive: true })
+});
path: lib/github-wrapper.js
user: login: korbit-ai[bot]
body: ### Unbounded Connection Pool <sub>![category Performance](https://img.shields.io/badge/Performance-4f46e5)</sub>

<details>
  <summary>Tell me more</summary>

###### What is the issue?
The keepAlive agent is configured but there's no maxSockets setting, which could lead to socket exhaustion under high load.

###### Why this matters
Without maxSockets limit, the connection pool might grow unbounded, potentially consuming excessive system resources and degrading performance during periods of high concurrent requests.

###### Suggested change ∙ *Feature Preview*
Set maxSockets in the https.Agent configuration to limit the maximum concurrent connections:

```javascript
httpsAgent: new https.Agent({ 
  keepAlive: true,
  maxSockets: 100  // Adjust based on expected concurrent requests
})
```


###### Provide feedback to improve future suggestions
[![Nice Catch](https://img.shields.io/badge/👍%20Nice%20Catch-71BC78)](https://app.korbit.ai/feedback/9edd23fc-12be-4bab-b20e-f5a30f0430ae/fca53dff-99be-458c-a81a-a425087418c7/upvote) [![Incorrect](https://img.shields.io/badge/👎%20Incorrect-white)](https://app.korbit.ai/feedback/9edd23fc-12be-4bab-b20e-f5a30f0430ae/fca53dff-99be-458c-a81a-a425087418c7?what_not_true=true)  [![Not in Scope](https://img.shields.io/badge/👎%20Out%20of%20PR%20scope-white)](https://app.korbit.ai/feedback/9edd23fc-12be-4bab-b20e-f5a30f0430ae/fca53dff-99be-458c-a81a-a425087418c7?what_out_of_scope=true) [![Not in coding standard](https://img.shields.io/badge/👎%20Not%20in%20our%20standards-white)](https://app.korbit.ai/feedback/9edd23fc-12be-4bab-b20e-f5a30f0430ae/fca53dff-99be-458c-a81a-a425087418c7?what_not_in_standard=true) [![Other](https://img.shields.io/badge/👎%20Other-white)](https://app.korbit.ai/feedback/9edd23fc-12be-4bab-b20e-f5a30f0430ae/fca53dff-99be-458c-a81a-a425087418c7)
</details>

<sub>

💬 Looking for more details? Reply to this comment to chat with Korbit.
</sub>

<!--- korbi internal id:05b2b49f-895a-483a-b095-6fee970a4e37 -->


[](05b2b49f-895a-483a-b095-6fee970a4e37)
created_at: 2025-05-07T06:19:38Z
author_association: NONE
diff_hunk: @@ -0,0 +1,78 @@
+/**
+ * GitHub API wrapper for the issue labeling workflow
+ * Provides functions for fetching issue content and applying labels
+ */
+const axios = require('axios');
+const { token } = require('../.secrets/github');
+
+const BASE_URL = 'https://api.github.com';
+
+// Create an axios instance with default configuration
+const https = require('https');
+const github = axios.create({
+  baseURL: BASE_URL,
+  headers: {
+    Authorization: `token ${token}`,
+    Accept: 'application/vnd.github.v3+json',
+    'User-Agent': 'issue-labeler-app'
+  },
+  timeout: 5000,
+  httpsAgent: new https.Agent({ keepAlive: true })
+});
+
+/**
+ * Fetch issue content from GitHub
+ * @param {Object} options - Options object
+ * @param {number} options.issueNumber - The issue number to fetch
+ * @param {string} options.owner - Repository owner
+ * @param {string} options.repo - Repository name
+ * @returns {Promise<Object>} - Issue object with title and body
+ */
+async function getIssueContent({ issueNumber, owner, repo }) {
+  try {
+    const response = await github.get(`/repos/${owner}/${repo}/issues/${issueNumber}`);
+    return {
+      title: response.data.title || '',
+      body: response.data.body || '',
+      number: response.data.number
+    };
+  } catch (error) {
+    console.error(`Error fetching issue #${issueNumber}: ${error.message}`);
+    if (error.response) {
+      console.error('API Error:', error.response.status, error.response.statusText);
+      console.error('Error details:', error.response.data);
+    }
+    throw error;
+  }
+}
+
+/**
+ * Apply labels to a GitHub issue
+ * @param {Object} options - Options object
+ * @param {number} options.issueNumber - The issue number to add labels to
+ * @param {string} options.owner - Repository owner
+ * @param {string} options.repo - Repository name
+ * @param {Array<string>} options.labels - Array of label names to add
+ * @returns {Promise<Array>} - Array of labels on the issue after the operation
+ */
+async function applyLabels({ issueNumber, owner, repo, labels }) {
+  try {
+    const response = await github.post(
+      `/repos/${owner}/${repo}/issues/${issueNumber}/labels`,
+      { labels }
+    );
path: lib/github-wrapper.js
user: login: korbit-ai[bot]
body: ### Missing Labels Array Validation <sub>![category Functionality](https://img.shields.io/badge/Functionality-0284c7)</sub>

<details>
  <summary>Tell me more</summary>

###### What is the issue?
The applyLabels function doesn't validate the labels array before sending it to the GitHub API. Empty or invalid labels could cause API failures.

###### Why this matters
GitHub's API will reject requests with invalid label formats or empty arrays, causing the operation to fail silently or with unclear error messages. This affects the core functionality of the automated labeling system.

###### Suggested change ∙ *Feature Preview*
Add validation before making the API call:
```javascript
async function applyLabels({ issueNumber, owner, repo, labels }) {
  if (!Array.isArray(labels) || labels.length === 0) {
    throw new Error('Labels must be a non-empty array of strings');
  }
  if (!labels.every(label => typeof label === 'string' && label.trim())) {
    throw new Error('All labels must be non-empty strings');
  }
  try {
    const response = await github.post(
      `/repos/${owner}/${repo}/issues/${issueNumber}/labels`,
      { labels }
    );
    return response.data;
  } catch (error) {
    console.error(`Error applying labels to issue #${issueNumber}: ${error.message}`);
    if (error.response) {
      console.error('API Error:', error.response.status, error.response.statusText);
      console.error('Error details:', error.response.data);
    }
    throw error;
  }
}
```


###### Provide feedback to improve future suggestions
[![Nice Catch](https://img.shields.io/badge/👍%20Nice%20Catch-71BC78)](https://app.korbit.ai/feedback/9edd23fc-12be-4bab-b20e-f5a30f0430ae/b70ef448-f3bb-4ebf-ba9c-775d06ad4f79/upvote) [![Incorrect](https://img.shields.io/badge/👎%20Incorrect-white)](https://app.korbit.ai/feedback/9edd23fc-12be-4bab-b20e-f5a30f0430ae/b70ef448-f3bb-4ebf-ba9c-775d06ad4f79?what_not_true=true)  [![Not in Scope](https://img.shields.io/badge/👎%20Out%20of%20PR%20scope-white)](https://app.korbit.ai/feedback/9edd23fc-12be-4bab-b20e-f5a30f0430ae/b70ef448-f3bb-4ebf-ba9c-775d06ad4f79?what_out_of_scope=true) [![Not in coding standard](https://img.shields.io/badge/👎%20Not%20in%20our%20standards-white)](https://app.korbit.ai/feedback/9edd23fc-12be-4bab-b20e-f5a30f0430ae/b70ef448-f3bb-4ebf-ba9c-775d06ad4f79?what_not_in_standard=true) [![Other](https://img.shields.io/badge/👎%20Other-white)](https://app.korbit.ai/feedback/9edd23fc-12be-4bab-b20e-f5a30f0430ae/b70ef448-f3bb-4ebf-ba9c-775d06ad4f79)
</details>

<sub>

💬 Looking for more details? Reply to this comment to chat with Korbit.
</sub>

<!--- korbi internal id:d2fc628e-348b-48b9-809a-fde4d785d4f0 -->


[](d2fc628e-348b-48b9-809a-fde4d785d4f0)
created_at: 2025-05-07T06:19:38Z
author_association: NONE
diff_hunk: @@ -0,0 +1,78 @@
+/**
+ * GitHub API wrapper for the issue labeling workflow
+ * Provides functions for fetching issue content and applying labels
+ */
+const axios = require('axios');
+const { token } = require('../.secrets/github');
path: lib/github-wrapper.js
user: login: korbit-ai[bot]
body: ### Insecure credentials storage <sub>![category Security](https://img.shields.io/badge/Security-e11d48)</sub>

<details>
  <summary>Tell me more</summary>

###### What is the issue?
The GitHub authentication token is being imported from a local secrets file that could be accidentally committed to version control.

###### Why this matters
If the .secrets directory is not properly gitignored, sensitive credentials could be exposed in the repository history, leading to potential unauthorized access to the GitHub API.

###### Suggested change ∙ *Feature Preview*
Use environment variables instead of local files for sensitive credentials:
```javascript
const token = process.env.GITHUB_TOKEN;

if (!token) {
  throw new Error('GITHUB_TOKEN environment variable is required');
}
```


###### Provide feedback to improve future suggestions
[![Nice Catch](https://img.shields.io/badge/👍%20Nice%20Catch-71BC78)](https://app.korbit.ai/feedback/9edd23fc-12be-4bab-b20e-f5a30f0430ae/b31e6304-5b41-4550-9049-6a004c34fd58/upvote) [![Incorrect](https://img.shields.io/badge/👎%20Incorrect-white)](https://app.korbit.ai/feedback/9edd23fc-12be-4bab-b20e-f5a30f0430ae/b31e6304-5b41-4550-9049-6a004c34fd58?what_not_true=true)  [![Not in Scope](https://img.shields.io/badge/👎%20Out%20of%20PR%20scope-white)](https://app.korbit.ai/feedback/9edd23fc-12be-4bab-b20e-f5a30f0430ae/b31e6304-5b41-4550-9049-6a004c34fd58?what_out_of_scope=true) [![Not in coding standard](https://img.shields.io/badge/👎%20Not%20in%20our%20standards-white)](https://app.korbit.ai/feedback/9edd23fc-12be-4bab-b20e-f5a30f0430ae/b31e6304-5b41-4550-9049-6a004c34fd58?what_not_in_standard=true) [![Other](https://img.shields.io/badge/👎%20Other-white)](https://app.korbit.ai/feedback/9edd23fc-12be-4bab-b20e-f5a30f0430ae/b31e6304-5b41-4550-9049-6a004c34fd58)
</details>

<sub>

💬 Looking for more details? Reply to this comment to chat with Korbit.
</sub>

<!--- korbi internal id:fd3e072a-4e1c-4ced-8e2a-15bc1b6671f9 -->


[](fd3e072a-4e1c-4ced-8e2a-15bc1b6671f9)
created_at: 2025-05-07T06:19:38Z
author_association: NONE
diff_hunk: @@ -0,0 +1,78 @@
+/**
+ * GitHub API wrapper for the issue labeling workflow
+ * Provides functions for fetching issue content and applying labels
+ */
+const axios = require('axios');
+const { token } = require('../.secrets/github');
+
+const BASE_URL = 'https://api.github.com';
+
+// Create an axios instance with default configuration
+const https = require('https');
+const github = axios.create({
+  baseURL: BASE_URL,
+  headers: {
+    Authorization: `token ${token}`,
+    Accept: 'application/vnd.github.v3+json',
+    'User-Agent': 'issue-labeler-app'
+  },
+  timeout: 5000,
+  httpsAgent: new https.Agent({ keepAlive: true })
+});
+
+/**
+ * Fetch issue content from GitHub
+ * @param {Object} options - Options object
+ * @param {number} options.issueNumber - The issue number to fetch
+ * @param {string} options.owner - Repository owner
+ * @param {string} options.repo - Repository name
+ * @returns {Promise<Object>} - Issue object with title and body
+ */
+async function getIssueContent({ issueNumber, owner, repo }) {
+  try {
+    const response = await github.get(`/repos/${owner}/${repo}/issues/${issueNumber}`);
path: lib/github-wrapper.js
user: login: korbit-ai[bot]
body: ### Unvalidated URL parameter interpolation <sub>![category Security](https://img.shields.io/badge/Security-e11d48)</sub>

<details>
  <summary>Tell me more</summary>

###### What is the issue?
User-provided input (owner, repo, issueNumber) is directly interpolated into URL strings without validation or sanitization.

###### Why this matters
Path traversal or injection attacks could be possible if these parameters contain malicious values, potentially accessing unintended API endpoints.

###### Suggested change ∙ *Feature Preview*
Add input validation before using parameters in URLs:
```javascript
function validatePathSegment(segment) {
  if (!/^[\w.-]+$/.test(segment)) {
    throw new Error('Invalid path segment');
  }
  return segment;
}

const path = `/repos/${validatePathSegment(owner)}/${validatePathSegment(repo)}/issues/${Number(issueNumber)}`;
```


###### Provide feedback to improve future suggestions
[![Nice Catch](https://img.shields.io/badge/👍%20Nice%20Catch-71BC78)](https://app.korbit.ai/feedback/9edd23fc-12be-4bab-b20e-f5a30f0430ae/7f08208a-ef99-4546-96da-065af1f4c664/upvote) [![Incorrect](https://img.shields.io/badge/👎%20Incorrect-white)](https://app.korbit.ai/feedback/9edd23fc-12be-4bab-b20e-f5a30f0430ae/7f08208a-ef99-4546-96da-065af1f4c664?what_not_true=true)  [![Not in Scope](https://img.shields.io/badge/👎%20Out%20of%20PR%20scope-white)](https://app.korbit.ai/feedback/9edd23fc-12be-4bab-b20e-f5a30f0430ae/7f08208a-ef99-4546-96da-065af1f4c664?what_out_of_scope=true) [![Not in coding standard](https://img.shields.io/badge/👎%20Not%20in%20our%20standards-white)](https://app.korbit.ai/feedback/9edd23fc-12be-4bab-b20e-f5a30f0430ae/7f08208a-ef99-4546-96da-065af1f4c664?what_not_in_standard=true) [![Other](https://img.shields.io/badge/👎%20Other-white)](https://app.korbit.ai/feedback/9edd23fc-12be-4bab-b20e-f5a30f0430ae/7f08208a-ef99-4546-96da-065af1f4c664)
</details>

<sub>

💬 Looking for more details? Reply to this comment to chat with Korbit.
</sub>

<!--- korbi internal id:6d785467-deff-4828-94e8-491cc70464d5 -->


[](6d785467-deff-4828-94e8-491cc70464d5)
created_at: 2025-05-07T06:19:38Z
author_association: NONE
diff_hunk: @@ -0,0 +1,78 @@
+/**
+ * GitHub API wrapper for the issue labeling workflow
+ * Provides functions for fetching issue content and applying labels
+ */
path: lib/github-wrapper.js
user: login: korbit-ai[bot]
body: ### Unclear wrapper benefits <sub>![category Documentation](https://img.shields.io/badge/Documentation-7c3aed)</sub>

<details>
  <summary>Tell me more</summary>

###### What is the issue?
The file-level documentation doesn't explain when or why to use this wrapper instead of direct API calls.

###### Why this matters
Other developers may bypass this wrapper and implement their own GitHub API calls, missing out on the standardized error handling and configuration.

###### Suggested change ∙ *Feature Preview*
/**
 * GitHub API wrapper for the issue labeling workflow
 * Centralizes error handling and API configuration for reliable GitHub interactions.
 * Use this wrapper instead of direct API calls to ensure consistent error handling and rate limit management.
 */


###### Provide feedback to improve future suggestions
[![Nice Catch](https://img.shields.io/badge/👍%20Nice%20Catch-71BC78)](https://app.korbit.ai/feedback/9edd23fc-12be-4bab-b20e-f5a30f0430ae/fa092507-2ba0-4a07-af27-f1c362b9e2c8/upvote) [![Incorrect](https://img.shields.io/badge/👎%20Incorrect-white)](https://app.korbit.ai/feedback/9edd23fc-12be-4bab-b20e-f5a30f0430ae/fa092507-2ba0-4a07-af27-f1c362b9e2c8?what_not_true=true)  [![Not in Scope](https://img.shields.io/badge/👎%20Out%20of%20PR%20scope-white)](https://app.korbit.ai/feedback/9edd23fc-12be-4bab-b20e-f5a30f0430ae/fa092507-2ba0-4a07-af27-f1c362b9e2c8?what_out_of_scope=true) [![Not in coding standard](https://img.shields.io/badge/👎%20Not%20in%20our%20standards-white)](https://app.korbit.ai/feedback/9edd23fc-12be-4bab-b20e-f5a30f0430ae/fa092507-2ba0-4a07-af27-f1c362b9e2c8?what_not_in_standard=true) [![Other](https://img.shields.io/badge/👎%20Other-white)](https://app.korbit.ai/feedback/9edd23fc-12be-4bab-b20e-f5a30f0430ae/fa092507-2ba0-4a07-af27-f1c362b9e2c8)
</details>

<sub>

💬 Looking for more details? Reply to this comment to chat with Korbit.
</sub>

<!--- korbi internal id:83435a28-10dc-485e-8527-d18761b646a7 -->


[](83435a28-10dc-485e-8527-d18761b646a7)
created_at: 2025-05-07T06:19:38Z
author_association: NONE
diff_hunk: @@ -0,0 +1,78 @@
+/**
+ * GitHub API wrapper for the issue labeling workflow
+ * Provides functions for fetching issue content and applying labels
+ */
+const axios = require('axios');
+const { token } = require('../.secrets/github');
+
+const BASE_URL = 'https://api.github.com';
+
+// Create an axios instance with default configuration
+const https = require('https');
+const github = axios.create({
+  baseURL: BASE_URL,
+  headers: {
+    Authorization: `token ${token}`,
+    Accept: 'application/vnd.github.v3+json',
+    'User-Agent': 'issue-labeler-app'
+  },
+  timeout: 5000,
+  httpsAgent: new https.Agent({ keepAlive: true })
+});
+
+/**
+ * Fetch issue content from GitHub
+ * @param {Object} options - Options object
+ * @param {number} options.issueNumber - The issue number to fetch
+ * @param {string} options.owner - Repository owner
+ * @param {string} options.repo - Repository name
+ * @returns {Promise<Object>} - Issue object with title and body
+ */
+async function getIssueContent({ issueNumber, owner, repo }) {
+  try {
+    const response = await github.get(`/repos/${owner}/${repo}/issues/${issueNumber}`);
+    return {
+      title: response.data.title || '',
+      body: response.data.body || '',
+      number: response.data.number
+    };
+  } catch (error) {
+    console.error(`Error fetching issue #${issueNumber}: ${error.message}`);
+    if (error.response) {
+      console.error('API Error:', error.response.status, error.response.statusText);
+      console.error('Error details:', error.response.data);
+    }
path: lib/github-wrapper.js
user: login: korbit-ai[bot]
body: ### Fragmented Error Logging <sub>![category Logging](https://img.shields.io/badge/Logging-4f46e5)</sub>

<details>
  <summary>Tell me more</summary>

###### What is the issue?
Error logging uses multiple console.error calls, making log aggregation and parsing more difficult.

###### Why this matters
Multiple separate log entries for a single error event can complicate log analysis and make it harder to correlate related error information.

###### Suggested change ∙ *Feature Preview*
```javascript
console.error('Error fetching issue:', {
  issueNumber,
  errorMessage: error.message,
  status: error.response?.status,
  statusText: error.response?.statusText,
  details: error.response?.data
});
```


###### Provide feedback to improve future suggestions
[![Nice Catch](https://img.shields.io/badge/👍%20Nice%20Catch-71BC78)](https://app.korbit.ai/feedback/9edd23fc-12be-4bab-b20e-f5a30f0430ae/2fda7f2d-79aa-4a45-9035-b45b01370f3e/upvote) [![Incorrect](https://img.shields.io/badge/👎%20Incorrect-white)](https://app.korbit.ai/feedback/9edd23fc-12be-4bab-b20e-f5a30f0430ae/2fda7f2d-79aa-4a45-9035-b45b01370f3e?what_not_true=true)  [![Not in Scope](https://img.shields.io/badge/👎%20Out%20of%20PR%20scope-white)](https://app.korbit.ai/feedback/9edd23fc-12be-4bab-b20e-f5a30f0430ae/2fda7f2d-79aa-4a45-9035-b45b01370f3e?what_out_of_scope=true) [![Not in coding standard](https://img.shields.io/badge/👎%20Not%20in%20our%20standards-white)](https://app.korbit.ai/feedback/9edd23fc-12be-4bab-b20e-f5a30f0430ae/2fda7f2d-79aa-4a45-9035-b45b01370f3e?what_not_in_standard=true) [![Other](https://img.shields.io/badge/👎%20Other-white)](https://app.korbit.ai/feedback/9edd23fc-12be-4bab-b20e-f5a30f0430ae/2fda7f2d-79aa-4a45-9035-b45b01370f3e)
</details>

<sub>

💬 Looking for more details? Reply to this comment to chat with Korbit.
</sub>

<!--- korbi internal id:80b1bf5e-66fd-44c7-b71d-0d284f6223ea -->


[](80b1bf5e-66fd-44c7-b71d-0d284f6223ea)
created_at: 2025-05-07T06:19:38Z
author_association: NONE
diff_hunk: @@ -0,0 +1,78 @@
+/**
+ * GitHub API wrapper for the issue labeling workflow
+ * Provides functions for fetching issue content and applying labels
+ */
+const axios = require('axios');
+const { token } = require('../.secrets/github');
+
+const BASE_URL = 'https://api.github.com';
+
+// Create an axios instance with default configuration
+const https = require('https');
+const github = axios.create({
+  baseURL: BASE_URL,
+  headers: {
+    Authorization: `token ${token}`,
+    Accept: 'application/vnd.github.v3+json',
+    'User-Agent': 'issue-labeler-app'
+  },
+  timeout: 5000,
+  httpsAgent: new https.Agent({ keepAlive: true })
+});
+
+/**
+ * Fetch issue content from GitHub
+ * @param {Object} options - Options object
+ * @param {number} options.issueNumber - The issue number to fetch
+ * @param {string} options.owner - Repository owner
+ * @param {string} options.repo - Repository name
+ * @returns {Promise<Object>} - Issue object with title and body
+ */
+async function getIssueContent({ issueNumber, owner, repo }) {
+  try {
+    const response = await github.get(`/repos/${owner}/${repo}/issues/${issueNumber}`);
+    return {
+      title: response.data.title || '',
+      body: response.data.body || '',
+      number: response.data.number
+    };
+  } catch (error) {
+    console.error(`Error fetching issue #${issueNumber}: ${error.message}`);
+    if (error.response) {
+      console.error('API Error:', error.response.status, error.response.statusText);
+      console.error('Error details:', error.response.data);
+    }
+    throw error;
+  }
path: lib/github-wrapper.js
user: login: korbit-ai[bot]
body: ### Duplicated Error Handling Logic <sub>![category Design](https://img.shields.io/badge/Design-0d9488)</sub>

<details>
  <summary>Tell me more</summary>

###### What is the issue?
Duplicate error handling logic in both functions with mixed concerns of logging and error propagation.

###### Why this matters
Violates DRY principle and makes error handling changes harder to maintain across the codebase.

###### Suggested change ∙ *Feature Preview*
Extract error handling into a dedicated function:
```javascript
const handleGitHubError = (error, context) => {
  console.error(`Error ${context}: ${error.message}`);
  if (error.response) {
    console.error('API Error:', error.response.status, error.response.statusText);
    console.error('Error details:', error.response.data);
  }
  throw error;
};

// Usage in functions
} catch (error) {
  handleGitHubError(error, `fetching issue #${issueNumber}`);
}
```


###### Provide feedback to improve future suggestions
[![Nice Catch](https://img.shields.io/badge/👍%20Nice%20Catch-71BC78)](https://app.korbit.ai/feedback/9edd23fc-12be-4bab-b20e-f5a30f0430ae/b2ba3633-c4c8-4901-a9e2-fa09d94aeb12/upvote) [![Incorrect](https://img.shields.io/badge/👎%20Incorrect-white)](https://app.korbit.ai/feedback/9edd23fc-12be-4bab-b20e-f5a30f0430ae/b2ba3633-c4c8-4901-a9e2-fa09d94aeb12?what_not_true=true)  [![Not in Scope](https://img.shields.io/badge/👎%20Out%20of%20PR%20scope-white)](https://app.korbit.ai/feedback/9edd23fc-12be-4bab-b20e-f5a30f0430ae/b2ba3633-c4c8-4901-a9e2-fa09d94aeb12?what_out_of_scope=true) [![Not in coding standard](https://img.shields.io/badge/👎%20Not%20in%20our%20standards-white)](https://app.korbit.ai/feedback/9edd23fc-12be-4bab-b20e-f5a30f0430ae/b2ba3633-c4c8-4901-a9e2-fa09d94aeb12?what_not_in_standard=true) [![Other](https://img.shields.io/badge/👎%20Other-white)](https://app.korbit.ai/feedback/9edd23fc-12be-4bab-b20e-f5a30f0430ae/b2ba3633-c4c8-4901-a9e2-fa09d94aeb12)
</details>

<sub>

💬 Looking for more details? Reply to this comment to chat with Korbit.
</sub>

<!--- korbi internal id:9e96aaf2-f604-4045-84c3-4beabe3fc399 -->


[](9e96aaf2-f604-4045-84c3-4beabe3fc399)
created_at: 2025-05-07T06:19:38Z
author_association: NONE
diff_hunk: @@ -0,0 +1,81 @@
+/**
+ * GitHub Issue Automatic Labeling Script
+ * This script automates labeling of GitHub issues using GitHub's AI models
+ * It fetches issue content, uses a model to determine urgency and importance,
+ * and then applies those labels to the issue.
+ */
+const { getIssueContent, applyLabels } = require('./lib/github-wrapper');
+const { callModel } = require('./lib/github-model');
+const fs = require('fs');
+const path = require('path');
+
+// Read the prompt template
+const promptTemplate = fs.readFileSync(path.join(__dirname, 'prompt-template.txt'), 'utf8');
+
+/**
+ * Main function to process and label an issue
+ * @param {Object} options - Options object
+ * @param {number} options.issueNumber - The issue number to process
+ * @param {string} options.owner - Repository owner
+ * @param {string} options.repo - Repository name
+ */
+async function main({ issueNumber, owner, repo }) {
+  console.log(`Processing issue #${issueNumber} from ${owner}/${repo}...`);
+  
+  try {
+    // Step 1: Fetch issue content from GitHub
+    console.log(`Fetching content for issue #${issueNumber}...`);
+    const issue = await getIssueContent({ issueNumber, owner, repo });
+    console.log(`Issue #${issue.number} fetched: "${issue.title}"`);
+
+    // Step 2: Prepare the prompt by replacing the placeholders with actual issue content
+    const prompt = promptTemplate
+      .replace('{{title}}', issue.title)
+      .replace('{{body}}', issue.body || '');
+    
+    // Step 3: Call the GitHub Model to determine urgency and importance
+    console.log('Analyzing issue content with GitHub Model...');
+    const { urgency, importance } = await callModel({ prompt });
+    console.log(`Model inference complete: urgency=${urgency}, importance=${importance}`);
+
+    // Step 4: Create and apply labels
+    const labels = [];
+    if (urgency) labels.push(urgency);
+    if (importance) labels.push(importance);
+
+    if (labels.length > 0) {
+      console.log(`Applying labels to issue #${issueNumber}: ${labels.join(', ')}...`);
+      await applyLabels({ issueNumber, owner, repo, labels });
+      console.log(`Successfully labeled issue #${issueNumber} with: ${labels.join(', ')}`);
+    } else {
+      console.warn('No valid labels were determined. No labels applied to the issue.');
+    }
+  } catch (error) {
+    console.error(`Error processing issue #${issueNumber}: ${error.message}`);
+    process.exit(1);
+  }
path: label-issue.js
user: login: korbit-ai[bot]
body: ### Incomplete Error Logging <sub>![category Error Handling](https://img.shields.io/badge/Error%20Handling-ea580c)</sub>

<details>
  <summary>Tell me more</summary>

###### What is the issue?
The catch block only logs the error message, potentially losing valuable error context and stack trace information.

###### Why this matters
When debugging production issues, having only the error message without the full stack trace can significantly increase the time needed to diagnose and fix problems.

###### Suggested change ∙ *Feature Preview*
Enhance error logging to include the full error object and stack trace:
```javascript
catch (error) {
    console.error(`Error processing issue #${issueNumber}:`, error);
    process.exit(1);
}
```


###### Provide feedback to improve future suggestions
[![Nice Catch](https://img.shields.io/badge/👍%20Nice%20Catch-71BC78)](https://app.korbit.ai/feedback/9edd23fc-12be-4bab-b20e-f5a30f0430ae/9ae2f0f8-3be6-44ac-9bb3-98dbaf634c6f/upvote) [![Incorrect](https://img.shields.io/badge/👎%20Incorrect-white)](https://app.korbit.ai/feedback/9edd23fc-12be-4bab-b20e-f5a30f0430ae/9ae2f0f8-3be6-44ac-9bb3-98dbaf634c6f?what_not_true=true)  [![Not in Scope](https://img.shields.io/badge/👎%20Out%20of%20PR%20scope-white)](https://app.korbit.ai/feedback/9edd23fc-12be-4bab-b20e-f5a30f0430ae/9ae2f0f8-3be6-44ac-9bb3-98dbaf634c6f?what_out_of_scope=true) [![Not in coding standard](https://img.shields.io/badge/👎%20Not%20in%20our%20standards-white)](https://app.korbit.ai/feedback/9edd23fc-12be-4bab-b20e-f5a30f0430ae/9ae2f0f8-3be6-44ac-9bb3-98dbaf634c6f?what_not_in_standard=true) [![Other](https://img.shields.io/badge/👎%20Other-white)](https://app.korbit.ai/feedback/9edd23fc-12be-4bab-b20e-f5a30f0430ae/9ae2f0f8-3be6-44ac-9bb3-98dbaf634c6f)
</details>

<sub>

💬 Looking for more details? Reply to this comment to chat with Korbit.
</sub>

<!--- korbi internal id:41c0c433-da24-443f-9a25-603711235768 -->


[](41c0c433-da24-443f-9a25-603711235768)
created_at: 2025-05-07T06:19:38Z
author_association: NONE
diff_hunk: @@ -0,0 +1,81 @@
+/**
+ * GitHub Issue Automatic Labeling Script
+ * This script automates labeling of GitHub issues using GitHub's AI models
+ * It fetches issue content, uses a model to determine urgency and importance,
+ * and then applies those labels to the issue.
+ */
+const { getIssueContent, applyLabels } = require('./lib/github-wrapper');
+const { callModel } = require('./lib/github-model');
+const fs = require('fs');
+const path = require('path');
+
+// Read the prompt template
+const promptTemplate = fs.readFileSync(path.join(__dirname, 'prompt-template.txt'), 'utf8');
+
+/**
+ * Main function to process and label an issue
+ * @param {Object} options - Options object
+ * @param {number} options.issueNumber - The issue number to process
+ * @param {string} options.owner - Repository owner
+ * @param {string} options.repo - Repository name
+ */
+async function main({ issueNumber, owner, repo }) {
+  console.log(`Processing issue #${issueNumber} from ${owner}/${repo}...`);
+  
+  try {
+    // Step 1: Fetch issue content from GitHub
+    console.log(`Fetching content for issue #${issueNumber}...`);
+    const issue = await getIssueContent({ issueNumber, owner, repo });
+    console.log(`Issue #${issue.number} fetched: "${issue.title}"`);
+
+    // Step 2: Prepare the prompt by replacing the placeholders with actual issue content
+    const prompt = promptTemplate
+      .replace('{{title}}', issue.title)
+      .replace('{{body}}', issue.body || '');
+    
+    // Step 3: Call the GitHub Model to determine urgency and importance
+    console.log('Analyzing issue content with GitHub Model...');
+    const { urgency, importance } = await callModel({ prompt });
+    console.log(`Model inference complete: urgency=${urgency}, importance=${importance}`);
+
+    // Step 4: Create and apply labels
+    const labels = [];
+    if (urgency) labels.push(urgency);
+    if (importance) labels.push(importance);
+
+    if (labels.length > 0) {
+      console.log(`Applying labels to issue #${issueNumber}: ${labels.join(', ')}...`);
+      await applyLabels({ issueNumber, owner, repo, labels });
+      console.log(`Successfully labeled issue #${issueNumber} with: ${labels.join(', ')}`);
+    } else {
+      console.warn('No valid labels were determined. No labels applied to the issue.');
+    }
+  } catch (error) {
+    console.error(`Error processing issue #${issueNumber}: ${error.message}`);
+    process.exit(1);
+  }
+}
+
+// Parse command-line arguments
+if (require.main === module) {
+  const args = process.argv.slice(2);
+  const issueNumber = parseInt(args[0], 10);
+  
+  // Set default repository info or get from command line if provided
+  const owner = args[1] || 'dmitriz';
+  const repo = args[2] || 'issue-labeler';
+
+  if (!issueNumber) {
+    console.error('Usage: node label-issue.js <issue-number> [owner] [repo]');
+    process.exit(1);
+  }
+
+  main({ issueNumber, owner, repo })
+    .then(() => console.log('Issue labeling process completed successfully'))
+    .catch(err => {
+      console.error('Fatal error:', err.message);
+      process.exit(1);
+    });
path: label-issue.js
user: login: korbit-ai[bot]
body: ### Insufficient Top-level Error Handling <sub>![category Error Handling](https://img.shields.io/badge/Error%20Handling-ea580c)</sub>

<details>
  <summary>Tell me more</summary>

###### What is the issue?
The catch handler in the command-line execution block logs only the error message, losing the full error context.

###### Why this matters
Loss of error context in the top-level error handler makes it difficult to diagnose issues that bubble up from the main function.

###### Suggested change ∙ *Feature Preview*
Log the complete error object in the top-level catch handler:
```javascript
.catch(err => {
    console.error('Fatal error:', err);
    process.exit(1);
})
```


###### Provide feedback to improve future suggestions
[![Nice Catch](https://img.shields.io/badge/👍%20Nice%20Catch-71BC78)](https://app.korbit.ai/feedback/9edd23fc-12be-4bab-b20e-f5a30f0430ae/9d207617-c5ff-49c8-bd9f-854cfdc1a4e8/upvote) [![Incorrect](https://img.shields.io/badge/👎%20Incorrect-white)](https://app.korbit.ai/feedback/9edd23fc-12be-4bab-b20e-f5a30f0430ae/9d207617-c5ff-49c8-bd9f-854cfdc1a4e8?what_not_true=true)  [![Not in Scope](https://img.shields.io/badge/👎%20Out%20of%20PR%20scope-white)](https://app.korbit.ai/feedback/9edd23fc-12be-4bab-b20e-f5a30f0430ae/9d207617-c5ff-49c8-bd9f-854cfdc1a4e8?what_out_of_scope=true) [![Not in coding standard](https://img.shields.io/badge/👎%20Not%20in%20our%20standards-white)](https://app.korbit.ai/feedback/9edd23fc-12be-4bab-b20e-f5a30f0430ae/9d207617-c5ff-49c8-bd9f-854cfdc1a4e8?what_not_in_standard=true) [![Other](https://img.shields.io/badge/👎%20Other-white)](https://app.korbit.ai/feedback/9edd23fc-12be-4bab-b20e-f5a30f0430ae/9d207617-c5ff-49c8-bd9f-854cfdc1a4e8)
</details>

<sub>

💬 Looking for more details? Reply to this comment to chat with Korbit.
</sub>

<!--- korbi internal id:8e08c899-eff3-47b4-a8cd-0cdebdc54249 -->


[](8e08c899-eff3-47b4-a8cd-0cdebdc54249)
created_at: 2025-05-07T06:19:38Z
author_association: NONE
diff_hunk: @@ -0,0 +1,81 @@
+/**
+ * GitHub Issue Automatic Labeling Script
+ * This script automates labeling of GitHub issues using GitHub's AI models
+ * It fetches issue content, uses a model to determine urgency and importance,
+ * and then applies those labels to the issue.
+ */
+const { getIssueContent, applyLabels } = require('./lib/github-wrapper');
+const { callModel } = require('./lib/github-model');
+const fs = require('fs');
+const path = require('path');
+
+// Read the prompt template
+const promptTemplate = fs.readFileSync(path.join(__dirname, 'prompt-template.txt'), 'utf8');
path: label-issue.js
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

**Check for existence of prompt template file**

The code assumes prompt-template.txt exists but doesn't handle the case when it's missing.

```diff
-// Read the prompt template
-const promptTemplate = fs.readFileSync(path.join(__dirname, 'prompt-template.txt'), 'utf8');
+// Read the prompt template
+const promptTemplatePath = path.join(__dirname, 'prompt-template.txt');
+let promptTemplate;
+try {
+  promptTemplate = fs.readFileSync(promptTemplatePath, 'utf8');
+} catch (error) {
+  console.error(`Error reading prompt template: ${error.message}`);
+  process.exit(1);
+}
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
// Read the prompt template
const promptTemplatePath = path.join(__dirname, 'prompt-template.txt');
let promptTemplate;
try {
  promptTemplate = fs.readFileSync(promptTemplatePath, 'utf8');
} catch (error) {
  console.error(`Error reading prompt template: ${error.message}`);
  process.exit(1);
}
`````

</details>

<!-- suggestion_end -->

<details>
<summary>🤖 Prompt for AI Agents (early access)</summary>

```
In label-issue.js around lines 12 to 13, the code reads the prompt-template.txt file without checking if it exists, which can cause errors if the file is missing. Add a check before reading the file to verify that prompt-template.txt exists using fs.existsSync or a similar method. If the file does not exist, handle the error gracefully by logging an appropriate message and exiting or providing a fallback to prevent runtime failures.
```

</details>

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-05-07T06:20:43Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,81 @@
+/**
+ * GitHub Issue Automatic Labeling Script
+ * This script automates labeling of GitHub issues using GitHub's AI models
+ * It fetches issue content, uses a model to determine urgency and importance,
+ * and then applies those labels to the issue.
+ */
+const { getIssueContent, applyLabels } = require('./lib/github-wrapper');
+const { callModel } = require('./lib/github-model');
+const fs = require('fs');
+const path = require('path');
+
+// Read the prompt template
+const promptTemplate = fs.readFileSync(path.join(__dirname, 'prompt-template.txt'), 'utf8');
+
+/**
+ * Main function to process and label an issue
+ * @param {Object} options - Options object
+ * @param {number} options.issueNumber - The issue number to process
+ * @param {string} options.owner - Repository owner
+ * @param {string} options.repo - Repository name
+ */
+async function main({ issueNumber, owner, repo }) {
+  console.log(`Processing issue #${issueNumber} from ${owner}/${repo}...`);
+  
+  try {
+    // Step 1: Fetch issue content from GitHub
+    console.log(`Fetching content for issue #${issueNumber}...`);
+    const issue = await getIssueContent({ issueNumber, owner, repo });
+    console.log(`Issue #${issue.number} fetched: "${issue.title}"`);
+
+    // Step 2: Prepare the prompt by replacing the placeholders with actual issue content
+    const prompt = promptTemplate
+      .replace('{{title}}', issue.title)
+      .replace('{{body}}', issue.body || '');
+    
+    // Step 3: Call the GitHub Model to determine urgency and importance
+    console.log('Analyzing issue content with GitHub Model...');
+    const { urgency, importance } = await callModel({ prompt });
+    console.log(`Model inference complete: urgency=${urgency}, importance=${importance}`);
+
+    // Step 4: Create and apply labels
+    const labels = [];
+    if (urgency) labels.push(urgency);
+    if (importance) labels.push(importance);
+
+    if (labels.length > 0) {
+      console.log(`Applying labels to issue #${issueNumber}: ${labels.join(', ')}...`);
+      await applyLabels({ issueNumber, owner, repo, labels });
+      console.log(`Successfully labeled issue #${issueNumber} with: ${labels.join(', ')}`);
+    } else {
+      console.warn('No valid labels were determined. No labels applied to the issue.');
+    }
+  } catch (error) {
+    console.error(`Error processing issue #${issueNumber}: ${error.message}`);
+    process.exit(1);
+  }
+}
+
+// Parse command-line arguments
+if (require.main === module) {
+  const args = process.argv.slice(2);
+  const issueNumber = parseInt(args[0], 10);
+  
+  // Set default repository info or get from command line if provided
+  const owner = args[1] || 'dmitriz';
+  const repo = args[2] || 'issue-labeler';
+
path: label-issue.js
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

**Hard-coded repository values may cause issues**

Default repository values are hard-coded, which may cause problems if the script is used in different contexts.

```diff
// Set default repository info or get from command line if provided
-const owner = args[1] || 'dmitriz';
-const repo = args[2] || 'issue-labeler';
+const owner = args[1] || process.env.GITHUB_REPOSITORY_OWNER || 'dmitriz';
+const repo = args[2] || process.env.GITHUB_REPOSITORY?.split('/')[1] || 'issue-labeler';
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
// Set default repository info or get from command line if provided
const owner = args[1] || process.env.GITHUB_REPOSITORY_OWNER || 'dmitriz';
const repo = args[2] || process.env.GITHUB_REPOSITORY?.split('/')[1] || 'issue-labeler';
`````

</details>

<!-- suggestion_end -->

<details>
<summary>🤖 Prompt for AI Agents (early access)</summary>

```
In label-issue.js around lines 64 to 67, the repository owner and name are hard-coded as default values, which limits flexibility. Modify the code to accept these defaults from environment variables or a configuration file instead of hard-coding them. This allows the script to be used in different contexts without changing the source code.
```

</details>

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-05-07T06:20:44Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,78 @@
+/**
+ * GitHub API wrapper for the issue labeling workflow
+ * Provides functions for fetching issue content and applying labels
+ */
+const axios = require('axios');
+const { token } = require('../.secrets/github');
path: lib/github-wrapper.js
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

**Security concern: Token import method**

The token is imported directly from a file path, which is less secure than using environment variables.

```diff
const axios = require('axios');
-const { token } = require('../.secrets/github');
+// Use environment variables for sensitive credentials
+const token = process.env.GITHUB_TOKEN;
+
+if (!token) {
+  console.error('GITHUB_TOKEN environment variable is not set');
+  process.exit(1);
+}
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
const axios = require('axios');
// Use environment variables for sensitive credentials
const token = process.env.GITHUB_TOKEN;

if (!token) {
  console.error('GITHUB_TOKEN environment variable is not set');
  process.exit(1);
}
`````

</details>

<!-- suggestion_end -->

<details>
<summary>🤖 Prompt for AI Agents (early access)</summary>

```
In lib/github-wrapper.js around lines 5 to 6, the GitHub token is imported directly from a file, which poses a security risk. Refactor the code to load the token from an environment variable instead of a file. Replace the require statement with code that reads the token from process.env, ensuring the token is securely managed outside the codebase.
```

</details>

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-05-07T06:20:44Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,74 @@
+/**
+ * GitHub Model API wrapper for the issue labeling workflow
+ * Provides a function for model inference to determine issue labels
+ */
+const axios = require('axios');
+const { token } = require('../.secrets/gh-model');
+const config = require('../config/github-model');
+const yaml = require('js-yaml');
path: lib/github-model.js
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

**Consistent security pattern needed across files**

Similar to github-wrapper.js, sensitive credentials are imported directly from a file.

```diff
const axios = require('axios');
-const { token } = require('../.secrets/gh-model');
+// Use environment variables for sensitive credentials
+const token = process.env.GITHUB_MODEL_TOKEN;
+
+if (!token) {
+  console.error('GITHUB_MODEL_TOKEN environment variable is not set');
+  process.exit(1);
+}
const config = require('../config/github-model');
const yaml = require('js-yaml');
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
const axios = require('axios');
// Use environment variables for sensitive credentials
const token = process.env.GITHUB_MODEL_TOKEN;

if (!token) {
  console.error('GITHUB_MODEL_TOKEN environment variable is not set');
  process.exit(1);
}
const config = require('../config/github-model');
const yaml = require('js-yaml');
`````

</details>

<!-- suggestion_end -->

<details>
<summary>🤖 Prompt for AI Agents (early access)</summary>

```
In lib/github-model.js around lines 5 to 8, sensitive credentials like the token are imported directly from a file, which is inconsistent with the security pattern used in github-wrapper.js. Refactor the code to load sensitive credentials securely, for example by using environment variables or a centralized secure config module, instead of directly requiring them from a file. This will ensure consistent and safer handling of secrets across the codebase.
```

</details>

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-05-07T06:20:44Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,81 @@
+/**
+ * GitHub Issue Automatic Labeling Script
+ * This script automates labeling of GitHub issues using GitHub's AI models
+ * It fetches issue content, uses a model to determine urgency and importance,
+ * and then applies those labels to the issue.
+ */
+const { getIssueContent, applyLabels } = require('./lib/github-wrapper');
+const { callModel } = require('./lib/github-model');
+const fs = require('fs');
+const path = require('path');
+
+// Read the prompt template
+const promptTemplate = fs.readFileSync(path.join(__dirname, 'prompt-template.txt'), 'utf8');
+
+/**
+ * Main function to process and label an issue
+ * @param {Object} options - Options object
+ * @param {number} options.issueNumber - The issue number to process
+ * @param {string} options.owner - Repository owner
+ * @param {string} options.repo - Repository name
+ */
+async function main({ issueNumber, owner, repo }) {
+  console.log(`Processing issue #${issueNumber} from ${owner}/${repo}...`);
+  
+  try {
+    // Step 1: Fetch issue content from GitHub
+    console.log(`Fetching content for issue #${issueNumber}...`);
+    const issue = await getIssueContent({ issueNumber, owner, repo });
+    console.log(`Issue #${issue.number} fetched: "${issue.title}"`);
+
+    // Step 2: Prepare the prompt by replacing the placeholders with actual issue content
+    const prompt = promptTemplate
+      .replace('{{title}}', issue.title)
+      .replace('{{body}}', issue.body || '');
+    
+    // Step 3: Call the GitHub Model to determine urgency and importance
+    console.log('Analyzing issue content with GitHub Model...');
+    const { urgency, importance } = await callModel({ prompt });
+    console.log(`Model inference complete: urgency=${urgency}, importance=${importance}`);
+
+    // Step 4: Create and apply labels
+    const labels = [];
+    if (urgency) labels.push(urgency);
+    if (importance) labels.push(importance);
+
+    if (labels.length > 0) {
+      console.log(`Applying labels to issue #${issueNumber}: ${labels.join(', ')}...`);
+      await applyLabels({ issueNumber, owner, repo, labels });
+      console.log(`Successfully labeled issue #${issueNumber} with: ${labels.join(', ')}`);
+    } else {
+      console.warn('No valid labels were determined. No labels applied to the issue.');
+    }
+  } catch (error) {
+    console.error(`Error processing issue #${issueNumber}: ${error.message}`);
+    process.exit(1);
+  }
+}
+
+// Parse command-line arguments
+if (require.main === module) {
+  const args = process.argv.slice(2);
+  const issueNumber = parseInt(args[0], 10);
+  
+  // Set default repository info or get from command line if provided
+  const owner = args[1] || 'dmitriz';
+  const repo = args[2] || 'issue-labeler';
+
+  if (!issueNumber) {
+    console.error('Usage: node label-issue.js <issue-number> [owner] [repo]');
+    process.exit(1);
+  }
path: label-issue.js
user: login: codeant-ai[bot]
body: **Suggestion:** Validate the CLI input to ensure issue number is a positive integer. [Input Validation]
```suggestion
  const issueNumber = parseInt(args[0], 10);
  if (isNaN(issueNumber) || issueNumber <= 0) {
    console.error('Usage: node label-issue.js <issue-number> [owner] [repo] - issue number must be a positive integer');
    process.exit(1);
  }
```
created_at: 2025-05-07T06:21:10Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,81 @@
+/**
+ * GitHub Issue Automatic Labeling Script
+ * This script automates labeling of GitHub issues using GitHub's AI models
+ * It fetches issue content, uses a model to determine urgency and importance,
+ * and then applies those labels to the issue.
+ */
+const { getIssueContent, applyLabels } = require('./lib/github-wrapper');
+const { callModel } = require('./lib/github-model');
+const fs = require('fs');
+const path = require('path');
+
+// Read the prompt template
+const promptTemplate = fs.readFileSync(path.join(__dirname, 'prompt-template.txt'), 'utf8');
path: label-issue.js
user: login: codeant-ai[bot]
body: **Suggestion:** Replace the synchronous file read with an asynchronous file read within the async main function. [performance]
```suggestion
// Remove the synchronous file read at the top of the file.
// Inside the async main function (at its start), add:
const promptTemplate = await fs.promises.readFile(path.join(__dirname, 'prompt-template.txt'), 'utf8');
```
created_at: 2025-05-07T06:21:10Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,74 @@
+/**
+ * GitHub Model API wrapper for the issue labeling workflow
+ * Provides a function for model inference to determine issue labels
+ */
+const axios = require('axios');
+const { token } = require('../.secrets/gh-model');
+const config = require('../config/github-model');
+const yaml = require('js-yaml');
+
+/**
+ * Call GitHub model to determine issue labels
+ * @param {Object} options - Options object
+ * @param {string} options.prompt - The formatted prompt to send to the model
+ * @returns {Promise<Object>} - Object containing urgency and importance labels
+ */
+async function callModel({ prompt }) {
+  try {
+    const https = require('https');
+    const axiosInstance = axios.create({
+      httpsAgent: new https.Agent({ keepAlive: true }),
+      timeout: 10000
+    });
path: lib/github-model.js
user: login: codeant-ai[bot]
body: **Suggestion:** Move the axios instance creation outside of the callModel function to reuse the same instance. [performance]
```suggestion
    // At the top of the module (outside any function), add:
    const https = require('https');
    const axiosInstance = axios.create({
      httpsAgent: new https.Agent({ keepAlive: true }),
      timeout: 10000
    });
    // Then, inside callModel, remove any local instantiation of axiosInstance.
```
created_at: 2025-05-07T06:21:10Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,74 @@
+/**
+ * GitHub Model API wrapper for the issue labeling workflow
+ * Provides a function for model inference to determine issue labels
+ */
+const axios = require('axios');
+const { token } = require('../.secrets/gh-model');
+const config = require('../config/github-model');
+const yaml = require('js-yaml');
+
+/**
+ * Call GitHub model to determine issue labels
+ * @param {Object} options - Options object
+ * @param {string} options.prompt - The formatted prompt to send to the model
+ * @returns {Promise<Object>} - Object containing urgency and importance labels
+ */
+async function callModel({ prompt }) {
+  try {
+    const https = require('https');
+    const axiosInstance = axios.create({
+      httpsAgent: new https.Agent({ keepAlive: true }),
+      timeout: 10000
+    });
+
+    const response = await axiosInstance.post("https://models.github.ai/inference/chat/completions", {
+      messages: [
+        { role: "user", content: prompt }
+      ],
+      model: config.model,
+      temperature: config.temperature,
+      max_tokens: config.max_tokens
+    }, {
+      headers: {
+        "Authorization": `Bearer ${token}`,
+        "Content-Type": "application/json"
+      }
+    });
+
+    // Extract content from response
+    if (!response.data.choices || !response.data.choices[0]?.message?.content) {
+      throw new Error('Invalid response format from GitHub model API');
+    }
+
+    const rawContent = response.data.choices[0].message.content.trim();
+    
+    // Extract YAML block from the response
+    let yamlContent = rawContent;
+    const yamlMatch = rawContent.match(/```(?:yaml)?\s*([\s\S]*?)```/);
+    if (yamlMatch && yamlMatch[1]) {
+      yamlContent = yamlMatch[1].trim();
+    }
+    
+    // Parse the YAML content to get urgency and importance values
+    try {
+      const parsedYaml = yaml.load(yamlContent);
+      return {
+        urgency: parsedYaml.urgency || null,
+        importance: parsedYaml.importance || null
+      };
path: lib/github-model.js
user: login: codeant-ai[bot]
body: **Suggestion:** Trim and validate the parsed YAML label values to avoid empty or whitespace-only entries. [Data Sanitization]
```suggestion
      return {
        urgency: parsedYaml.urgency && parsedYaml.urgency.trim() !== '' ? parsedYaml.urgency.trim() : null,
        importance: parsedYaml.importance && parsedYaml.importance.trim() !== '' ? parsedYaml.importance.trim() : null
      };
```
created_at: 2025-05-07T06:21:10Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,74 @@
+/**
+ * GitHub Model API wrapper for the issue labeling workflow
+ * Provides a function for model inference to determine issue labels
+ */
+const axios = require('axios');
+const { token } = require('../.secrets/gh-model');
+const config = require('../config/github-model');
+const yaml = require('js-yaml');
+
+/**
+ * Call GitHub model to determine issue labels
+ * @param {Object} options - Options object
+ * @param {string} options.prompt - The formatted prompt to send to the model
+ * @returns {Promise<Object>} - Object containing urgency and importance labels
+ */
+async function callModel({ prompt }) {
+  try {
+    const https = require('https');
+    const axiosInstance = axios.create({
+      httpsAgent: new https.Agent({ keepAlive: true }),
+      timeout: 10000
+    });
+
+    const response = await axiosInstance.post("https://models.github.ai/inference/chat/completions", {
+      messages: [
+        { role: "user", content: prompt }
+      ],
+      model: config.model,
+      temperature: config.temperature,
+      max_tokens: config.max_tokens
+    }, {
+      headers: {
+        "Authorization": `Bearer ${token}`,
+        "Content-Type": "application/json"
+      }
+    });
+
+    // Extract content from response
+    if (!response.data.choices || !response.data.choices[0]?.message?.content) {
+      throw new Error('Invalid response format from GitHub model API');
+    }
+
+    const rawContent = response.data.choices[0].message.content.trim();
+    
+    // Extract YAML block from the response
+    let yamlContent = rawContent;
+    const yamlMatch = rawContent.match(/```(?:yaml)?\s*([\s\S]*?)```/);
+    if (yamlMatch && yamlMatch[1]) {
+      yamlContent = yamlMatch[1].trim();
+    }
+    
+    // Parse the YAML content to get urgency and importance values
+    try {
+      const parsedYaml = yaml.load(yamlContent);
path: lib/github-model.js
user: login: codeant-ai[bot]
body: **Suggestion:** Use a safe YAML schema while parsing the response to mitigate injection risks. [Security Parsing]
```suggestion
      const parsedYaml = yaml.load(yamlContent, { schema: yaml.JSON_SCHEMA });
```
created_at: 2025-05-07T06:21:10Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,74 @@
+/**
+ * GitHub Model API wrapper for the issue labeling workflow
+ * Provides a function for model inference to determine issue labels
+ */
+const axios = require('axios');
+const { token } = require('../.secrets/gh-model');
path: lib/github-model.js
user: login: codeant-ai[bot]
body: **Suggestion:** Load the GitHub model token from an environment variable for better secret management. [Secrets Management]
```suggestion
const token = process.env.GH_MODEL_TOKEN || require('../.secrets/gh-model').token;
```
created_at: 2025-05-07T06:21:10Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,74 @@
+/**
+ * GitHub Model API wrapper for the issue labeling workflow
+ * Provides a function for model inference to determine issue labels
+ */
+const axios = require('axios');
+const { token } = require('../.secrets/gh-model');
+const config = require('../config/github-model');
+const yaml = require('js-yaml');
+
+/**
+ * Call GitHub model to determine issue labels
+ * @param {Object} options - Options object
+ * @param {string} options.prompt - The formatted prompt to send to the model
+ * @returns {Promise<Object>} - Object containing urgency and importance labels
+ */
+async function callModel({ prompt }) {
+  try {
+    const https = require('https');
+    const axiosInstance = axios.create({
+      httpsAgent: new https.Agent({ keepAlive: true }),
+      timeout: 10000
+    });
+
+    const response = await axiosInstance.post("https://models.github.ai/inference/chat/completions", {
+      messages: [
+        { role: "user", content: prompt }
+      ],
+      model: config.model,
+      temperature: config.temperature,
+      max_tokens: config.max_tokens
+    }, {
+      headers: {
+        "Authorization": `Bearer ${token}`,
+        "Content-Type": "application/json"
+      }
+    });
+
+    // Extract content from response
+    if (!response.data.choices || !response.data.choices[0]?.message?.content) {
+      throw new Error('Invalid response format from GitHub model API');
+    }
+
+    const rawContent = response.data.choices[0].message.content.trim();
+    
+    // Extract YAML block from the response
+    let yamlContent = rawContent;
+    const yamlMatch = rawContent.match(/```(?:yaml)?\s*([\s\S]*?)```/);
+    if (yamlMatch && yamlMatch[1]) {
+      yamlContent = yamlMatch[1].trim();
+    }
+    
+    // Parse the YAML content to get urgency and importance values
+    try {
+      const parsedYaml = yaml.load(yamlContent);
+      return {
+        urgency: parsedYaml.urgency || null,
+        importance: parsedYaml.importance || null
+      };
+    } catch (parseError) {
+      console.error('Failed to parse YAML response:', parseError.message);
+      console.error('Raw response:', rawContent);
+      throw new Error('Failed to parse model response');
+    }
+  } catch (error) {
+    console.error('Error calling GitHub model:', error.message);
+    if (error.response) {
+      console.error('API Error:', error.response.status, error.response.statusText);
+      console.error('Error details:', error.response.data);
+    }
path: lib/github-model.js
user: login: codeant-ai[bot]
body: **Suggestion:** Remove detailed logging of API error responses to avoid leaking sensitive information. [Error Logging]
```suggestion
    if (error.response) {
      console.error('API Error:', error.response.status, error.response.statusText);
      // Detailed error logging removed to prevent exposure of sensitive information.
    }
```
created_at: 2025-05-07T06:21:10Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,78 @@
+/**
+ * GitHub API wrapper for the issue labeling workflow
+ * Provides functions for fetching issue content and applying labels
+ */
+const axios = require('axios');
+const { token } = require('../.secrets/github');
path: lib/github-wrapper.js
user: login: codeant-ai[bot]
body: **Suggestion:** Retrieve the GitHub API token from an environment variable to improve secret management. [Secrets Management]
```suggestion
const token = process.env.GITHUB_TOKEN || require('../.secrets/github').token;
```
created_at: 2025-05-07T06:21:10Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,78 @@
+/**
+ * GitHub API wrapper for the issue labeling workflow
+ * Provides functions for fetching issue content and applying labels
+ */
+const axios = require('axios');
+const { token } = require('../.secrets/github');
+
+const BASE_URL = 'https://api.github.com';
+
+// Create an axios instance with default configuration
+const https = require('https');
+const github = axios.create({
+  baseURL: BASE_URL,
+  headers: {
+    Authorization: `token ${token}`,
+    Accept: 'application/vnd.github.v3+json',
+    'User-Agent': 'issue-labeler-app'
+  },
+  timeout: 5000,
+  httpsAgent: new https.Agent({ keepAlive: true })
+});
+
+/**
+ * Fetch issue content from GitHub
+ * @param {Object} options - Options object
+ * @param {number} options.issueNumber - The issue number to fetch
+ * @param {string} options.owner - Repository owner
+ * @param {string} options.repo - Repository name
+ * @returns {Promise<Object>} - Issue object with title and body
+ */
+async function getIssueContent({ issueNumber, owner, repo }) {
+  try {
+    const response = await github.get(`/repos/${owner}/${repo}/issues/${issueNumber}`);
+    return {
+      title: response.data.title || '',
+      body: response.data.body || '',
+      number: response.data.number
+    };
+  } catch (error) {
+    console.error(`Error fetching issue #${issueNumber}: ${error.message}`);
+    if (error.response) {
+      console.error('API Error:', error.response.status, error.response.statusText);
+      console.error('Error details:', error.response.data);
+    }
+    throw error;
+  }
+}
+
+/**
+ * Apply labels to a GitHub issue
+ * @param {Object} options - Options object
+ * @param {number} options.issueNumber - The issue number to add labels to
+ * @param {string} options.owner - Repository owner
+ * @param {string} options.repo - Repository name
+ * @param {Array<string>} options.labels - Array of label names to add
+ * @returns {Promise<Array>} - Array of labels on the issue after the operation
+ */
+async function applyLabels({ issueNumber, owner, repo, labels }) {
+  try {
+    const response = await github.post(
+      `/repos/${owner}/${repo}/issues/${issueNumber}/labels`,
+      { labels }
+    );
+    return response.data;
+  } catch (error) {
+    console.error(`Error applying labels to issue #${issueNumber}: ${error.message}`);
+    if (error.response) {
+      console.error('API Error:', error.response.status, error.response.statusText);
+      console.error('Error details:', error.response.data);
+    }
path: lib/github-wrapper.js
user: login: codeant-ai[bot]
body: **Suggestion:** Remove detailed logging of full error response data to prevent exposure of sensitive information. [Error Logging]
```suggestion
    if (error.response) {
      console.error('API Error:', error.response.status, error.response.statusText);
      // Detailed error logging removed to prevent exposure of sensitive information.
    }
```
created_at: 2025-05-07T06:21:10Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,76 @@
+/**
+ * GitHub Model API wrapper for the issue labeling workflow
+ * Provides a function for model inference to determine issue labels
+ */
+const axios = require('axios');
+const { token } = require('../.secrets/gh-model');
+const config = require('../config/github-model');
+const yaml = require('js-yaml');
+
+/**
+ * Call GitHub model to determine issue labels
+ * @param {Object} options - Options object
+ * @param {string} options.prompt - The formatted prompt to send to the model
+ * @returns {Promise<Object>} - Object containing urgency and importance labels
+ */
+async function callModel({ prompt }) {
+  try {
+    const https = require('https');
+    const axiosInstance = axios.create({
+      httpsAgent: new https.Agent({ keepAlive: true }),
+      timeout: 10000
+    });
+
+    const response = await axiosInstance.post("https://models.github.ai/inference/chat/completions", {
path: lib/github-model.js
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

**Use configuration for API endpoint URL**

The API endpoint URL is hardcoded. Move this to the configuration file for better maintainability.


```diff
-    const response = await axiosInstance.post("https://models.github.ai/inference/chat/completions", {
+    const response = await axiosInstance.post(config.apiEndpoint, {
```

Add the endpoint URL to your config file:
```javascript
// In ../config/github-model.js
module.exports = {
  // ... existing config
  apiEndpoint: "https://models.github.ai/inference/chat/completions"
};
```

<details>
<summary>🤖 Prompt for AI Agents (early access)</summary>

```
In lib/github-model.js at line 24, the API endpoint URL is hardcoded in the axios post call. To improve maintainability, move this URL to the configuration file by adding an apiEndpoint property in ../config/github-model.js, then import and use this config value in place of the hardcoded string in the axios post request.
```

</details>

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-05-07T06:35:57Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,112 @@
+/**
+ * GitHub API wrapper for the issue labeling workflow
+ * Centralizes error handling and API configuration for reliable GitHub interactions.
+ * Use this wrapper instead of direct API calls to ensure consistent error handling and rate limit management.
+ */
+const axios = require('axios');
+const https = require('https');
+
+// Use environment variables for sensitive credentials
+const token = process.env.GITHUB_TOKEN;
+
+if (!token) {
+  console.error('GITHUB_TOKEN environment variable is not set');
+  process.exit(1);
+}
+
+const BASE_URL = 'https://api.github.com';
+const GITHUB_API_TIMEOUT_MS = 5000; // Timeout for GitHub API requests in milliseconds
+
+// Create an axios instance with default configuration
+function validatePathSegment(segment) {
+  if (!/^[\w.-]+$/.test(segment)) {
+    throw new Error('Invalid path segment');
+  }
+  return segment;
+}
+
+const github = axios.create({
+  baseURL: BASE_URL,
+  headers: {
+    Authorization: `token ${token}`,
+    Accept: 'application/vnd.github.v3+json',
+    'User-Agent': 'issue-labeler-app'
+  },
+  timeout: GITHUB_API_TIMEOUT_MS,
+  httpsAgent: new https.Agent({
+    keepAlive: true,
+    maxSockets: 100 // Limit the maximum concurrent connections
+  })
+});
+
+// Centralized error handling function
+function handleGitHubError(error, context) {
+  console.error(`Error ${context}: ${error.message}`);
+  if (error.response) {
+    console.error('API Error:', error.response.status, error.response.statusText);
+    console.error('Error details:', error.response.data);
+  }
+  throw error;
+}
+
+// Wrap the URL construction with validation
+function constructIssueUrl({ owner, repo, issueNumber }) {
+  return `/repos/${validatePathSegment(owner)}/${validatePathSegment(repo)}/issues/${Number(issueNumber)}`;
+}
path: lib/github-wrapper.js
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

**URL construction with validation is created but unused**

A secure URL construction function is defined but not used in the API methods below.

Apply this diff to use the secure URL construction function:

```diff
async function getIssueContent({ issueNumber, owner, repo }) {
  try {
-    const response = await github.get(`/repos/${owner}/${repo}/issues/${issueNumber}`);
+    const response = await github.get(constructIssueUrl({ owner, repo, issueNumber }));
    return {
      title: response.data.title || '',
      body: response.data.body || '',
      number: response.data.number
    };
  } catch (error) {
    handleGitHubError(error, `fetching issue #${issueNumber}`);
  }
}
```

<details>
<summary>🤖 Prompt for AI Agents (early access)</summary>

```
In lib/github-wrapper.js around lines 52 to 55, the function constructIssueUrl
is defined to securely build issue URLs with validation but is not used in the
API methods that construct issue URLs. Update all relevant API methods to
replace direct URL string construction with calls to constructIssueUrl, passing
the appropriate owner, repo, and issueNumber parameters to ensure consistent and
validated URL generation.
```

</details>

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-05-07T06:54:23Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,104 @@
+/**
+ * GitHub Issue Automatic Labeling Script
+ * This script automates labeling of GitHub issues using GitHub's AI models
+ * It fetches issue content, uses a model to determine urgency and importance,
+ * and then applies those labels to the issue.
+ */
+const { getIssueContent, applyLabels } = require('./lib/github-wrapper');
+const { callModel } = require('./lib/github-model');
+const fs = require('fs');
+const path = require('path');
+const { Command } = require('commander');
+
+// Read the prompt template
+// Remove the synchronous file read at the top of the file.
+// Inside the async main function (at its start), add:
+const promptTemplate = await fs.promises.readFile(path.join(__dirname, 'prompt-template.txt'), 'utf8');
+let promptTemplate;
+try {
+  promptTemplate = fs.readFileSync(promptTemplatePath, 'utf8');
+} catch (error) {
+  console.error(`Error reading prompt template: ${error.message}`);
+  process.exit(1);
+}
+// Inside the async main function (at its start), add:
+promptTemplate = await fs.promises.readFile(path.join(__dirname, 'prompt-template.txt'), 'utf8');
path: label-issue.js
user: login: coderabbitai[bot]
body: _⚠️ Potential issue_

**Fix prompt template loading logic**

The code for loading the prompt template has several critical issues:
1. Attempting to use `await` outside an async function
2. Redeclaring `promptTemplate` multiple times
3. Using an undefined `promptTemplatePath` variable
4. Mixing different file loading approaches


Replace this entire block with a cleaner implementation:

```diff
-// Read the prompt template
-// Remove the synchronous file read at the top of the file.
-// Inside the async main function (at its start), add:
-const promptTemplate = await fs.promises.readFile(path.join(__dirname, 'prompt-template.txt'), 'utf8');
-let promptTemplate;
-try {
-  promptTemplate = fs.readFileSync(promptTemplatePath, 'utf8');
-} catch (error) {
-  console.error(`Error reading prompt template: ${error.message}`);
-  process.exit(1);
-}
-// Inside the async main function (at its start), add:
-promptTemplate = await fs.promises.readFile(path.join(__dirname, 'prompt-template.txt'), 'utf8');
+// Define the path to the prompt template
+const promptTemplatePath = path.join(__dirname, 'prompt-template.txt');
```

Then move the file reading into the `main` function:

```diff
async function main({ issueNumber, owner, repo }) {
  console.log(`Processing issue #${issueNumber} from ${owner}/${repo}...`);
  
  try {
+    // Read the prompt template
+    console.log('Loading prompt template...');
+    const promptTemplate = await fs.promises.readFile(promptTemplatePath, 'utf8');
+    
    // Step 1: Fetch issue content from GitHub
    console.log(`Fetching content for issue #${issueNumber}...`);
```

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 Biome (1.9.4)</summary>

[error] 17-17: Shouldn't redeclare 'promptTemplate'. Consider to delete it or rename it.

'promptTemplate' is defined here:


(lint/suspicious/noRedeclare)

</details>

</details>

<details>
<summary>🤖 Prompt for AI Agents (early access)</summary>

```
In label-issue.js around lines 13 to 25, the prompt template loading code
incorrectly uses await outside an async function, redeclares the promptTemplate
variable multiple times, references an undefined promptTemplatePath, and mixes
synchronous and asynchronous file reading methods. To fix this, remove all
prompt template reading code from the top level and instead, inside the async
main function at its start, declare a single promptTemplate variable and assign
it the result of awaiting fs.promises.readFile with the correct path. Wrap this
in a try-catch block to handle errors by logging and exiting the process.
```

</details>

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-05-07T06:54:23Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,82 @@
+/**
+ * GitHub Model API wrapper for the issue labeling workflow
+ * Provides a function for model inference to determine issue labels
+ */
+const axios = require('axios');
+const token = process.env.GITHUB_MODEL_TOKEN || require('../.secrets/gh-model').token;
+
+if (!token) {
+  console.error('GITHUB_MODEL_TOKEN environment variable is not set');
+  process.exit(1);
+}
+const config = require('../config/github-model');
+const yaml = require('js-yaml');
+
+// Create a reusable axios instance with keepAlive enabled
+const https = require('https');
+const https = require('https');
+const axiosInstance = axios.create({
+  httpsAgent: new https.Agent({ keepAlive: true }),
+  timeout: 10000
+});
+
+/**
+ * Calls the GitHub model API for inference.
+ * @param {string} prompt - The input prompt for the model.
+ * @returns {Object} - Object containing urgency and importance values.
+ */
+async function callModel(prompt) {
path: lib/github-model.js
user: login: coderabbitai[bot]
body: _⚠️ Potential issue_

**Function parameter is incorrect**

The `callModel` function's parameter should be an object with a prompt property.

Apply this diff to fix the function parameter:

```diff
-async function callModel(prompt) {
+async function callModel({ prompt }) {
```

This matches how the function is called in `label-issue.js`:
```javascript
const { urgency, importance } = await callModel({ prompt });
```

<details>
<summary>🤖 Prompt for AI Agents (early access)</summary>

```
In lib/github-model.js at line 28, the callModel function currently takes a
single parameter but it should accept an object with a prompt property. Change
the function signature to destructure the prompt from an object parameter, i.e.,
update it to async function callModel({ prompt }) to match how it is called in
label-issue.js.
```

</details>

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-05-07T06:54:23Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,82 @@
+/**
+ * GitHub Model API wrapper for the issue labeling workflow
+ * Provides a function for model inference to determine issue labels
+ */
+const axios = require('axios');
+const token = process.env.GITHUB_MODEL_TOKEN || require('../.secrets/gh-model').token;
+
+if (!token) {
+  console.error('GITHUB_MODEL_TOKEN environment variable is not set');
+  process.exit(1);
+}
+const config = require('../config/github-model');
+const yaml = require('js-yaml');
+
+// Create a reusable axios instance with keepAlive enabled
+const https = require('https');
+const https = require('https');
+const axiosInstance = axios.create({
+  httpsAgent: new https.Agent({ keepAlive: true }),
+  timeout: 10000
+});
+
+/**
+ * Calls the GitHub model API for inference.
+ * @param {string} prompt - The input prompt for the model.
+ * @returns {Object} - Object containing urgency and importance values.
+ */
+async function callModel(prompt) {
+  try {
+    const response = await axiosInstance.post("https://models.github.ai/inference/chat/completions", {
+      messages: [
+        { role: "user", content: prompt }
+      ],
+      model: config.model,
+      temperature: config.temperature,
+      max_tokens: config.max_tokens
+    }, {
+      headers: {
+        "Authorization": `Bearer ${token}`,
+        "Content-Type": "application/json"
+      }
+    });
path: lib/github-model.js
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

**Avoid repeating headers in each request and extract API endpoint to config**

The headers are defined in the axios instance creation but then repeated in each request. Also, the API endpoint is hardcoded.

Apply this diff to move the API endpoint to config and avoid repeating headers:

```diff
async function callModel(prompt) {
  try {
-    const response = await axiosInstance.post("https://models.github.ai/inference/chat/completions", {
+    const response = await axiosInstance.post(config.apiEndpoint, {
      messages: [
        { role: "user", content: prompt }
      ],
      model: config.model,
      temperature: config.temperature,
      max_tokens: config.max_tokens
-    }, {
-      headers: {
-        "Authorization": `Bearer ${token}`,
-        "Content-Type": "application/json"
-      }
     });
```

Then add to your config file:
```javascript
// In ../config/github-model.js
module.exports = {
  // ... existing config
  apiEndpoint: "https://models.github.ai/inference/chat/completions"
};
```

<details>
<summary>🤖 Prompt for AI Agents (early access)</summary>

```
In lib/github-model.js around lines 28 to 42, the API endpoint URL is hardcoded
and the headers are redundantly specified in the request instead of using the
axios instance defaults. To fix this, move the API endpoint URL to the config
file as config.apiEndpoint, then update the axiosInstance.post call to use
config.apiEndpoint instead of the hardcoded string. Also, remove the headers
object from the post request options so the axios instance's default headers are
used.
```

</details>

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-05-07T06:54:23Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,82 @@
+/**
+ * GitHub Model API wrapper for the issue labeling workflow
+ * Provides a function for model inference to determine issue labels
+ */
+const axios = require('axios');
+const token = process.env.GITHUB_MODEL_TOKEN || require('../.secrets/gh-model').token;
+
+if (!token) {
+  console.error('GITHUB_MODEL_TOKEN environment variable is not set');
+  process.exit(1);
+}
+const config = require('../config/github-model');
+const yaml = require('js-yaml');
+
+// Create a reusable axios instance with keepAlive enabled
+const https = require('https');
+const https = require('https');
+const axiosInstance = axios.create({
+  httpsAgent: new https.Agent({ keepAlive: true }),
+  timeout: 10000
+});
+
+/**
+ * Calls the GitHub model API for inference.
+ * @param {string} prompt - The input prompt for the model.
+ * @returns {Object} - Object containing urgency and importance values.
+ */
+async function callModel(prompt) {
+  try {
+    const response = await axiosInstance.post("https://models.github.ai/inference/chat/completions", {
+      messages: [
+        { role: "user", content: prompt }
+      ],
+      model: config.model,
+      temperature: config.temperature,
+      max_tokens: config.max_tokens
+    }, {
+      headers: {
+        "Authorization": `Bearer ${token}`,
+        "Content-Type": "application/json"
+      }
+    });
+
+    // Extract content from response
+    if (!response.data.choices || !response.data.choices[0]?.message?.content) {
+      throw new Error('Invalid response format from GitHub model API');
+    }
+
+    const rawContent = response.data.choices[0].message.content.trim();
+    
+    // Extract YAML block from the response
+    let yamlContent = rawContent;
+    const yamlMatch = rawContent.match(/```(?:`yaml`)?\s*([\s\S]*?)```/);
+      const parsedYaml = yaml.load(yamlContent, { schema: yaml.JSON_SCHEMA });
+      return {
+        urgency: parsedYaml.urgency && parsedYaml.urgency.trim() !== '' ? parsedYaml.urgency.trim() : null,
+        importance: parsedYaml.importance && parsedYaml.importance.trim() !== '' ? parsedYaml.importance.trim() : null
+      };
+    try {
+      const parsedYaml = yaml.load(yamlContent);
+      return {
+        urgency: parsedYaml.urgency || null,
+        importance: parsedYaml.importance || null
+      };
+    } catch (parseError) {
+      console.error('Failed to parse YAML response:', parseError.message);
+      console.error('Raw response:', rawContent);
+      throw new Error('Failed to parse model response');
+    }
path: lib/github-model.js
user: login: coderabbitai[bot]
body: _⚠️ Potential issue_

**Fix unreachable code and inconsistent YAML parsing logic**

There's unreachable code due to an early return and inconsistent YAML parsing logic.

Apply this diff to fix the YAML parsing logic:

```diff
    // Extract YAML block from the response
    let yamlContent = rawContent;
    const yamlMatch = rawContent.match(/```(?:`yaml`)?\s*([\s\S]*?)```/);
-      const parsedYaml = yaml.load(yamlContent, { schema: yaml.JSON_SCHEMA });
-      return {
-        urgency: parsedYaml.urgency && parsedYaml.urgency.trim() !== '' ? parsedYaml.urgency.trim() : null,
-        importance: parsedYaml.importance && parsedYaml.importance.trim() !== '' ? parsedYaml.importance.trim() : null
-      };
+    
+    // If there's a YAML code block, extract just that content
+    if (yamlMatch && yamlMatch[1]) {
+      yamlContent = yamlMatch[1].trim();
+    }
+    
    try {
-      const parsedYaml = yaml.load(yamlContent);
+      const parsedYaml = yaml.load(yamlContent, { schema: yaml.JSON_SCHEMA });
      return {
-        urgency: parsedYaml.urgency || null,
-        importance: parsedYaml.importance || null
+        urgency: parsedYaml.urgency && parsedYaml.urgency.trim() !== '' ? parsedYaml.urgency.trim() : null,
+        importance: parsedYaml.importance && parsedYaml.importance.trim() !== '' ? parsedYaml.importance.trim() : null
      };
    } catch (parseError) {
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
// Extract YAML block from the response
    let yamlContent = rawContent;
    const yamlMatch = rawContent.match(/
`````

</details>

<!-- suggestion_end -->

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 Biome (1.9.4)</summary>

[error] 59-69: This code is unreachable

... because either this statement will throw an exception ...

... or this statement will return from the function beforehand


(lint/correctness/noUnreachable)

</details>

</details>

<details>
<summary>🤖 Prompt for AI Agents (early access)</summary>

```
In lib/github-model.js between lines 51 and 69, the current code has unreachable
code due to an early return and inconsistent YAML parsing logic. To fix this,
remove the early return after matching the YAML block and instead check if the
YAML code block exists; if so, extract and trim that content to yamlContent.
Then, inside a try-catch block, parse yamlContent using yaml.load with the
JSON_SCHEMA option. Finally, return the urgency and importance fields after
trimming and validating they are non-empty strings, or null otherwise. This
ensures consistent parsing and proper error handling without unreachable code.
```

</details>

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-05-07T06:54:23Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,82 @@
+/**
+ * GitHub Model API wrapper for the issue labeling workflow
+ * Provides a function for model inference to determine issue labels
+ */
+const axios = require('axios');
+const token = process.env.GITHUB_MODEL_TOKEN || require('../.secrets/gh-model').token;
+
+if (!token) {
+  console.error('GITHUB_MODEL_TOKEN environment variable is not set');
+  process.exit(1);
+}
+const config = require('../config/github-model');
+const yaml = require('js-yaml');
+
+// Create a reusable axios instance with keepAlive enabled
+const https = require('https');
+const https = require('https');
+const axiosInstance = axios.create({
+  httpsAgent: new https.Agent({ keepAlive: true }),
+  timeout: 10000
+});
path: lib/github-model.js
user: login: coderabbitai[bot]
body: _⚠️ Potential issue_

**Fix duplicate https import and improve axios instance configuration**

There's a duplicate import of the https module, and the axios instance setup should be enhanced.

Apply this diff to fix these issues:

```diff
// Create a reusable axios instance with keepAlive enabled
const https = require('https');
-const https = require('https');
const axiosInstance = axios.create({
  httpsAgent: new https.Agent({ keepAlive: true }),
-  timeout: 10000
+  timeout: 10000,
+  headers: {
+    "Authorization": `Bearer ${token}`,
+    "Content-Type": "application/json"
+  }
});
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
// Create a reusable axios instance with keepAlive enabled
const https = require('https');
const axiosInstance = axios.create({
  httpsAgent: new https.Agent({ keepAlive: true }),
  timeout: 10000,
  headers: {
    "Authorization": `Bearer ${token}`,
    "Content-Type": "application/json"
  }
});
`````

</details>

<!-- suggestion_end -->

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 Biome (1.9.4)</summary>

[error] 17-17: Shouldn't redeclare 'https'. Consider to delete it or rename it.

'https' is defined here:


(lint/suspicious/noRedeclare)

</details>

</details>

<details>
<summary>🤖 Prompt for AI Agents (early access)</summary>

```
In lib/github-model.js around lines 15 to 21, remove the duplicate import of the
https module to avoid redundancy. Then, improve the axios instance configuration
by ensuring the httpsAgent is correctly set with keepAlive enabled and consider
adding any missing relevant options such as headers or baseURL if needed for
better reuse and performance.
```

</details>

<!-- This is an auto-generated comment by CodeRabbit -->

✅ Addressed in commits 2c9a72b to ff1454d
created_at: 2025-05-07T06:54:24Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,82 @@
+/**
+ * GitHub Model API wrapper for the issue labeling workflow
+ * Provides a function for model inference to determine issue labels
+ */
+const axios = require('axios');
+const token = process.env.GITHUB_MODEL_TOKEN || require('../.secrets/gh-model').token;
+
+if (!token) {
+  console.error('GITHUB_MODEL_TOKEN environment variable is not set');
+  process.exit(1);
+}
+const config = require('../config/github-model');
+const yaml = require('js-yaml');
+
+// Create a reusable axios instance with keepAlive enabled
+const https = require('https');
+const https = require('https');
+const axiosInstance = axios.create({
+  httpsAgent: new https.Agent({ keepAlive: true }),
+  timeout: 10000
+});
+
+/**
+ * Calls the GitHub model API for inference.
+ * @param {string} prompt - The input prompt for the model.
+ * @returns {Object} - Object containing urgency and importance values.
+ */
+async function callModel(prompt) {
+  try {
+    const response = await axiosInstance.post(config.apiEndpoint, {
+      messages: [
+      { role: "user", content: prompt }
+      ],
+      model: config.model,
+      temperature: config.temperature,
+      max_tokens: config.max_tokens
+    }, {
+      headers: {
+      "Authorization": `Bearer ${token}`,
+      "Content-Type": "application/json"
+      }
+    });
path: lib/github-model.js
user: login: coderabbitai[bot]
body: _💡 Verification agent_

<details>
<summary>🧩 Analysis chain</summary>

**Verify GitHub Model API configuration**

Ensure that the model configuration parameters being used are appropriate for the task of labeling issues.

---

🏁 Script executed:

```shell
#!/bin/bash
# Verify config file contents
if [ -f "../config/github-model.js" ]; then
  echo "Config file exists. Checking contents:"
  cat "../config/github-model.js"
else
  echo "Config file does not exist."
fi

# Check if the API endpoint is reachable
# Note: We're only checking if the URL format is valid, not making an actual request
if grep -q "apiEndpoint" "../config/github-model.js"; then
  echo "apiEndpoint is defined in config file."
else
  echo "apiEndpoint is not defined in config file."
fi
```

Length of output: 305

---

I need to locate where the model’s config is defined. Let’s search the repo for any config files and `apiEndpoint` references:

---

🏁 Script executed:

```shell
#!/bin/bash
set -e

echo "1. Finding all files with “config” in their path:"
fd --type f --ignore-case config || find . -type f | grep -i config

echo
echo "2. Searching for any occurrences of “apiEndpoint”:"
rg --with-filename --line-number "apiEndpoint"

echo
echo "3. Checking how ‘config’ is imported in lib/github-model.js:"
rg --with-filename --line-number "require.*config" -n lib/github-model.js
```

Length of output: 724

---

Let’s inspect the actual config files to confirm where `apiEndpoint` is defined and how it’s exported:

---

🏁 Script executed:

```shell
#!/bin/bash
set -e

echo "----- config.json -----"
cat config.json || echo "(not found)"

echo
echo "----- test-config.js -----"
cat test-config.js || echo "(not found)"
```

Length of output: 399

---



</details>

**Fix missing GitHub model config and include `apiEndpoint`**

At lib/github-model.js you’re doing:
```js
const config = require('../config/github-model');
```
—but there is no `config/github-model.js`. You need to either:

• Create config/github-model.js that exports at least:
```js
module.exports = {
  apiEndpoint: 'https://api.openai.com/v1/chat/completions',
  model: 'gpt-4',
  temperature: 0.3,
  max_tokens: 1000,
  token: process.env.GITHUB_MODEL_TOKEN
};
```
• Or update the import to pull these values from your existing config.json (and ensure it contains `apiEndpoint`, `token`, etc.).

Next steps:
- [ ] Add or rename the config file at config/github-model.js  
- [ ] Populate it with `apiEndpoint`, `model`, `temperature`, `max_tokens`, and auth token  
- [ ] Verify lib/github-model.js uses the correct path  

This will prevent runtime errors and ensure the model call is properly configured.

<details>
<summary>🤖 Prompt for AI Agents (early access)</summary>

```
In lib/github-model.js around lines 30 to 42, the code imports a config from
'../config/github-model' which does not exist, causing runtime errors and
missing API configuration. To fix this, create a new config file at
config/github-model.js exporting an object with keys: apiEndpoint (e.g.,
'https://api.openai.com/v1/chat/completions'), model (e.g., 'gpt-4'),
temperature (e.g., 0.3), max_tokens (e.g., 1000), and token (from
process.env.GITHUB_MODEL_TOKEN). Alternatively, update the import to use an
existing config file that contains these properties. Ensure lib/github-model.js
references the correct config path and that all required config values are
present to properly configure the model API call.
```

</details>

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-05-07T06:56:45Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,112 @@
+/**
+ * GitHub API wrapper for the issue labeling workflow
+ * Centralizes error handling and API configuration for reliable GitHub interactions.
+ * Use this wrapper instead of direct API calls to ensure consistent error handling and rate limit management.
+ */
+const axios = require('axios');
+const https = require('https');
+
+// Use environment variables for sensitive credentials
+const token = process.env.GITHUB_TOKEN;
+
+if (!token) {
+  console.error('GITHUB_TOKEN environment variable is not set');
+  process.exit(1);
+}
+
+const BASE_URL = 'https://api.github.com';
+const GITHUB_API_TIMEOUT_MS = 5000; // Timeout for GitHub API requests in milliseconds
+
+// Create an axios instance with default configuration
+function validatePathSegment(segment) {
path: lib/github-wrapper.js
user: login: codiumai-pr-agent-free[bot]
body: **Suggestion:** Update GitHub API authentication
```suggestion
const github = axios.create({
  baseURL: BASE_URL,
  headers: {
    Authorization: `Bearer ${token}`,
    Accept: 'application/vnd.github.v3+json',
    'User-Agent': 'issue-labeler-app'
  },
  timeout: 5000,
  httpsAgent: new https.Agent({ keepAlive: true })
});
```

<!-- manually_applied -->
created_at: 2025-05-07T07:05:57Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,112 @@
+/**
+ * GitHub API wrapper for the issue labeling workflow
+ * Centralizes error handling and API configuration for reliable GitHub interactions.
+ * Use this wrapper instead of direct API calls to ensure consistent error handling and rate limit management.
+ */
+const axios = require('axios');
+const https = require('https');
+
+// Use environment variables for sensitive credentials
+const token = process.env.GITHUB_TOKEN;
+
+if (!token) {
+  console.error('GITHUB_TOKEN environment variable is not set');
+  process.exit(1);
+}
+
+const BASE_URL = 'https://api.github.com';
+const GITHUB_API_TIMEOUT_MS = 5000; // Timeout for GitHub API requests in milliseconds
+
+// Create an axios instance with default configuration
+function validatePathSegment(segment) {
path: lib/github-wrapper.js
user: login: qodo-code-review[bot]
body: **Suggestion:** Update GitHub API authentication
```suggestion
const github = axios.create({
  baseURL: BASE_URL,
  headers: {
    Authorization: `Bearer ${token}`,
    Accept: 'application/vnd.github.v3+json',
    'User-Agent': 'issue-labeler-app'
  },
  timeout: 5000,
  httpsAgent: new https.Agent({ keepAlive: true })
});
```

<!-- manually_applied -->
created_at: 2025-05-07T07:05:57Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,104 @@
+/**
+ * GitHub Issue Automatic Labeling Script
+ * This script automates labeling of GitHub issues using GitHub's AI models
+ * It fetches issue content, uses a model to determine urgency and importance,
+ * and then applies those labels to the issue.
+ */
+const { getIssueContent, applyLabels } = require('./lib/github-wrapper');
+const { callModel } = require('./lib/github-model');
+const fs = require('fs');
+const path = require('path');
+const { Command } = require('commander');
+
+// Read the prompt template
+// Remove the synchronous file read at the top of the file.
+// Inside the async main function (at its start), add:
+const promptTemplate = await fs.promises.readFile(path.join(__dirname, 'prompt-template.txt'), 'utf8');
+let promptTemplate;
+try {
+  promptTemplate = fs.readFileSync(promptTemplatePath, 'utf8');
+} catch (error) {
+  console.error(`Error reading prompt template: ${error.message}`);
+  process.exit(1);
+}
+// Inside the async main function (at its start), add:
+promptTemplate = await fs.promises.readFile(path.join(__dirname, 'prompt-template.txt'), 'utf8');
+
+/**
+ * Main function to process and label an issue
+ * @param {Object} options - Options object
+ * @param {number} options.issueNumber - The issue number to process
+ * @param {string} options.owner - Repository owner
+ * @param {string} options.repo - Repository name
+ */
+async function main({ issueNumber, owner, repo }) {
path: label-issue.js
user: login: qodo-code-review[bot]
body: **Suggestion:** Use global regex replacement
```suggestion
 const prompt = promptTemplate
   .replace(/\{\{title\}\}/g, issue.title)
   .replace(/\{\{body\}\}/g, issue.body || '');
```

<!-- manually_applied -->
created_at: 2025-05-07T07:06:11Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,104 @@
+/**
+ * GitHub Issue Automatic Labeling Script
+ * This script automates labeling of GitHub issues using GitHub's AI models
+ * It fetches issue content, uses a model to determine urgency and importance,
+ * and then applies those labels to the issue.
+ */
+const { getIssueContent, applyLabels } = require('./lib/github-wrapper');
+const { callModel } = require('./lib/github-model');
+const fs = require('fs');
+const path = require('path');
+const { Command } = require('commander');
+
+// Read the prompt template
+// Remove the synchronous file read at the top of the file.
+// Inside the async main function (at its start), add:
+const promptTemplate = await fs.promises.readFile(path.join(__dirname, 'prompt-template.txt'), 'utf8');
+let promptTemplate;
+try {
+  promptTemplate = fs.readFileSync(promptTemplatePath, 'utf8');
+} catch (error) {
+  console.error(`Error reading prompt template: ${error.message}`);
+  process.exit(1);
+}
+// Inside the async main function (at its start), add:
+promptTemplate = await fs.promises.readFile(path.join(__dirname, 'prompt-template.txt'), 'utf8');
+
+/**
+ * Main function to process and label an issue
+ * @param {Object} options - Options object
+ * @param {number} options.issueNumber - The issue number to process
+ * @param {string} options.owner - Repository owner
+ * @param {string} options.repo - Repository name
+ */
+async function main({ issueNumber, owner, repo }) {
path: label-issue.js
user: login: codiumai-pr-agent-free[bot]
body: **Suggestion:** Use global regex replacement
```suggestion
 const prompt = promptTemplate
   .replace(/\{\{title\}\}/g, issue.title)
   .replace(/\{\{body\}\}/g, issue.body || '');
```

<!-- manually_applied -->
created_at: 2025-05-07T07:06:11Z
author_association: CONTRIBUTOR

PR comments:
user: login: codeant-ai[bot]
created_at: 2025-05-07T06:16:50Z
body: CodeAnt AI is reviewing your PR.
author_association: CONTRIBUTOR
user: login: coderabbitai[bot]
created_at: 2025-05-07T06:16:54Z
body: <!-- This is an auto-generated comment: summarize by coderabbit.ai -->
<!-- This is an auto-generated comment: rate limited by coderabbit.ai -->

> [!WARNING]
> ## Rate limit exceeded
> 
> @dmitriz has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait **7 minutes and 15 seconds** before requesting another review.
> 
> <details>
> <summary>⌛ How to resolve this issue?</summary>
> 
> After the wait time has elapsed, a review can be triggered using the `@coderabbitai review` command as a PR comment. Alternatively, push new commits to this PR.
> 
> We recommend that you space out your commits to avoid hitting the rate limit.
> 
> </details>
> 
> 
> <details>
> <summary>🚦 How do rate limits work?</summary>
> 
> CodeRabbit enforces hourly rate limits for each developer per organization.
> 
> Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.
> 
> Please see our [FAQ](https://docs.coderabbit.ai/faq) for further information.
> 
> </details>
> 
> <details>
> <summary>📥 Commits</summary>
> 
> Reviewing files that changed from the base of the PR and between 2b081d82fb2cf6000a621fbd17144fa1d8d76555 and 3377a074ba01eca7dbd264428bede7c50a9c8bef.
> 
> </details>
> 
> <details>
> <summary>⛔ Files ignored due to path filters (1)</summary>
> 
> * `package-lock.json` is excluded by `!**/package-lock.json`
> 
> </details>
> 
> <details>
> <summary>📒 Files selected for processing (5)</summary>
> 
> * `label-issue.js` (1 hunks)
> * `lib/github-model.js` (1 hunks)
> * `lib/github-wrapper.js` (1 hunks)
> * `package.json` (0 hunks)
> * `prompt-template.txt` (1 hunks)
> 
> </details>

<!-- end of auto-generated comment: rate limited by coderabbit.ai -->
<!-- walkthrough_start -->

<details>
<summary>📝 Walkthrough</summary>

## Summary by CodeRabbit

- **New Features**
  - Introduced an automated tool for labeling GitHub issues using AI, which infers urgency and importance labels and applies them to issues.
- **Chores**
  - Added a new dependency to support YAML parsing.
## Summary by CodeRabbit

- **New Features**
  - Introduced automated labeling for GitHub issues using AI to infer urgency and importance.
  - Added command-line support for labeling issues with customizable repository and issue selection.

- **Chores**
  - Added a new dependency for YAML parsing to support structured AI responses.

## Walkthrough

This change introduces an automated GitHub issue labeling workflow using AI. It adds scripts and modules to fetch issue content, generate labeling prompts, call a GitHub model for label inference, and apply the resulting labels. Supporting modules for GitHub API interaction and YAML parsing are also included, along with a new dependency.

## Changes

| File(s)                         | Change Summary                                                                                           |
|---------------------------------|----------------------------------------------------------------------------------------------------------|
| label-issue.js                  | New script to automate issue labeling: reads prompt, fetches issue, calls AI model, applies labels, exports `main`. |
| lib/github-model.js             | New module: wraps GitHub model API, sends prompts, parses YAML responses for urgency/importance labels, exports `callModel`. |
| lib/github-wrapper.js           | New module: provides `getIssueContent` and `applyLabels` functions for GitHub issue API interaction with detailed error handling. |
| package.json                   | Adds `js-yaml` dependency for YAML parsing.                                                              |

## Sequence Diagram(s)

```mermaid
sequenceDiagram
    participant User
    participant label-issue.js
    participant github-wrapper.js
    participant github-model.js
    participant GitHubAPI
    participant GitHubModel

    User->>label-issue.js: Run script with issue number, owner, repo
    label-issue.js->>github-wrapper.js: getIssueContent({issueNumber, owner, repo})
    github-wrapper.js->>GitHubAPI: GET /repos/:owner/:repo/issues/:issueNumber
    GitHubAPI-->>github-wrapper.js: Issue title/body/number
    github-wrapper.js-->>label-issue.js: Issue content
    label-issue.js->>github-model.js: callModel({prompt})
    github-model.js->>GitHubModel: POST /chat/completions (with prompt)
    GitHubModel-->>github-model.js: Model response (YAML labels)
    github-model.js-->>label-issue.js: {urgency, importance}
    label-issue.js->>github-wrapper.js: applyLabels({issueNumber, owner, repo, labels})
    github-wrapper.js->>GitHubAPI: POST /repos/:owner/:repo/issues/:issueNumber/labels
    GitHubAPI-->>github-wrapper.js: Confirmation
    github-wrapper.js-->>label-issue.js: Done
```

## Possibly related PRs

- dmitriz/issue-labeler#3: Implements a similar GitHub model API integration for AI-driven issue labeling, overlapping in model calls and response processing.
- dmitriz/issue-labeler#1: Contains an initial prototype for issue labeling with simulated label suggestions, related to the same functionality but less complete.

## Poem

> 🤖✨  
> An issue appears, a label awaits,  
> AI reads the prompt, no room for debates.  
> With YAML in hand and a model so keen,  
> Urgency and importance—applied, nice and clean.  
> Automation now reigns, with code standing tall,  
> Labeling issues—no hassle at all!  
> 🚀

</details>

<!-- walkthrough_end -->
<!-- internal state start -->


<!-- DwQgtGAEAqAWCWBnSTIEMB26CuAXA9mAOYCmGJATmriQCaQDG+Ats2bgFyQAOFk+AIwBWJBrngA3EsgEBPRvlqU0AgfFwA6NPEgQAfACgjoCEYDEZyAAUASpETZWaCrKNxU3bABsvkCiQBHbGlcABp7bAFmdRp6OUhsREpIWmjcCngAL3D4DHTFbAZpdEhyAHdIADMSamx/Kvw+NDwWangGNB95LxUSL1yiSABxdQAJSJREB2KBNCT6fCxcWBJ4PiY89gTEAcgAQQBJSGZFPsQNGBXj7Sw0Wlp1eEXJksQGDO5cUrQ2egADHoCPpgJDTDRCRB/SDLag4AjMajFZYkHgUfBFKb8SpVEi4BgIDCDUHBBSbPLhTCdWSZXbqSBldSwEojXDjAT7I4nJS+AgpXGUaLkBIUUgYBjyTD0eDMbiNXCYIoUjD0ZG3bjcLyyXbI9DqtG8eCIyCAs7Q/DQq7EkgXOAot4fL6dRDm3IMLzYJTIShoviwSX9QnoZURdVy5BMJzKsABlHOIiOLbcZw7QkaIwHW73R7PXk6hG5ezveCfcI62zoe7IXBlc17KxHMpUdXJLneaQ2q6VNaIMKQAHwAQAeiIjMiYEbaGbFHBkPCvHwEngnqq2DF4kWyEqjRQmyoYl2DOWFpRLLZ+3rOTF7oegb+pFwBymwQAwosaHkobz/Ol4CQpJNpj5eV4C8ZBJT7ScNVkAAZXpQM/c07noE1QLNADgnOS5pBRSpV33DcdyYChZSoGhIG9bc/WVANBnAvAQKyWMsD2AAPJ5kFyHsFRRDYu3jfx6EPJklEqZovC+JJcHEQlMNtexREWQSm24Shwn7IcR2WMcuT6Gc/nCUSGAY+UaHDFhmFXdo2meITj2GMYJkOY5Tl8OsDguA5cGQP4Oh8ABZFyoVwtcniwJJlTAhoKARKS6FRFhPjQnUnJ0rxwhIFj0jQMQqyuABNPY/OgsAt2i6hYj8aRZQwJIlXoJMKCSDivl5Jd2HgSp5B1OpRXFIMpRlOVuONODziMAAxXJOk1UsrnLbBuFoRFcpRecRDEPkVOVMgjJmCUs0DHU/ghMBZB+LwoSTBgAGs0FINCyBULxVuTXZ8GxAqisqxBqqayrcDqcg4i6q4UpcjtUEkrFGCo0gzJ8URxCkTVyIwJ7imaeFEVocJDjAWgMikLAULe7FTwmK0q3NHt/HO3IUSta40dINg8npRprsqLx8DKMaDAsSBX1YdhkAcJwXDcK5FhRiNWa8/glgQZBPB8SqghCdB6kqNFmDsxJklSdQMmyelYHNWUezi5Z/BRNgpju4peLWQVBgcBgMUQXCeRCZB3rsknA2C/C0f6XB5EXNA7PJ9k3IuABlFSjK7XyZpSNJjcqkiKphL4UOQNHfj7UzcGjOCwAARgAdgAFgANgAVgATkbuuAAZy4AJgADn0ouQlLoEvArmva9r1vW/rrv69b6vG978C/mLgfgSruux9byuAGZN/b6uoTKShY3uOK3Y9r2Uet/BsCIJk3OhH2O2whRhbyDjlSssic7soicJqAH6j9pBTUuw87xSIFQJw4gU7yDKHMe+lt6DgSkBkLsVtzQ1goNddAb93xKFoBcAAcohLM64Q7PzlogcI6seyhUofwPgDw3iJB2ARA+9R/BETwQrOyKtfD+GoZoIw+hjDgCgGQBY2JMaEFFMoCqst2BcF4PwYQiNJB7QUEoKgqh1BaB0MIkwUB3AcTArcFoxAyCyLivIvIXAqAVDFgiFwkB4hME0SoNQmhtC6DAIYERpgDAoRBE+a0EIOAGAAESRP5pYPGMjSIn0cI4+Qft8SYDhkYPYpQSD2KLIlAEZcrR6ReLkfItBChoLhK0MiAdBh+2juhYoiRdjRwAOTIDBtyTCnkUCDQoPLFYXgVJ8CDqQqmOI8RMkZhsd8jpgxAO6KNOqJQRmhTQinOyHTdJYULA6SqdxIrzhlC1EgMoehkW1iwEo3NfJVBArGRAsgxSwDRBgK+iAUawgoKucQbAPJfBEvTEx2DHn4heW8vs+YMBBTwqQi0sJ5TXQxlgRmGBHBAgoOEfwFt1CNGSWUcgGL+qZ3wDsAgTiC7Wm2X8SF0KQrPGqBMpElpgmkhmXOLFzgMbxSOc4+QuR1rSUGDqK0bToTqGekSgQig+V5HNDqGgpzERLJTitDkzluRoVyNUPgPUdoSmDNKLOw084eWxKAzl/1AZ0ByI6dU/QmUnKSsysE2yVnPG5kQL02UmSW24ESqitBnpegoD6GQ3R8BEBHIGcCGVHiBlspHUSIE6g8VOBcAA6isLAXzbiRS4sqToiw7S5N7HSfWqqfLmUlJQKE/QBBUCcbyBqSRyGSmjPTTW8YKFRTsiitFql+D4oHeBLF+Bwi2REmJL4EhOgYR7TzAlRLR1/MgDO/oS1TK9pZai5g6LO0JjZuBXIEh8CItVZClcdKMDhADTRIM8g8LNBvl8CijVH7qrbORFiWcK00svcHHtGUaAUDIfrNMRhon7HErI2hTqURKDdM4ayNVoYZSznFbcngBD9AYKjcQ4hpBCMgONGFqykJ0C4H8OYIL/2wshQACgAN4NIIf2wlC6B2jsgAAXwAJRQjQ3KOKFzdb5MHkEsEEI/gRKiQYCAYAjB1uHKOAQYBUozjCZE8JkHYkWPifQBxzhknYlSYSQjBhMnlA/RK9SymtKqfU1J4peQ0RlKKIgyK9SAoarvhOKcPbMANJGoPA8HMuY8xXYJvpQLqNPLBYkWjqyfLTW830T8fpHTuxIJ8A5OtEo012CO3EgMgWCAFfSRkfZdVilkFCBehqhpihIFCNdc6tXemEzrOy6ntmtglbTWgQK74EERVgETqNFwvLlqu5whpsPFG3JHa5nR5LvFxLc566U2KCpQNiV5QpUBblXPgyA3S1vLSDPsNiJKdz5qKBVo8oxoDQCsHHSAiLstgE6Goolkdy6tzAEkDYKppQkCvoIgwclkv+UCol544VBslCsAAeTjtANWGEWpyquPU1KO5tU7RROI2UJSHtMh1IcxKJygTHylLcGGsIIwalxKsu2iAHZRRipeN0Hpdh46dvxZDPAkNsGA5uLrkcBcbcpd0tdS4Ls6n8D9DcOFGhc6/VlHKdk7GsvYEs8qJyctoWbbGSAn1oIaJRNh9EWDxuK6qir995uXgm5VOaIDe4vh/Gq+KOrwY/gNb6dxIKv4vCDdLBkVgB4ECmSukxeg35Aa7FRarDqty+iI4tYbsO9DjiggGOBg4H1CoW+bbsJNoFwhKGAs9egr7jQRrAvUA0mw6c85vEKq4Oule/RIPrrA9fUCX3xe+t1txQLmlvcUfhmPyIhsaGGhvkbdj17Zw7OhT2Xv2BMgl1x0hMUO5qvB6gkc09oBnSBdGSz69cRujIEgpUUTfmeTzbUVxX0Q2QH1onYpJwODOQ6iuKrOFhUH7AjoVtyolLmDjg5OyKlHOK9IGOoKLOkIUP/HFM7j3irksmiAIIkC+vPr6P6AXhBjptBqRLBtAfBqID0BQQRH7FFhVJhpEDhnho8OZlACRlehWHgpRrFrhmPowCli5PRpTpwNvhkISLxlwFYDrEgCQMAMxj7rIFwAVoGAAD6lDeBpQ9JGpNaqE/gaFaGqzcZ6ACbfpCb0Dja2aaSwDaTgxSYybaZyb+JKa2Fjh+ZDIaZOE6YHDmIErYwRDizGYM5maIAZJZIVD1K+bKQtgFASqoAlKublJu4hhZxBY1LsyYIgFdJfDlroDXbIAJqkh8Qpp053YohfyujXgYx4BZpQJC4RwlBDLOhkLZQexmijZVBdZkCTaLDTYzoZDox0LRH1iQArB3CUB0KRz6xNCii9iRz1yA4KTBg/Jg54D96QCb6vboDzFk7vYkCfbfb/jgSRwIhsQWS6x+z/atykgMB1D+BszOg3S4iyRXDf4zbroXZJhHhJBEDdpNr+BExfDHrfb0AACqNgFuGwNMaBoUK6RQLm32mQxQ9et6B4lW1e2gteS+UatS2Iq+0g7OcM4Q2x2+tQ0xwYWBR+KQJ+s0ZA/0L+vMc+oa4G0ANYwKcWiw4KY+TeROFhfSFGRg5cFwd4uIj40wr4ZIuAfwXA40uI+IgB+Gm2zi0qSyO6e6gC9gicHUv49A9SjM8QCI10EBQwAAoujjPhrFQfZKyBTMEu0mMcTvgCUiuh6sgFiXcnXoQfjmrkLvStiSms4g/o0E/riEyW/kToQeBh3KKfMrBIPJCFwHsHar+DFiGmgCEaAryJHD9KILqXFAaSykaWgCadGtYKjpaYELPjaUWYBKAnfM6a6adrnI3iyduGvnDESmSVxADL7GNoGfUHgV8CwMgUBNiXFPXhupHDmSekuF+tioGOFKSj9lqn6aQvSdmuGWiMyTqB/pLLbPEfyWORTmiB0WnvbkEGsIWbAZ0Qyb0WsP0VsIMXNgkfnPgBJLiO+owcJqRgRO0dlrMvwJ8LBmVojKyjcJGcSqSrioOgSgftimSvIBSjkNumxksqVBBHajBKNL3DqOahmbIOBgLHsOQchmMjqAhrQeRahgKUwXwFhqwe1ARuEXJpAAQtknDgPnRRRhBA8mKFxZAPeBKS+G+OwExixuhXBZxtluaHxlCAWDYSpuOLEdOI4VABxRUIIT+bQHwfxQIX+WqFBAmWcBJVaKxrugOhxoSqOuEKAvJTuH2G4cpZ4ZQHpD4S4QpgYFdLdKQDOIsJplEiRX4XEoEYZk4ikrDOZnJEoFtEoDVsaEgCCVgH8D5Q7P5VCvSHAgtBuhUkhCUFZmlaQGpCdGdMwBdKulMUlgAHrVwaAimtx/CELmgfkrDrBRVjKxXiI7Rpm572jFjyxsK2yTHgbmAxJkWjJwZ8iIZ0EoYME8ULAMUsHtBsEsVGBELkBphaZCI+J+JiLBiAJmKhVyLmQKJ+BoD2KJJGa8qW5aIeK6LeKGAGLkLqAAD6S4iAr1QJv4B8tAr1XEfSj1e16AJArctctAlcokm8AgjcDcRQDAHclQ5cAg1cDAm8CNXcjclcAgm8tAM8HcDA9cMNAgXcQNz1QIlcaAXcDAAgM8o8tc1cEN7sDAXcDcJA5c9clQ1clcXclc9cJAY8lQ9caArcuNm8ZNoikAjc5c5cmNtA1c/2m8HcswXcrclQ9wtcHclc3c1NHcaA1cAgPNG8DAokaAoN1cEtEAkAytrcXc5ctAXciNAgBNlQ68rcaAmt5clQAgtAVc5c1c1cok9tXcENDc9c9cQNBgz1ssb1H1X1f4P1dAr14ij1QAA=== -->

<!-- internal state end -->
<!-- finishing_touch_checkbox_start -->

<details open="true">
<summary>✨ Finishing Touches</summary>

- [ ] <!-- {"checkboxId": "7962f53c-55bc-4827-bfbf-6a18da830691"} --> 📝 Generate Docstrings

</details>

<!-- finishing_touch_checkbox_end -->
<!-- tips_start -->

---

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

<details>
<summary>❤️ Share</summary>

- [X](https://twitter.com/intent/tweet?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A&url=https%3A//coderabbit.ai)
- [Mastodon](https://mastodon.social/share?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A%20https%3A%2F%2Fcoderabbit.ai)
- [Reddit](https://www.reddit.com/submit?title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&text=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code.%20Check%20it%20out%3A%20https%3A//coderabbit.ai)
- [LinkedIn](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fcoderabbit.ai&mini=true&title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&summary=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code)

</details>

<details>
<summary>🪧 Tips</summary>

### Chat

There are 3 ways to chat with [CodeRabbit](https://coderabbit.ai?utm_source=oss&utm_medium=github&utm_campaign=dmitriz/issue-labeler&utm_content=9):

- Review comments: Directly reply to a review comment made by CodeRabbit. Example:
  - `I pushed a fix in commit <commit_id>, please review it.`
  - `Generate unit testing code for this file.`
  - `Open a follow-up GitHub issue for this discussion.`
- Files and specific lines of code (under the "Files changed" tab): Tag `@coderabbitai` in a new review comment at the desired location with your query. Examples:
  - `@coderabbitai generate unit testing code for this file.`
  -	`@coderabbitai modularize this function.`
- PR comments: Tag `@coderabbitai` in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
  - `@coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.`
  - `@coderabbitai read src/utils.ts and generate unit testing code.`
  - `@coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.`
  - `@coderabbitai help me debug CodeRabbit configuration file.`

### Support

Need help? Create a ticket on our [support page](https://www.coderabbit.ai/contact-us/support) for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

### CodeRabbit Commands (Invoked using PR comments)

- `@coderabbitai pause` to pause the reviews on a PR.
- `@coderabbitai resume` to resume the paused reviews.
- `@coderabbitai review` to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
- `@coderabbitai full review` to do a full review from scratch and review all the files again.
- `@coderabbitai summary` to regenerate the summary of the PR.
- `@coderabbitai generate docstrings` to [generate docstrings](https://docs.coderabbit.ai/finishing-touches/docstrings) for this PR.
- `@coderabbitai generate sequence diagram` to generate a sequence diagram of the changes in this PR.
- `@coderabbitai resolve` resolve all the CodeRabbit review comments.
- `@coderabbitai configuration` to show the current CodeRabbit configuration for the repository.
- `@coderabbitai help` to get help.

### Other keywords and placeholders

- Add `@coderabbitai ignore` anywhere in the PR description to prevent this PR from being reviewed.
- Add `@coderabbitai summary` to generate the high-level summary at a specific location in the PR description.
- Add `@coderabbitai` anywhere in the PR title to generate the title automatically.

### CodeRabbit Configuration File (`.coderabbit.yaml`)

- You can programmatically configure CodeRabbit by adding a `.coderabbit.yaml` file to the root of your repository.
- Please see the [configuration documentation](https://docs.coderabbit.ai/guides/configure-coderabbit) for more information.
- If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: `# yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json`

### Documentation and Community

- Visit our [Documentation](https://docs.coderabbit.ai) for detailed information on how to use CodeRabbit.
- Join our [Discord Community](http://discord.gg/coderabbit) to get help, request features, and share feedback.
- Follow us on [X/Twitter](https://twitter.com/coderabbitai) for updates and announcements.

</details>

<!-- tips_end -->
author_association: CONTRIBUTOR
user: login: qodo-code-review[bot]
created_at: 2025-05-07T06:17:11Z
body: ## PR Reviewer Guide 🔍

#### (Review updated until commit https://github.com/dmitriz/issue-labeler/commit/ae06d7fa3b965cec2f1b4c3c2897b3d042c5b9b8)


Here are some key observations to aid the review process:

<table>
<tr><td>⏱️&nbsp;<strong>Estimated effort to review</strong>: 3 🔵🔵🔵⚪⚪</td></tr>
<tr><td>🧪&nbsp;<strong>No relevant tests</strong></td></tr>
<tr><td>🔒&nbsp;<strong>Security concerns</strong><br><br>

<strong>Sensitive information exposure:</strong><br> The code imports authentication tokens directly from file paths (`.secrets/gh-model` and `.secrets/github`). This approach is less secure than using environment variables or a dedicated secrets management solution. If these files are accidentally committed to version control or exposed, it could lead to credential leakage. Consider using environment variables with dotenv (which is already a dependency) instead of storing tokens in files.</td></tr>
<tr><td>⚡&nbsp;<strong>Recommended focus areas for review</strong><br><br>

<details><summary><a href='https://github.com/dmitriz/issue-labeler/pull/9/files#diff-5f8aa460a2d281acf7e49e043887bc1562e079bd70b3cbdd575b873c893e2bfdR6-R6'><strong>Security Concern</strong></a>

The token is imported directly from a file path. Consider using environment variables or a more secure secret management approach instead of storing tokens in files.
</summary>

```javascript
const { token } = require('../.secrets/gh-model');
const config = require('../config/github-model');
```

</details>

<details><summary><a href='https://github.com/dmitriz/issue-labeler/pull/9/files#diff-09e8ff86ffa0113dcc5c155273affbacfa7bcfe337ade3371e36b55983abf61cR6-R6'><strong>Security Concern</strong></a>

Similar to the model file, the GitHub token is imported directly from a file path rather than using a more secure approach like environment variables.
</summary>

```javascript
const { token } = require('../.secrets/github');

```

</details>

<details><summary><a href='https://github.com/dmitriz/issue-labeler/pull/9/files#diff-a08f0fee89a7629735079a9b5fe2a2c9dc2f992ff80d130b8784c4ffae5d1a0eR54-R56'><strong>Error Handling</strong></a>

The script exits with process.exit(1) in the catch block, which might terminate the process abruptly without proper cleanup. Consider implementing more graceful error handling.
</summary>

```javascript
  console.error(`Error processing issue #${issueNumber}: ${error.message}`);
  process.exit(1);
}
```

</details>

</td></tr>
</table>

author_association: CONTRIBUTOR
user: login: codiumai-pr-agent-free[bot]
created_at: 2025-05-07T06:17:56Z
body: ## PR Code Suggestions ✨
<!-- ae06d7f -->

Latest suggestions up to ae06d7f
Explore these optional code suggestions:

<table><thead><tr><td><strong>Category</strong></td><td align=left><strong>Suggestion&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </strong></td><td align=center><strong>Impact</strong></td></tr><tbody><tr><td rowspan=2>General</td>
<td>



<details><summary>
Use global regex replacement
</summary>

___


**The current implementation only replaces the first occurrence of each <br>placeholder. Use a global regex replacement to ensure all instances of <br>placeholders are replaced in the template.**

[label-issue.js [32-34]](https://github.com/dmitriz/issue-labeler/pull/9/files#diff-a08f0fee89a7629735079a9b5fe2a2c9dc2f992ff80d130b8784c4ffae5d1a0eR32-R34)

```diff
 const prompt = promptTemplate
-  .replace('{{title}}', issue.title)
-  .replace('{{body}}', issue.body || '');
+  .replace(/\{\{title\}\}/g, issue.title)
+  .replace(/\{\{body\}\}/g, issue.body || '');
```

`[Suggestion processed]`

<details><summary>Suggestion importance[1-10]: 7</summary>

__

Why: The suggestion correctly points out that `String.prototype.replace()` with a string argument only replaces the first occurrence. Using a global regex (`/g`) is more robust if placeholders like `{{title}}` or `{{body}}` can appear multiple times in the `promptTemplate`.


</details></details></td><td align=center>Medium

</td></tr><tr><td>



<details><summary>✅ 
Update GitHub API authentication<!-- not_implemented -->
</summary>

___

<details><summary><b>Suggestion Impact:</b></summary>The commit implemented the suggested change by using 'Bearer ${token}' instead of 'token ${token}' for GitHub API authentication in the headers configuration

code diff:

```diff
+    Authorization: `Bearer ${token}`,
```

</details>


___


**The GitHub API token format has changed. The current format using <code>token ${token}</code> <br>is deprecated. Use the Bearer authentication format instead for better <br>compatibility with GitHub's API.**

[lib/github-wrapper.js [12-21]](https://github.com/dmitriz/issue-labeler/pull/9/files#diff-09e8ff86ffa0113dcc5c155273affbacfa7bcfe337ade3371e36b55983abf61cR12-R21)

```diff
 const github = axios.create({
   baseURL: BASE_URL,
   headers: {
-    Authorization: `token ${token}`,
+    Authorization: `Bearer ${token}`,
     Accept: 'application/vnd.github.v3+json',
     'User-Agent': 'issue-labeler-app'
   },
   timeout: 5000,
   httpsAgent: new https.Agent({ keepAlive: true })
 });
```

`[Suggestion processed]`

<details><summary>Suggestion importance[1-10]: 6</summary>

__

Why: The suggestion to use `Bearer ${token}` instead of `token ${token}` for GitHub API authentication is good practice. It aligns with modern standards and the `Authorization` header format used in `lib/github-model.js`.


</details></details></td><td align=center>Low

</td></tr>
<tr><td align="center" colspan="2">

- [ ] Update <!-- /improve --more_suggestions=true -->

</td><td></td></tr></tbody></table>

___

#### Previous suggestions
<details><summary>✅ Suggestions up to commit ae06d7f</summary>
<br><table><thead><tr><td><strong>Category</strong></td><td align=left><strong>Suggestion&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </strong></td><td align=center><strong>Impact</strong></td></tr><tbody><tr><td rowspan=2>General</td>
<td>



<details><summary>Fix placeholder replacement</summary>

___


**The current implementation only replaces the first occurrence of each <br>placeholder. Use a global regex replacement to ensure all instances of <br>placeholders are replaced in the template.**

[label-issue.js [32-34]](https://github.com/dmitriz/issue-labeler/pull/9/files#diff-a08f0fee89a7629735079a9b5fe2a2c9dc2f992ff80d130b8784c4ffae5d1a0eR32-R34)

```diff
 const prompt = promptTemplate
-  .replace('{{title}}', issue.title)
-  .replace('{{body}}', issue.body || '');
+  .replace(/\{\{title\}\}/g, issue.title)
+  .replace(/\{\{body\}\}/g, issue.body || '');
```

 <!-- /improve --apply_suggestion=0 -->

<details><summary>Suggestion importance[1-10]: 7</summary>

__

Why: The suggestion correctly identifies that `String.prototype.replace()` with a string argument only replaces the first match. Using a global regular expression ensures all placeholder instances are replaced, improving the robustness of the templating.


</details></details></td><td align=center>Medium

</td></tr><tr><td>



<details><summary>✅ 
Avoid hardcoded API endpoint<!-- not_implemented -->
</summary>

___

<details><summary><b>Suggestion Impact:</b></summary>The commit implemented the suggestion by replacing the hardcoded API endpoint URL 'https://models.github.ai/inference/chat/completions' with a reference to config.apiEndpoint

code diff:

```diff
-    const response = await axiosInstance.post("https://models.github.ai/inference/chat/completions", {
+    const response = await axiosInstance.post(config.apiEndpoint, {
```

</details>


___


**The API endpoint URL is hardcoded. Move it to the configuration file to make it <br>easier to update or change environments without modifying the code.**

[lib/github-model.js [24-36]](https://github.com/dmitriz/issue-labeler/pull/9/files#diff-5f8aa460a2d281acf7e49e043887bc1562e079bd70b3cbdd575b873c893e2bfdR24-R36)

```diff
-const response = await axiosInstance.post("https://models.github.ai/inference/chat/completions", {
+const response = await axiosInstance.post(config.apiEndpoint, {
   messages: [
     { role: "user", content: prompt }
   ],
   model: config.model,
   temperature: config.temperature,
   max_tokens: config.max_tokens
 }, {
   headers: {
     "Authorization": `Bearer ${token}`,
     "Content-Type": "application/json"
   }
 });
```

 <!-- /improve --apply_suggestion=1 -->

<details><summary>Suggestion importance[1-10]: 6</summary>

__

Why: Moving the hardcoded API endpoint URL to a configuration file (`config.apiEndpoint`) is a good practice for maintainability and configurability. This aligns with the existing pattern of sourcing other parameters like `model` and `temperature` from the `config` object.


</details></details></td><td align=center>Low

</td></tr>
<tr><td align="center" colspan="2">

 <!-- /improve --more_suggestions=true -->

</td><td></td></tr></tbody></table>

</details>
author_association: CONTRIBUTOR
user: login: codeant-ai[bot]
created_at: 2025-05-07T06:18:30Z
body: ## Pull Request Feedback 🔍

<table>
<tr><td>🔒&nbsp;<strong>No security issues identified</strong></td></tr>
<tr><td>⚡&nbsp;<strong>Recommended areas for review</strong><br><br>

<a href='https://github.com/dmitriz/issue-labeler/pull/9/files#diff-a08f0fee89a7629735079a9b5fe2a2c9dc2f992ff80d130b8784c4ffae5d1a0eR12-R13'><strong>Error Handling</strong></a><br>Consider adding error handling around the file read operation for the prompt template. If the file (prompt-template.txt) is missing or unreadable, the script should fail gracefully rather than throwing an unhandled exception.

<a href='https://github.com/dmitriz/issue-labeler/pull/9/files#diff-a08f0fee89a7629735079a9b5fe2a2c9dc2f992ff80d130b8784c4ffae5d1a0eR60-R82'><strong>Argument Validation</strong></a><br>The command-line argument parsing relies on minimal validation. It would be beneficial to more thoroughly validate inputs (such as ensuring `issueNumber` is a valid number) before proceeding.

<a href='https://github.com/dmitriz/issue-labeler/pull/9/files#diff-5f8aa460a2d281acf7e49e043887bc1562e079bd70b3cbdd575b873c893e2bfdR45-R50'><strong>YAML Extraction</strong></a><br>The regex-based extraction for the YAML block from the model's response might not cover all format variations. Consider adding additional checks to verify that the parsed YAML contains the expected keys (e.g., urgency and importance) and to handle unexpected formats.

</td></tr>
</table>

author_association: CONTRIBUTOR
user: login: codeant-ai[bot]
created_at: 2025-05-07T06:21:30Z
body: CodeAnt AI finished reviewing your PR.
author_association: CONTRIBUTOR
user: login: dmitriz
created_at: 2025-05-07T06:31:01Z
body: Label test-label-1746599460128 added via API test ✅ [2025-05-07T06:31:01.122Z]
author_association: OWNER
user: login: dmitriz
created_at: 2025-05-07T06:49:45Z
body: Label test-label-1746600585049 added via API test ✅ [2025-05-07T06:49:45.901Z]
author_association: OWNER
user: login: dmitriz
created_at: 2025-05-07T06:52:13Z
body: Label test-label-1746600733014 added via API test ✅ [2025-05-07T06:52:13.867Z]
author_association: OWNER
user: login: dmitriz
created_at: 2025-05-07T06:55:50Z
body: Label test-label-1746600949755 added via API test ✅ [2025-05-07T06:55:50.674Z]
author_association: OWNER
user: login: dmitriz
created_at: 2025-05-07T06:58:49Z
body: Label test-label-1746601128268 added via API test ✅ [2025-05-07T06:58:49.259Z]
author_association: OWNER
user: login: dmitriz
created_at: 2025-05-07T07:09:31Z
body: Label test-label-1746601770072 added via API test ✅ [2025-05-07T07:09:31.123Z]
author_association: OWNER