kernelbot 1.0.22 → 1.0.24

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,232 @@
1
+ import axios from 'axios';
2
+
3
+ /**
4
+ * Create an axios instance configured for the JIRA REST API.
5
+ * Supports both Atlassian Cloud (*.atlassian.net) and JIRA Server instances.
6
+ *
7
+ * Authentication:
8
+ * - Cloud: email + API token (Basic auth)
9
+ * - Server: username + password/token (Basic auth)
10
+ *
11
+ * Config precedence: config.jira.* → JIRA_* env vars
12
+ */
13
+ function getJiraClient(config) {
14
+ const baseUrl = config.jira?.base_url || process.env.JIRA_BASE_URL;
15
+ const email = config.jira?.email || process.env.JIRA_EMAIL;
16
+ const token = config.jira?.api_token || process.env.JIRA_API_TOKEN;
17
+
18
+ if (!baseUrl) throw new Error('JIRA base URL not configured. Set JIRA_BASE_URL or jira.base_url in config.');
19
+ if (!email) throw new Error('JIRA email/username not configured. Set JIRA_EMAIL or jira.email in config.');
20
+ if (!token) throw new Error('JIRA API token not configured. Set JIRA_API_TOKEN or jira.api_token in config.');
21
+
22
+ const cleanBase = baseUrl.replace(/\/+$/, '');
23
+
24
+ return axios.create({
25
+ baseURL: `${cleanBase}/rest/api/2`,
26
+ auth: { username: email, password: token },
27
+ headers: {
28
+ 'Accept': 'application/json',
29
+ 'Content-Type': 'application/json',
30
+ },
31
+ timeout: 30000,
32
+ });
33
+ }
34
+
35
+ /**
36
+ * Extract structured ticket data from a JIRA issue response.
37
+ */
38
+ function formatIssue(issue) {
39
+ const fields = issue.fields || {};
40
+ return {
41
+ key: issue.key,
42
+ summary: fields.summary || '',
43
+ description: fields.description || '',
44
+ status: fields.status?.name || '',
45
+ assignee: fields.assignee?.displayName || 'Unassigned',
46
+ reporter: fields.reporter?.displayName || '',
47
+ priority: fields.priority?.name || '',
48
+ type: fields.issuetype?.name || '',
49
+ labels: fields.labels || [],
50
+ created: fields.created || '',
51
+ updated: fields.updated || '',
52
+ project: fields.project?.key || '',
53
+ };
54
+ }
55
+
56
+ export const definitions = [
57
+ {
58
+ name: 'jira_get_ticket',
59
+ description: 'Get details of a specific JIRA ticket by its key (e.g. PROJ-123).',
60
+ input_schema: {
61
+ type: 'object',
62
+ properties: {
63
+ ticket_key: {
64
+ type: 'string',
65
+ description: 'The JIRA ticket key (e.g. PROJ-123)',
66
+ },
67
+ },
68
+ required: ['ticket_key'],
69
+ },
70
+ },
71
+ {
72
+ name: 'jira_search_tickets',
73
+ description: 'Search for JIRA tickets using JQL (JIRA Query Language). Example: "project = PROJ AND status = Open".',
74
+ input_schema: {
75
+ type: 'object',
76
+ properties: {
77
+ jql_query: {
78
+ type: 'string',
79
+ description: 'JQL query string',
80
+ },
81
+ max_results: {
82
+ type: 'number',
83
+ description: 'Maximum number of results to return (default 20)',
84
+ default: 20,
85
+ },
86
+ },
87
+ required: ['jql_query'],
88
+ },
89
+ },
90
+ {
91
+ name: 'jira_list_my_tickets',
92
+ description: 'List JIRA tickets assigned to a user. Defaults to the authenticated user.',
93
+ input_schema: {
94
+ type: 'object',
95
+ properties: {
96
+ assignee: {
97
+ type: 'string',
98
+ description: 'Assignee username or "currentUser()" (default)',
99
+ default: 'currentUser()',
100
+ },
101
+ max_results: {
102
+ type: 'number',
103
+ description: 'Maximum number of results to return (default 20)',
104
+ default: 20,
105
+ },
106
+ },
107
+ },
108
+ },
109
+ {
110
+ name: 'jira_get_project_tickets',
111
+ description: 'Get tickets from a specific JIRA project.',
112
+ input_schema: {
113
+ type: 'object',
114
+ properties: {
115
+ project_key: {
116
+ type: 'string',
117
+ description: 'The JIRA project key (e.g. PROJ)',
118
+ },
119
+ max_results: {
120
+ type: 'number',
121
+ description: 'Maximum number of results to return (default 20)',
122
+ default: 20,
123
+ },
124
+ },
125
+ required: ['project_key'],
126
+ },
127
+ },
128
+ ];
129
+
130
+ export const handlers = {
131
+ /**
132
+ * Get details of a specific JIRA ticket.
133
+ * @param {{ ticket_key: string }} params
134
+ * @param {{ config: object }} context
135
+ */
136
+ jira_get_ticket: async (params, context) => {
137
+ try {
138
+ const client = getJiraClient(context.config);
139
+ const { data } = await client.get(`/issue/${params.ticket_key}`);
140
+ return { ticket: formatIssue(data) };
141
+ } catch (err) {
142
+ if (err.response?.status === 404) {
143
+ return { error: `Ticket ${params.ticket_key} not found` };
144
+ }
145
+ return { error: err.response?.data?.errorMessages?.join('; ') || err.message };
146
+ }
147
+ },
148
+
149
+ /**
150
+ * Search for JIRA tickets using JQL.
151
+ * @param {{ jql_query: string, max_results?: number }} params
152
+ * @param {{ config: object }} context
153
+ */
154
+ jira_search_tickets: async (params, context) => {
155
+ try {
156
+ const client = getJiraClient(context.config);
157
+ const maxResults = params.max_results || 20;
158
+
159
+ const { data } = await client.get('/search', {
160
+ params: {
161
+ jql: params.jql_query,
162
+ maxResults,
163
+ fields: 'summary,description,status,assignee,reporter,priority,issuetype,labels,created,updated,project',
164
+ },
165
+ });
166
+
167
+ return {
168
+ total: data.total,
169
+ tickets: (data.issues || []).map(formatIssue),
170
+ };
171
+ } catch (err) {
172
+ return { error: err.response?.data?.errorMessages?.join('; ') || err.message };
173
+ }
174
+ },
175
+
176
+ /**
177
+ * List tickets assigned to a user.
178
+ * @param {{ assignee?: string, max_results?: number }} params
179
+ * @param {{ config: object }} context
180
+ */
181
+ jira_list_my_tickets: async (params, context) => {
182
+ try {
183
+ const client = getJiraClient(context.config);
184
+ const assignee = params.assignee || 'currentUser()';
185
+ const maxResults = params.max_results || 20;
186
+ const jql = `assignee = ${assignee} ORDER BY updated DESC`;
187
+
188
+ const { data } = await client.get('/search', {
189
+ params: {
190
+ jql,
191
+ maxResults,
192
+ fields: 'summary,description,status,assignee,reporter,priority,issuetype,labels,created,updated,project',
193
+ },
194
+ });
195
+
196
+ return {
197
+ total: data.total,
198
+ tickets: (data.issues || []).map(formatIssue),
199
+ };
200
+ } catch (err) {
201
+ return { error: err.response?.data?.errorMessages?.join('; ') || err.message };
202
+ }
203
+ },
204
+
205
+ /**
206
+ * Get tickets from a specific JIRA project.
207
+ * @param {{ project_key: string, max_results?: number }} params
208
+ * @param {{ config: object }} context
209
+ */
210
+ jira_get_project_tickets: async (params, context) => {
211
+ try {
212
+ const client = getJiraClient(context.config);
213
+ const maxResults = params.max_results || 20;
214
+ const jql = `project = ${params.project_key} ORDER BY updated DESC`;
215
+
216
+ const { data } = await client.get('/search', {
217
+ params: {
218
+ jql,
219
+ maxResults,
220
+ fields: 'summary,description,status,assignee,reporter,priority,issuetype,labels,created,updated,project',
221
+ },
222
+ });
223
+
224
+ return {
225
+ total: data.total,
226
+ tickets: (data.issues || []).map(formatIssue),
227
+ };
228
+ } catch (err) {
229
+ return { error: err.response?.data?.errorMessages?.join('; ') || err.message };
230
+ }
231
+ },
232
+ };
@@ -177,6 +177,12 @@ export function loadConfig() {
177
177
  if (!config.github) config.github = {};
178
178
  config.github.token = process.env.GITHUB_TOKEN;
179
179
  }
180
+ if (process.env.JIRA_BASE_URL || process.env.JIRA_EMAIL || process.env.JIRA_API_TOKEN) {
181
+ if (!config.jira) config.jira = {};
182
+ if (process.env.JIRA_BASE_URL) config.jira.base_url = process.env.JIRA_BASE_URL;
183
+ if (process.env.JIRA_EMAIL) config.jira.email = process.env.JIRA_EMAIL;
184
+ if (process.env.JIRA_API_TOKEN) config.jira.api_token = process.env.JIRA_API_TOKEN;
185
+ }
180
186
 
181
187
  return config;
182
188
  }
@@ -220,6 +226,18 @@ export function saveCredential(config, envKey, value) {
220
226
  case 'TELEGRAM_BOT_TOKEN':
221
227
  config.telegram.bot_token = value;
222
228
  break;
229
+ case 'JIRA_BASE_URL':
230
+ if (!config.jira) config.jira = {};
231
+ config.jira.base_url = value;
232
+ break;
233
+ case 'JIRA_EMAIL':
234
+ if (!config.jira) config.jira = {};
235
+ config.jira.email = value;
236
+ break;
237
+ case 'JIRA_API_TOKEN':
238
+ if (!config.jira) config.jira = {};
239
+ config.jira.api_token = value;
240
+ break;
223
241
  }
224
242
 
225
243
  // Also set in process.env so tools pick it up
@@ -239,5 +257,19 @@ export function getMissingCredential(toolName, config) {
239
257
  }
240
258
  }
241
259
 
260
+ const jiraTools = ['jira_get_ticket', 'jira_search_tickets', 'jira_list_my_tickets', 'jira_get_project_tickets'];
261
+
262
+ if (jiraTools.includes(toolName)) {
263
+ if (!config.jira?.base_url && !process.env.JIRA_BASE_URL) {
264
+ return { envKey: 'JIRA_BASE_URL', label: 'JIRA Base URL (e.g. https://yourcompany.atlassian.net)' };
265
+ }
266
+ if (!config.jira?.email && !process.env.JIRA_EMAIL) {
267
+ return { envKey: 'JIRA_EMAIL', label: 'JIRA Email / Username' };
268
+ }
269
+ if (!config.jira?.api_token && !process.env.JIRA_API_TOKEN) {
270
+ return { envKey: 'JIRA_API_TOKEN', label: 'JIRA API Token' };
271
+ }
272
+ }
273
+
242
274
  return null;
243
275
  }
@@ -26,19 +26,17 @@ const LOGO = `
26
26
  ╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝╚═╝ ╚═══╝╚══════╝╚══════╝╚═════╝ ╚═════╝ ╚═╝
27
27
  `;
28
28
 
29
- // Create a vibrant rainbow gradient
30
- const rainbowGradient = gradient([
31
- '#FF0080', // Hot Pink
32
- '#FF8C00', // Dark Orange
33
- '#FFD700', // Gold
34
- '#00FF00', // Lime Green
35
- '#00CED1', // Dark Turquoise
36
- '#1E90FF', // Dodger Blue
37
- '#9370DB' // Medium Purple
29
+ // White to ~70% black gradient
30
+ const monoGradient = gradient([
31
+ '#FFFFFF',
32
+ '#D0D0D0',
33
+ '#A0A0A0',
34
+ '#707070',
35
+ '#4D4D4D',
38
36
  ]);
39
37
 
40
38
  export function showLogo() {
41
- console.log(rainbowGradient.multiline(LOGO));
39
+ console.log(monoGradient.multiline(LOGO));
42
40
  console.log(chalk.dim(` AI Engineering Agent — v${getVersion()}\n`));
43
41
  console.log(
44
42
  boxen(
@@ -1,21 +0,0 @@
1
- # Hello World! 🌍
2
-
3
- This is a **test file** created to verify that everything is working properly.
4
-
5
- ## About This File
6
-
7
- This file demonstrates:
8
- - *Basic markdown formatting*
9
- - **Bold text**
10
- - Simple lists
11
-
12
- ## Features Tested
13
-
14
- - ✅ File creation
15
- - ✅ Markdown formatting
16
- - ✅ Emoji support 🚀
17
- - ✅ Basic structure
18
-
19
- ---
20
-
21
- *Created as a test for the KernelBot project!* 🤖
@@ -1,11 +0,0 @@
1
- # Hello World 👋
2
-
3
- Welcome to **newnew-1**! This is a simple hello world file.
4
-
5
- ## Quick Example
6
-
7
- ```python
8
- print("Hello, World!")
9
- ```
10
-
11
- > Keep it simple. Keep it fun.