@neobiotechlabs/neobiotech-dev-agent 0.1.36 → 0.1.37
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/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/dist/index.js +53 -6
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
"source": "npm",
|
|
11
11
|
"package": "@neobiotechlabs/neobiotech-dev-agent",
|
|
12
12
|
"registry": "https://registry.npmjs.org",
|
|
13
|
-
"version": "0.1.
|
|
13
|
+
"version": "0.1.37"
|
|
14
14
|
},
|
|
15
15
|
"description": "의료기기 SW 규제 개발 자동화 (Jira·Confluence·Risk·CVSS·Xray·IEC 62304/62366·MDR·문서 렌더)",
|
|
16
16
|
"category": "medical-device",
|
package/dist/index.js
CHANGED
|
@@ -159735,6 +159735,31 @@ true\r
|
|
|
159735
159735
|
clearTimeout(timer);
|
|
159736
159736
|
}
|
|
159737
159737
|
}
|
|
159738
|
+
/**
|
|
159739
|
+
* 페이지 첨부 목록 조회. Confluence REST는 페이지 자체 attachments 도 expand 가능하지만
|
|
159740
|
+
* 명시적 list도 지원 — 여기선 GET child/attachment?limit=N.
|
|
159741
|
+
*/
|
|
159742
|
+
async listAttachments(pageId, limit = 100) {
|
|
159743
|
+
const resp = await this.request(
|
|
159744
|
+
`${this.baseUrl}/wiki/rest/api/content/${encodeURIComponent(pageId)}/child/attachment?limit=${limit}`,
|
|
159745
|
+
{ label: "listAttachments" }
|
|
159746
|
+
);
|
|
159747
|
+
const data = resp;
|
|
159748
|
+
const items = Array.isArray(data?.results) ? data.results : [];
|
|
159749
|
+
return items.map((it2) => ({ id: String(it2.id), title: String(it2.title ?? "") }));
|
|
159750
|
+
}
|
|
159751
|
+
/**
|
|
159752
|
+
* 첨부 삭제. POST .../child/attachment/{id}/remove 와 동일 — REST API.
|
|
159753
|
+
* 위 endpoint URL의 page_id는 첫 pageId(이 attachment가 속한 페이지)지만, GET 응답의 id 경로의
|
|
159754
|
+
* second segment가 attachment id. Confluence Cloud는 id로 직접 delete 가능:
|
|
159755
|
+
* DELETE /wiki/rest/api/content/{pageId}/child/attachment/{attachmentId}
|
|
159756
|
+
*/
|
|
159757
|
+
async deleteAttachment(pageId, attachmentId) {
|
|
159758
|
+
await this.request(
|
|
159759
|
+
`${this.baseUrl}/wiki/rest/api/content/${encodeURIComponent(pageId)}/child/attachment/${encodeURIComponent(attachmentId)}`,
|
|
159760
|
+
{ method: "DELETE", label: "deleteAttachment" }
|
|
159761
|
+
);
|
|
159762
|
+
}
|
|
159738
159763
|
};
|
|
159739
159764
|
function escapeMimeFilename2(name) {
|
|
159740
159765
|
return name.replace(/"/g, "%22").replace(/\r/g, "%0D").replace(/\n/g, "%0A");
|
|
@@ -160267,9 +160292,10 @@ var registerConfluenceTools = (ctx2) => {
|
|
|
160267
160292
|
parent_page_id: external_exports.string().optional().describe("Parent page ID \u2014 create mode only, nests the new page under this parent. Ignored in update mode."),
|
|
160268
160293
|
page_id: external_exports.string().optional().describe("Existing page ID. When provided, the page is UPDATED (current version fetched then incremented). When omitted, a NEW page is created."),
|
|
160269
160294
|
render_diagrams: external_exports.boolean().optional().default(true).describe("Render ```plantuml fences to PNG/SVG and embed as attachments. Default true. Falls back to code block on render failure."),
|
|
160295
|
+
clean_stale_attachments: external_exports.boolean().optional().default(false).describe("update \uBAA8\uB4DC\uC77C \uB54C \uAC8C\uC7AC \uC9C1\uC804 \uD398\uC774\uC9C0\uC5D0\uC11C diagram-* prefix \uCCA8\uBD80 \uC77C\uAD04 \uC0AD\uC81C. \uB204\uC801 \uBC29\uC9C0\uC6A9. Default false (\uC548\uC804). \uCCAB \uAC8C\uC7AC(create)\uC5D4 \uD6A8\uACFC \uC5C6\uC74C."),
|
|
160270
160296
|
diagram_format: external_exports.enum(["png", "svg"]).optional().default("png").describe("Image format for rendered diagrams. Default png."),
|
|
160271
160297
|
diagram_dpi: external_exports.number().optional().default(96).describe("Rendering DPI (PNG only, ignored for SVG). Default 96.")
|
|
160272
|
-
}, async ({ space_key, title, markdown, markdown_file, parent_page_id, page_id, render_diagrams = true, diagram_format = "png", diagram_dpi = 96 }) => {
|
|
160298
|
+
}, async ({ space_key, title, markdown, markdown_file, parent_page_id, page_id, render_diagrams = true, clean_stale_attachments = false, diagram_format = "png", diagram_dpi = 96 }) => {
|
|
160273
160299
|
if (!markdown && !markdown_file) {
|
|
160274
160300
|
throw new Error("Either markdown or markdown_file must be provided");
|
|
160275
160301
|
}
|
|
@@ -160329,15 +160355,36 @@ var registerConfluenceTools = (ctx2) => {
|
|
|
160329
160355
|
currentPageId = result?.id;
|
|
160330
160356
|
currentVersion = result?.version?.number ?? 1;
|
|
160331
160357
|
}
|
|
160358
|
+
if (mode === "update" && clean_stale_attachments) {
|
|
160359
|
+
renderTrace.push("cleanup entry");
|
|
160360
|
+
try {
|
|
160361
|
+
const existing = await confluence2.listAttachments(currentPageId);
|
|
160362
|
+
const targets = existing.filter((a) => /^diagram-/.test(a.title));
|
|
160363
|
+
let deleted = 0;
|
|
160364
|
+
for (const att of targets) {
|
|
160365
|
+
try {
|
|
160366
|
+
await confluence2.deleteAttachment(currentPageId, att.id);
|
|
160367
|
+
deleted++;
|
|
160368
|
+
} catch (err) {
|
|
160369
|
+
renderTrace.push(`cleanup delete FAIL ${att.title}: ${err?.message ?? err}`);
|
|
160370
|
+
}
|
|
160371
|
+
}
|
|
160372
|
+
renderTrace.push(`cleanup done: deleted=${deleted}/${targets.length}`);
|
|
160373
|
+
} catch (err) {
|
|
160374
|
+
renderTrace.push(`cleanup FAIL: ${err?.message ?? err}`);
|
|
160375
|
+
}
|
|
160376
|
+
}
|
|
160332
160377
|
let finalStorage = storage;
|
|
160333
160378
|
renderTrace.push(`round2 entry: renderedDiagrams.size=${renderedDiagrams.size}`);
|
|
160334
160379
|
if (render_diagrams && renderedDiagrams.size > 0) {
|
|
160335
160380
|
let attIter = 0;
|
|
160381
|
+
const attachmentNames = /* @__PURE__ */ new Map();
|
|
160336
160382
|
for (const [id, diag] of renderedDiagrams) {
|
|
160337
160383
|
attIter++;
|
|
160338
|
-
const filename = `diagram-${id}.${diag.format}`;
|
|
160384
|
+
const filename = `diagram-${id}-${Date.now()}.${diag.format}`;
|
|
160339
160385
|
try {
|
|
160340
160386
|
await confluence2.addAttachment(currentPageId, filename, diag.buffer, diag.contentType);
|
|
160387
|
+
attachmentNames.set(id, filename);
|
|
160341
160388
|
renderTrace.push(`attach ${attIter}/${renderedDiagrams.size}: ${filename} OK`);
|
|
160342
160389
|
} catch (err) {
|
|
160343
160390
|
renderTrace.push(`attach ${attIter}/${renderedDiagrams.size}: ${filename} FAIL \u2014 ${err?.message ?? err}`);
|
|
@@ -160347,7 +160394,7 @@ var registerConfluenceTools = (ctx2) => {
|
|
|
160347
160394
|
}
|
|
160348
160395
|
}
|
|
160349
160396
|
renderTrace.push(`round2 pre-replace: renderedDiagrams.size=${renderedDiagrams.size}`);
|
|
160350
|
-
finalStorage = replacePumlMarkers(storage, renderedDiagrams, pumlRegistry);
|
|
160397
|
+
finalStorage = replacePumlMarkers(storage, renderedDiagrams, pumlRegistry, attachmentNames);
|
|
160351
160398
|
renderTrace.push(`round2 post-replace: finalStorage.length=${finalStorage.length}, contains-image=${finalStorage.includes("<ac:image")}`);
|
|
160352
160399
|
result = await confluence2.updatePage(currentPageId, title, finalStorage, currentVersion);
|
|
160353
160400
|
renderTrace.push(`round2 update: v${result?.version?.number}, body_chars=${finalStorage.length}`);
|
|
@@ -160382,10 +160429,10 @@ var registerConfluenceTools = (ctx2) => {
|
|
|
160382
160429
|
};
|
|
160383
160430
|
});
|
|
160384
160431
|
};
|
|
160385
|
-
function replacePumlMarkers(storage, rendered, pumlRegistry) {
|
|
160432
|
+
function replacePumlMarkers(storage, rendered, pumlRegistry, attachmentNames) {
|
|
160386
160433
|
let out = storage;
|
|
160387
160434
|
for (const [id, diag] of rendered) {
|
|
160388
|
-
const filename = `diagram-${id}.${diag.format}`;
|
|
160435
|
+
const filename = attachmentNames?.get(id) ?? `diagram-${id}.${diag.format}`;
|
|
160389
160436
|
const imageMacro = `<ac:image ac:alt="PlantUML Diagram ${id}"><ri:attachment ri:filename="${filename}"/></ac:image>`;
|
|
160390
160437
|
out = out.replace(
|
|
160391
160438
|
new RegExp(`<p data-puml-id="${id}"></p>`),
|
|
@@ -185563,7 +185610,7 @@ var xrayClientSecret = process.env.XRAY_CLIENT_SECRET || "";
|
|
|
185563
185610
|
var xray = xrayClientId && xrayClientSecret ? new XrayClient(xrayClientId, xrayClientSecret, jiraUrl, jiraEmail, jiraToken) : null;
|
|
185564
185611
|
var server = new McpServer({
|
|
185565
185612
|
name: "jira-confluence",
|
|
185566
|
-
version: "0.1.
|
|
185613
|
+
version: "0.1.37"
|
|
185567
185614
|
});
|
|
185568
185615
|
var ctx = { server, jira, confluence, risk, xray };
|
|
185569
185616
|
registerJiraCrudTools(ctx);
|