@hanhnd/agent-kit 1.0.12 → 1.0.13

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";
@@ -21366,6 +21364,130 @@ function getCredential(key) {
21366
21364
  return loadCredentials()[key];
21367
21365
  }
21368
21366
 
21367
+ // src/utils/parser.ts
21368
+ function adfToMarkdown(adf) {
21369
+ if (!adf || typeof adf !== "object") return "No description";
21370
+ const content = adf.content;
21371
+ if (!content || !Array.isArray(content)) return "No description";
21372
+ function renderNodes(nodes, listDepth = 0, parentType) {
21373
+ let result = "";
21374
+ if (!nodes || !Array.isArray(nodes)) return result;
21375
+ for (const node of nodes) {
21376
+ switch (node.type) {
21377
+ case "paragraph": {
21378
+ const text = renderNodes(node.content, listDepth, node.type);
21379
+ result += parentType === "listItem" ? text : text + "\n\n";
21380
+ break;
21381
+ }
21382
+ case "heading": {
21383
+ const level = node.attrs?.level || 1;
21384
+ result += "#".repeat(level) + " " + renderNodes(node.content, listDepth, node.type) + "\n\n";
21385
+ break;
21386
+ }
21387
+ case "text": {
21388
+ let text = node.text || "";
21389
+ if (node.marks) {
21390
+ for (const mark of node.marks) {
21391
+ switch (mark.type) {
21392
+ case "strong":
21393
+ text = `**${text}**`;
21394
+ break;
21395
+ case "em":
21396
+ text = `*${text}*`;
21397
+ break;
21398
+ case "code":
21399
+ text = `\`${text}\``;
21400
+ break;
21401
+ case "strike":
21402
+ text = `~~${text}~~`;
21403
+ break;
21404
+ case "link":
21405
+ text = `[${text}](${mark.attrs?.href})`;
21406
+ break;
21407
+ }
21408
+ }
21409
+ }
21410
+ result += text;
21411
+ break;
21412
+ }
21413
+ case "bulletList":
21414
+ case "orderedList":
21415
+ result += renderNodes(node.content, listDepth + 1, node.type);
21416
+ if (listDepth === 0) result += "\n";
21417
+ break;
21418
+ case "listItem": {
21419
+ const indent = " ".repeat(Math.max(0, listDepth - 1));
21420
+ const bullet = parentType === "orderedList" ? "1." : "-";
21421
+ const text = renderNodes(node.content, listDepth, node.type).trim();
21422
+ result += `${indent}${bullet} ${text}
21423
+ `;
21424
+ break;
21425
+ }
21426
+ case "blockquote":
21427
+ result += "> " + renderNodes(node.content, listDepth, node.type).trim().replace(/\n/g, "\n> ") + "\n\n";
21428
+ break;
21429
+ case "codeBlock":
21430
+ result += `\`\`\`${node.attrs?.language || ""}
21431
+ ${renderNodes(node.content, listDepth, node.type).trim()}
21432
+ \`\`\`
21433
+
21434
+ `;
21435
+ break;
21436
+ case "rule":
21437
+ result += "---\n\n";
21438
+ break;
21439
+ case "hardBreak":
21440
+ result += "\n";
21441
+ break;
21442
+ case "table":
21443
+ result += renderTable(node.content);
21444
+ break;
21445
+ case "mention":
21446
+ result += `@${node.attrs?.text || node.attrs?.displayName || "user"}`;
21447
+ break;
21448
+ case "emoji":
21449
+ result += node.attrs?.text || "";
21450
+ break;
21451
+ case "inlineCard":
21452
+ case "blockCard":
21453
+ result += `[${node.attrs?.url}](${node.attrs?.url})`;
21454
+ if (node.type === "blockCard") result += "\n\n";
21455
+ break;
21456
+ case "mediaSingle":
21457
+ case "mediaInline":
21458
+ case "media":
21459
+ result += "[media omitted]";
21460
+ break;
21461
+ default:
21462
+ if (node.content) {
21463
+ result += renderNodes(node.content, listDepth, node.type);
21464
+ }
21465
+ break;
21466
+ }
21467
+ }
21468
+ return result;
21469
+ }
21470
+ function renderTable(rows) {
21471
+ if (!rows || !Array.isArray(rows)) return "";
21472
+ let result = "\n";
21473
+ let isFirstRow = true;
21474
+ for (const row of rows) {
21475
+ if (row.type !== "tableRow") continue;
21476
+ const cells = row.content || [];
21477
+ const rendered = cells.map(
21478
+ (c) => renderNodes(c.content).trim().replace(/\n+/g, " ").replace(/\|/g, "\\|")
21479
+ );
21480
+ result += "| " + rendered.join(" | ") + " |\n";
21481
+ if (isFirstRow) {
21482
+ result += "| " + cells.map(() => "---").join(" | ") + " |\n";
21483
+ isFirstRow = false;
21484
+ }
21485
+ }
21486
+ return result + "\n";
21487
+ }
21488
+ return renderNodes(content).trim() || "No description";
21489
+ }
21490
+
21369
21491
  // src/tools/integration.ts
21370
21492
  var BitbucketPrSchema = external_exports.object({
21371
21493
  id: external_exports.number(),
@@ -21404,24 +21526,6 @@ var JiraTicketSchema = external_exports.object({
21404
21526
  errorMessages: external_exports.array(external_exports.string()).optional(),
21405
21527
  fields: JiraFieldsSchema
21406
21528
  });
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
21529
  function buildJiraBasicAuth() {
21426
21530
  const email2 = getCredential("ATLASSIAN_USER_EMAIL");
21427
21531
  const token = getCredential("ATLASSIAN_API_TOKEN");
@@ -21665,7 +21769,7 @@ ${ticket.errorMessages.join("\n")}`
21665
21769
  **Type:** ${ticket.fields.issuetype?.name || "Unknown"}
21666
21770
 
21667
21771
  ### Description
21668
- ${typeof ticket.fields.description === "string" ? ticket.fields.description : extractAdfText(ticket.fields.description)}
21772
+ ${typeof ticket.fields.description === "string" ? ticket.fields.description : adfToMarkdown(ticket.fields.description)}
21669
21773
 
21670
21774
  ### Labels
21671
21775
  ${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
@@ -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.13",
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",
File without changes