@neobiotechlabs/neobiotech-dev-agent 0.1.16 → 0.1.18

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/index.js CHANGED
@@ -3780,6 +3780,7 @@ var require_fast_uri = __commonJS({
3780
3780
  return uriTokens.join("");
3781
3781
  }
3782
3782
  var URI_PARSE = /^(?:([^#/:?]+):)?(?:\/\/((?:([^#/?@]*)@)?(\[[^#/?\]]+\]|[^#/:?]*)(?::(\d*))?))?([^#?]*)(?:\?([^#]*))?(?:#((?:.|[\n\r])*))?/u;
3783
+ var AUTHORITY_PREFIX = /^(?:[^#/:?]+:)?\/\/([^/?#]*)/;
3783
3784
  function getParseError(parsed, matches) {
3784
3785
  if (matches[2] !== void 0 && parsed.path && parsed.path[0] !== "/") {
3785
3786
  return 'URI path must start with "/" when authority is present.';
@@ -3809,6 +3810,11 @@ var require_fast_uri = __commonJS({
3809
3810
  uri = "//" + uri;
3810
3811
  }
3811
3812
  }
3813
+ const authorityMatch = uri.match(AUTHORITY_PREFIX);
3814
+ if (authorityMatch !== null && authorityMatch[1].indexOf("\\") !== -1) {
3815
+ parsed.error = "URI authority must not contain a literal backslash.";
3816
+ malformedAuthorityOrPort = true;
3817
+ }
3812
3818
  const matches = uri.match(URI_PARSE);
3813
3819
  if (matches) {
3814
3820
  parsed.scheme = matches[1];
@@ -17100,8 +17106,8 @@ var require_adm_zip = __commonJS({
17100
17106
  return null;
17101
17107
  }
17102
17108
  function fixPath(zipPath) {
17103
- const { join: join4, normalize, sep } = pth.posix;
17104
- return join4(pth.isAbsolute(zipPath) ? "/" : ".", normalize(sep + zipPath.split("\\").join(sep) + sep));
17109
+ const { join: join5, normalize, sep } = pth.posix;
17110
+ return join5(pth.isAbsolute(zipPath) ? "/" : ".", normalize(sep + zipPath.split("\\").join(sep) + sep));
17105
17111
  }
17106
17112
  function filenameFilter(filterfn) {
17107
17113
  if (filterfn instanceof RegExp) {
@@ -160536,7 +160542,7 @@ var PA_DOCUMENTS = [
160536
160542
  "[Classification]",
160537
160543
  "[SW Development Plan]",
160538
160544
  "[Risk Management Plan]",
160539
- "[Security Maintenance Plan]",
160545
+ "[Security Management Plan]",
160540
160546
  "[Configuration Management Plan]"
160541
160547
  ];
160542
160548
  var EA_DOCUMENTS = [
@@ -160741,7 +160747,7 @@ var TOOL_CATALOG = [
160741
160747
  { name: "jira_init_project", description: "\uD504\uB85C\uC81D\uD2B8 \uD3F4\uB354\uC5D0 Jira \uD504\uB85C\uC81D\uD2B8 \uD0A4 \uCD08\uAE30 \uC124\uC815 (CLAUDE.md \uC5C5\uB370\uC774\uD2B8)", category: "\uCD08\uAE30 \uC124\uC815" },
160742
160748
  { name: "help", description: "\uC0AC\uC6A9 \uAC00\uB2A5\uD55C \uB3C4\uAD6C \uCE74\uD0C8\uB85C\uADF8\uC640 \uC6CC\uD06C\uD50C\uB85C\uC6B0 \uAC00\uC774\uB4DC \uC870\uD68C (topic \uC9C0\uC815 \uAC00\uB2A5)", category: "\uB3C4\uC6C0\uB9D0" },
160743
160749
  // 문서 첨부 — 마크다운 → PDF 변환 + Jira 첨부
160744
- { name: "markdown_to_pdf", description: "\uB9C8\uD06C\uB2E4\uC6B4 \u2192 PDF \uBCC0\uD658 (\uC2DC\uC2A4\uD15C \uD3F0\uD2B8 \uC790\uB3D9 \uAC10\uC9C0, \uD45C\uC9C0/\uBA38\uB9AC\uB9D0\xB7\uAF2C\uB9AC\uB9D0 \uC635\uC158)", category: "\uBB38\uC11C \uCCA8\uBD80" },
160750
+ { name: "markdown_to_pdf", description: "\uB9C8\uD06C\uB2E4\uC6B4 \u2192 PDF \uBCC0\uD658 (\uBC88\uB4E4 Pretendard Regular+Bold, \uD45C\uC9C0/\uBA38\uB9AC\uB9D0\xB7\uAF2C\uB9AC\uB9D0 \uC635\uC158)", category: "\uBB38\uC11C \uCCA8\uBD80" },
160745
160751
  { name: "jira_attach_file", description: "Jira \uC774\uC288\uC5D0 \uD30C\uC77C \uCCA8\uBD80 (file_path \uB610\uB294 base64 \uC785\uB825, 50MB \uCD08\uACFC \uC2DC warning, 100MB \uCD08\uACFC \uC2DC hard limit)", category: "\uBB38\uC11C \uCCA8\uBD80" },
160746
160752
  { name: "jira_attach_pdf_from_markdown", description: "\uB9C8\uD06C\uB2E4\uC6B4 \u2192 PDF \u2192 Jira \uCCA8\uBD80 \uC6D0\uC0F7 (description \uBBF8\uC218\uC815, summary \uCF54\uBA58\uD2B8 \uC635\uC158)", category: "\uBB38\uC11C \uCCA8\uBD80" },
160747
160753
  {
@@ -160771,7 +160777,7 @@ var WORKFLOWS = {
160771
160777
  "4. [Classification] \u2192 \uBD84\uB958 \uD2F0\uCF13 (MDR Class \uD310\uC815)",
160772
160778
  "5. [SW Development Plan] \u2192 \uAC1C\uBC1C \uACC4\uD68D\uC11C \uC791\uC131",
160773
160779
  "6. [Risk Management Plan] \u2192 \uB9AC\uC2A4\uD06C \uAD00\uB9AC \uACC4\uD68D\uC11C \uC791\uC131",
160774
- "7. [Security Maintenance Plan] \u2192 \uBCF4\uC548 \uC720\uC9C0\uBCF4\uC218 \uACC4\uD68D\uC11C",
160780
+ "7. [Security Management Plan] \u2192 \uBCF4\uC548 \uAD00\uB9AC \uACC4\uD68D\uC11C (IEC 81001-5-1)",
160775
160781
  "8. [Configuration Management Plan] \u2192 \uD615\uC0C1 \uAD00\uB9AC \uACC4\uD68D\uC11C",
160776
160782
  "",
160777
160783
  "\uC0AC\uC6A9\uC131 \uACF5\uD559 (IEC 62366-1)\uB3C4 PA \uB2E8\uACC4\uC5D0\uC11C \uC2DC\uC791:",
@@ -161471,6 +161477,8 @@ var TEMPLATE_PATHS = {
161471
161477
  risk_management_plan: { path: "mdr_docs/PA/risk-management-plan.md", mode: "freeform", domain: "mdr", gate: "PA" },
161472
161478
  mdr_classification_document: { path: "mdr_docs/PA/mdr-classification-document.md", mode: "freeform", domain: "mdr", gate: "PA" },
161473
161479
  software_development_maintenance_plan: { path: "mdr_docs/PA/software-development-maintenance-plan.md", mode: "freeform", domain: "mdr", gate: "PA" },
161480
+ configuration_management_plan: { path: "mdr_docs/PA/configuration-management-plan.md", mode: "freeform", domain: "mdr", gate: "PA" },
161481
+ security_management_plan: { path: "mdr_docs/PA/security-management-plan.md", mode: "freeform", domain: "mdr", gate: "PA" },
161474
161482
  // mdr_docs — EA gate (IEC 62304 Evolutionary Assessment 산출물)
161475
161483
  software_requirements_list: { path: "mdr_docs/EA/software-requirements-list.md", mode: "freeform", domain: "mdr", gate: "EA" },
161476
161484
  software_architecture_description: { path: "mdr_docs/EA/software-architecture-description.md", mode: "freeform", domain: "mdr", gate: "EA" },
@@ -161481,7 +161489,7 @@ var TEMPLATE_PATHS = {
161481
161489
  list_of_hazard_related_use_scenarios: { path: "mdr_docs/EA/list-of-hazard-related-use-scenarios.md", mode: "freeform", domain: "mdr", gate: "EA" }
161482
161490
  };
161483
161491
  var TEMPLATE_NAMES = Object.keys(TEMPLATE_PATHS);
161484
- var TEMPLATE_VERSION = "4dc0c46";
161492
+ var TEMPLATE_VERSION = "b1dbcb6";
161485
161493
 
161486
161494
  // src/tools/doc-render.ts
161487
161495
  init_adf();
@@ -161493,7 +161501,7 @@ var registerDocRenderTools = (ctx2) => {
161493
161501
  "doc_render",
161494
161502
  "dev_docs(PRD/Spec/Plan/Tasks)\xB7mdr_docs(PA/EA gate \uBB38\uC11C \uC804\uCCB4) \uD15C\uD50C\uB9BF\uC744 \uBAA8\uB4DC \uAE30\uBC18 \uACB0\uC815\uC801 \uB80C\uB354(freeform). \uD2F0\uCF13 \uBA54\uD0C0({TICKET_ID}/{DATE} \uB4F1)\uB9CC \uCE58\uD658\uD558\uACE0, \uC790\uC720 \uD14D\uC2A4\uD2B8/\uAEBE\uC1E0 placeholder/AI \uC9C0\uCE68/\uBE48 \uB370\uC774\uD130 \uD45C\uB294 consumer\uAC00 \uCC44\uC6B0\uB3C4\uB85D \uBC18\uD658. (\uB808\uC774\uC5B4 \u2461 \u2014 LLM \uD638\uCD9C \uC5C6\uC74C)",
161495
161503
  {
161496
- template: external_exports.enum(TEMPLATE_NAMES).describe("\uB80C\uB354\uD560 \uD15C\uD50C\uB9BF \u2014 dev: prd|spec|plan|tasks / PA gate: intended_use|system_requirements_spec|risk_management_plan|mdr_classification_document|software_development_maintenance_plan / EA gate: software_requirements_list|software_architecture_description|risk_table_fmea|soup_list|usability_evaluation_plan|clinical_evaluation_plan|list_of_hazard_related_use_scenarios"),
161504
+ template: external_exports.enum(TEMPLATE_NAMES).describe("\uB80C\uB354\uD560 \uD15C\uD50C\uB9BF \u2014 dev: prd|spec|plan|tasks / PA gate: intended_use|system_requirements_spec|risk_management_plan|mdr_classification_document|software_development_maintenance_plan|configuration_management_plan|security_management_plan / EA gate: software_requirements_list|software_architecture_description|risk_table_fmea|soup_list|usability_evaluation_plan|clinical_evaluation_plan|list_of_hazard_related_use_scenarios"),
161497
161505
  ticket_key: external_exports.string().describe("Jira \uD2F0\uCF13 \uD0A4 (\uC608: ER-123). description/\uBA54\uD0C0 fetch\uC5D0 \uC0AC\uC6A9"),
161498
161506
  feature_slug: external_exports.string().optional().describe("{FEATURE_SLUG} \uCE58\uD658\uC6A9 (\uC608: add-login). \uBBF8\uC81C\uACF5 \uC2DC placeholder\uB85C \uB0A8\uC74C")
161499
161507
  },
@@ -161574,13 +161582,13 @@ var registerDocRenderTools = (ctx2) => {
161574
161582
  };
161575
161583
 
161576
161584
  // src/tools/jira-attach.ts
161577
- import { readFileSync as readFileSync5 } from "fs";
161585
+ import { readFileSync as readFileSync4 } from "fs";
161578
161586
  import { execFileSync } from "child_process";
161579
161587
 
161580
161588
  // src/utils/markdownToPdf.ts
161581
- import { existsSync as existsSync2, writeFileSync as writeFileSync3 } from "fs";
161589
+ import { writeFileSync as writeFileSync3 } from "fs";
161582
161590
  import { tmpdir } from "os";
161583
- import { join as join3 } from "path";
161591
+ import { join as join4 } from "path";
161584
161592
  import { randomUUID } from "crypto";
161585
161593
 
161586
161594
  // node_modules/pdfkit/js/pdfkit.es.js
@@ -183629,45 +183637,59 @@ PDFDocument.LineWrapper = LineWrapper;
183629
183637
 
183630
183638
  // src/utils/markdownToPdf.ts
183631
183639
  init_marked_esm();
183632
- var FRONTMATTER_RE2 = /^---\r?\n[\s\S]*?\r?\n---\r?\n?/;
183633
- function stripFrontmatter2(md) {
183634
- return md.replace(FRONTMATTER_RE2, "");
183640
+
183641
+ // src/utils/pdf-fonts.ts
183642
+ import { existsSync as existsSync2 } from "fs";
183643
+ import { join as join3, dirname } from "path";
183644
+ import { fileURLToPath as fileURLToPath2 } from "url";
183645
+ var BUNDLED_FONT_FAMILY = "BodyFont";
183646
+ var BUNDLED_FONT_FAMILY_BOLD = "BodyFont-Bold";
183647
+ var DEFAULT_FAMILY = "BodyFont";
183648
+ function bundledFontPaths() {
183649
+ const here = typeof __dirname !== "undefined" ? __dirname : dirname(fileURLToPath2(import.meta.url));
183650
+ const rootFromSrc = join3(here, "..", "..");
183651
+ const candidates = [join3(here, "fonts"), join3(rootFromSrc, "dist", "fonts")];
183652
+ const fontsDir = candidates.find(
183653
+ (d2) => existsSync2(join3(d2, "Pretendard-Regular.ttf")) && existsSync2(join3(d2, "Pretendard-Bold.ttf"))
183654
+ );
183655
+ const dir = fontsDir || candidates[0];
183656
+ return {
183657
+ regular: join3(dir, "Pretendard-Regular.ttf"),
183658
+ bold: join3(dir, "Pretendard-Bold.ttf")
183659
+ };
183635
183660
  }
183636
- function getCandidates() {
183637
- if (process.platform === "win32") {
183638
- const winDir = process.env.WINDIR || process.env.SystemRoot || "C:\\Windows";
183639
- const fontsDir = join3(winDir, "Fonts");
183640
- return [
183641
- { path: join3(fontsDir, "malgun.ttf"), family: "MalgunGothic", isCjk: true, source: "malgun-gothic" },
183642
- { path: join3(fontsDir, "msgothic.ttc"), family: "MSGothic", isCjk: true, source: "msgothic" },
183643
- { path: join3(fontsDir, "YuGothM.ttc"), family: "YuGothic", isCjk: true, source: "yu-gothic" },
183644
- { path: join3(fontsDir, "arial.ttf"), family: "Arial", isCjk: false, source: "arial" }
183645
- ];
183661
+ function resolveFonts(opts) {
183662
+ const familyName = opts.fontFamilyName || DEFAULT_FAMILY;
183663
+ if (opts.fontPath) {
183664
+ return {
183665
+ regularFamily: familyName,
183666
+ boldFamily: familyName,
183667
+ regularPath: opts.fontPath
183668
+ };
183646
183669
  }
183647
- if (process.platform === "darwin") {
183648
- return [
183649
- { path: "/System/Library/Fonts/AppleSDGothicNeo.ttc", family: "AppleSDGothicNeo", isCjk: true, source: "apple-sd-gothic" },
183650
- { path: "/System/Library/Fonts/PingFang.ttc", family: "PingFang", isCjk: true, source: "pingfang" },
183651
- { path: "/System/Library/Fonts/Helvetica.ttc", family: "Helvetica", isCjk: false, source: "helvetica" }
183652
- ];
183670
+ const paths = bundledFontPaths();
183671
+ if (existsSync2(paths.regular) && existsSync2(paths.bold)) {
183672
+ return {
183673
+ regularFamily: BUNDLED_FONT_FAMILY,
183674
+ boldFamily: BUNDLED_FONT_FAMILY_BOLD,
183675
+ regularPath: paths.regular,
183676
+ boldPath: paths.bold
183677
+ };
183653
183678
  }
183654
- return [
183655
- { path: "/usr/share/fonts/opentype/noto/NotoSansCJK-Regular.ttc", family: "NotoSansCJK", isCjk: true, source: "noto-cjk" },
183656
- { path: "/usr/share/fonts/opentype/noto/NotoSansCJK-Regular.otf", family: "NotoSansCJK", isCjk: true, source: "noto-cjk" },
183657
- { path: "/usr/share/fonts/truetype/noto/NotoSansCJK-Regular.ttc", family: "NotoSansCJK", isCjk: true, source: "noto-cjk" },
183658
- { path: "/usr/share/fonts/truetype/nanum/NanumGothic.ttf", family: "NanumGothic", isCjk: true, source: "nanum-gothic" },
183659
- { path: "/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf", family: "DejaVuSans", isCjk: false, source: "dejavu" }
183660
- ];
183679
+ const warning = `[pdf-fonts] Bundled Pretendard font not found at ${paths.regular}. Falling back to pdfkit default (Helvetica). CJK characters will render as missing glyphs. Run \`npm run build\` to copy fonts.`;
183680
+ console.error(warning);
183681
+ return {
183682
+ regularFamily: "Helvetica",
183683
+ boldFamily: "Helvetica",
183684
+ warning
183685
+ };
183661
183686
  }
183662
- function detectSystemFont() {
183663
- for (const c of getCandidates()) {
183664
- if (existsSync2(c.path)) {
183665
- return { fontPath: c.path, familyName: c.family, isCjk: c.isCjk, source: c.source };
183666
- }
183667
- }
183668
- return null;
183687
+
183688
+ // src/utils/markdownToPdf.ts
183689
+ var FRONTMATTER_RE2 = /^---\r?\n[\s\S]*?\r?\n---\r?\n?/;
183690
+ function stripFrontmatter2(md) {
183691
+ return md.replace(FRONTMATTER_RE2, "");
183669
183692
  }
183670
- var FONT_FAMILY_DEFAULT = "BodyFont";
183671
183693
  var PAGE_SIZE_DEFAULT = "A4";
183672
183694
  var MARGIN_DEFAULT = 56;
183673
183695
  async function markdownToPdf(md, opts = {}) {
@@ -183677,7 +183699,7 @@ async function markdownToPdf(md, opts = {}) {
183677
183699
  includeCover = false,
183678
183700
  author = "jira-mcp-server",
183679
183701
  fontPath,
183680
- fontFamilyName = FONT_FAMILY_DEFAULT,
183702
+ fontFamilyName,
183681
183703
  pageSize = PAGE_SIZE_DEFAULT,
183682
183704
  margin = {},
183683
183705
  language = "en",
@@ -183689,19 +183711,8 @@ async function markdownToPdf(md, opts = {}) {
183689
183711
  const right = margin.right ?? MARGIN_DEFAULT;
183690
183712
  const resolvedTitle = title || sourceRef || "Document";
183691
183713
  const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
183692
- let fontFamily = fontFamilyName;
183693
- let fontWarning;
183694
- if (fontPath) {
183695
- fontFamily = fontFamilyName;
183696
- } else {
183697
- const detected = detectSystemFont();
183698
- if (detected) {
183699
- fontFamily = detected.familyName;
183700
- } else {
183701
- fontWarning = `[markdownToPdf] No CJK-capable system font detected; using pdfkit default (Helvetica). CJK characters will render as missing glyphs. Pass fontPath to override.`;
183702
- console.error(fontWarning);
183703
- }
183704
- }
183714
+ const resolved = resolveFonts({ fontPath, fontFamilyName });
183715
+ let fontWarning = resolved.warning;
183705
183716
  const doc = new PDFDocument({
183706
183717
  info: { Title: resolvedTitle, Author: author, CreationDate: /* @__PURE__ */ new Date() },
183707
183718
  size: pageSize,
@@ -183710,35 +183721,29 @@ async function markdownToPdf(md, opts = {}) {
183710
183721
  // 머리말/꼬리말 후 페이지 사후 그리기 + bufferedPageRange() 사용
183711
183722
  });
183712
183723
  void language;
183713
- if (fontPath) {
183724
+ if (resolved.regularPath) {
183714
183725
  try {
183715
- doc.registerFont(fontFamilyName, fontPath);
183716
- doc.font(fontFamilyName);
183717
- fontFamily = fontFamilyName;
183726
+ doc.registerFont(resolved.regularFamily, resolved.regularPath);
183727
+ doc.font(resolved.regularFamily);
183718
183728
  } catch (err) {
183719
- fontWarning = `[markdownToPdf] Failed to register font "${fontPath}": ${err?.message ?? err}. Falling back to pdfkit default.`;
183729
+ fontWarning = `[markdownToPdf] Failed to register regular font "${resolved.regularPath}": ${err?.message ?? err}. Falling back to pdfkit default.`;
183720
183730
  console.error(fontWarning);
183721
- doc.font("Helvetica");
183722
- fontFamily = "Helvetica";
183731
+ resolved.regularFamily = "Helvetica";
183732
+ resolved.boldFamily = "Helvetica";
183723
183733
  }
183724
- } else if (fontFamily !== FONT_FAMILY_DEFAULT) {
183725
- const detected = detectSystemFont();
183726
- if (detected) {
183727
- try {
183728
- doc.registerFont(detected.familyName, detected.fontPath);
183729
- doc.font(detected.familyName);
183730
- fontFamily = detected.familyName;
183731
- } catch (err) {
183732
- fontWarning = `[markdownToPdf] Failed to register detected font "${detected.fontPath}": ${err?.message ?? err}. Falling back to pdfkit default.`;
183733
- console.error(fontWarning);
183734
- doc.font("Helvetica");
183735
- fontFamily = "Helvetica";
183736
- }
183734
+ }
183735
+ if (resolved.boldPath && resolved.boldFamily !== resolved.regularFamily) {
183736
+ try {
183737
+ doc.registerFont(resolved.boldFamily, resolved.boldPath);
183738
+ doc.font(resolved.boldFamily);
183739
+ } catch (err) {
183740
+ fontWarning = `[markdownToPdf] Failed to register bold font "${resolved.boldPath}": ${err?.message ?? err}. Bold falls back to regular.`;
183741
+ console.error(fontWarning);
183742
+ resolved.boldFamily = resolved.regularFamily;
183737
183743
  }
183738
- } else {
183739
- doc.font("Helvetica");
183740
- fontFamily = "Helvetica";
183741
183744
  }
183745
+ doc.font(resolved.regularFamily);
183746
+ const fonts = resolved;
183742
183747
  if (includeCover) {
183743
183748
  doc.moveDown(8);
183744
183749
  doc.fontSize(28).fillColor("#000").text(resolvedTitle, { align: "left" });
@@ -183758,7 +183763,7 @@ async function markdownToPdf(md, opts = {}) {
183758
183763
  const body = stripFrontmatter2(md ?? "");
183759
183764
  const tokens = g.lexer(body);
183760
183765
  for (const tok of tokens) {
183761
- renderToken(doc, tok, fontFamily);
183766
+ renderToken(doc, tok, fonts);
183762
183767
  }
183763
183768
  if (tokens.length === 0) {
183764
183769
  doc.fontSize(11).fillColor("#000").text("");
@@ -183854,26 +183859,26 @@ function lineHeightOf(doc) {
183854
183859
  const size = doc._fontSize ?? 11;
183855
183860
  return size * 1.2;
183856
183861
  }
183857
- function renderToken(doc, tok, fontFamily) {
183862
+ function renderToken(doc, tok, fonts) {
183858
183863
  if (!tok || typeof tok !== "object") return;
183859
183864
  switch (tok.type) {
183860
183865
  case "heading":
183861
- renderHeading(doc, tok, fontFamily);
183866
+ renderHeading(doc, tok, fonts);
183862
183867
  return;
183863
183868
  case "paragraph":
183864
- renderParagraph(doc, tok, fontFamily);
183869
+ renderParagraph(doc, tok, fonts);
183865
183870
  return;
183866
183871
  case "code":
183867
183872
  renderCodeBlock(doc, tok);
183868
183873
  return;
183869
183874
  case "blockquote":
183870
- renderBlockquote(doc, tok, fontFamily);
183875
+ renderBlockquote(doc, tok, fonts);
183871
183876
  return;
183872
183877
  case "list":
183873
- renderList(doc, tok, fontFamily, 0);
183878
+ renderList(doc, tok, fonts, 0);
183874
183879
  return;
183875
183880
  case "table":
183876
- renderTable(doc, tok, fontFamily);
183881
+ renderTable(doc, tok, fonts);
183877
183882
  return;
183878
183883
  case "hr":
183879
183884
  renderHr(doc);
@@ -183883,18 +183888,18 @@ function renderToken(doc, tok, fontFamily) {
183883
183888
  return;
183884
183889
  default:
183885
183890
  if (tok.text) {
183886
- doc.fontSize(BODY_FONT_SIZE).fillColor("#000").font(fontFamily).text(String(tok.text));
183891
+ doc.fontSize(BODY_FONT_SIZE).fillColor("#000").font(fonts.regularFamily).text(String(tok.text));
183887
183892
  }
183888
183893
  }
183889
183894
  }
183890
- function renderHeading(doc, tok, fontFamily) {
183895
+ function renderHeading(doc, tok, fonts) {
183891
183896
  const level = Math.min(Math.max(Number(tok.depth) || 1, 1), 6);
183892
183897
  const size = HEADING_SIZES[level - 1];
183893
183898
  const text = tok.text ?? "";
183894
183899
  const marginL = doc.page.margins.left;
183895
183900
  const marginR = doc.page.margins.right;
183896
183901
  const width = doc.page.width - marginL - marginR;
183897
- doc.fontSize(size).fillColor("#111").font(fontFamily);
183902
+ doc.fontSize(size).fillColor("#111").font(fonts.boldFamily);
183898
183903
  const before = level === 1 ? SPACE2.H1_BEFORE : level === 2 ? SPACE2.H2_BEFORE : level === 3 ? SPACE2.H3_BEFORE : SPACE2.H4_H6_BEFORE;
183899
183904
  doc.y += before;
183900
183905
  const lineHeight = lineHeightOf(doc);
@@ -183907,12 +183912,12 @@ function renderHeading(doc, tok, fontFamily) {
183907
183912
  doc.y += after;
183908
183913
  void lines;
183909
183914
  }
183910
- function renderParagraph(doc, tok, fontFamily) {
183915
+ function renderParagraph(doc, tok, fonts) {
183911
183916
  const marginL = doc.page.margins.left;
183912
183917
  const marginR = doc.page.margins.right;
183913
183918
  const width = doc.page.width - marginL - marginR;
183914
183919
  const text = inlineToPlainText(tok.tokens ?? [{ type: "text", text: tok.text ?? "" }]);
183915
- doc.fontSize(BODY_FONT_SIZE).fillColor("#222").font(fontFamily);
183920
+ doc.fontSize(BODY_FONT_SIZE).fillColor("#222").font(fonts.regularFamily);
183916
183921
  doc.y += SPACE2.PARAGRAPH_BEFORE;
183917
183922
  const lineHeight = lineHeightOf(doc);
183918
183923
  if (text) {
@@ -183975,13 +183980,13 @@ function renderCodeBlock(doc, tok) {
183975
183980
  doc.fillColor("#000");
183976
183981
  doc.y = boxY + blockHeight + SPACE2.CODE_AFTER;
183977
183982
  }
183978
- function renderBlockquote(doc, tok, fontFamily) {
183983
+ function renderBlockquote(doc, tok, fonts) {
183979
183984
  const marginL = doc.page.margins.left;
183980
183985
  const marginR = doc.page.margins.right;
183981
183986
  const indent = 20;
183982
183987
  const width = doc.page.width - marginL - marginR - indent;
183983
183988
  doc.y += SPACE2.BLOCKQUOTE_BEFORE;
183984
- doc.fontSize(BODY_FONT_SIZE).fillColor("#555").font(fontFamily);
183989
+ doc.fontSize(BODY_FONT_SIZE).fillColor("#555").font(fonts.regularFamily);
183985
183990
  const flat = (tok.tokens ?? []).map(extractInlineText).filter(Boolean).join(" ");
183986
183991
  if (flat) {
183987
183992
  const lineHeight = lineHeightOf(doc);
@@ -184003,7 +184008,7 @@ function extractInlineText(node) {
184003
184008
  if (Array.isArray(node.tokens)) return node.tokens.map(extractInlineText).join("");
184004
184009
  return "";
184005
184010
  }
184006
- function renderList(doc, tok, fontFamily, depth) {
184011
+ function renderList(doc, tok, fonts, depth) {
184007
184012
  const items = tok.items ?? [];
184008
184013
  const ordered = !!tok.ordered;
184009
184014
  const marginL = doc.page.margins.left;
@@ -184018,43 +184023,38 @@ function renderList(doc, tok, fontFamily, depth) {
184018
184023
  for (const item of items) {
184019
184024
  const marker = ordered ? `${counter}. ` : "\u2022 ";
184020
184025
  counter += 1;
184021
- doc.fontSize(BODY_FONT_SIZE).fillColor("#222").font(fontFamily);
184026
+ doc.fontSize(BODY_FONT_SIZE).fillColor("#222").font(fonts.regularFamily);
184022
184027
  const markerX = marginL + indent;
184023
184028
  doc.text(marker, markerX, doc.y, { width: markerWidth, continued: false });
184024
184029
  doc.x = markerX + markerWidth;
184025
184030
  const bodyStartY = doc.y - lineHeightOf(doc);
184026
184031
  doc.y = bodyStartY;
184027
184032
  const subs = Array.isArray(item.tokens) ? item.tokens : [];
184028
- let firstInline = true;
184029
184033
  let drewAnything = false;
184030
184034
  for (const sub of subs) {
184031
184035
  if (sub?.type === "text" && Array.isArray(sub.tokens)) {
184032
- drawInlineInBox(doc, sub.tokens, fontFamily, doc.x, bodyStartY, itemWidth - markerWidth);
184036
+ drawInlineInBox(doc, sub.tokens, fonts, doc.x, bodyStartY, itemWidth - markerWidth);
184033
184037
  drewAnything = true;
184034
- firstInline = false;
184035
184038
  } else if (sub?.type === "paragraph") {
184036
184039
  drawInlineInBox(
184037
184040
  doc,
184038
184041
  sub.tokens ?? [{ type: "text", text: sub.text ?? "" }],
184039
- fontFamily,
184042
+ fonts,
184040
184043
  doc.x,
184041
184044
  bodyStartY,
184042
184045
  itemWidth - markerWidth
184043
184046
  );
184044
184047
  drewAnything = true;
184045
- firstInline = false;
184046
184048
  } else if (sub?.type === "list") {
184047
184049
  doc.x = marginL + indent + 12;
184048
184050
  doc.y = doc.y + SPACE2.LIST_ITEM_BEFORE;
184049
- renderList(doc, sub, fontFamily, depth + 1);
184050
- firstInline = false;
184051
+ renderList(doc, sub, fonts, depth + 1);
184051
184052
  } else {
184052
- renderToken(doc, sub, fontFamily);
184053
- firstInline = false;
184053
+ renderToken(doc, sub, fonts);
184054
184054
  }
184055
184055
  }
184056
184056
  if (!drewAnything && item.text) {
184057
- doc.font(fontFamily).text(String(item.text), doc.x, doc.y, {
184057
+ doc.font(fonts.regularFamily).text(String(item.text), doc.x, doc.y, {
184058
184058
  width: itemWidth - markerWidth
184059
184059
  });
184060
184060
  doc.y += lineHeightOf(doc);
@@ -184065,9 +184065,9 @@ function renderList(doc, tok, fontFamily, depth) {
184065
184065
  }
184066
184066
  doc.y += SPACE2.LIST_AFTER;
184067
184067
  }
184068
- function drawInlineInBox(doc, tokens, fontFamily, x3, y2, width) {
184068
+ function drawInlineInBox(doc, tokens, fonts, x3, y2, width) {
184069
184069
  const text = inlineToPlainText(tokens);
184070
- doc.fontSize(BODY_FONT_SIZE).fillColor("#222").font(fontFamily);
184070
+ doc.fontSize(BODY_FONT_SIZE).fillColor("#222").font(fonts.regularFamily);
184071
184071
  if (!text) return;
184072
184072
  const lineHeight = lineHeightOf(doc);
184073
184073
  const height = doc.heightOfString(text, { width });
@@ -184076,7 +184076,7 @@ function drawInlineInBox(doc, tokens, fontFamily, x3, y2, width) {
184076
184076
  doc.y += lineHeight;
184077
184077
  void lines;
184078
184078
  }
184079
- function renderTable(doc, tok, fontFamily) {
184079
+ function renderTable(doc, tok, fonts) {
184080
184080
  const header = tok.header ?? [];
184081
184081
  const rows = tok.rows ?? [];
184082
184082
  if (!header.length && !rows.length) return;
@@ -184095,7 +184095,7 @@ function renderTable(doc, tok, fontFamily) {
184095
184095
  if (totalColWidth > width) colWidth = width / colCount;
184096
184096
  const cellPadding = 4;
184097
184097
  const rowMinHeight = 14;
184098
- doc.fontSize(10).font(fontFamily).fillColor("#222");
184098
+ doc.fontSize(10).font(fonts.regularFamily).fillColor("#222");
184099
184099
  doc.y += SPACE2.TABLE_BEFORE;
184100
184100
  const drawRow = (cells, y3, isHeader) => {
184101
184101
  const texts = [];
@@ -184151,7 +184151,7 @@ function renderHr(doc) {
184151
184151
  doc.y = y2 + SPACE2.HR_AFTER;
184152
184152
  }
184153
184153
  function writePdfToTempFile(buffer) {
184154
- const path3 = join3(tmpdir(), `${randomUUID()}.pdf`);
184154
+ const path3 = join4(tmpdir(), `${randomUUID()}.pdf`);
184155
184155
  writeFileSync3(path3, buffer);
184156
184156
  return path3;
184157
184157
  }
@@ -184192,7 +184192,7 @@ var registerJiraAttachTools = (ctx2) => {
184192
184192
  const { server: server2, jira: jira2 } = ctx2;
184193
184193
  server2.tool(
184194
184194
  "markdown_to_pdf",
184195
- "\uB9C8\uD06C\uB2E4\uC6B4\uC744 PDF\uB85C \uBCC0\uD658 (\uC2DC\uC2A4\uD15C \uD3F0\uD2B8 \uC790\uB3D9 \uAC10\uC9C0, \uD45C\uC9C0/\uBA38\uB9AC\uB9D0\xB7\uAF2C\uB9AC\uB9D0 \uC635\uC158). Jira \uCCA8\uBD80 \uC5C6\uC774 PDF \uACB0\uACFC\uB9CC \uBC18\uD658.",
184195
+ "\uB9C8\uD06C\uB2E4\uC6B4\uC744 PDF\uB85C \uBCC0\uD658 (\uBC88\uB4E4 Pretendard Regular+Bold, \uD45C\uC9C0/\uBA38\uB9AC\uB9D0\xB7\uAF2C\uB9AC\uB9D0 \uC635\uC158). Jira \uCCA8\uBD80 \uC5C6\uC774 PDF \uACB0\uACFC\uB9CC \uBC18\uD658.",
184196
184196
  {
184197
184197
  markdown: external_exports.string().optional().describe("PDF\uB85C \uBCC0\uD658\uD560 \uB9C8\uD06C\uB2E4\uC6B4 \uBCF8\uBB38 (inline). markdown_file\uACFC \uB3D9\uC2DC \uC0AC\uC6A9 \uBD88\uAC00."),
184198
184198
  markdown_file: external_exports.string().optional().describe("\uB85C\uCEEC \uB9C8\uD06C\uB2E4\uC6B4 \uD30C\uC77C \uACBD\uB85C. \uD070 \uBB38\uC11C(25KB+)\uB294 \uBAA8\uB378 \uD234 \uD638\uCD9C \uCD9C\uB825 \uD55C\uACC4\uB85C inline markdown \uC778\uC790\uAC00 \uC798\uB9B4 \uC218 \uC788\uC5B4 \uD30C\uC77C \uC9C1\uC811 \uC77D\uAE30 \uAD8C\uC7A5. markdown\uACFC \uB3D9\uC2DC \uC0AC\uC6A9 \uBD88\uAC00."),
@@ -184200,7 +184200,7 @@ var registerJiraAttachTools = (ctx2) => {
184200
184200
  source_ref: external_exports.string().optional().describe('\uAF2C\uB9AC\uB9D0\xB7\uD45C\uC9C0 \uD478\uD130\uC5D0 \uD45C\uC2DC\uD560 \uCD9C\uCC98 (\uC608: "DYN-100 \xB7 2026-07-14")'),
184201
184201
  include_cover: external_exports.boolean().optional().default(false).describe("\uD45C\uC9C0 \uD398\uC774\uC9C0 \uD3EC\uD568 \uC5EC\uBD80 (\uAE30\uBCF8 false)"),
184202
184202
  author: external_exports.string().optional().describe('PDF /Author \uBA54\uD0C0 (\uAE30\uBCF8 "jira-mcp-server")'),
184203
- font_path: external_exports.string().optional().describe("\uD3F0\uD2B8 \uD30C\uC77C \uC808\uB300 \uACBD\uB85C (.ttf/.otf/.ttc). \uBBF8\uC9C0\uC815 \uC2DC \uC2DC\uC2A4\uD15C \uD3F0\uD2B8 \uC790\uB3D9 \uAC10\uC9C0"),
184203
+ font_path: external_exports.string().optional().describe("\uD3F0\uD2B8 \uD30C\uC77C \uC808\uB300 \uACBD\uB85C (.ttf/.otf/.ttc). \uBBF8\uC9C0\uC815 \uC2DC \uBC88\uB4E4 Pretendard \uC0AC\uC6A9 (\uBD80\uC7AC \uC2DC Helvetica \uD3F4\uBC31)"),
184204
184204
  font_family_name: external_exports.string().optional().describe('pdfkit registerFont \uC774\uB984 (\uAE30\uBCF8 "BodyFont")'),
184205
184205
  page_size: external_exports.enum(["A4", "LETTER", "LEGAL"]).optional().default("A4").describe("\uD398\uC774\uC9C0 \uD06C\uAE30"),
184206
184206
  output_mode: external_exports.enum(["base64", "tmpfile"]).optional().default("base64").describe("base64(\uAE30\uBCF8, JSON \uC548\uC804) / tmpfile(\uC808\uB300\uACBD\uB85C \uBC18\uD658, consumer\uAC00 unlink \uCC45\uC784)")
@@ -184215,7 +184215,7 @@ var registerJiraAttachTools = (ctx2) => {
184215
184215
  let md;
184216
184216
  if (markdown_file) {
184217
184217
  try {
184218
- md = readFileSync5(markdown_file, "utf8");
184218
+ md = readFileSync4(markdown_file, "utf8");
184219
184219
  } catch (err) {
184220
184220
  throw new Error(`Failed to read markdown_file "${markdown_file}": ${err?.message ?? err}`);
184221
184221
  }
@@ -184273,7 +184273,7 @@ var registerJiraAttachTools = (ctx2) => {
184273
184273
  let buffer;
184274
184274
  if (file_path) {
184275
184275
  try {
184276
- buffer = readFileSync5(file_path);
184276
+ buffer = readFileSync4(file_path);
184277
184277
  } catch (err) {
184278
184278
  throw new Error(`Failed to read file_path "${file_path}": ${err?.message ?? err}`);
184279
184279
  }
@@ -184349,7 +184349,7 @@ var registerJiraAttachTools = (ctx2) => {
184349
184349
  let md;
184350
184350
  if (markdown_file) {
184351
184351
  try {
184352
- md = readFileSync5(markdown_file, "utf8");
184352
+ md = readFileSync4(markdown_file, "utf8");
184353
184353
  } catch (err) {
184354
184354
  throw new Error(`Failed to read markdown_file "${markdown_file}": ${err?.message ?? err}`);
184355
184355
  }
@@ -184641,10 +184641,10 @@ var registerExpertTools = (ctx2) => {
184641
184641
  };
184642
184642
 
184643
184643
  // src/tools/review-criteria.ts
184644
- import { readFileSync as readFileSync6 } from "fs";
184645
- import { fileURLToPath as fileURLToPath2 } from "url";
184644
+ import { readFileSync as readFileSync5 } from "fs";
184645
+ import { fileURLToPath as fileURLToPath3 } from "url";
184646
184646
  import path2 from "path";
184647
- var __dirname3 = path2.dirname(fileURLToPath2(import.meta.url));
184647
+ var __dirname3 = path2.dirname(fileURLToPath3(import.meta.url));
184648
184648
  var CRITERIA_PATH = path2.resolve(__dirname3, "../data/review-criteria.md");
184649
184649
  var registerReviewCriteriaTools = (ctx2) => {
184650
184650
  ctx2.server.tool(
@@ -184654,7 +184654,7 @@ var registerReviewCriteriaTools = (ctx2) => {
184654
184654
  async () => {
184655
184655
  let body;
184656
184656
  try {
184657
- body = readFileSync6(CRITERIA_PATH, "utf8");
184657
+ body = readFileSync5(CRITERIA_PATH, "utf8");
184658
184658
  } catch (err) {
184659
184659
  body = [
184660
184660
  "# Review Criteria \u2014 \uB85C\uB4DC \uC2E4\uD328",
@@ -184691,7 +184691,7 @@ var xrayClientSecret = process.env.XRAY_CLIENT_SECRET || "";
184691
184691
  var xray = xrayClientId && xrayClientSecret ? new XrayClient(xrayClientId, xrayClientSecret, jiraUrl, jiraEmail, jiraToken) : null;
184692
184692
  var server = new McpServer({
184693
184693
  name: "jira-confluence",
184694
- version: "0.1.16"
184694
+ version: "0.1.18"
184695
184695
  });
184696
184696
  var ctx = { server, jira, confluence, risk, xray };
184697
184697
  registerJiraCrudTools(ctx);