@kodocagent/cli 0.4.0 → 0.4.1

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
@@ -9739,9 +9739,9 @@ var require_load = __commonJS({
9739
9739
  var require_lib3 = __commonJS({
9740
9740
  "../../node_modules/.pnpm/jszip@3.10.1/node_modules/jszip/lib/index.js"(exports, module) {
9741
9741
  "use strict";
9742
- function JSZip3() {
9743
- if (!(this instanceof JSZip3)) {
9744
- return new JSZip3();
9742
+ function JSZip5() {
9743
+ if (!(this instanceof JSZip5)) {
9744
+ return new JSZip5();
9745
9745
  }
9746
9746
  if (arguments.length) {
9747
9747
  throw new Error("The constructor with parameters has been removed in JSZip 3.0, please check the upgrade guide.");
@@ -9750,7 +9750,7 @@ var require_lib3 = __commonJS({
9750
9750
  this.comment = null;
9751
9751
  this.root = "";
9752
9752
  this.clone = function() {
9753
- var newObj = new JSZip3();
9753
+ var newObj = new JSZip5();
9754
9754
  for (var i in this) {
9755
9755
  if (typeof this[i] !== "function") {
9756
9756
  newObj[i] = this[i];
@@ -9759,16 +9759,16 @@ var require_lib3 = __commonJS({
9759
9759
  return newObj;
9760
9760
  };
9761
9761
  }
9762
- JSZip3.prototype = require_object();
9763
- JSZip3.prototype.loadAsync = require_load();
9764
- JSZip3.support = require_support();
9765
- JSZip3.defaults = require_defaults();
9766
- JSZip3.version = "3.10.1";
9767
- JSZip3.loadAsync = function(content, options) {
9768
- return new JSZip3().loadAsync(content, options);
9762
+ JSZip5.prototype = require_object();
9763
+ JSZip5.prototype.loadAsync = require_load();
9764
+ JSZip5.support = require_support();
9765
+ JSZip5.defaults = require_defaults();
9766
+ JSZip5.version = "3.10.1";
9767
+ JSZip5.loadAsync = function(content, options) {
9768
+ return new JSZip5().loadAsync(content, options);
9769
9769
  };
9770
- JSZip3.external = require_external();
9771
- module.exports = JSZip3;
9770
+ JSZip5.external = require_external();
9771
+ module.exports = JSZip5;
9772
9772
  }
9773
9773
  });
9774
9774
 
@@ -10803,6 +10803,7 @@ import { copyFile, mkdir as mkdir3, readdir as readdir2, readFile as readFile3,
10803
10803
  import { basename as basename2, dirname as dirname22, extname, join as join22 } from "path";
10804
10804
  var import_jszip = __toESM(require_lib3(), 1);
10805
10805
  var import_jszip2 = __toESM(require_lib3(), 1);
10806
+ var import_jszip3 = __toESM(require_lib3(), 1);
10806
10807
  import { createTwoFilesPatch } from "diff";
10807
10808
  import { readFile as readFile22 } from "fs/promises";
10808
10809
  import { blocksToMarkdown, compare } from "@clazic/kordoc";
@@ -10833,26 +10834,35 @@ import {
10833
10834
  } from "docx";
10834
10835
  import { readFile as readFile6 } from "fs/promises";
10835
10836
  import { extname as extname6 } from "path";
10836
- import { extractFormFields, markdownToHwpx as markdownToHwpx2, parse as parse2 } from "@clazic/kordoc";
10837
+ import { parse as parse2 } from "@clazic/kordoc";
10837
10838
  import { z as z6 } from "zod";
10838
10839
  import { readFile as readFile7 } from "fs/promises";
10839
10840
  import { extname as extname7 } from "path";
10840
- import ExcelJS from "exceljs";
10841
+ import { extractFormFields, markdownToHwpx as markdownToHwpx2, parse as parse3 } from "@clazic/kordoc";
10842
+ var import_jszip4 = __toESM(require_lib3(), 1);
10841
10843
  import { z as z7 } from "zod";
10842
- import { readFile as readFile8, stat as stat3 } from "fs/promises";
10844
+ import { readFile as readFile8 } from "fs/promises";
10843
10845
  import { extname as extname8 } from "path";
10844
- import { parse as parse3 } from "@clazic/kordoc";
10846
+ import ExcelJS from "exceljs";
10845
10847
  import { z as z8 } from "zod";
10846
- import { readFile as fsReadFile, stat as stat4 } from "fs/promises";
10848
+ import { readFile as readFile9 } from "fs/promises";
10849
+ import { extname as extname9 } from "path";
10850
+ import { parse as parse4 } from "@clazic/kordoc";
10847
10851
  import { z as z9 } from "zod";
10852
+ import { readFile as readFile10, stat as stat3 } from "fs/promises";
10853
+ import { extname as extname10 } from "path";
10854
+ import { parse as parse5 } from "@clazic/kordoc";
10855
+ import { z as z10 } from "zod";
10856
+ import { readFile as fsReadFile, stat as stat4 } from "fs/promises";
10857
+ import { z as z11 } from "zod";
10848
10858
  import { stat as stat5 } from "fs/promises";
10849
- import { extname as extname9 } from "path";
10859
+ import { extname as extname11 } from "path";
10850
10860
  import { markdownToHwpx as markdownToHwpx3 } from "@clazic/kordoc";
10851
- import { z as z10 } from "zod";
10861
+ import { z as z12 } from "zod";
10852
10862
  import { stat as stat6 } from "fs/promises";
10853
- import { extname as extname10 } from "path";
10863
+ import { extname as extname12 } from "path";
10854
10864
  import ExcelJS2 from "exceljs";
10855
- import { z as z11 } from "zod";
10865
+ import { z as z13 } from "zod";
10856
10866
  async function resolveSafePath(cwd, p) {
10857
10867
  const normalizedCwd = normalize(cwd).normalize("NFC");
10858
10868
  const normalizedP = p.normalize("NFC");
@@ -11236,7 +11246,7 @@ function applyFormObjectEdits(xml, edits) {
11236
11246
  };
11237
11247
  continue;
11238
11248
  }
11239
- const openTag = xml.slice(pos, openTagEnd + 1);
11249
+ const _openTag = xml.slice(pos, openTagEnd + 1);
11240
11250
  const closeTag = `</${xmlTag}>`;
11241
11251
  const closeIdx = xml.indexOf(closeTag, openTagEnd);
11242
11252
  if (closeIdx < 0) {
@@ -11433,7 +11443,7 @@ function replaceAttrInOpenTag(xml, tagStart, tagEnd, attr, newValue) {
11433
11443
  text: newValue
11434
11444
  };
11435
11445
  }
11436
- function replaceEditText(xml, pos, elementContent, newText) {
11446
+ function replaceEditText(_xml, pos, elementContent, newText) {
11437
11447
  const escaped = escapeXml(newText);
11438
11448
  const selfCloseRe = /<hp:text\s*\/>/;
11439
11449
  const scm = selfCloseRe.exec(elementContent);
@@ -11837,39 +11847,22 @@ var listFilesTool = {
11837
11847
  return lines.join("\n") + truncateNotice;
11838
11848
  }
11839
11849
  };
11840
- var coordinateCellEditItemSchema = z4.object({
11841
- tableIndex: z4.number().int().nonnegative().describe(
11842
- "0-based \uD45C \uC778\uB371\uC2A4. read_document\uB85C \uC77D\uC740 kordoc \uBE14\uB85D \uC21C\uC11C\uC640 \uB3D9\uC77C (\uC911\uCCA9 \uD45C\uB294 \uBCC4\uB3C4 \uCE74\uC6B4\uD305 \uC548 \uD568)"
11843
- ),
11844
- row: z4.number().int().nonnegative().describe("\uC140\uC758 rowAddr (0-based, <hp:cellAddr rowAddr=..> \uAC12)"),
11845
- col: z4.number().int().nonnegative().describe("\uC140\uC758 colAddr (0-based, <hp:cellAddr colAddr=..> \uAC12)"),
11846
- newText: z4.string().describe("\uC140\uC5D0 \uC4F8 \uC0C8 \uD14D\uC2A4\uD2B8"),
11847
- expectedText: z4.string().optional().describe(
11848
- "\uD604\uC7AC \uC140 \uD14D\uC2A4\uD2B8 (\uC548\uC804 \uAC80\uC99D\uC6A9). \uC81C\uACF5 \uC2DC \uD604\uC7AC \uC140 \uD14D\uC2A4\uD2B8\uC640 \uC77C\uCE58\uD558\uC9C0 \uC54A\uC73C\uBA74 \uC218\uC815\uD558\uC9C0 \uC54A\uC74C. \uC798\uBABB\uB41C \uC140 \uC218\uC815\uC744 \uBC29\uC9C0\uD558\uB824\uBA74 \uBC18\uB4DC\uC2DC \uC0AC\uC6A9\uD558\uC138\uC694."
11849
- ),
11850
- // label 필드 없음 — 좌표 모드 전용
11851
- label: z4.undefined().optional()
11852
- });
11853
- var labelCellEditItemSchema = z4.object({
11854
- label: z4.string().describe(
11855
- "\uB808\uC774\uBE14 \uC140\uC758 \uD14D\uC2A4\uD2B8 (\uD2B8\uB9BC \uBE44\uAD50). \uC774 \uC140\uC758 \uC778\uC811 \uBC29\uD5A5(direction)\uC5D0 \uC788\uB294 \uC140\uC5D0 newText\uB97C \uAE30\uB85D\uD568."
11856
- ),
11857
- direction: z4.enum(["right", "below"]).optional().default("right").describe(
11858
- "\uB808\uC774\uBE14 \uAE30\uC900 \uB300\uC0C1 \uC140 \uBC29\uD5A5. right(\uAE30\uBCF8): \uC624\uB978\uCABD \uC140, below: \uC544\uB798 \uC140. \uBCD1\uD569 \uC140\uC740 span\uC744 \uACE0\uB824\uD558\uC5EC \uB300\uC0C1 \uC140 \uC8FC\uC18C\uB97C \uACC4\uC0B0\uD569\uB2C8\uB2E4."
11859
- ),
11850
+ var cellEditItemSchema = z4.object({
11860
11851
  tableIndex: z4.number().int().nonnegative().optional().describe(
11861
- "\uD0D0\uC0C9\uC744 \uC81C\uD55C\uD560 \uD45C \uC778\uB371\uC2A4 (0-based). \uC0DD\uB7B5\uD558\uBA74 \uBB38\uC11C \uB0B4 \uBAA8\uB4E0 \uCD5C\uC0C1\uC704 \uD45C\uC5D0\uC11C \uD0D0\uC0C9. \uB808\uC774\uBE14\uC774 \uC911\uBCF5\uB420 \uACBD\uC6B0 tableIndex\uB85C \uBC94\uC704\uB97C \uC881\uD788\uC138\uC694."
11852
+ "\uC88C\uD45C \uBAA8\uB4DC: 0-based \uD45C \uC778\uB371\uC2A4(read_document\uC758 kordoc \uBE14\uB85D \uC21C\uC11C, \uC911\uCCA9\uD45C \uC81C\uC678). \uB808\uC774\uBE14 \uBAA8\uB4DC\uC5D0\uC120 \uD0D0\uC0C9 \uBC94\uC704 \uC81C\uD55C\uC6A9(\uC120\uD0DD)."
11853
+ ),
11854
+ row: z4.number().int().nonnegative().optional().describe("\uC88C\uD45C \uBAA8\uB4DC: \uC140\uC758 rowAddr (0-based)"),
11855
+ col: z4.number().int().nonnegative().optional().describe("\uC88C\uD45C \uBAA8\uB4DC: \uC140\uC758 colAddr (0-based)"),
11856
+ label: z4.string().optional().describe(
11857
+ "\uB808\uC774\uBE14 \uBAA8\uB4DC: \uAE30\uC900 \uC140 \uD14D\uC2A4\uD2B8(\uD2B8\uB9BC \uBE44\uAD50). \uC774 \uC140\uC758 direction \uBC29\uD5A5 \uC778\uC811 \uC140\uC5D0 newText\uB97C \uAE30\uB85D. \uC88C\uD45C \uBAA8\uB4DC\uBA74 \uC0DD\uB7B5."
11862
11858
  ),
11859
+ direction: z4.enum(["right", "below"]).optional().describe("\uB808\uC774\uBE14 \uBAA8\uB4DC \uBC29\uD5A5. \uAE30\uBCF8 right(\uC624\uB978\uCABD \uC140), below(\uC544\uB798 \uC140). \uBCD1\uD569 span \uACE0\uB824."),
11863
11860
  newText: z4.string().describe("\uC140\uC5D0 \uC4F8 \uC0C8 \uD14D\uC2A4\uD2B8"),
11864
11861
  expectedText: z4.string().optional().describe(
11865
- "\uD604\uC7AC \uC140 \uD14D\uC2A4\uD2B8 (\uC548\uC804 \uAC80\uC99D\uC6A9). \uC81C\uACF5 \uC2DC \uD604\uC7AC \uC140 \uD14D\uC2A4\uD2B8\uC640 \uC77C\uCE58\uD558\uC9C0 \uC54A\uC73C\uBA74 \uC218\uC815\uD558\uC9C0 \uC54A\uC74C. \uC798\uBABB\uB41C \uC140 \uC218\uC815\uC744 \uBC29\uC9C0\uD558\uB824\uBA74 \uBC18\uB4DC\uC2DC \uC0AC\uC6A9\uD558\uC138\uC694."
11866
- ),
11867
- // row/col 필드 없음 — 레이블 모드 전용
11868
- row: z4.undefined().optional(),
11869
- col: z4.undefined().optional()
11870
- });
11871
- var cellEditItemSchema = z4.union([coordinateCellEditItemSchema, labelCellEditItemSchema]).describe(
11872
- "\uD3B8\uC9D1 \uD56D\uBAA9: \uC88C\uD45C \uBAA8\uB4DC(tableIndex+row+col) \uB610\uB294 \uB808\uC774\uBE14 \uBAA8\uB4DC(label+direction) \uC911 \uD558\uB098 \uC0AC\uC6A9. \uB450 \uBAA8\uB4DC\uB97C \uB3D9\uC2DC\uC5D0 \uC9C0\uC815\uD558\uAC70\uB098 \uB458 \uB2E4 \uC0DD\uB7B5\uD558\uBA74 \uC720\uD6A8\uC131 \uC624\uB958\uAC00 \uBC1C\uC0DD\uD569\uB2C8\uB2E4."
11862
+ "\uD604\uC7AC \uC140 \uD14D\uC2A4\uD2B8(\uC548\uC804 \uAC80\uC99D\uC6A9). \uBD88\uC77C\uCE58 \uC2DC \uC218\uC815\uD558\uC9C0 \uC54A\uC74C. \uC798\uBABB\uB41C \uC140 \uC218\uC815 \uBC29\uC9C0\uB97C \uC704\uD574 \uAD8C\uC7A5."
11863
+ )
11864
+ }).describe(
11865
+ "\uD3B8\uC9D1 \uD56D\uBAA9. \uC88C\uD45C \uBAA8\uB4DC(tableIndex+row+col) \uB610\uB294 \uB808\uC774\uBE14 \uBAA8\uB4DC(label[+direction]) \uC911 \uD558\uB098\uB97C \uC0AC\uC6A9\uD558\uC138\uC694. \uB458 \uB2E4 \uC9C0\uC815\uD558\uAC70\uB098 \uB458 \uB2E4 \uC0DD\uB7B5\uD558\uBA74 \uC624\uB958\uC785\uB2C8\uB2E4."
11873
11866
  );
11874
11867
  var proposeCellEditSchema = z4.object({
11875
11868
  path: z4.string().describe("\uC218\uC815\uD560 .hwpx \uD30C\uC77C \uACBD\uB85C (cwd \uAE30\uC900 \uC0C1\uB300 \uACBD\uB85C \uB610\uB294 \uC808\uB300 \uACBD\uB85C)"),
@@ -12337,7 +12330,11 @@ var proposeCellEditTool = {
12337
12330
  for (let i = 0; i < input.edits.length; i++) {
12338
12331
  const e = input.edits[i];
12339
12332
  if (!e) continue;
12340
- if ("label" in e && e.label !== void 0) {
12333
+ if (e.label !== void 0 && (e.row !== void 0 || e.col !== void 0)) {
12334
+ resolveErrors.push(
12335
+ `\uD3B8\uC9D1 #${i + 1}: label\uACFC row/col\uC744 \uB3D9\uC2DC\uC5D0 \uC9C0\uC815\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. \uC88C\uD45C \uBAA8\uB4DC(tableIndex+row+col) \uB610\uB294 \uB808\uC774\uBE14 \uBAA8\uB4DC(label) \uC911 \uD558\uB098\uB9CC \uC0AC\uC6A9\uD558\uC138\uC694.`
12336
+ );
12337
+ } else if (e.label !== void 0) {
12341
12338
  const direction = e.direction ?? "right";
12342
12339
  const resolved = resolveLabelAcrossSections(e.label, direction, e.tableIndex);
12343
12340
  if ("error" in resolved) {
@@ -12352,15 +12349,18 @@ var proposeCellEditTool = {
12352
12349
  label: e.label
12353
12350
  });
12354
12351
  }
12355
- } else {
12356
- const coord = e;
12352
+ } else if (e.tableIndex !== void 0 && e.row !== void 0 && e.col !== void 0) {
12357
12353
  resolvedEdits.push({
12358
- tableIndex: coord.tableIndex,
12359
- row: coord.row,
12360
- col: coord.col,
12361
- newText: coord.newText,
12362
- expectedText: coord.expectedText
12354
+ tableIndex: e.tableIndex,
12355
+ row: e.row,
12356
+ col: e.col,
12357
+ newText: e.newText,
12358
+ expectedText: e.expectedText
12363
12359
  });
12360
+ } else {
12361
+ resolveErrors.push(
12362
+ `\uD3B8\uC9D1 #${i + 1}: \uC88C\uD45C \uBAA8\uB4DC(tableIndex+row+col) \uB610\uB294 \uB808\uC774\uBE14 \uBAA8\uB4DC(label) \uC911 \uD558\uB098\uB97C \uC644\uC804\uD788 \uC9C0\uC815\uD558\uC138\uC694. read_document\uB85C \uD45C \uB0B4\uC6A9\uC744 \uBA3C\uC800 \uD655\uC778\uD558\uC138\uC694.`
12363
+ );
12364
12364
  }
12365
12365
  }
12366
12366
  if (resolveErrors.length > 0) {
@@ -12631,11 +12631,245 @@ ${diff}`;
12631
12631
  };
12632
12632
  }
12633
12633
  };
12634
- var proposeFormFillSchema = z6.object({
12635
- path: z6.string().describe("\uC591\uC2DD \uBB38\uC11C \uACBD\uB85C (cwd \uAE30\uC900 \uC0C1\uB300 \uACBD\uB85C \uB610\uB294 \uC808\uB300 \uACBD\uB85C)"),
12636
- fields: z6.record(z6.string(), z6.string()).describe("\uCC44\uC6B8 \uD544\uB4DC \uB9E4\uD551: { \uB77C\uBCA8: \uAC12 }. read_document\uB85C \uBA3C\uC800 \uD544\uB4DC \uBAA9\uB85D\uC744 \uD655\uC778\uD558\uC138\uC694"),
12634
+ var proposeFindReplaceSchema = z6.object({
12635
+ path: z6.string().describe("\uC218\uC815\uD560 .hwpx \uD30C\uC77C \uACBD\uB85C (cwd \uAE30\uC900 \uC0C1\uB300 \uACBD\uB85C \uB610\uB294 \uC808\uB300 \uACBD\uB85C)"),
12636
+ find: z6.string().min(1).describe("\uCC3E\uC744 \uD14D\uC2A4\uD2B8"),
12637
+ replace: z6.string().describe("\uBC14\uAFC0 \uD14D\uC2A4\uD2B8"),
12638
+ caseSensitive: z6.boolean().optional().default(false).describe("\uB300\uC18C\uBB38\uC790 \uAD6C\uBD84 (\uAE30\uBCF8\uAC12: false)"),
12639
+ all: z6.boolean().optional().default(true).describe("\uBAA8\uB4E0 \uD56D\uBAA9\uC744 \uAD50\uCCB4\uD560\uC9C0 \uC5EC\uBD80 (\uAE30\uBCF8\uAC12: true). false\uC774\uBA74 \uCCAB \uBC88\uC9F8 \uB9E4\uCE58\uB9CC \uAD50\uCCB4"),
12637
12640
  summary: z6.string().describe("\uBCC0\uACBD \uC694\uC57D (\uD55C\uAD6D\uC5B4 1-2\uBB38\uC7A5)")
12638
12641
  });
12642
+ function escapeXml3(text3) {
12643
+ return text3.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
12644
+ }
12645
+ function replaceInSectionXml(xml, find, replace, caseSensitive, replaceAll, alreadyReplaced) {
12646
+ if (!replaceAll && alreadyReplaced > 0) {
12647
+ return { xml, count: 0 };
12648
+ }
12649
+ const escapedFind = escapeXml3(find);
12650
+ const escapedReplace = escapeXml3(replace);
12651
+ if (escapedFind.length === 0) {
12652
+ return { xml, count: 0 };
12653
+ }
12654
+ const normFind = caseSensitive ? escapedFind : escapedFind.toLowerCase();
12655
+ const normReplace = escapedReplace;
12656
+ let totalCount = 0;
12657
+ let result = xml;
12658
+ const tNodeRe = /<hp:t>([\s\S]*?)<\/hp:t>/g;
12659
+ let offset = 0;
12660
+ let m = tNodeRe.exec(xml);
12661
+ while (m !== null) {
12662
+ const content = m[1];
12663
+ const nodeResult = replaceInContent(
12664
+ content,
12665
+ normFind,
12666
+ normReplace,
12667
+ caseSensitive,
12668
+ replaceAll,
12669
+ alreadyReplaced + totalCount
12670
+ );
12671
+ if (nodeResult.count > 0) {
12672
+ const openTagLen = "<hp:t>".length;
12673
+ const contentStart = m.index + offset + openTagLen;
12674
+ const contentEnd = contentStart + content.length;
12675
+ result = result.substring(0, contentStart) + nodeResult.text + result.substring(contentEnd);
12676
+ offset += nodeResult.text.length - content.length;
12677
+ totalCount += nodeResult.count;
12678
+ if (!replaceAll && alreadyReplaced + totalCount >= 1) {
12679
+ break;
12680
+ }
12681
+ }
12682
+ m = tNodeRe.exec(xml);
12683
+ }
12684
+ return { xml: result, count: totalCount };
12685
+ }
12686
+ function replaceInContent(content, normFind, escapedReplace, caseSensitive, replaceAll, alreadyReplaced) {
12687
+ const searchIn = caseSensitive ? content : content.toLowerCase();
12688
+ const findLen = normFind.length;
12689
+ if (findLen === 0) return { text: content, count: 0 };
12690
+ let result = "";
12691
+ let pos = 0;
12692
+ let count = 0;
12693
+ while (pos <= content.length) {
12694
+ if (!replaceAll && alreadyReplaced + count >= 1) {
12695
+ result += content.substring(pos);
12696
+ break;
12697
+ }
12698
+ const idx = searchIn.indexOf(normFind, pos);
12699
+ if (idx === -1) {
12700
+ result += content.substring(pos);
12701
+ break;
12702
+ }
12703
+ result += content.substring(pos, idx) + escapedReplace;
12704
+ pos = idx + findLen;
12705
+ count++;
12706
+ }
12707
+ return { text: result, count };
12708
+ }
12709
+ function countOccurrences(str, sub) {
12710
+ if (sub.length === 0) return 0;
12711
+ let count = 0;
12712
+ let pos = 0;
12713
+ while (true) {
12714
+ const idx = str.indexOf(sub, pos);
12715
+ if (idx === -1) break;
12716
+ count++;
12717
+ pos = idx + sub.length;
12718
+ }
12719
+ return count;
12720
+ }
12721
+ async function applyFindReplaceToHwpx(hwpxBuffer, find, replace, caseSensitive, replaceAll) {
12722
+ const zip = await import_jszip3.default.loadAsync(hwpxBuffer);
12723
+ const sectionFiles = Object.keys(zip.files).filter((name) => /^Contents\/section\d+\.xml$/.test(name)).sort();
12724
+ const sectionXmls = [];
12725
+ for (const sf of sectionFiles) {
12726
+ const entry = zip.file(sf);
12727
+ const xml = entry ? await entry.async("string") : "";
12728
+ sectionXmls.push(xml);
12729
+ }
12730
+ let totalCount = 0;
12731
+ const newSectionXmls = [];
12732
+ for (let si = 0; si < sectionFiles.length; si++) {
12733
+ const srcXml = sectionXmls[si] ?? "";
12734
+ const { xml: newXml, count } = replaceInSectionXml(
12735
+ srcXml,
12736
+ find,
12737
+ replace,
12738
+ caseSensitive,
12739
+ replaceAll,
12740
+ totalCount
12741
+ );
12742
+ newSectionXmls.push(newXml);
12743
+ totalCount += count;
12744
+ if (!replaceAll && totalCount >= 1) {
12745
+ for (let j = si + 1; j < sectionFiles.length; j++) {
12746
+ newSectionXmls.push(sectionXmls[j] ?? "");
12747
+ }
12748
+ break;
12749
+ }
12750
+ }
12751
+ if (totalCount === 0) {
12752
+ return { buffer: hwpxBuffer, count: 0 };
12753
+ }
12754
+ const out = new import_jszip3.default();
12755
+ const mimetypeEntry = zip.file("mimetype");
12756
+ if (mimetypeEntry) {
12757
+ out.file("mimetype", await mimetypeEntry.async("uint8array"), { compression: "STORE" });
12758
+ }
12759
+ for (const [name, entry] of Object.entries(zip.files)) {
12760
+ if (name === "mimetype" || entry.dir) continue;
12761
+ const sectionIdx = sectionFiles.indexOf(name);
12762
+ if (sectionIdx >= 0) {
12763
+ out.file(name, newSectionXmls[sectionIdx] ?? "");
12764
+ } else {
12765
+ out.file(name, await entry.async("uint8array"));
12766
+ }
12767
+ }
12768
+ const buf = await out.generateAsync({ type: "nodebuffer", compression: "DEFLATE" });
12769
+ return {
12770
+ buffer: new Uint8Array(buf),
12771
+ count: totalCount
12772
+ };
12773
+ }
12774
+ var proposeFindReplaceTool = {
12775
+ name: "propose_find_replace",
12776
+ description: "HWPX \uBB38\uC11C \uC804\uCCB4\uC5D0\uC11C \uD14D\uC2A4\uD2B8\uB97C \uCC3E\uC544 \uBC14\uAFC9\uB2C8\uB2E4. \uBCF8\uBB38\xB7\uD45C\xB7\uBA38\uB9AC\uB9D0\xB7\uAF2C\uB9AC\uB9D0 \uB4F1 \uBAA8\uB4E0 \uC139\uC158\uC744 \uB300\uC0C1\uC73C\uB85C \uBB38\uC11C XML\uC744 \uC9C1\uC811 \uD328\uCE58\uD569\uB2C8\uB2E4(rhwp \uC5D4\uC9C4 \uBBF8\uC0AC\uC6A9). \uC774\uBBF8\uC9C0\xB7\uD45C\xB7\uB808\uC774\uC544\uC6C3 \uB4F1 \uBB38\uC11C \uAD6C\uC870\uB97C \uC644\uC804\uD788 \uBCF4\uC874\uD569\uB2C8\uB2E4. .hwpx \uC804\uC6A9\uC785\uB2C8\uB2E4. .hwp(\uAD6C\uD615 OLE \uBC14\uC774\uB108\uB9AC)\uB294 \uC9C0\uC6D0\uD558\uC9C0 \uC54A\uC73C\uBA70, \uD55C\uAE00 \uD504\uB85C\uADF8\uB7A8\uC5D0\uC11C '\uB2E4\uB978 \uC774\uB984\uC73C\uB85C \uC800\uC7A5 \u2192 .hwpx'\uB85C \uC800\uC7A5\uD55C \uD6C4 \uC0AC\uC6A9\uD558\uC138\uC694. all:true(\uAE30\uBCF8\uAC12)\uC774\uBA74 \uBAA8\uB4E0 \uD56D\uBAA9\uC744 \uAD50\uCCB4\uD558\uACE0, all:false\uC774\uBA74 \uCCAB \uBC88\uC9F8 \uB9E4\uCE58\uB9CC \uAD50\uCCB4\uD569\uB2C8\uB2E4. \uD14D\uC2A4\uD2B8\uAC00 \uC5EC\uB7EC \uC11C\uC2DD \uB7F0\uC5D0 \uB098\uB258\uC5B4 \uC788\uC73C\uBA74 \uACBD\uACC4\uB97C \uAC00\uB85C\uC9C0\uB974\uB294 \uD328\uD134\uC740 \uAD50\uCCB4\uB418\uC9C0 \uC54A\uC744 \uC218 \uC788\uC2B5\uB2C8\uB2E4(\uC11C\uC2DD\uC774 \uB098\uB25C \uD14D\uC2A4\uD2B8). \uCC3E\uC744 \uD14D\uC2A4\uD2B8\uAC00 \uC5C6\uC73C\uBA74 \uD30C\uC77C\uC744 \uC218\uC815\uD558\uC9C0 \uC54A\uACE0 \uC624\uB958\uB97C \uBC18\uD658\uD569\uB2C8\uB2E4. \uBCC0\uACBD \uC0AC\uD56D\uC740 diff \uBBF8\uB9AC\uBCF4\uAE30\uC640 \uD568\uAED8 \uC0AC\uC6A9\uC790 \uC2B9\uC778\uC744 \uBC1B\uC740 \uD6C4\uC5D0\uB9CC \uC800\uC7A5\uB429\uB2C8\uB2E4.",
12777
+ inputSchema: proposeFindReplaceSchema,
12778
+ requiresApproval: true,
12779
+ propose: async ({
12780
+ input,
12781
+ ctx
12782
+ }) => {
12783
+ const safePath = await resolveSafePath(ctx.cwd, input.path);
12784
+ const ext = extname6(safePath).toLowerCase();
12785
+ if (ext === ".hwp") {
12786
+ return "\uC624\uB958: propose_find_replace\uB294 .hwpx \uD30C\uC77C\uB9CC \uC9C0\uC6D0\uD569\uB2C8\uB2E4. .hwp(\uAD6C\uD615 OLE \uBC14\uC774\uB108\uB9AC)\uB294 XML \uC9C1\uC811 \uD3B8\uC9D1\uC774 \uBD88\uAC00\uD569\uB2C8\uB2E4. \uD55C\uAE00 \uD504\uB85C\uADF8\uB7A8\uC5D0\uC11C '\uB2E4\uB978 \uC774\uB984\uC73C\uB85C \uC800\uC7A5 \u2192 .hwpx'\uB85C \uC800\uC7A5\uD55C \uD6C4 \uB2E4\uC2DC \uC2DC\uB3C4\uD558\uC138\uC694.";
12787
+ }
12788
+ if (ext !== ".hwpx") {
12789
+ return `\uC624\uB958: propose_find_replace\uB294 .hwpx \uD30C\uC77C\uB9CC \uC9C0\uC6D0\uD569\uB2C8\uB2E4. \uD604\uC7AC \uD30C\uC77C \uD655\uC7A5\uC790: ${ext}. .hwpx \uD30C\uC77C\uC744 \uC9C0\uC815\uD558\uC138\uC694.`;
12790
+ }
12791
+ let originalBuf;
12792
+ try {
12793
+ originalBuf = await readFile6(safePath);
12794
+ } catch {
12795
+ return `\uC624\uB958: \uD30C\uC77C\uC744 \uC77D\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${input.path}. \uACBD\uB85C\uB97C \uD655\uC778\uD558\uC138\uC694.`;
12796
+ }
12797
+ if (originalBuf[0] !== 80 || originalBuf[1] !== 75) {
12798
+ return "\uC624\uB958: \uD30C\uC77C\uC774 \uC720\uD6A8\uD55C .hwpx(ZIP) \uD3EC\uB9F7\uC774 \uC544\uB2D9\uB2C8\uB2E4. \uD30C\uC77C\uC774 \uC190\uC0C1\uB418\uC5C8\uAC70\uB098 \uAD6C\uD615 .hwp(OLE \uBC14\uC774\uB108\uB9AC) \uD3EC\uB9F7\uC77C \uC218 \uC788\uC2B5\uB2C8\uB2E4. \uD55C\uAE00 \uD504\uB85C\uADF8\uB7A8\uC5D0\uC11C .hwpx\uB85C \uC800\uC7A5 \uD6C4 \uB2E4\uC2DC \uC2DC\uB3C4\uD558\uC138\uC694.";
12799
+ }
12800
+ const originalBytes = new Uint8Array(
12801
+ originalBuf.buffer,
12802
+ originalBuf.byteOffset,
12803
+ originalBuf.byteLength
12804
+ );
12805
+ let newBytes;
12806
+ let replacedCount;
12807
+ try {
12808
+ const result = await applyFindReplaceToHwpx(
12809
+ originalBytes,
12810
+ input.find,
12811
+ input.replace,
12812
+ input.caseSensitive ?? false,
12813
+ input.all ?? true
12814
+ );
12815
+ newBytes = result.buffer;
12816
+ replacedCount = result.count;
12817
+ } catch (e) {
12818
+ return `\uC624\uB958: \uCE58\uD658 \uC911 \uC624\uB958\uAC00 \uBC1C\uC0DD\uD588\uC2B5\uB2C8\uB2E4. ${String(e)}`;
12819
+ }
12820
+ if (replacedCount === 0) {
12821
+ const caseNote = input.caseSensitive ? " (\uB300\uC18C\uBB38\uC790 \uAD6C\uBD84)" : "";
12822
+ return `\uC624\uB958: \uCC3E\uC744 \uD14D\uC2A4\uD2B8\uB97C \uBB38\uC11C\uC5D0\uC11C \uBC1C\uACAC\uD558\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4: "${input.find}"${caseNote}. read_document\uB85C \uBB38\uC11C \uB0B4\uC6A9\uC744 \uD655\uC778\uD558\uACE0 \uC815\uD655\uD55C \uD14D\uC2A4\uD2B8\uB97C \uC9C0\uC815\uD558\uC138\uC694.`;
12823
+ }
12824
+ const warnings = [];
12825
+ try {
12826
+ const exportedResult = await parse2(newBytes.buffer);
12827
+ if (exportedResult.success) {
12828
+ const exportedMd = exportedResult.markdown;
12829
+ const normAfter = input.caseSensitive ?? false ? exportedMd : exportedMd.toLowerCase();
12830
+ const normFind = input.caseSensitive ?? false ? input.find : input.find.toLowerCase();
12831
+ const normReplace = input.caseSensitive ?? false ? input.replace : input.replace.toLowerCase();
12832
+ if (!normReplace.includes(normFind)) {
12833
+ const remaining = countOccurrences(normAfter, normFind);
12834
+ if (remaining > 0) {
12835
+ warnings.push(
12836
+ `\uC77C\uBD80 "${input.find}"(${remaining}\uACF3)\uC774 \uAD50\uCCB4\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4. \uD14D\uC2A4\uD2B8\uAC00 \uC5EC\uB7EC \uC11C\uC2DD \uB7F0\uC5D0 \uB098\uB258\uC5B4 \uC788\uC5B4 \uACBD\uACC4\uB97C \uAC00\uB85C\uC9C0\uB974\uB294 \uD328\uD134\uC740 \uAD50\uCCB4\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4(\uC11C\uC2DD\uC774 \uB098\uB25C \uD14D\uC2A4\uD2B8). \uC774\uBBF8 \uAD50\uCCB4\uB41C ${replacedCount}\uACF3\uC740 \uC815\uC0C1 \uBC18\uC601\uB418\uC5C8\uC2B5\uB2C8\uB2E4.`
12837
+ );
12838
+ }
12839
+ }
12840
+ }
12841
+ } catch {
12842
+ }
12843
+ const { outputPath, willConvertFormat } = resolveOutputPath(safePath);
12844
+ const allLabel = input.all ?? true ? `${replacedCount}\uACF3` : "\uCCAB \uBC88\uC9F8 1\uACF3";
12845
+ const diff = `\uCC3E\uAE30: "${input.find}" \u2192 \uBC14\uAFB8\uAE30: "${input.replace}" (${allLabel} \uAD50\uCCB4\uB428)`;
12846
+ const stagedPath = await stageFile(ctx.sessionId, outputPath, newBytes);
12847
+ const proposalId = crypto.randomUUID();
12848
+ return {
12849
+ proposal: {
12850
+ id: proposalId,
12851
+ kind: "find-replace",
12852
+ targetPath: outputPath,
12853
+ stagedPath,
12854
+ summary: input.summary,
12855
+ diff,
12856
+ warnings,
12857
+ willConvertFormat
12858
+ },
12859
+ commit: async () => {
12860
+ const backupPath = await backupFile(safePath);
12861
+ await commitStaged(stagedPath, outputPath);
12862
+ const backupInfo = backupPath ? ` (\uBC31\uC5C5: ${backupPath})` : "";
12863
+ return `\uC800\uC7A5 \uC644\uB8CC: ${outputPath}${backupInfo}`;
12864
+ }
12865
+ };
12866
+ }
12867
+ };
12868
+ var proposeFormFillSchema = z7.object({
12869
+ path: z7.string().describe("\uC591\uC2DD \uBB38\uC11C \uACBD\uB85C (cwd \uAE30\uC900 \uC0C1\uB300 \uACBD\uB85C \uB610\uB294 \uC808\uB300 \uACBD\uB85C)"),
12870
+ fields: z7.record(z7.string(), z7.string()).describe("\uCC44\uC6B8 \uD544\uB4DC \uB9E4\uD551: { \uB77C\uBCA8: \uAC12 }. read_document\uB85C \uBA3C\uC800 \uD544\uB4DC \uBAA9\uB85D\uC744 \uD655\uC778\uD558\uC138\uC694"),
12871
+ summary: z7.string().describe("\uBCC0\uACBD \uC694\uC57D (\uD55C\uAD6D\uC5B4 1-2\uBB38\uC7A5)")
12872
+ });
12639
12873
  var proposeFormFillTool = {
12640
12874
  name: "propose_form_fill",
12641
12875
  description: "HWPX/HWP \uC591\uC2DD \uBB38\uC11C\uC758 \uD544\uB4DC\uB97C \uCC44\uC6C1\uB2C8\uB2E4. \uBC18\uB4DC\uC2DC read_document\uB85C \uD604\uC7AC \uD544\uB4DC \uBAA9\uB85D\uC744 \uBA3C\uC800 \uD655\uC778\uD55C \uD6C4 \uC0AC\uC6A9\uD558\uC138\uC694. \uBCC0\uACBD \uC0AC\uD56D\uC740 diff \uBBF8\uB9AC\uBCF4\uAE30\uC640 \uD568\uAED8 \uC0AC\uC6A9\uC790 \uC2B9\uC778\uC744 \uBC1B\uC740 \uD6C4\uC5D0\uB9CC \uC800\uC7A5\uB429\uB2C8\uB2E4.",
@@ -12646,17 +12880,17 @@ var proposeFormFillTool = {
12646
12880
  ctx
12647
12881
  }) => {
12648
12882
  const safePath = await resolveSafePath(ctx.cwd, input.path);
12649
- const ext = extname6(safePath).toLowerCase();
12883
+ const ext = extname7(safePath).toLowerCase();
12650
12884
  if (ext !== ".hwpx" && ext !== ".hwp") {
12651
12885
  return `\uC624\uB958: propose_form_fill\uC740 .hwp/.hwpx \uD30C\uC77C\uB9CC \uC9C0\uC6D0\uD569\uB2C8\uB2E4. \uD604\uC7AC \uD30C\uC77C: ${ext}. .docx \uD30C\uC77C\uC740 propose_edit\uC744 \uC0AC\uC6A9\uD558\uC138\uC694.`;
12652
12886
  }
12653
12887
  let originalBuffer;
12654
12888
  try {
12655
- originalBuffer = await readFile6(safePath);
12889
+ originalBuffer = await readFile7(safePath);
12656
12890
  } catch {
12657
12891
  return `\uC624\uB958: \uD30C\uC77C\uC744 \uC77D\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${input.path}. \uACBD\uB85C\uB97C \uD655\uC778\uD558\uC138\uC694.`;
12658
12892
  }
12659
- const parseResult = await parse2(originalBuffer.buffer);
12893
+ const parseResult = await parse3(originalBuffer.buffer);
12660
12894
  if (!parseResult.success) {
12661
12895
  const msg = kordocErrorMessage(
12662
12896
  parseResult.code,
@@ -12720,16 +12954,16 @@ var proposeFormFillTool = {
12720
12954
  };
12721
12955
  }
12722
12956
  };
12723
- var proposeSheetEditSchema = z7.object({
12724
- path: z7.string().describe("\uC218\uC815\uD560 XLSX \uD30C\uC77C \uACBD\uB85C (cwd \uAE30\uC900 \uC0C1\uB300 \uACBD\uB85C \uB610\uB294 \uC808\uB300 \uACBD\uB85C)"),
12725
- updates: z7.array(
12726
- z7.object({
12727
- sheet: z7.string().describe("\uC2DC\uD2B8 \uC774\uB984"),
12728
- cell: z7.string().describe("\uC140 \uC8FC\uC18C (\uC608: A1, B3)"),
12729
- value: z7.union([z7.string(), z7.number()]).describe("\uC0C8 \uAC12")
12957
+ var proposeSheetEditSchema = z8.object({
12958
+ path: z8.string().describe("\uC218\uC815\uD560 XLSX \uD30C\uC77C \uACBD\uB85C (cwd \uAE30\uC900 \uC0C1\uB300 \uACBD\uB85C \uB610\uB294 \uC808\uB300 \uACBD\uB85C)"),
12959
+ updates: z8.array(
12960
+ z8.object({
12961
+ sheet: z8.string().describe("\uC2DC\uD2B8 \uC774\uB984"),
12962
+ cell: z8.string().describe("\uC140 \uC8FC\uC18C (\uC608: A1, B3)"),
12963
+ value: z8.union([z8.string(), z8.number()]).describe("\uC0C8 \uAC12")
12730
12964
  })
12731
12965
  ).min(1).describe("\uC218\uC815\uD560 \uC140 \uBAA9\uB85D. read_document\uB85C \uC6D0\uBCF8 \uC2DC\uD2B8 \uAD6C\uC870\uB97C \uBA3C\uC800 \uD655\uC778\uD558\uC138\uC694"),
12732
- summary: z7.string().describe("\uBCC0\uACBD \uC694\uC57D (\uD55C\uAD6D\uC5B4 1-2\uBB38\uC7A5)")
12966
+ summary: z8.string().describe("\uBCC0\uACBD \uC694\uC57D (\uD55C\uAD6D\uC5B4 1-2\uBB38\uC7A5)")
12733
12967
  });
12734
12968
  var proposeSheetEditTool = {
12735
12969
  name: "propose_sheet_edit",
@@ -12741,13 +12975,13 @@ var proposeSheetEditTool = {
12741
12975
  ctx
12742
12976
  }) => {
12743
12977
  const safePath = await resolveSafePath(ctx.cwd, input.path);
12744
- const ext = extname7(safePath).toLowerCase();
12978
+ const ext = extname8(safePath).toLowerCase();
12745
12979
  if (ext !== ".xlsx" && ext !== ".xls") {
12746
12980
  return `\uC624\uB958: propose_sheet_edit\uC740 .xlsx/.xls \uD30C\uC77C\uB9CC \uC9C0\uC6D0\uD569\uB2C8\uB2E4. \uD604\uC7AC \uD30C\uC77C: ${ext}.`;
12747
12981
  }
12748
12982
  let originalBuffer;
12749
12983
  try {
12750
- originalBuffer = await readFile7(safePath);
12984
+ originalBuffer = await readFile8(safePath);
12751
12985
  } catch {
12752
12986
  return `\uC624\uB958: \uD30C\uC77C\uC744 \uC77D\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${input.path}. \uACBD\uB85C\uB97C \uD655\uC778\uD558\uC138\uC694.`;
12753
12987
  }
@@ -12807,6 +13041,881 @@ var proposeSheetEditTool = {
12807
13041
  };
12808
13042
  }
12809
13043
  };
13044
+ function detectStructuralLoss(beforeBlocks, afterBlocks) {
13045
+ const LABELS = {
13046
+ paragraph: "\uB2E8\uB77D",
13047
+ table: "\uD45C",
13048
+ heading: "\uC81C\uBAA9",
13049
+ list: "\uBAA9\uB85D",
13050
+ image: "\uC774\uBBF8\uC9C0",
13051
+ separator: "\uAD6C\uBD84\uC120"
13052
+ };
13053
+ function histogram(blocks) {
13054
+ const h = {};
13055
+ for (const b of blocks) {
13056
+ h[b.type] = (h[b.type] ?? 0) + 1;
13057
+ }
13058
+ return h;
13059
+ }
13060
+ const beforeH = histogram(beforeBlocks);
13061
+ const afterH = histogram(afterBlocks);
13062
+ const dropParts = [];
13063
+ for (const [type, beforeCount] of Object.entries(beforeH)) {
13064
+ const afterCount = afterH[type] ?? 0;
13065
+ if (afterCount < beforeCount) {
13066
+ const label = LABELS[type] ?? type;
13067
+ dropParts.push(`${label} ${beforeCount}\u2192${afterCount}`);
13068
+ }
13069
+ }
13070
+ if (dropParts.length > 0) {
13071
+ return { lost: true, detail: dropParts.join(", ") };
13072
+ }
13073
+ return { lost: false, detail: "" };
13074
+ }
13075
+ var insertRowOpSchema = z9.object({
13076
+ type: z9.literal("insertRow"),
13077
+ row: z9.number().int().nonnegative().describe("\uAE30\uC900 \uD589 \uC778\uB371\uC2A4 (0-based)"),
13078
+ position: z9.enum(["above", "below"]).describe("\uC0BD\uC785 \uC704\uCE58: above=row \uC704\uC5D0, below=row \uC544\uB798\uC5D0")
13079
+ });
13080
+ var deleteRowOpSchema = z9.object({
13081
+ type: z9.literal("deleteRow"),
13082
+ row: z9.number().int().nonnegative().describe("\uC0AD\uC81C\uD560 \uD589 \uC778\uB371\uC2A4 (0-based)")
13083
+ });
13084
+ var insertColumnOpSchema = z9.object({
13085
+ type: z9.literal("insertColumn"),
13086
+ col: z9.number().int().nonnegative().describe("\uAE30\uC900 \uC5F4 \uC778\uB371\uC2A4 (0-based)"),
13087
+ position: z9.enum(["left", "right"]).describe("\uC0BD\uC785 \uC704\uCE58: left=col \uC67C\uCABD\uC5D0, right=col \uC624\uB978\uCABD\uC5D0")
13088
+ });
13089
+ var deleteColumnOpSchema = z9.object({
13090
+ type: z9.literal("deleteColumn"),
13091
+ col: z9.number().int().nonnegative().describe("\uC0AD\uC81C\uD560 \uC5F4 \uC778\uB371\uC2A4 (0-based)")
13092
+ });
13093
+ var mergeCellsOpSchema = z9.object({
13094
+ type: z9.literal("mergeCells"),
13095
+ startRow: z9.number().int().nonnegative().describe("\uBCD1\uD569 \uC2DC\uC791 \uD589 (0-based)"),
13096
+ startCol: z9.number().int().nonnegative().describe("\uBCD1\uD569 \uC2DC\uC791 \uC5F4 (0-based)"),
13097
+ endRow: z9.number().int().nonnegative().describe("\uBCD1\uD569 \uB05D \uD589 (0-based, \uD3EC\uD568)"),
13098
+ endCol: z9.number().int().nonnegative().describe("\uBCD1\uD569 \uB05D \uC5F4 (0-based, \uD3EC\uD568)")
13099
+ });
13100
+ var operationSchema = z9.discriminatedUnion("type", [
13101
+ insertRowOpSchema,
13102
+ deleteRowOpSchema,
13103
+ insertColumnOpSchema,
13104
+ deleteColumnOpSchema,
13105
+ mergeCellsOpSchema
13106
+ ]).describe("\uD45C \uAD6C\uC870 \uC5F0\uC0B0. \uB098\uC911 \uC5F0\uC0B0\uC758 row/col \uC778\uB371\uC2A4\uB294 \uC55E \uC5F0\uC0B0 \uC801\uC6A9 \uD6C4 \uC0C1\uD0DC\uB97C \uAE30\uC900\uC73C\uB85C \uD55C\uB2E4.");
13107
+ var proposeTableStructureSchema = z9.object({
13108
+ path: z9.string().describe("\uC218\uC815\uD560 .hwpx \uD30C\uC77C \uACBD\uB85C (cwd \uAE30\uC900 \uC0C1\uB300 \uACBD\uB85C \uB610\uB294 \uC808\uB300 \uACBD\uB85C)"),
13109
+ anchor: z9.string().min(1).describe(
13110
+ "\uB300\uC0C1 \uD45C\uB97C \uC2DD\uBCC4\uD558\uB294 \uC575\uCEE4 \uD14D\uC2A4\uD2B8. \uD45C \uC548\uC5D0\uB9CC \uC788\uB294 \uB3C5\uD2B9\uD55C \uC140 \uD14D\uC2A4\uD2B8\uB97C \uC9C0\uC815\uD558\uC138\uC694. (\uBD80\uBD84 \uC77C\uCE58, \uACF5\uBC31 \uD2B8\uB9BC) \u2014 read_document\uB85C \uD655\uC778 \uD6C4 \uC0AC\uC6A9 \uAD8C\uC7A5."
13111
+ ),
13112
+ operations: z9.array(operationSchema).min(1).describe(
13113
+ "\uC801\uC6A9\uD560 \uD45C \uAD6C\uC870 \uC5F0\uC0B0 \uBAA9\uB85D (\uC21C\uC11C\uB300\uB85C \uC2E4\uD589). \uAC01 \uC5F0\uC0B0\uC740 \uC774\uC804 \uC5F0\uC0B0\uC774 \uC801\uC6A9\uB41C \uD6C4\uC758 \uD45C \uC0C1\uD0DC \uAE30\uC900\uC73C\uB85C row/col\uC744 \uC9C0\uC815\uD574\uC57C \uD569\uB2C8\uB2E4."
13114
+ ),
13115
+ summary: z9.string().describe("\uBCC0\uACBD \uC694\uC57D (\uD55C\uAD6D\uC5B4 1-2\uBB38\uC7A5)")
13116
+ });
13117
+ function tokenizeHwpxXml2(xml) {
13118
+ const tokens = [];
13119
+ const re = /<hp:tbl[\s>]|<\/hp:tbl>|<hp:tr[\s>]|<\/hp:tr>|<hp:tc[\s>]|<\/hp:tc>|<hp:t\/>|<hp:t>|<\/hp:t>|<hp:cellAddr[^/>]*|<hp:cellSpan[^/>]*|<hp:cellSz[^/>]*/g;
13120
+ let m = re.exec(xml);
13121
+ while (m !== null) {
13122
+ const raw = m[0];
13123
+ const pos = m.index;
13124
+ if (raw.startsWith("<hp:tbl")) {
13125
+ tokens.push({ kind: "tbl_open", pos, end: pos + raw.length });
13126
+ } else if (raw === "</hp:tbl>") {
13127
+ tokens.push({ kind: "tbl_close", pos, end: pos + raw.length });
13128
+ } else if (raw.startsWith("<hp:tr")) {
13129
+ tokens.push({ kind: "tr_open", pos, end: pos + raw.length });
13130
+ } else if (raw === "</hp:tr>") {
13131
+ tokens.push({ kind: "tr_close", pos, end: pos + raw.length });
13132
+ } else if (raw.startsWith("<hp:tc")) {
13133
+ tokens.push({ kind: "tc_open", pos, end: pos + raw.length });
13134
+ } else if (raw === "</hp:tc>") {
13135
+ tokens.push({ kind: "tc_close", pos, end: pos + raw.length });
13136
+ } else if (raw === "<hp:t/>") {
13137
+ tokens.push({ kind: "t_empty", pos, end: pos + raw.length });
13138
+ } else if (raw === "<hp:t>") {
13139
+ tokens.push({ kind: "t_open", pos, end: pos + raw.length });
13140
+ } else if (raw === "</hp:t>") {
13141
+ tokens.push({ kind: "t_close", pos, end: pos + raw.length });
13142
+ } else if (raw.startsWith("<hp:cellAddr")) {
13143
+ const colM = raw.match(/colAddr="(\d+)"/);
13144
+ const rowM = raw.match(/rowAddr="(\d+)"/);
13145
+ if (colM && rowM) {
13146
+ const selfClose = xml.indexOf("/>", pos);
13147
+ const end = selfClose >= 0 ? selfClose + 2 : pos + raw.length;
13148
+ tokens.push({
13149
+ kind: "cell_addr",
13150
+ pos,
13151
+ end,
13152
+ colAddr: Number(colM[1]),
13153
+ rowAddr: Number(rowM[1])
13154
+ });
13155
+ }
13156
+ } else if (raw.startsWith("<hp:cellSpan")) {
13157
+ const colM = raw.match(/colSpan="(\d+)"/);
13158
+ const rowM = raw.match(/rowSpan="(\d+)"/);
13159
+ const selfClose = xml.indexOf("/>", pos);
13160
+ const end = selfClose >= 0 ? selfClose + 2 : pos + raw.length;
13161
+ tokens.push({
13162
+ kind: "cell_span",
13163
+ pos,
13164
+ end,
13165
+ colSpan: colM ? Number(colM[1]) : 1,
13166
+ rowSpan: rowM ? Number(rowM[1]) : 1
13167
+ });
13168
+ } else if (raw.startsWith("<hp:cellSz")) {
13169
+ const selfClose = xml.indexOf("/>", pos);
13170
+ const end = selfClose >= 0 ? selfClose + 2 : pos + raw.length;
13171
+ tokens.push({ kind: "cell_sz", pos, end });
13172
+ }
13173
+ m = re.exec(xml);
13174
+ }
13175
+ return tokens;
13176
+ }
13177
+ function findAllTopLevelTableRanges(tokens) {
13178
+ const ranges = [];
13179
+ let depth = 0;
13180
+ let topCount = 0;
13181
+ let startPos = -1;
13182
+ for (const tok of tokens) {
13183
+ if (tok.kind === "tbl_open") {
13184
+ if (depth === 0) {
13185
+ startPos = tok.pos;
13186
+ }
13187
+ depth++;
13188
+ } else if (tok.kind === "tbl_close") {
13189
+ depth--;
13190
+ if (depth === 0 && startPos >= 0) {
13191
+ ranges.push({ start: startPos, end: tok.end, index: topCount });
13192
+ topCount++;
13193
+ startPos = -1;
13194
+ }
13195
+ }
13196
+ }
13197
+ return ranges;
13198
+ }
13199
+ function collectTableText(xml, tokens, tblStart, tblEnd) {
13200
+ const parts = [];
13201
+ const tblTokens = tokens.filter((t) => t.pos >= tblStart && t.pos < tblEnd);
13202
+ for (const tok of tblTokens) {
13203
+ if (tok.kind === "t_open") {
13204
+ const closePos = xml.indexOf("</hp:t>", tok.end);
13205
+ if (closePos >= 0 && closePos < tblEnd) {
13206
+ const raw = xml.substring(tok.end, closePos);
13207
+ parts.push(raw.replace(/&amp;/g, "&").replace(/&lt;/g, "<").replace(/&gt;/g, ">"));
13208
+ }
13209
+ }
13210
+ }
13211
+ return parts.join("");
13212
+ }
13213
+ function parseTableRows(_xml, tokens, tblStart, tblEnd) {
13214
+ const tblTokens = tokens.filter((t) => t.pos > tblStart && t.pos < tblEnd);
13215
+ const rows = [];
13216
+ const trStack = [];
13217
+ const tcStack = [];
13218
+ let innerTblDepth = 0;
13219
+ let currentRowCells = [];
13220
+ for (const tok of tblTokens) {
13221
+ if (tok.kind === "tbl_open") {
13222
+ innerTblDepth++;
13223
+ } else if (tok.kind === "tbl_close") {
13224
+ innerTblDepth--;
13225
+ } else if (innerTblDepth > 0) {
13226
+ } else if (tok.kind === "tr_open") {
13227
+ trStack.push({ pos: tok.pos });
13228
+ currentRowCells = [];
13229
+ } else if (tok.kind === "tr_close") {
13230
+ const trEntry = trStack.pop();
13231
+ if (trEntry !== void 0) {
13232
+ rows.push({ trStart: trEntry.pos, trEnd: tok.end, cells: currentRowCells });
13233
+ currentRowCells = [];
13234
+ }
13235
+ } else if (tok.kind === "tc_open") {
13236
+ tcStack.push({ pos: tok.pos });
13237
+ } else if (tok.kind === "tc_close") {
13238
+ const tcEntry = tcStack.pop();
13239
+ if (tcEntry !== void 0) {
13240
+ const tcTokens = tblTokens.filter(
13241
+ (t) => t.pos >= tcEntry.pos && t.pos < tok.end && innerTblDepth === 0
13242
+ );
13243
+ let colAddr = -1;
13244
+ let rowAddr = -1;
13245
+ let colSpan = 1;
13246
+ let rowSpan = 1;
13247
+ for (const t of tcTokens) {
13248
+ if (t.kind === "cell_addr" && t.colAddr !== void 0 && t.rowAddr !== void 0) {
13249
+ colAddr = t.colAddr;
13250
+ rowAddr = t.rowAddr;
13251
+ } else if (t.kind === "cell_span" && t.colSpan !== void 0 && t.rowSpan !== void 0) {
13252
+ colSpan = t.colSpan;
13253
+ rowSpan = t.rowSpan;
13254
+ }
13255
+ }
13256
+ if (colAddr >= 0 && rowAddr >= 0) {
13257
+ currentRowCells.push({
13258
+ colAddr,
13259
+ rowAddr,
13260
+ colSpan,
13261
+ rowSpan,
13262
+ tcStart: tcEntry.pos,
13263
+ tcEnd: tok.end
13264
+ });
13265
+ }
13266
+ }
13267
+ }
13268
+ }
13269
+ return rows;
13270
+ }
13271
+ function parseTblDimensions(xml, tblStart) {
13272
+ const tagEnd = xml.indexOf(">", tblStart);
13273
+ const tagStr = xml.substring(tblStart, tagEnd + 1);
13274
+ const rowM = tagStr.match(/rowCnt="(\d+)"/);
13275
+ const colM = tagStr.match(/colCnt="(\d+)"/);
13276
+ return {
13277
+ rowCnt: rowM ? Number(rowM[1]) : 0,
13278
+ colCnt: colM ? Number(colM[1]) : 0
13279
+ };
13280
+ }
13281
+ function updateTblAttr(tblBlock, attr, newVal) {
13282
+ return tblBlock.replace(new RegExp(`(${attr}=")\\d+(")`), `$1${newVal}$2`);
13283
+ }
13284
+ function clearCellText(tcXml) {
13285
+ let first = true;
13286
+ return tcXml.replace(/<hp:t>([\s\S]*?)<\/hp:t>/g, (_match, _content) => {
13287
+ if (first) {
13288
+ first = false;
13289
+ return "<hp:t/>";
13290
+ }
13291
+ return "<hp:t/>";
13292
+ });
13293
+ }
13294
+ function setCellRowAddr(tcXml, newRow) {
13295
+ return tcXml.replace(/(rowAddr=")(\d+)(")/, `$1${newRow}$3`);
13296
+ }
13297
+ function setCellColAddr(tcXml, newCol) {
13298
+ return tcXml.replace(/(colAddr=")(\d+)(")/, `$1${newCol}$3`);
13299
+ }
13300
+ function resetCellSpan(tcXml) {
13301
+ return tcXml.replace(/(colSpan=")(\d+)(")/, `$11$3`).replace(/(rowSpan=")(\d+)(")/, `$11$3`);
13302
+ }
13303
+ function setCellSpan(tcXml, colSpan, rowSpan) {
13304
+ return tcXml.replace(/(colSpan=")(\d+)(")/, `$1${colSpan}$3`).replace(/(rowSpan=")(\d+)(")/, `$1${rowSpan}$3`);
13305
+ }
13306
+ function insertRowInTbl(tblXml, row, below) {
13307
+ const tokens = tokenizeHwpxXml2(tblXml);
13308
+ const tblStart = 0;
13309
+ const tblEnd = tblXml.length;
13310
+ const rows = parseTableRows(tblXml, tokens, tblStart, tblEnd);
13311
+ const dims = parseTblDimensions(tblXml, tblStart);
13312
+ if (rows.length === 0) {
13313
+ return { ok: false, error: "\uD45C\uC5D0 \uD589\uC774 \uC5C6\uC5B4 \uC0BD\uC785\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4." };
13314
+ }
13315
+ const targetRow = rows.find((r) => r.cells.some((c) => c.rowAddr === row));
13316
+ if (!targetRow) {
13317
+ return {
13318
+ ok: false,
13319
+ error: `\uD589 ${row}\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. \uD45C\uC5D0 rowAddr ${row}\uC778 \uC140\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.`
13320
+ };
13321
+ }
13322
+ const newRowAddr = below ? row + 1 : row;
13323
+ for (const r of rows) {
13324
+ for (const c of r.cells) {
13325
+ if (c.rowSpan > 1) {
13326
+ const spanEnd = c.rowAddr + c.rowSpan - 1;
13327
+ if (c.rowAddr < newRowAddr && newRowAddr <= spanEnd) {
13328
+ return {
13329
+ ok: false,
13330
+ error: `\uC774 \uD45C\uC758 \uAE30\uC874 \uBCD1\uD569 \uC140\uACFC \uACB9\uCCD0 \uC548\uC804\uD558\uAC8C \uCC98\uB9AC\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4 (\uC140 (\uD589 ${c.rowAddr}, \uC5F4 ${c.colAddr})\uC758 rowSpan=${c.rowSpan}\uC774 \uC0BD\uC785 \uC704\uCE58 \uD589 ${newRowAddr}\uC744 \uAC00\uB85C\uC9C0\uB985\uB2C8\uB2E4). \uBCD1\uD569\uC744 \uD574\uC81C\uD55C \uD6C4 \uC2DC\uB3C4\uD558\uC138\uC694.`
13331
+ };
13332
+ }
13333
+ }
13334
+ }
13335
+ }
13336
+ const trOffset = targetRow.trStart;
13337
+ let modifiedTr = tblXml.substring(targetRow.trStart, targetRow.trEnd);
13338
+ const sortedCells = [...targetRow.cells].sort((a, b) => b.tcStart - a.tcStart);
13339
+ for (const cell of sortedCells) {
13340
+ const tcLocalStart = cell.tcStart - trOffset;
13341
+ const tcLocalEnd = cell.tcEnd - trOffset;
13342
+ let tcXml = modifiedTr.substring(tcLocalStart, tcLocalEnd);
13343
+ tcXml = setCellRowAddr(tcXml, newRowAddr);
13344
+ tcXml = resetCellSpan(tcXml);
13345
+ tcXml = clearCellText(tcXml);
13346
+ modifiedTr = modifiedTr.substring(0, tcLocalStart) + tcXml + modifiedTr.substring(tcLocalEnd);
13347
+ }
13348
+ let result = tblXml;
13349
+ const allTokens = tokenizeHwpxXml2(result);
13350
+ const addrPatches = [];
13351
+ for (const tok of allTokens) {
13352
+ if (tok.kind === "cell_addr" && tok.rowAddr !== void 0 && tok.rowAddr >= newRowAddr) {
13353
+ addrPatches.push({ pos: tok.pos, end: tok.end, newAddr: tok.rowAddr + 1 });
13354
+ }
13355
+ }
13356
+ addrPatches.sort((a, b) => b.pos - a.pos);
13357
+ for (const p of addrPatches) {
13358
+ const oldTag = result.substring(p.pos, p.end);
13359
+ const newTag = oldTag.replace(/(rowAddr=")(\d+)(")/, `$1${p.newAddr}$3`);
13360
+ result = result.substring(0, p.pos) + newTag + result.substring(p.end);
13361
+ }
13362
+ const resultTokens = tokenizeHwpxXml2(result);
13363
+ const resultRows = parseTableRows(result, resultTokens, 0, result.length);
13364
+ let insertAfterPos;
13365
+ let insertBeforePos;
13366
+ if (below) {
13367
+ const refRow = resultRows.find((r) => r.cells.some((c) => c.rowAddr === row));
13368
+ if (!refRow) {
13369
+ return { ok: false, error: "\uC0BD\uC785 \uAE30\uC900 \uD589\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4 (\uB0B4\uBD80 \uC624\uB958)." };
13370
+ }
13371
+ insertAfterPos = refRow.trEnd;
13372
+ } else {
13373
+ const refRow = resultRows.find((r) => r.cells.some((c) => c.rowAddr === row + 1));
13374
+ if (!refRow) {
13375
+ const firstRow = resultRows[0];
13376
+ if (!firstRow) {
13377
+ return { ok: false, error: "\uC0BD\uC785 \uC704\uCE58\uB97C \uACB0\uC815\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4 (\uB0B4\uBD80 \uC624\uB958)." };
13378
+ }
13379
+ insertBeforePos = firstRow.trStart;
13380
+ } else {
13381
+ insertBeforePos = refRow.trStart;
13382
+ }
13383
+ insertAfterPos = -1;
13384
+ }
13385
+ if (below) {
13386
+ result = result.substring(0, insertAfterPos) + modifiedTr + result.substring(insertAfterPos);
13387
+ } else {
13388
+ result = result.substring(0, insertBeforePos) + modifiedTr + result.substring(insertBeforePos);
13389
+ }
13390
+ result = updateTblAttr(result, "rowCnt", dims.rowCnt + 1);
13391
+ return { ok: true, xml: result };
13392
+ }
13393
+ function deleteRowInTbl(tblXml, row) {
13394
+ const tokens = tokenizeHwpxXml2(tblXml);
13395
+ const rows = parseTableRows(tblXml, tokens, 0, tblXml.length);
13396
+ const dims = parseTblDimensions(tblXml, 0);
13397
+ if (rows.length === 0) {
13398
+ return { ok: false, error: "\uD45C\uC5D0 \uD589\uC774 \uC5C6\uC2B5\uB2C8\uB2E4." };
13399
+ }
13400
+ const targetRow = rows.find((r) => r.cells.some((c) => c.rowAddr === row));
13401
+ if (!targetRow) {
13402
+ return {
13403
+ ok: false,
13404
+ error: `\uD589 ${row}\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. \uD45C\uC5D0 rowAddr ${row}\uC778 \uC140\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.`
13405
+ };
13406
+ }
13407
+ for (const cell of targetRow.cells) {
13408
+ if (cell.rowSpan > 1) {
13409
+ return {
13410
+ ok: false,
13411
+ error: `\uC774 \uD45C\uC758 \uAE30\uC874 \uBCD1\uD569 \uC140\uACFC \uACB9\uCCD0 \uC548\uC804\uD558\uAC8C \uCC98\uB9AC\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4 (\uD589 ${row}\uC758 \uC140 (\uC5F4 ${cell.colAddr})\uC5D0 rowSpan=${cell.rowSpan} \uBCD1\uD569\uC774 \uC788\uC2B5\uB2C8\uB2E4). \uBCD1\uD569\uC744 \uD574\uC81C\uD55C \uD6C4 \uC2DC\uB3C4\uD558\uC138\uC694.`
13412
+ };
13413
+ }
13414
+ }
13415
+ for (const r of rows) {
13416
+ if (r.trStart === targetRow.trStart) continue;
13417
+ for (const c of r.cells) {
13418
+ if (c.rowSpan > 1) {
13419
+ const spanEnd = c.rowAddr + c.rowSpan - 1;
13420
+ if (c.rowAddr < row && row <= spanEnd) {
13421
+ return {
13422
+ ok: false,
13423
+ error: `\uC774 \uD45C\uC758 \uAE30\uC874 \uBCD1\uD569 \uC140\uACFC \uACB9\uCCD0 \uC548\uC804\uD558\uAC8C \uCC98\uB9AC\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4 (\uB2E4\uB978 \uD589 \uC140 (\uD589 ${c.rowAddr}, \uC5F4 ${c.colAddr})\uC758 rowSpan=${c.rowSpan}\uC774 \uC0AD\uC81C\uD560 \uD589 ${row}\uC744 \uD3EC\uD568\uD569\uB2C8\uB2E4). \uBCD1\uD569\uC744 \uD574\uC81C\uD55C \uD6C4 \uC2DC\uB3C4\uD558\uC138\uC694.`
13424
+ };
13425
+ }
13426
+ }
13427
+ }
13428
+ }
13429
+ let result = tblXml;
13430
+ result = result.substring(0, targetRow.trStart) + result.substring(targetRow.trEnd);
13431
+ const afterTokens = tokenizeHwpxXml2(result);
13432
+ const addrPatches = [];
13433
+ for (const tok of afterTokens) {
13434
+ if (tok.kind === "cell_addr" && tok.rowAddr !== void 0 && tok.rowAddr > row) {
13435
+ addrPatches.push({ pos: tok.pos, end: tok.end, newAddr: tok.rowAddr - 1 });
13436
+ }
13437
+ }
13438
+ addrPatches.sort((a, b) => b.pos - a.pos);
13439
+ for (const p of addrPatches) {
13440
+ const oldTag = result.substring(p.pos, p.end);
13441
+ const newTag = oldTag.replace(/(rowAddr=")(\d+)(")/, `$1${p.newAddr}$3`);
13442
+ result = result.substring(0, p.pos) + newTag + result.substring(p.end);
13443
+ }
13444
+ result = updateTblAttr(result, "rowCnt", dims.rowCnt - 1);
13445
+ return { ok: true, xml: result };
13446
+ }
13447
+ function insertColumnInTbl(tblXml, col, right) {
13448
+ const tokens = tokenizeHwpxXml2(tblXml);
13449
+ const rows = parseTableRows(tblXml, tokens, 0, tblXml.length);
13450
+ const dims = parseTblDimensions(tblXml, 0);
13451
+ if (rows.length === 0) {
13452
+ return { ok: false, error: "\uD45C\uC5D0 \uD589\uC774 \uC5C6\uC5B4 \uC5F4\uC744 \uC0BD\uC785\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4." };
13453
+ }
13454
+ const newColAddr = right ? col + 1 : col;
13455
+ for (const r of rows) {
13456
+ for (const c of r.cells) {
13457
+ if (c.colSpan > 1) {
13458
+ const spanEnd = c.colAddr + c.colSpan - 1;
13459
+ if (c.colAddr < newColAddr && newColAddr <= spanEnd) {
13460
+ return {
13461
+ ok: false,
13462
+ error: `\uC774 \uD45C\uC758 \uAE30\uC874 \uBCD1\uD569 \uC140\uACFC \uACB9\uCCD0 \uC548\uC804\uD558\uAC8C \uCC98\uB9AC\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4 (\uC140 (\uD589 ${c.rowAddr}, \uC5F4 ${c.colAddr})\uC758 colSpan=${c.colSpan}\uC774 \uC0BD\uC785 \uC704\uCE58 \uC5F4 ${newColAddr}\uC744 \uAC00\uB85C\uC9C0\uB985\uB2C8\uB2E4). \uBCD1\uD569\uC744 \uD574\uC81C\uD55C \uD6C4 \uC2DC\uB3C4\uD558\uC138\uC694.`
13463
+ };
13464
+ }
13465
+ }
13466
+ }
13467
+ }
13468
+ let result = tblXml;
13469
+ const allTokens = tokenizeHwpxXml2(result);
13470
+ const addrPatches = [];
13471
+ for (const tok of allTokens) {
13472
+ if (tok.kind === "cell_addr" && tok.colAddr !== void 0 && tok.colAddr >= newColAddr) {
13473
+ addrPatches.push({ pos: tok.pos, end: tok.end, newAddr: tok.colAddr + 1 });
13474
+ }
13475
+ }
13476
+ addrPatches.sort((a, b) => b.pos - a.pos);
13477
+ for (const p of addrPatches) {
13478
+ const oldTag = result.substring(p.pos, p.end);
13479
+ const newTag = oldTag.replace(/(colAddr=")(\d+)(")/, `$1${p.newAddr}$3`);
13480
+ result = result.substring(0, p.pos) + newTag + result.substring(p.end);
13481
+ }
13482
+ const resultTokens2 = tokenizeHwpxXml2(result);
13483
+ const resultRows = parseTableRows(result, resultTokens2, 0, result.length);
13484
+ const sortedRows = [...resultRows].sort((a, b) => b.trStart - a.trStart);
13485
+ for (const r of sortedRows) {
13486
+ const refRowAddr = r.cells[0]?.rowAddr ?? 0;
13487
+ const cloneSourceCell = r.cells.find((c) => c.colAddr === col + 1);
13488
+ const anyCell = cloneSourceCell ?? r.cells[0];
13489
+ if (!anyCell) continue;
13490
+ let newTcXml = result.substring(anyCell.tcStart, anyCell.tcEnd);
13491
+ newTcXml = setCellColAddr(newTcXml, newColAddr);
13492
+ newTcXml = setCellRowAddr(newTcXml, refRowAddr);
13493
+ newTcXml = resetCellSpan(newTcXml);
13494
+ newTcXml = clearCellText(newTcXml);
13495
+ const refCell = r.cells.find((c) => c.colAddr === col + 1);
13496
+ let insertPos;
13497
+ if (refCell) {
13498
+ insertPos = refCell.tcStart;
13499
+ } else {
13500
+ const lastCell = r.cells[r.cells.length - 1];
13501
+ insertPos = lastCell ? lastCell.tcEnd : r.trEnd - "</hp:tr>".length;
13502
+ }
13503
+ result = result.substring(0, insertPos) + newTcXml + result.substring(insertPos);
13504
+ }
13505
+ result = updateTblAttr(result, "colCnt", dims.colCnt + 1);
13506
+ return { ok: true, xml: result };
13507
+ }
13508
+ function deleteColumnInTbl(tblXml, col) {
13509
+ const tokens = tokenizeHwpxXml2(tblXml);
13510
+ const rows = parseTableRows(tblXml, tokens, 0, tblXml.length);
13511
+ const dims = parseTblDimensions(tblXml, 0);
13512
+ if (rows.length === 0) {
13513
+ return { ok: false, error: "\uD45C\uC5D0 \uD589\uC774 \uC5C6\uC2B5\uB2C8\uB2E4." };
13514
+ }
13515
+ for (const r of rows) {
13516
+ const cell = r.cells.find((c) => c.colAddr === col);
13517
+ if (cell && cell.colSpan > 1) {
13518
+ return {
13519
+ ok: false,
13520
+ error: `\uC774 \uD45C\uC758 \uAE30\uC874 \uBCD1\uD569 \uC140\uACFC \uACB9\uCCD0 \uC548\uC804\uD558\uAC8C \uCC98\uB9AC\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4 (\uC5F4 ${col}\uC758 \uC140 (\uD589 ${cell.rowAddr})\uC5D0 colSpan=${cell.colSpan} \uBCD1\uD569\uC774 \uC788\uC2B5\uB2C8\uB2E4). \uBCD1\uD569\uC744 \uD574\uC81C\uD55C \uD6C4 \uC2DC\uB3C4\uD558\uC138\uC694.`
13521
+ };
13522
+ }
13523
+ for (const c of r.cells) {
13524
+ if (c.colAddr !== col && c.colSpan > 1) {
13525
+ const spanEnd = c.colAddr + c.colSpan - 1;
13526
+ if (c.colAddr < col && col <= spanEnd) {
13527
+ return {
13528
+ ok: false,
13529
+ error: `\uC774 \uD45C\uC758 \uAE30\uC874 \uBCD1\uD569 \uC140\uACFC \uACB9\uCCD0 \uC548\uC804\uD558\uAC8C \uCC98\uB9AC\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4 (\uC140 (\uD589 ${c.rowAddr}, \uC5F4 ${c.colAddr})\uC758 colSpan=${c.colSpan}\uC774 \uC0AD\uC81C\uD560 \uC5F4 ${col}\uC744 \uD3EC\uD568\uD569\uB2C8\uB2E4). \uBCD1\uD569\uC744 \uD574\uC81C\uD55C \uD6C4 \uC2DC\uB3C4\uD558\uC138\uC694.`
13530
+ };
13531
+ }
13532
+ }
13533
+ }
13534
+ }
13535
+ let result = tblXml;
13536
+ const sortedRows = [...rows].sort((a, b) => b.trStart - a.trStart);
13537
+ for (const r of sortedRows) {
13538
+ const cell = r.cells.find((c) => c.colAddr === col);
13539
+ if (cell) {
13540
+ result = result.substring(0, cell.tcStart) + result.substring(cell.tcEnd);
13541
+ }
13542
+ }
13543
+ const afterTokens = tokenizeHwpxXml2(result);
13544
+ const addrPatches = [];
13545
+ for (const tok of afterTokens) {
13546
+ if (tok.kind === "cell_addr" && tok.colAddr !== void 0 && tok.colAddr > col) {
13547
+ addrPatches.push({ pos: tok.pos, end: tok.end, newAddr: tok.colAddr - 1 });
13548
+ }
13549
+ }
13550
+ addrPatches.sort((a, b) => b.pos - a.pos);
13551
+ for (const p of addrPatches) {
13552
+ const oldTag = result.substring(p.pos, p.end);
13553
+ const newTag = oldTag.replace(/(colAddr=")(\d+)(")/, `$1${p.newAddr}$3`);
13554
+ result = result.substring(0, p.pos) + newTag + result.substring(p.end);
13555
+ }
13556
+ result = updateTblAttr(result, "colCnt", dims.colCnt - 1);
13557
+ return { ok: true, xml: result };
13558
+ }
13559
+ function mergeCellsInTbl(tblXml, startRow, startCol, endRow, endCol) {
13560
+ if (startRow === endRow && startCol === endCol) {
13561
+ return { ok: false, error: "\uBCD1\uD569 \uBC94\uC704\uAC00 \uB2E8\uC77C \uC140\uC785\uB2C8\uB2E4. \uCD5C\uC18C 2\uAC1C \uC774\uC0C1\uC758 \uC140\uC744 \uC9C0\uC815\uD558\uC138\uC694." };
13562
+ }
13563
+ if (startRow > endRow || startCol > endCol) {
13564
+ return { ok: false, error: "\uBCD1\uD569 \uBC94\uC704\uAC00 \uC798\uBABB\uB418\uC5C8\uC2B5\uB2C8\uB2E4 (start > end)." };
13565
+ }
13566
+ const tokens = tokenizeHwpxXml2(tblXml);
13567
+ const rows = parseTableRows(tblXml, tokens, 0, tblXml.length);
13568
+ const rangeCells = [];
13569
+ for (const r of rows) {
13570
+ for (const c of r.cells) {
13571
+ if (c.rowAddr >= startRow && c.rowAddr <= endRow && c.colAddr >= startCol && c.colAddr <= endCol) {
13572
+ rangeCells.push(c);
13573
+ }
13574
+ }
13575
+ }
13576
+ if (rangeCells.length === 0) {
13577
+ return {
13578
+ ok: false,
13579
+ error: `\uBCD1\uD569 \uBC94\uC704 (${startRow},${startCol})~(${endRow},${endCol})\uC5D0 \uC140\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.`
13580
+ };
13581
+ }
13582
+ for (const c of rangeCells) {
13583
+ if (c.colSpan > 1 || c.rowSpan > 1) {
13584
+ return {
13585
+ ok: false,
13586
+ error: `\uC774 \uD45C\uC758 \uAE30\uC874 \uBCD1\uD569 \uC140\uACFC \uACB9\uCCD0 \uC548\uC804\uD558\uAC8C \uCC98\uB9AC\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4 (\uC140 (\uD589 ${c.rowAddr}, \uC5F4 ${c.colAddr})\uC774 \uC774\uBBF8 colSpan=${c.colSpan}, rowSpan=${c.rowSpan}\uC73C\uB85C \uBCD1\uD569\uB418\uC5B4 \uC788\uC2B5\uB2C8\uB2E4). \uAE30\uC874 \uBCD1\uD569\uC744 \uD574\uC81C\uD55C \uD6C4 \uC2DC\uB3C4\uD558\uC138\uC694.`
13587
+ };
13588
+ }
13589
+ }
13590
+ for (const r of rows) {
13591
+ for (const c of r.cells) {
13592
+ if (c.colSpan <= 1 && c.rowSpan <= 1) continue;
13593
+ const inRange = c.rowAddr >= startRow && c.rowAddr <= endRow && c.colAddr >= startCol && c.colAddr <= endCol;
13594
+ if (inRange) continue;
13595
+ const colEnd = c.colAddr + c.colSpan - 1;
13596
+ const rowEnd = c.rowAddr + c.rowSpan - 1;
13597
+ if (c.colAddr <= endCol && colEnd >= startCol && c.rowAddr <= endRow && rowEnd >= startRow) {
13598
+ return {
13599
+ ok: false,
13600
+ error: `\uC774 \uD45C\uC758 \uAE30\uC874 \uBCD1\uD569 \uC140\uACFC \uACB9\uCCD0 \uC548\uC804\uD558\uAC8C \uCC98\uB9AC\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4 (\uC140 (\uD589 ${c.rowAddr}, \uC5F4 ${c.colAddr})\uC758 span\uC774 \uBCD1\uD569 \uBC94\uC704\uC640 \uACB9\uCE69\uB2C8\uB2E4). \uAE30\uC874 \uBCD1\uD569\uC744 \uD574\uC81C\uD55C \uD6C4 \uC2DC\uB3C4\uD558\uC138\uC694.`
13601
+ };
13602
+ }
13603
+ }
13604
+ }
13605
+ const colSpan = endCol - startCol + 1;
13606
+ const rowSpan = endRow - startRow + 1;
13607
+ const topLeftCell = rangeCells.find((c) => c.rowAddr === startRow && c.colAddr === startCol);
13608
+ if (!topLeftCell) {
13609
+ return {
13610
+ ok: false,
13611
+ error: `\uBCD1\uD569 \uBC94\uC704\uC758 \uC88C\uC0C1\uB2E8 \uC140 (\uD589 ${startRow}, \uC5F4 ${startCol})\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.`
13612
+ };
13613
+ }
13614
+ const cellsToRemove = rangeCells.filter(
13615
+ (c) => !(c.rowAddr === startRow && c.colAddr === startCol)
13616
+ );
13617
+ let result = tblXml;
13618
+ const sortedRemove = [...cellsToRemove].sort((a, b) => b.tcStart - a.tcStart);
13619
+ for (const c of sortedRemove) {
13620
+ result = result.substring(0, c.tcStart) + result.substring(c.tcEnd);
13621
+ }
13622
+ const afterTokens = tokenizeHwpxXml2(result);
13623
+ const afterRows = parseTableRows(result, afterTokens, 0, result.length);
13624
+ const afterTopLeft = afterRows.flatMap((r) => r.cells).find((c) => c.rowAddr === startRow && c.colAddr === startCol);
13625
+ if (!afterTopLeft) {
13626
+ return { ok: false, error: "\uBCD1\uD569 \uD6C4 \uC88C\uC0C1\uB2E8 \uC140\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4 (\uB0B4\uBD80 \uC624\uB958)." };
13627
+ }
13628
+ let tcXml = result.substring(afterTopLeft.tcStart, afterTopLeft.tcEnd);
13629
+ tcXml = setCellSpan(tcXml, colSpan, rowSpan);
13630
+ result = result.substring(0, afterTopLeft.tcStart) + tcXml + result.substring(afterTopLeft.tcEnd);
13631
+ return { ok: true, xml: result };
13632
+ }
13633
+ function findTableByAnchorInXml(xml, anchor) {
13634
+ const tokens = tokenizeHwpxXml2(xml);
13635
+ const allRanges = findAllTopLevelTableRanges(tokens);
13636
+ const trimmedAnchor = anchor.trim();
13637
+ const matched = [];
13638
+ for (const r of allRanges) {
13639
+ const text3 = collectTableText(xml, tokens, r.start, r.end);
13640
+ if (text3.includes(trimmedAnchor)) {
13641
+ matched.push(r);
13642
+ }
13643
+ }
13644
+ if (matched.length === 0) {
13645
+ return {
13646
+ error: `anchor\uB97C \uD3EC\uD568\uD55C \uD45C\uB97C \uCC3E\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4: "${anchor}". read_document\uB85C \uD45C \uB0B4\uC6A9\uC744 \uD655\uC778\uD558\uACE0 \uD45C \uC548\uC5D0 \uC788\uB294 \uB3C5\uD2B9\uD55C \uD14D\uC2A4\uD2B8\uB97C anchor\uB85C \uC9C0\uC815\uD558\uC138\uC694.`
13647
+ };
13648
+ }
13649
+ if (matched.length > 1) {
13650
+ return {
13651
+ error: `anchor "${anchor}"\uC774(\uAC00) ${matched.length}\uAC1C\uC758 \uD45C\uC5D0\uC11C \uBC1C\uACAC\uB418\uC5C8\uC2B5\uB2C8\uB2E4. \uB354 \uAD6C\uCCB4\uC801\uC778 anchor \uD14D\uC2A4\uD2B8\uB97C \uC0AC\uC6A9\uD558\uC5EC \uD45C\uB97C \uD55C \uAC1C\uB9CC \uC120\uD0DD\uD560 \uC218 \uC788\uB3C4\uB85D \uD558\uC138\uC694.`
13652
+ };
13653
+ }
13654
+ return { range: matched[0], sectionXml: xml };
13655
+ }
13656
+ function computeExpectedDelta(ops) {
13657
+ let rowDelta = 0;
13658
+ let colDelta = 0;
13659
+ for (const op of ops) {
13660
+ if (op.type === "mergeCells") return null;
13661
+ if (op.type === "insertRow") rowDelta++;
13662
+ else if (op.type === "deleteRow") rowDelta--;
13663
+ else if (op.type === "insertColumn") colDelta++;
13664
+ else if (op.type === "deleteColumn") colDelta--;
13665
+ }
13666
+ return { rowDelta, colDelta };
13667
+ }
13668
+ function getTblDims(tblXml) {
13669
+ return parseTblDimensions(tblXml, 0);
13670
+ }
13671
+ async function applyOpsToHwpx(hwpxBuffer, anchor, operations) {
13672
+ const zip = await import_jszip4.default.loadAsync(hwpxBuffer);
13673
+ const sectionFiles = Object.keys(zip.files).filter((name) => /^Contents\/section\d+\.xml$/.test(name)).sort();
13674
+ let targetSectionIdx = -1;
13675
+ let targetRange = null;
13676
+ const sectionXmls = [];
13677
+ for (let si = 0; si < sectionFiles.length; si++) {
13678
+ const entry = zip.file(sectionFiles[si] ?? "");
13679
+ const xml = entry ? await entry.async("string") : "";
13680
+ sectionXmls.push(xml);
13681
+ if (targetSectionIdx === -1) {
13682
+ const found = findTableByAnchorInXml(xml, anchor);
13683
+ if ("range" in found) {
13684
+ targetSectionIdx = si;
13685
+ targetRange = found.range;
13686
+ } else if (found.error.includes("\uC774(\uAC00)") && found.error.includes("\uAC1C\uC758 \uD45C\uC5D0\uC11C")) {
13687
+ return { ok: false, error: `\uC624\uB958: ${found.error}` };
13688
+ }
13689
+ }
13690
+ }
13691
+ for (let si = sectionXmls.length; si < sectionFiles.length; si++) {
13692
+ const entry = zip.file(sectionFiles[si] ?? "");
13693
+ const xml = entry ? await entry.async("string") : "";
13694
+ sectionXmls.push(xml);
13695
+ }
13696
+ if (targetSectionIdx === -1 || targetRange === null) {
13697
+ return {
13698
+ ok: false,
13699
+ error: `\uC624\uB958: anchor\uB97C \uD3EC\uD568\uD55C \uD45C\uB97C \uCC3E\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4: "${anchor}". read_document\uB85C \uD45C \uB0B4\uC6A9\uC744 \uD655\uC778\uD558\uACE0 \uD45C \uC548\uC5D0 \uC788\uB294 \uB3C5\uD2B9\uD55C \uD14D\uC2A4\uD2B8\uB97C anchor\uB85C \uC9C0\uC815\uD558\uC138\uC694.`
13700
+ };
13701
+ }
13702
+ const sectionXml = sectionXmls[targetSectionIdx] ?? "";
13703
+ let tblBlock = sectionXml.substring(targetRange.start, targetRange.end);
13704
+ const beforeDims = getTblDims(tblBlock);
13705
+ for (let i = 0; i < operations.length; i++) {
13706
+ const op = operations[i];
13707
+ if (!op) continue;
13708
+ let opResult;
13709
+ if (op.type === "insertRow") {
13710
+ opResult = insertRowInTbl(tblBlock, op.row, op.position === "below");
13711
+ } else if (op.type === "deleteRow") {
13712
+ opResult = deleteRowInTbl(tblBlock, op.row);
13713
+ } else if (op.type === "insertColumn") {
13714
+ opResult = insertColumnInTbl(tblBlock, op.col, op.position === "right");
13715
+ } else if (op.type === "deleteColumn") {
13716
+ opResult = deleteColumnInTbl(tblBlock, op.col);
13717
+ } else {
13718
+ opResult = mergeCellsInTbl(tblBlock, op.startRow, op.startCol, op.endRow, op.endCol);
13719
+ }
13720
+ if (!opResult.ok) {
13721
+ return {
13722
+ ok: false,
13723
+ error: `\uC624\uB958: \uC5F0\uC0B0 #${i + 1} (${op.type}) \uC2E4\uD328. \uD30C\uC77C\uC744 \uBCC0\uACBD\uD558\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4. \uC6D0\uC778: ${opResult.error}`
13724
+ };
13725
+ }
13726
+ tblBlock = opResult.xml;
13727
+ }
13728
+ const afterDims = getTblDims(tblBlock);
13729
+ const newSectionXml = sectionXml.substring(0, targetRange.start) + tblBlock + sectionXml.substring(targetRange.end);
13730
+ sectionXmls[targetSectionIdx] = newSectionXml;
13731
+ const out = new import_jszip4.default();
13732
+ const mimetypeEntry = zip.file("mimetype");
13733
+ if (mimetypeEntry) {
13734
+ out.file("mimetype", await mimetypeEntry.async("uint8array"), { compression: "STORE" });
13735
+ }
13736
+ for (const [name, entry] of Object.entries(zip.files)) {
13737
+ if (name === "mimetype" || entry.dir) continue;
13738
+ const sectionIdx = sectionFiles.indexOf(name);
13739
+ if (sectionIdx >= 0) {
13740
+ out.file(name, sectionXmls[sectionIdx] ?? "");
13741
+ } else {
13742
+ out.file(name, await entry.async("uint8array"));
13743
+ }
13744
+ }
13745
+ const buf = await out.generateAsync({ type: "nodebuffer", compression: "DEFLATE" });
13746
+ return {
13747
+ ok: true,
13748
+ buffer: new Uint8Array(buf),
13749
+ beforeDims,
13750
+ afterDims,
13751
+ anchorTableIndex: targetRange.index
13752
+ };
13753
+ }
13754
+ async function verifyOutputDims(newBytes, anchor, expectedRowCnt, expectedColCnt) {
13755
+ const zip = await import_jszip4.default.loadAsync(newBytes);
13756
+ const sectionFiles = Object.keys(zip.files).filter((name) => /^Contents\/section\d+\.xml$/.test(name)).sort();
13757
+ for (const sf of sectionFiles) {
13758
+ const entry = zip.file(sf);
13759
+ const xml = entry ? await entry.async("string") : "";
13760
+ const tokens = tokenizeHwpxXml2(xml);
13761
+ const allRanges = findAllTopLevelTableRanges(tokens);
13762
+ const trimmedAnchor = anchor.trim();
13763
+ for (const r of allRanges) {
13764
+ const text3 = collectTableText(xml, tokens, r.start, r.end);
13765
+ if (text3.includes(trimmedAnchor)) {
13766
+ const tblBlock = xml.substring(r.start, r.end);
13767
+ const dims = getTblDims(tblBlock);
13768
+ if (dims.rowCnt !== expectedRowCnt || dims.colCnt !== expectedColCnt) {
13769
+ return {
13770
+ ok: false,
13771
+ error: `\uC790\uAE30\uAC80\uC99D \uC2E4\uD328 \u2014 \uC608\uC0C1 \uCE58\uC218 (${expectedRowCnt}\uD589 \xD7 ${expectedColCnt}\uC5F4)\uC640 \uC2E4\uC81C \uCE58\uC218 (${dims.rowCnt}\uD589 \xD7 ${dims.colCnt}\uC5F4)\uAC00 \uB2E4\uB985\uB2C8\uB2E4.`
13772
+ };
13773
+ }
13774
+ return { ok: true };
13775
+ }
13776
+ }
13777
+ }
13778
+ return { ok: true };
13779
+ }
13780
+ var proposeTableStructureTool = {
13781
+ name: "propose_table_structure",
13782
+ description: "HWPX \uBB38\uC11C\uC5D0\uC11C anchor \uD14D\uC2A4\uD2B8\uB85C \uC2DD\uBCC4\uB41C \uD45C\uC758 \uAD6C\uC870(\uD589/\uC5F4/\uC140 \uBCD1\uD569)\uB97C \uC218\uC815\uD569\uB2C8\uB2E4. ZIP XML \uC9C1\uC811 \uD328\uCE58 \uBC29\uC2DD\uC73C\uB85C \uB3D9\uC791\uD569\uB2C8\uB2E4(rhwp \uBBF8\uC0AC\uC6A9). \uC774\uBBF8\uC9C0\xB7\uC911\uCCA9\uD45C \uB4F1 \uBCF5\uC7A1\uD55C \uBB38\uC11C\uB3C4 \uAD6C\uC870 \uC190\uC2E4 \uC5C6\uC774 \uC548\uC804\uD558\uAC8C \uD3B8\uC9D1 \uAC00\uB2A5\uD569\uB2C8\uB2E4. anchor\uB294 \uB300\uC0C1 \uD45C \uC548\uC5D0\uB9CC \uC788\uB294 \uB3C5\uD2B9\uD55C \uC140 \uD14D\uC2A4\uD2B8\uB97C \uC9C0\uC815\uD558\uC138\uC694 \u2014 \uBA3C\uC800 read_document\uB85C \uBB38\uC11C\uB97C \uC77D\uC5B4 \uD45C \uB0B4\uC6A9\uC744 \uD655\uC778\uD558\uC138\uC694. \uC5F0\uC0B0\uC740 \uC9C0\uC815\uB41C \uC21C\uC11C\uB300\uB85C \uC801\uC6A9\uB418\uBA70, \uB098\uC911 \uC5F0\uC0B0\uC758 row/col \uC778\uB371\uC2A4\uB294 \uC55E \uC5F0\uC0B0 \uC801\uC6A9 \uD6C4\uC758 \uD45C \uC0C1\uD0DC\uB97C \uAE30\uC900\uC73C\uB85C \uD569\uB2C8\uB2E4. .hwpx \uC804\uC6A9\uC785\uB2C8\uB2E4 (.hwp\uB294 \uC9C0\uC6D0\uD558\uC9C0 \uC54A\uC73C\uBA70, Hancom\uC5D0\uC11C .hwpx\uB85C \uC800\uC7A5 \uD6C4 \uC0AC\uC6A9\uD558\uC138\uC694). \uAE30\uC874 \uBCD1\uD569 \uC140\uACFC \uAD50\uCC28\uD558\uB294 \uC5F0\uC0B0\uC740 \uC548\uC804\uC744 \uC704\uD574 \uAC70\uBD80\uB420 \uC218 \uC788\uC2B5\uB2C8\uB2E4. \uBCC0\uACBD \uC0AC\uD56D\uC740 \uC790\uAE30\uAC80\uC99D \uAC8C\uC774\uD2B8\uB97C \uD1B5\uACFC\uD55C \uD6C4 \uC0AC\uC6A9\uC790 \uC2B9\uC778\uC744 \uBC1B\uC544\uC57C\uB9CC \uC800\uC7A5\uB429\uB2C8\uB2E4.",
13783
+ inputSchema: proposeTableStructureSchema,
13784
+ requiresApproval: true,
13785
+ propose: async ({
13786
+ input,
13787
+ ctx
13788
+ }) => {
13789
+ const safePath = await resolveSafePath(ctx.cwd, input.path);
13790
+ const ext = extname9(safePath).toLowerCase();
13791
+ if (ext === ".hwp") {
13792
+ return "\uC624\uB958: propose_table_structure\uB294 .hwpx \uD30C\uC77C\uB9CC \uC9C0\uC6D0\uD569\uB2C8\uB2E4. .hwp(\uAD6C\uD615 OLE \uBC14\uC774\uB108\uB9AC)\uB294 XML \uC9C1\uC811 \uD3B8\uC9D1\uC774 \uBD88\uAC00\uD569\uB2C8\uB2E4. \uD55C\uAE00 \uD504\uB85C\uADF8\uB7A8\uC5D0\uC11C '\uB2E4\uB978 \uC774\uB984\uC73C\uB85C \uC800\uC7A5 \u2192 .hwpx'\uB85C \uC800\uC7A5\uD55C \uD6C4 \uB2E4\uC2DC \uC2DC\uB3C4\uD558\uC138\uC694.";
13793
+ }
13794
+ if (ext !== ".hwpx") {
13795
+ return `\uC624\uB958: propose_table_structure\uB294 .hwpx \uD30C\uC77C\uB9CC \uC9C0\uC6D0\uD569\uB2C8\uB2E4. \uD604\uC7AC \uD30C\uC77C \uD655\uC7A5\uC790: ${ext}. .hwpx \uD30C\uC77C\uC744 \uC9C0\uC815\uD558\uC138\uC694.`;
13796
+ }
13797
+ let originalBuf;
13798
+ try {
13799
+ originalBuf = await readFile9(safePath);
13800
+ } catch {
13801
+ return `\uC624\uB958: \uD30C\uC77C\uC744 \uC77D\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${input.path}. \uACBD\uB85C\uB97C \uD655\uC778\uD558\uC138\uC694.`;
13802
+ }
13803
+ if (originalBuf[0] !== 80 || originalBuf[1] !== 75) {
13804
+ return "\uC624\uB958: \uD30C\uC77C\uC774 \uC720\uD6A8\uD55C .hwpx(ZIP) \uD3EC\uB9F7\uC774 \uC544\uB2D9\uB2C8\uB2E4. \uD30C\uC77C\uC774 \uC190\uC0C1\uB418\uC5C8\uAC70\uB098 \uAD6C\uD615 .hwp(OLE \uBC14\uC774\uB108\uB9AC) \uD3EC\uB9F7\uC785\uB2C8\uB2E4. \uD55C\uAE00 \uD504\uB85C\uADF8\uB7A8\uC5D0\uC11C .hwpx\uB85C \uC800\uC7A5 \uD6C4 \uB2E4\uC2DC \uC2DC\uB3C4\uD558\uC138\uC694.";
13805
+ }
13806
+ const originalBytes = new Uint8Array(
13807
+ originalBuf.buffer,
13808
+ originalBuf.byteOffset,
13809
+ originalBuf.byteLength
13810
+ );
13811
+ let originalBlocks = null;
13812
+ try {
13813
+ const origResult = await parse4(originalBuf.buffer);
13814
+ if (origResult.success) {
13815
+ originalBlocks = origResult.blocks;
13816
+ }
13817
+ } catch {
13818
+ }
13819
+ let applyResult;
13820
+ try {
13821
+ applyResult = await applyOpsToHwpx(originalBytes, input.anchor, input.operations);
13822
+ } catch (e) {
13823
+ return `\uC624\uB958: \uD45C \uAD6C\uC870 \uD3B8\uC9D1 \uC911 \uC608\uC678\uAC00 \uBC1C\uC0DD\uD588\uC2B5\uB2C8\uB2E4. ${String(e)}`;
13824
+ }
13825
+ if (!applyResult.ok) {
13826
+ return applyResult.error;
13827
+ }
13828
+ const { buffer: newBytes, beforeDims, afterDims } = applyResult;
13829
+ const warnings = [];
13830
+ let exportedMd = "";
13831
+ let kordocOk = false;
13832
+ let exportedBlocks = null;
13833
+ try {
13834
+ const exportedResult = await parse4(newBytes.buffer);
13835
+ if (exportedResult.success) {
13836
+ exportedMd = exportedResult.markdown;
13837
+ exportedBlocks = exportedResult.blocks;
13838
+ kordocOk = true;
13839
+ }
13840
+ } catch {
13841
+ }
13842
+ if (!kordocOk) {
13843
+ return `\uC624\uB958: \uB0B4\uBCF4\uB0B8 \uBB38\uC11C\uB97C kordoc\uC73C\uB85C \uC7AC\uD30C\uC2F1\uD558\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4. \uBB38\uC11C\uAC00 \uC190\uC0C1\uB418\uC5C8\uC744 \uC218 \uC788\uC73C\uBBC0\uB85C \uD30C\uC77C\uC744 \uC800\uC7A5\uD558\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4.`;
13844
+ }
13845
+ if (originalBlocks !== null && exportedBlocks !== null) {
13846
+ const lossResult = detectStructuralLoss(originalBlocks, exportedBlocks);
13847
+ if (lossResult.lost) {
13848
+ return `\uC624\uB958: XML \uD3B8\uC9D1 \uD6C4 \uAD6C\uC870 \uC190\uC2E4\uC774 \uAC10\uC9C0\uB418\uC5C8\uC2B5\uB2C8\uB2E4(${lossResult.detail}). XML \uD328\uCE58\uC5D0 \uBC84\uADF8\uAC00 \uC788\uC744 \uC218 \uC788\uC2B5\uB2C8\uB2E4. \uD30C\uC77C\uC744 \uC800\uC7A5\uD558\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4.`;
13849
+ }
13850
+ }
13851
+ const hasDeleteOp = input.operations.some(
13852
+ (op) => op.type === "deleteRow" || op.type === "deleteColumn"
13853
+ );
13854
+ const anchorInExported = exportedMd.includes(input.anchor.trim());
13855
+ if (!anchorInExported) {
13856
+ if (!hasDeleteOp) {
13857
+ return `\uC624\uB958: \uC790\uAE30\uAC80\uC99D \uC2E4\uD328 \u2014 \uB0B4\uBCF4\uB0B8 \uBB38\uC11C\uC5D0\uC11C anchor "${input.anchor}"\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. \uD45C \uAD6C\uC870\uAC00 \uC190\uC0C1\uB418\uC5C8\uC744 \uC218 \uC788\uC73C\uBBC0\uB85C \uD30C\uC77C\uC744 \uC800\uC7A5\uD558\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4.`;
13858
+ }
13859
+ warnings.push(
13860
+ `\uC790\uAE30\uAC80\uC99D: \uB0B4\uBCF4\uB0B8 \uBB38\uC11C\uC5D0\uC11C anchor "${input.anchor}"\uAC00 \uBCF4\uC774\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4. \uC0AD\uC81C \uC5F0\uC0B0\uC73C\uB85C anchor\uAC00 \uD3EC\uD568\uB41C \uD589/\uC5F4\uC774 \uC81C\uAC70\uB418\uC5C8\uC744 \uC218 \uC788\uC2B5\uB2C8\uB2E4. \uACB0\uACFC\uB97C \uC9C1\uC811 \uD655\uC778\uD558\uC138\uC694.`
13861
+ );
13862
+ }
13863
+ const delta = computeExpectedDelta(input.operations);
13864
+ if (delta !== null) {
13865
+ const expectedRowCnt = beforeDims.rowCnt + delta.rowDelta;
13866
+ const expectedColCnt = beforeDims.colCnt + delta.colDelta;
13867
+ if (afterDims.rowCnt !== expectedRowCnt || afterDims.colCnt !== expectedColCnt) {
13868
+ return `\uC624\uB958: \uC790\uAE30\uAC80\uC99D \uC2E4\uD328 \u2014 \uC608\uC0C1 \uCE58\uC218 (${expectedRowCnt}\uD589 \xD7 ${expectedColCnt}\uC5F4)\uC640 \uC2E4\uC81C \uCE58\uC218 (${afterDims.rowCnt}\uD589 \xD7 ${afterDims.colCnt}\uC5F4)\uAC00 \uB2E4\uB985\uB2C8\uB2E4. \uD30C\uC77C\uC744 \uC800\uC7A5\uD558\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4.`;
13869
+ }
13870
+ if (anchorInExported) {
13871
+ const dimVerify = await verifyOutputDims(
13872
+ newBytes,
13873
+ input.anchor,
13874
+ expectedRowCnt,
13875
+ expectedColCnt
13876
+ );
13877
+ if (!dimVerify.ok) {
13878
+ return `\uC624\uB958: ${dimVerify.error} \uD30C\uC77C\uC744 \uC800\uC7A5\uD558\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4.`;
13879
+ }
13880
+ }
13881
+ }
13882
+ const { outputPath, willConvertFormat } = resolveOutputPath(safePath);
13883
+ const opDescriptions = input.operations.map((op, i) => {
13884
+ if (op.type === "insertRow")
13885
+ return ` ${i + 1}. \uD589 ${op.row} ${op.position === "below" ? "\uC544\uB798" : "\uC704"}\uC5D0 \uD589 \uC0BD\uC785`;
13886
+ if (op.type === "deleteRow") return ` ${i + 1}. \uD589 ${op.row} \uC0AD\uC81C`;
13887
+ if (op.type === "insertColumn")
13888
+ return ` ${i + 1}. \uC5F4 ${op.col} ${op.position === "right" ? "\uC624\uB978\uCABD" : "\uC67C\uCABD"}\uC5D0 \uC5F4 \uC0BD\uC785`;
13889
+ if (op.type === "deleteColumn") return ` ${i + 1}. \uC5F4 ${op.col} \uC0AD\uC81C`;
13890
+ return ` ${i + 1}. (${op.startRow},${op.startCol})~(${op.endRow},${op.endCol}) \uC140 \uBCD1\uD569`;
13891
+ });
13892
+ const diff = `anchor: "${input.anchor}" (\uD45C \uC778\uB371\uC2A4 ${applyResult.anchorTableIndex})
13893
+ \uC774\uC804: ${beforeDims.rowCnt}\uD589 \xD7 ${beforeDims.colCnt}\uC5F4
13894
+ \uC774\uD6C4: ${afterDims.rowCnt}\uD589 \xD7 ${afterDims.colCnt}\uC5F4
13895
+ \uC5F0\uC0B0 (${input.operations.length}\uAC1C):
13896
+ ${opDescriptions.join("\n")}`;
13897
+ const stagedPath = await stageFile(ctx.sessionId, outputPath, newBytes);
13898
+ const proposalId = crypto.randomUUID();
13899
+ return {
13900
+ proposal: {
13901
+ id: proposalId,
13902
+ kind: "table-structure",
13903
+ targetPath: outputPath,
13904
+ stagedPath,
13905
+ summary: input.summary,
13906
+ diff,
13907
+ warnings,
13908
+ willConvertFormat
13909
+ },
13910
+ commit: async () => {
13911
+ const backupPath = await backupFile(safePath);
13912
+ await commitStaged(stagedPath, outputPath);
13913
+ const backupInfo = backupPath ? ` (\uBC31\uC5C5: ${backupPath})` : "";
13914
+ return `\uC800\uC7A5 \uC644\uB8CC: ${outputPath}${backupInfo}`;
13915
+ }
13916
+ };
13917
+ }
13918
+ };
12810
13919
  var MAX_MARKDOWN_LENGTH2 = 8e4;
12811
13920
  var MAX_FILE_SIZE_BYTES = 100 * 1024 * 1024;
12812
13921
  var PLAIN_TEXT_EXTS = /* @__PURE__ */ new Set([".md", ".markdown", ".txt", ".text"]);
@@ -12883,11 +13992,11 @@ function searchExcerpts(markdown, query) {
12883
13992
  }
12884
13993
  return result;
12885
13994
  }
12886
- var readDocumentSchema = z8.object({
12887
- path: z8.string().describe("\uC77D\uC744 \uBB38\uC11C \uACBD\uB85C (cwd \uAE30\uC900 \uC0C1\uB300 \uACBD\uB85C \uB610\uB294 \uC808\uB300 \uACBD\uB85C)"),
12888
- pages: z8.string().optional().describe('\uC77D\uC744 \uD398\uC774\uC9C0 \uBC94\uC704 (\uC608: "1-3", "1,3,5") \u2014 \uBBF8\uC9C0\uC815 \uC2DC \uC804\uCCB4'),
12889
- outline: z8.boolean().optional().describe("\uD5E4\uB529(\uC81C\uBAA9) \uAD6C\uC870\uB9CC \uBC18\uD658\uD574 \uBB38\uC11C \uAC1C\uC694 \uD30C\uC545 (\uB300\uD615 \uBB38\uC11C \uD0D0\uC0C9\uC6A9)"),
12890
- search: z8.string().optional().describe("\uD0A4\uC6CC\uB4DC\uAC00 \uD3EC\uD568\uB41C \uBD80\uBD84\uACFC \uC8FC\uBCC0 \uB9E5\uB77D\uB9CC \uBC18\uD658 (\uB300\uD615 \uBB38\uC11C\uC5D0\uC11C \uD544\uC694\uD55C \uBD80\uBD84\uB9CC \uC77D\uAE30)")
13995
+ var readDocumentSchema = z10.object({
13996
+ path: z10.string().describe("\uC77D\uC744 \uBB38\uC11C \uACBD\uB85C (cwd \uAE30\uC900 \uC0C1\uB300 \uACBD\uB85C \uB610\uB294 \uC808\uB300 \uACBD\uB85C)"),
13997
+ pages: z10.string().optional().describe('\uC77D\uC744 \uD398\uC774\uC9C0 \uBC94\uC704 (\uC608: "1-3", "1,3,5") \u2014 \uBBF8\uC9C0\uC815 \uC2DC \uC804\uCCB4'),
13998
+ outline: z10.boolean().optional().describe("\uD5E4\uB529(\uC81C\uBAA9) \uAD6C\uC870\uB9CC \uBC18\uD658\uD574 \uBB38\uC11C \uAC1C\uC694 \uD30C\uC545 (\uB300\uD615 \uBB38\uC11C \uD0D0\uC0C9\uC6A9)"),
13999
+ search: z10.string().optional().describe("\uD0A4\uC6CC\uB4DC\uAC00 \uD3EC\uD568\uB41C \uBD80\uBD84\uACFC \uC8FC\uBCC0 \uB9E5\uB77D\uB9CC \uBC18\uD658 (\uB300\uD615 \uBB38\uC11C\uC5D0\uC11C \uD544\uC694\uD55C \uBD80\uBD84\uB9CC \uC77D\uAE30)")
12891
14000
  });
12892
14001
  function applyReadMode(body, outline, search) {
12893
14002
  if (outline === true) {
@@ -12916,11 +14025,11 @@ var readDocumentTool = {
12916
14025
  const msg = e instanceof Error ? e.message : String(e);
12917
14026
  return `\uC624\uB958: \uD30C\uC77C\uC744 \uC77D\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${msg}`;
12918
14027
  }
12919
- const ext = extname8(safePath).toLowerCase();
14028
+ const ext = extname10(safePath).toLowerCase();
12920
14029
  if (PLAIN_TEXT_EXTS.has(ext)) {
12921
14030
  let raw;
12922
14031
  try {
12923
- raw = await readFile8(safePath, "utf-8");
14032
+ raw = await readFile10(safePath, "utf-8");
12924
14033
  } catch (e) {
12925
14034
  const msg = e instanceof Error ? e.message : String(e);
12926
14035
  return `\uC624\uB958: \uD30C\uC77C\uC744 \uC77D\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${msg}`;
@@ -12944,7 +14053,7 @@ var readDocumentTool = {
12944
14053
  \u26A0\uFE0F \uB0B4\uC6A9\uC774 \uB108\uBB34 \uAE38\uC5B4 \uC57D 80,000\uC790\uC5D0\uC11C \uC798\uB838\uC2B5\uB2C8\uB2E4. (\uD3C9\uBB38 \uD14D\uC2A4\uD2B8 \uD30C\uC77C\uC774\uB77C \uD398\uC774\uC9C0 \uB2E8\uC704 \uBD84\uD560 \uC77D\uAE30\uB294 \uC9C0\uC6D0\uB418\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4)` : "";
12945
14054
  return `${meta2}${body2}${truncationNotice2}`;
12946
14055
  }
12947
- const result = await parse3(safePath, input.pages ? { pages: input.pages } : void 0);
14056
+ const result = await parse5(safePath, input.pages ? { pages: input.pages } : void 0);
12948
14057
  if (!result.success) {
12949
14058
  const msg = kordocErrorMessage(result.code, `\uBB38\uC11C\uB97C \uC77D\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${result.error}`);
12950
14059
  return `\uC624\uB958: ${msg}`;
@@ -13020,8 +14129,8 @@ var TEXT_EXTENSIONS = /* @__PURE__ */ new Set([
13020
14129
  ".csv",
13021
14130
  ".log"
13022
14131
  ]);
13023
- var readFileSchema = z9.object({
13024
- path: z9.string().describe("\uC77D\uC744 \uD30C\uC77C \uACBD\uB85C (cwd \uAE30\uC900 \uC0C1\uB300 \uACBD\uB85C \uB610\uB294 \uC808\uB300 \uACBD\uB85C)")
14132
+ var readFileSchema = z11.object({
14133
+ path: z11.string().describe("\uC77D\uC744 \uD30C\uC77C \uACBD\uB85C (cwd \uAE30\uC900 \uC0C1\uB300 \uACBD\uB85C \uB610\uB294 \uC808\uB300 \uACBD\uB85C)")
13025
14134
  });
13026
14135
  var readFileTool = {
13027
14136
  name: "read_file",
@@ -13045,8 +14154,8 @@ var readFileTool = {
13045
14154
  if (info.size > MAX_SIZE) {
13046
14155
  return `\uC624\uB958: \uD30C\uC77C\uC774 \uB108\uBB34 \uD07D\uB2C8\uB2E4 (${Math.round(info.size / 1024)}KB). \uCD5C\uB300 256KB\uAE4C\uC9C0 \uC77D\uC744 \uC218 \uC788\uC2B5\uB2C8\uB2E4.`;
13047
14156
  }
13048
- const { extname: extname11 } = await import("path");
13049
- const ext = extname11(safePath).toLowerCase();
14157
+ const { extname: extname13 } = await import("path");
14158
+ const ext = extname13(safePath).toLowerCase();
13050
14159
  if (!TEXT_EXTENSIONS.has(ext) && ext !== "") {
13051
14160
  return `\uC624\uB958: '${ext}' \uD615\uC2DD\uC740 \uD14D\uC2A4\uD2B8 \uD30C\uC77C\uB85C \uC77D\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. \uBB38\uC11C \uD30C\uC77C\uC774\uB77C\uBA74 read_document \uD234\uC744 \uC0AC\uC6A9\uD558\uC138\uC694.`;
13052
14161
  }
@@ -13061,9 +14170,9 @@ var readFileTool = {
13061
14170
  }
13062
14171
  };
13063
14172
  var MAX_PREVIEW_CHARS = 1e4;
13064
- var writeNewDocumentSchema = z10.object({
13065
- path: z10.string().describe("\uC0DD\uC131\uD560 \uBB38\uC11C \uACBD\uB85C (cwd \uAE30\uC900 \uC0C1\uB300 \uACBD\uB85C \uB610\uB294 \uC808\uB300 \uACBD\uB85C)"),
13066
- markdown: z10.string().describe("\uC0C8 \uBB38\uC11C \uB0B4\uC6A9 (\uB9C8\uD06C\uB2E4\uC6B4 \uD615\uC2DD)")
14173
+ var writeNewDocumentSchema = z12.object({
14174
+ path: z12.string().describe("\uC0DD\uC131\uD560 \uBB38\uC11C \uACBD\uB85C (cwd \uAE30\uC900 \uC0C1\uB300 \uACBD\uB85C \uB610\uB294 \uC808\uB300 \uACBD\uB85C)"),
14175
+ markdown: z12.string().describe("\uC0C8 \uBB38\uC11C \uB0B4\uC6A9 (\uB9C8\uD06C\uB2E4\uC6B4 \uD615\uC2DD)")
13067
14176
  });
13068
14177
  var writeNewDocumentTool = {
13069
14178
  name: "write_new_document",
@@ -13075,7 +14184,7 @@ var writeNewDocumentTool = {
13075
14184
  ctx
13076
14185
  }) => {
13077
14186
  const safePath = await resolveSafePath(ctx.cwd, input.path);
13078
- const ext = extname9(safePath).toLowerCase();
14187
+ const ext = extname11(safePath).toLowerCase();
13079
14188
  try {
13080
14189
  await stat5(safePath);
13081
14190
  return `\uC624\uB958: \uD30C\uC77C\uC774 \uC774\uBBF8 \uC874\uC7AC\uD569\uB2C8\uB2E4: ${input.path}. \uAE30\uC874 \uD30C\uC77C\uC744 \uC218\uC815\uD558\uB824\uBA74 propose_edit\uC744 \uC0AC\uC6A9\uD558\uC138\uC694.`;
@@ -13125,12 +14234,12 @@ ${preview}`,
13125
14234
  };
13126
14235
  }
13127
14236
  };
13128
- var writeNewSpreadsheetSchema = z11.object({
13129
- path: z11.string().describe("\uC0DD\uC131\uD560 XLSX \uD30C\uC77C \uACBD\uB85C (cwd \uAE30\uC900 \uC0C1\uB300 \uACBD\uB85C \uB610\uB294 \uC808\uB300 \uACBD\uB85C)"),
13130
- sheets: z11.array(
13131
- z11.object({
13132
- name: z11.string().describe("\uC2DC\uD2B8 \uC774\uB984"),
13133
- rows: z11.array(z11.array(z11.string())).describe("\uD589 \uB370\uC774\uD130 \uBC30\uC5F4 (\uAC01 \uD589\uC740 \uC140 \uAC12 \uBC30\uC5F4)")
14237
+ var writeNewSpreadsheetSchema = z13.object({
14238
+ path: z13.string().describe("\uC0DD\uC131\uD560 XLSX \uD30C\uC77C \uACBD\uB85C (cwd \uAE30\uC900 \uC0C1\uB300 \uACBD\uB85C \uB610\uB294 \uC808\uB300 \uACBD\uB85C)"),
14239
+ sheets: z13.array(
14240
+ z13.object({
14241
+ name: z13.string().describe("\uC2DC\uD2B8 \uC774\uB984"),
14242
+ rows: z13.array(z13.array(z13.string())).describe("\uD589 \uB370\uC774\uD130 \uBC30\uC5F4 (\uAC01 \uD589\uC740 \uC140 \uAC12 \uBC30\uC5F4)")
13134
14243
  })
13135
14244
  ).min(1).describe("\uC0DD\uC131\uD560 \uC2DC\uD2B8 \uBAA9\uB85D")
13136
14245
  });
@@ -13144,7 +14253,7 @@ var writeNewSpreadsheetTool = {
13144
14253
  ctx
13145
14254
  }) => {
13146
14255
  const safePath = await resolveSafePath(ctx.cwd, input.path);
13147
- const ext = extname10(safePath).toLowerCase();
14256
+ const ext = extname12(safePath).toLowerCase();
13148
14257
  if (ext !== ".xlsx") {
13149
14258
  return `\uC624\uB958: write_new_spreadsheet\uC740 .xlsx \uD30C\uC77C\uB9CC \uC9C0\uC6D0\uD569\uB2C8\uB2E4. \uD604\uC7AC \uD655\uC7A5\uC790: ${ext}.`;
13150
14259
  }
@@ -13206,7 +14315,9 @@ function createDocTools(_ctx) {
13206
14315
  proposeEditTool,
13207
14316
  proposeFormFillTool,
13208
14317
  proposeCellEditTool,
14318
+ proposeFindReplaceTool,
13209
14319
  proposeSheetEditTool,
14320
+ proposeTableStructureTool,
13210
14321
  listFormObjectsTool,
13211
14322
  proposeFormObjectTool,
13212
14323
  writeNewDocumentTool,
@@ -14014,10 +15125,10 @@ async function runOnboarding() {
14014
15125
 
14015
15126
  // src/update.ts
14016
15127
  import { spawn } from "child_process";
14017
- import { mkdir as mkdir4, readFile as readFile9, writeFile as writeFile3 } from "fs/promises";
15128
+ import { mkdir as mkdir4, readFile as readFile11, writeFile as writeFile3 } from "fs/promises";
14018
15129
  import { dirname as dirname3 } from "path";
14019
15130
  function compareSemver(a, b) {
14020
- const parse4 = (v) => {
15131
+ const parse6 = (v) => {
14021
15132
  const clean = v.replace(/^v/, "").split("-")[0] ?? "";
14022
15133
  const parts = clean.split(".").map((p) => {
14023
15134
  const n = parseInt(p, 10);
@@ -14025,8 +15136,8 @@ function compareSemver(a, b) {
14025
15136
  });
14026
15137
  return [parts[0] ?? 0, parts[1] ?? 0, parts[2] ?? 0];
14027
15138
  };
14028
- const [aMaj, aMin, aPat] = parse4(a);
14029
- const [bMaj, bMin, bPat] = parse4(b);
15139
+ const [aMaj, aMin, aPat] = parse6(a);
15140
+ const [bMaj, bMin, bPat] = parse6(b);
14030
15141
  if (aMaj !== bMaj) return aMaj < bMaj ? -1 : 1;
14031
15142
  if (aMin !== bMin) return aMin < bMin ? -1 : 1;
14032
15143
  if (aPat !== bPat) return aPat < bPat ? -1 : 1;
@@ -14035,7 +15146,7 @@ function compareSemver(a, b) {
14035
15146
  var CACHE_TTL_MS = 24 * 60 * 60 * 1e3;
14036
15147
  async function readCache(cachePath) {
14037
15148
  try {
14038
- const raw = await readFile9(cachePath, "utf-8");
15149
+ const raw = await readFile11(cachePath, "utf-8");
14039
15150
  const parsed = JSON.parse(raw);
14040
15151
  if (parsed !== null && typeof parsed === "object" && "checkedAt" in parsed && "latest" in parsed && typeof parsed.checkedAt === "string" && typeof parsed.latest === "string") {
14041
15152
  return parsed;