@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.
- package/dist/kit-server.js +130 -25
- package/dist/tools/agent.js +2 -2
- package/dist/tools/core.js +1 -1
- package/dist/tools/integration.js +3 -26
- package/dist/tools/security.js +2 -2
- package/dist/{credentials.js → utils/credentials.js} +2 -1
- package/dist/utils/parser.d.ts +1 -0
- package/dist/utils/parser.js +126 -0
- package/dist/{utils.d.ts → utils/utils.d.ts} +0 -3
- package/dist/{utils.js → utils/utils.js} +0 -3
- package/package.json +1 -1
- /package/dist/{credentials.d.ts → utils/credentials.d.ts} +0 -0
package/dist/kit-server.js
CHANGED
|
@@ -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
|
|
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 :
|
|
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"}`;
|
package/dist/tools/agent.js
CHANGED
|
@@ -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,
|
|
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();
|
package/dist/tools/core.js
CHANGED
|
@@ -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
|
-
:
|
|
289
|
+
: adfToMarkdown(ticket.fields.description)}
|
|
313
290
|
|
|
314
291
|
### Labels
|
|
315
292
|
${ticket.fields.labels?.join(', ') || 'None'}`;
|
package/dist/tools/security.js
CHANGED
|
@@ -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
|
|
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
|
|
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
|
+
}
|
package/package.json
CHANGED
|
File without changes
|