@hanhnd/agent-kit 1.0.12 → 1.0.14

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.
@@ -20993,7 +20993,7 @@ var StdioServerTransport = class {
20993
20993
  import * as fs from "fs";
20994
20994
  import * as path from "path";
20995
20995
 
20996
- // src/utils.ts
20996
+ // src/utils/utils.ts
20997
20997
  function getWorkspaceRoot() {
20998
20998
  return process.env.WORKSPACE_DIR || process.env.CLAUDE_WORKSPACE || process.env.GEMINI_WORKSPACE || process.env.INIT_CWD || process.env.PWD || process.cwd();
20999
20999
  }
@@ -21097,9 +21097,7 @@ function validatePath(filePath, baseDir = process.cwd()) {
21097
21097
  }
21098
21098
  }
21099
21099
  const fileName = path2.basename(resolved);
21100
- const isExactForbidden = FORBIDDEN_FILES.some(
21101
- (f) => fileName.toLowerCase() === f.toLowerCase()
21102
- );
21100
+ const isExactForbidden = FORBIDDEN_FILES.some((f) => fileName.toLowerCase() === f.toLowerCase());
21103
21101
  const isPatternForbidden = FORBIDDEN_PATTERNS.some((re) => re.test(fileName));
21104
21102
  if (isExactForbidden || isPatternForbidden) {
21105
21103
  throw new Error(`Access to sensitive file is FORBIDDEN: ${fileName}`);
@@ -21306,7 +21304,7 @@ function registerAgentTools(server2) {
21306
21304
  // src/tools/integration.ts
21307
21305
  import { writeFileSync as writeFileSync3 } from "fs";
21308
21306
 
21309
- // src/credentials.ts
21307
+ // src/utils/credentials.ts
21310
21308
  import * as fs3 from "fs";
21311
21309
  import * as os from "os";
21312
21310
  import * as path4 from "path";
@@ -21326,7 +21324,8 @@ function parseIni(content) {
21326
21324
  const eqIdx = line.indexOf("=");
21327
21325
  if (eqIdx === -1) continue;
21328
21326
  const key = line.slice(0, eqIdx).trim();
21329
- const value = line.slice(eqIdx + 1).trim();
21327
+ const rawValue = line.slice(eqIdx + 1).trim();
21328
+ const value = rawValue.replace(/^(['"])(.*)\1$/, "$2");
21330
21329
  if (!result[currentSection]) result[currentSection] = {};
21331
21330
  result[currentSection][key] = value;
21332
21331
  }
@@ -21366,6 +21365,130 @@ function getCredential(key) {
21366
21365
  return loadCredentials()[key];
21367
21366
  }
21368
21367
 
21368
+ // src/utils/parser.ts
21369
+ function adfToMarkdown(adf) {
21370
+ if (!adf || typeof adf !== "object") return "No description";
21371
+ const content = adf.content;
21372
+ if (!content || !Array.isArray(content)) return "No description";
21373
+ function renderNodes(nodes, listDepth = 0, parentType) {
21374
+ let result = "";
21375
+ if (!nodes || !Array.isArray(nodes)) return result;
21376
+ for (const node of nodes) {
21377
+ switch (node.type) {
21378
+ case "paragraph": {
21379
+ const text = renderNodes(node.content, listDepth, node.type);
21380
+ result += parentType === "listItem" ? text : text + "\n\n";
21381
+ break;
21382
+ }
21383
+ case "heading": {
21384
+ const level = node.attrs?.level || 1;
21385
+ result += "#".repeat(level) + " " + renderNodes(node.content, listDepth, node.type) + "\n\n";
21386
+ break;
21387
+ }
21388
+ case "text": {
21389
+ let text = node.text || "";
21390
+ if (node.marks) {
21391
+ for (const mark of node.marks) {
21392
+ switch (mark.type) {
21393
+ case "strong":
21394
+ text = `**${text}**`;
21395
+ break;
21396
+ case "em":
21397
+ text = `*${text}*`;
21398
+ break;
21399
+ case "code":
21400
+ text = `\`${text}\``;
21401
+ break;
21402
+ case "strike":
21403
+ text = `~~${text}~~`;
21404
+ break;
21405
+ case "link":
21406
+ text = `[${text}](${mark.attrs?.href})`;
21407
+ break;
21408
+ }
21409
+ }
21410
+ }
21411
+ result += text;
21412
+ break;
21413
+ }
21414
+ case "bulletList":
21415
+ case "orderedList":
21416
+ result += renderNodes(node.content, listDepth + 1, node.type);
21417
+ if (listDepth === 0) result += "\n";
21418
+ break;
21419
+ case "listItem": {
21420
+ const indent = " ".repeat(Math.max(0, listDepth - 1));
21421
+ const bullet = parentType === "orderedList" ? "1." : "-";
21422
+ const text = renderNodes(node.content, listDepth, node.type).trim();
21423
+ result += `${indent}${bullet} ${text}
21424
+ `;
21425
+ break;
21426
+ }
21427
+ case "blockquote":
21428
+ result += "> " + renderNodes(node.content, listDepth, node.type).trim().replace(/\n/g, "\n> ") + "\n\n";
21429
+ break;
21430
+ case "codeBlock":
21431
+ result += `\`\`\`${node.attrs?.language || ""}
21432
+ ${renderNodes(node.content, listDepth, node.type).trim()}
21433
+ \`\`\`
21434
+
21435
+ `;
21436
+ break;
21437
+ case "rule":
21438
+ result += "---\n\n";
21439
+ break;
21440
+ case "hardBreak":
21441
+ result += "\n";
21442
+ break;
21443
+ case "table":
21444
+ result += renderTable(node.content);
21445
+ break;
21446
+ case "mention":
21447
+ result += `@${node.attrs?.text || node.attrs?.displayName || "user"}`;
21448
+ break;
21449
+ case "emoji":
21450
+ result += node.attrs?.text || "";
21451
+ break;
21452
+ case "inlineCard":
21453
+ case "blockCard":
21454
+ result += `[${node.attrs?.url}](${node.attrs?.url})`;
21455
+ if (node.type === "blockCard") result += "\n\n";
21456
+ break;
21457
+ case "mediaSingle":
21458
+ case "mediaInline":
21459
+ case "media":
21460
+ result += "[media omitted]";
21461
+ break;
21462
+ default:
21463
+ if (node.content) {
21464
+ result += renderNodes(node.content, listDepth, node.type);
21465
+ }
21466
+ break;
21467
+ }
21468
+ }
21469
+ return result;
21470
+ }
21471
+ function renderTable(rows) {
21472
+ if (!rows || !Array.isArray(rows)) return "";
21473
+ let result = "\n";
21474
+ let isFirstRow = true;
21475
+ for (const row of rows) {
21476
+ if (row.type !== "tableRow") continue;
21477
+ const cells = row.content || [];
21478
+ const rendered = cells.map(
21479
+ (c) => renderNodes(c.content).trim().replace(/\n+/g, " ").replace(/\|/g, "\\|")
21480
+ );
21481
+ result += "| " + rendered.join(" | ") + " |\n";
21482
+ if (isFirstRow) {
21483
+ result += "| " + cells.map(() => "---").join(" | ") + " |\n";
21484
+ isFirstRow = false;
21485
+ }
21486
+ }
21487
+ return result + "\n";
21488
+ }
21489
+ return renderNodes(content).trim() || "No description";
21490
+ }
21491
+
21369
21492
  // src/tools/integration.ts
21370
21493
  var BitbucketPrSchema = external_exports.object({
21371
21494
  id: external_exports.number(),
@@ -21404,24 +21527,6 @@ var JiraTicketSchema = external_exports.object({
21404
21527
  errorMessages: external_exports.array(external_exports.string()).optional(),
21405
21528
  fields: JiraFieldsSchema
21406
21529
  });
21407
- function extractAdfText(adf) {
21408
- if (!adf || typeof adf !== "object") return "No description";
21409
- const content = adf.content;
21410
- if (!content || !Array.isArray(content)) return "No description";
21411
- const textParts = [];
21412
- function extractText(nodes) {
21413
- for (const node of nodes) {
21414
- if (typeof node.text === "string") {
21415
- textParts.push(node.text);
21416
- }
21417
- if (Array.isArray(node.content)) {
21418
- extractText(node.content);
21419
- }
21420
- }
21421
- }
21422
- extractText(content);
21423
- return textParts.join(" ") || "No description";
21424
- }
21425
21530
  function buildJiraBasicAuth() {
21426
21531
  const email2 = getCredential("ATLASSIAN_USER_EMAIL");
21427
21532
  const token = getCredential("ATLASSIAN_API_TOKEN");
@@ -21665,7 +21770,7 @@ ${ticket.errorMessages.join("\n")}`
21665
21770
  **Type:** ${ticket.fields.issuetype?.name || "Unknown"}
21666
21771
 
21667
21772
  ### Description
21668
- ${typeof ticket.fields.description === "string" ? ticket.fields.description : extractAdfText(ticket.fields.description)}
21773
+ ${typeof ticket.fields.description === "string" ? ticket.fields.description : adfToMarkdown(ticket.fields.description)}
21669
21774
 
21670
21775
  ### Labels
21671
21776
  ${ticket.fields.labels?.join(", ") || "None"}`;
@@ -7,8 +7,8 @@ import { randomUUID } from 'crypto';
7
7
  import * as fs from 'fs';
8
8
  import * as path from 'path';
9
9
  import { z } from 'zod';
10
- import { getWorkspaceRoot } from '../utils.js';
11
- import { commandExists, validatePath, sanitizeOutput } from './security.js';
10
+ import { getWorkspaceRoot } from '../utils/utils.js';
11
+ import { commandExists, sanitizeOutput, validatePath } from './security.js';
12
12
  // Configurable timeout via environment variable (default: 5 minutes)
13
13
  const AGENT_TIMEOUT = parseInt(process.env.KIT_AGENT_TIMEOUT || '300000', 10);
14
14
  const jobRegistry = new Map();
@@ -4,7 +4,7 @@
4
4
  import * as fs from 'fs';
5
5
  import * as path from 'path';
6
6
  import { z } from 'zod';
7
- import { getWorkspaceRoot } from '../utils.js';
7
+ import { getWorkspaceRoot } from '../utils/utils.js';
8
8
  import { DEFAULT_EXTENSIONS } from './config.js';
9
9
  /**
10
10
  * Register core tools with MCP server
@@ -4,7 +4,8 @@
4
4
  */
5
5
  import { writeFileSync } from 'fs';
6
6
  import { z } from 'zod';
7
- import { getCredential } from '../credentials.js';
7
+ import { getCredential } from '../utils/credentials.js';
8
+ import { adfToMarkdown } from '../utils/parser.js';
8
9
  import { sanitize } from './security.js';
9
10
  // Zod schema for Bitbucket PR REST API response
10
11
  const BitbucketPrSchema = z.object({
@@ -53,30 +54,6 @@ const JiraTicketSchema = z.object({
53
54
  errorMessages: z.array(z.string()).optional(),
54
55
  fields: JiraFieldsSchema,
55
56
  });
56
- /**
57
- * Extract plain text from ADF (Atlassian Document Format) content
58
- * Recursively walks the content tree to find text nodes
59
- */
60
- function extractAdfText(adf) {
61
- if (!adf || typeof adf !== 'object')
62
- return 'No description';
63
- const content = adf.content;
64
- if (!content || !Array.isArray(content))
65
- return 'No description';
66
- const textParts = [];
67
- function extractText(nodes) {
68
- for (const node of nodes) {
69
- if (typeof node.text === 'string') {
70
- textParts.push(node.text);
71
- }
72
- if (Array.isArray(node.content)) {
73
- extractText(node.content);
74
- }
75
- }
76
- }
77
- extractText(content);
78
- return textParts.join(' ') || 'No description';
79
- }
80
57
  function buildJiraBasicAuth() {
81
58
  const email = getCredential('ATLASSIAN_USER_EMAIL');
82
59
  const token = getCredential('ATLASSIAN_API_TOKEN');
@@ -309,7 +286,7 @@ ${pr.description || 'No description'}`;
309
286
  ### Description
310
287
  ${typeof ticket.fields.description === 'string'
311
288
  ? ticket.fields.description
312
- : extractAdfText(ticket.fields.description)}
289
+ : adfToMarkdown(ticket.fields.description)}
313
290
 
314
291
  ### Labels
315
292
  ${ticket.fields.labels?.join(', ') || 'None'}`;
@@ -5,8 +5,8 @@
5
5
  import { execFileSync } from 'child_process';
6
6
  import * as fs from 'fs';
7
7
  import * as path from 'path';
8
- import { getWorkspaceRoot } from '../utils.js';
9
- import { FORBIDDEN_FILES, FORBIDDEN_PATTERNS, FORBIDDEN_DIRS } from './config.js';
8
+ import { getWorkspaceRoot } from '../utils/utils.js';
9
+ import { FORBIDDEN_DIRS, FORBIDDEN_FILES, FORBIDDEN_PATTERNS } from './config.js';
10
10
  /**
11
11
  * Sanitize string for safe use with execFileSync
12
12
  * Only removes dangerous shell operators - safe chars like !?#* are allowed
@@ -38,7 +38,8 @@ function parseIni(content) {
38
38
  if (eqIdx === -1)
39
39
  continue;
40
40
  const key = line.slice(0, eqIdx).trim();
41
- const value = line.slice(eqIdx + 1).trim();
41
+ const rawValue = line.slice(eqIdx + 1).trim();
42
+ const value = rawValue.replace(/^(['"])(.*)\1$/, '$2');
42
43
  if (!result[currentSection])
43
44
  result[currentSection] = {};
44
45
  result[currentSection][key] = value;
@@ -0,0 +1 @@
1
+ export declare function adfToMarkdown(adf: Record<string, unknown> | null | undefined): string;
@@ -0,0 +1,126 @@
1
+ export function adfToMarkdown(adf) {
2
+ if (!adf || typeof adf !== 'object')
3
+ return 'No description';
4
+ const content = adf.content;
5
+ if (!content || !Array.isArray(content))
6
+ return 'No description';
7
+ function renderNodes(nodes, listDepth = 0, parentType) {
8
+ let result = '';
9
+ if (!nodes || !Array.isArray(nodes))
10
+ return result;
11
+ for (const node of nodes) {
12
+ switch (node.type) {
13
+ case 'paragraph': {
14
+ const text = renderNodes(node.content, listDepth, node.type);
15
+ result += parentType === 'listItem' ? text : text + '\n\n';
16
+ break;
17
+ }
18
+ case 'heading': {
19
+ const level = node.attrs?.level || 1;
20
+ result +=
21
+ '#'.repeat(level) + ' ' + renderNodes(node.content, listDepth, node.type) + '\n\n';
22
+ break;
23
+ }
24
+ case 'text': {
25
+ let text = node.text || '';
26
+ if (node.marks) {
27
+ for (const mark of node.marks) {
28
+ switch (mark.type) {
29
+ case 'strong':
30
+ text = `**${text}**`;
31
+ break;
32
+ case 'em':
33
+ text = `*${text}*`;
34
+ break;
35
+ case 'code':
36
+ text = `\`${text}\``;
37
+ break;
38
+ case 'strike':
39
+ text = `~~${text}~~`;
40
+ break;
41
+ case 'link':
42
+ text = `[${text}](${mark.attrs?.href})`;
43
+ break;
44
+ }
45
+ }
46
+ }
47
+ result += text;
48
+ break;
49
+ }
50
+ case 'bulletList':
51
+ case 'orderedList':
52
+ result += renderNodes(node.content, listDepth + 1, node.type);
53
+ if (listDepth === 0)
54
+ result += '\n';
55
+ break;
56
+ case 'listItem': {
57
+ const indent = ' '.repeat(Math.max(0, listDepth - 1));
58
+ const bullet = parentType === 'orderedList' ? '1.' : '-';
59
+ const text = renderNodes(node.content, listDepth, node.type).trim();
60
+ result += `${indent}${bullet} ${text}\n`;
61
+ break;
62
+ }
63
+ case 'blockquote':
64
+ result +=
65
+ '> ' +
66
+ renderNodes(node.content, listDepth, node.type).trim().replace(/\n/g, '\n> ') +
67
+ '\n\n';
68
+ break;
69
+ case 'codeBlock':
70
+ result += `\`\`\`${node.attrs?.language || ''}\n${renderNodes(node.content, listDepth, node.type).trim()}\n\`\`\`\n\n`;
71
+ break;
72
+ case 'rule':
73
+ result += '---\n\n';
74
+ break;
75
+ case 'hardBreak':
76
+ result += '\n';
77
+ break;
78
+ case 'table':
79
+ result += renderTable(node.content);
80
+ break;
81
+ case 'mention':
82
+ result += `@${node.attrs?.text || node.attrs?.displayName || 'user'}`;
83
+ break;
84
+ case 'emoji':
85
+ result += node.attrs?.text || '';
86
+ break;
87
+ case 'inlineCard':
88
+ case 'blockCard':
89
+ result += `[${node.attrs?.url}](${node.attrs?.url})`;
90
+ if (node.type === 'blockCard')
91
+ result += '\n\n';
92
+ break;
93
+ case 'mediaSingle':
94
+ case 'mediaInline':
95
+ case 'media':
96
+ result += '[media omitted]';
97
+ break;
98
+ default:
99
+ if (node.content) {
100
+ result += renderNodes(node.content, listDepth, node.type);
101
+ }
102
+ break;
103
+ }
104
+ }
105
+ return result;
106
+ }
107
+ function renderTable(rows) {
108
+ if (!rows || !Array.isArray(rows))
109
+ return '';
110
+ let result = '\n';
111
+ let isFirstRow = true;
112
+ for (const row of rows) {
113
+ if (row.type !== 'tableRow')
114
+ continue;
115
+ const cells = row.content || [];
116
+ const rendered = cells.map((c) => renderNodes(c.content).trim().replace(/\n+/g, ' ').replace(/\|/g, '\\|'));
117
+ result += '| ' + rendered.join(' | ') + ' |\n';
118
+ if (isFirstRow) {
119
+ result += '| ' + cells.map(() => '---').join(' | ') + ' |\n';
120
+ isFirstRow = false;
121
+ }
122
+ }
123
+ return result + '\n';
124
+ }
125
+ return renderNodes(content).trim() || 'No description';
126
+ }
@@ -1,4 +1 @@
1
- /**
2
- * Utility functions for agent-kit
3
- */
4
1
  export declare function getWorkspaceRoot(): string;
@@ -1,6 +1,3 @@
1
- /**
2
- * Utility functions for agent-kit
3
- */
4
1
  export function getWorkspaceRoot() {
5
2
  return (process.env.WORKSPACE_DIR ||
6
3
  process.env.CLAUDE_WORKSPACE ||
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hanhnd/agent-kit",
3
- "version": "1.0.12",
3
+ "version": "1.0.14",
4
4
  "description": "Super Engineer - Team of AI Agents for software development (Claude Code Plugin)",
5
5
  "type": "module",
6
6
  "main": "dist/kit-server.js",