@kodocagent/cli 0.4.2 → 0.4.4

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
@@ -318,7 +318,7 @@ var require_BufferList = __commonJS({
318
318
  this.head = this.tail = null;
319
319
  this.length = 0;
320
320
  };
321
- BufferList.prototype.join = function join4(s) {
321
+ BufferList.prototype.join = function join5(s) {
322
322
  if (this.length === 0) return "";
323
323
  var p = this.head;
324
324
  var ret = "" + p.data;
@@ -9890,7 +9890,8 @@ var DOCUMENT_RULES_SECTION = `## \uBB38\uC11C \uADDC\uCE59
9890
9890
  4. \`.hwp\` \uD30C\uC77C\uC744 \uD3B8\uC9D1\uD55C \uACB0\uACFC\uB294 \`.hwpx\` \uD615\uC2DD\uC73C\uB85C \uC800\uC7A5\uB429\uB2C8\uB2E4. \uC774 \uBCC0\uD658 \uC0AC\uC2E4\uC744 \uC0AC\uC6A9\uC790\uC5D0\uAC8C \uBBF8\uB9AC \uC548\uB0B4\uD558\uC138\uC694.
9891
9891
  5. \uACBD\uB85C\uB294 \uD604\uC7AC \uC791\uC5C5 \uB514\uB809\uD130\uB9AC\uB97C \uAE30\uC900\uC73C\uB85C \uC0C1\uB300 \uACBD\uB85C \uB610\uB294 \uC808\uB300 \uACBD\uB85C\uB97C \uC0AC\uC6A9\uD560 \uC218 \uC788\uC2B5\uB2C8\uB2E4.
9892
9892
  6. \uC0AC\uC6A9\uC790\uAC00 \uC218\uC815\uC548\uC744 \uAC70\uC808\uD558\uBA74 \uAC19\uC740 \uC81C\uC548\uC744 \uC790\uB3D9\uC73C\uB85C \uBC18\uBCF5\uD558\uC9C0 \uB9D0\uACE0, \uC0AC\uC6A9\uC790\uC758 \uB2E4\uC74C \uC9C0\uC2DC\uB97C \uAE30\uB2E4\uB9AC\uC138\uC694.
9893
- 7. \uD070 \uBB38\uC11C\uB294 \uBA3C\uC800 \`outline\`\uC73C\uB85C \uAD6C\uC870\uB97C \uD30C\uC545\uD558\uACE0, \`search\`\uB098 \`pages\`\uB85C \uD544\uC694\uD55C \uBD80\uBD84\uB9CC \uC77D\uC5B4 \uCEE8\uD14D\uC2A4\uD2B8\uB97C \uC544\uB07C\uC138\uC694.`;
9893
+ 7. \uD070 \uBB38\uC11C\uB294 \uBA3C\uC800 \`outline\`\uC73C\uB85C \uAD6C\uC870\uB97C \uD30C\uC545\uD558\uACE0, \`search\`\uB098 \`pages\`\uB85C \uD544\uC694\uD55C \uBD80\uBD84\uB9CC \uC77D\uC5B4 \uCEE8\uD14D\uC2A4\uD2B8\uB97C \uC544\uB07C\uC138\uC694.
9894
+ 8. \uC0AC\uC6A9\uC790\uAC00 \uC9C1\uC804 \uBCC0\uACBD\uC744 \uB418\uB3CC\uB9AC\uAE38 \uC6D0\uD558\uBA74 \`list_backups\`\uB85C \uBC31\uC5C5\uC744 \uD655\uC778\uD558\uACE0 \`restore_backup\`\uC73C\uB85C \uBCF5\uC6D0\uD558\uC138\uC694. \uBCF5\uC6D0\uB3C4 \uC2B9\uC778\uC744 \uAC70\uCE58\uBA70, \uBCF5\uC6D0 \uC804 \uD604\uC7AC \uC0C1\uD0DC\uAC00 \uC790\uB3D9 \uBC31\uC5C5\uB429\uB2C8\uB2E4.`;
9894
9895
  var LAW_RULES_SECTION = `## \uBC95\uB839 \uADDC\uCE59
9895
9896
 
9896
9897
  1. \uBC95\uB839 \uC778\uC6A9 \uD615\uC2DD: \u300C\uBC95\uB839\uBA85\u300D \uC81CN\uC870 \uC81CN\uD56D \uC81CN\uD638
@@ -10009,6 +10010,13 @@ var AgentSession = class {
10009
10010
  opts;
10010
10011
  messages = [];
10011
10012
  openDocuments = [];
10013
+ /** 열람한 문서 경로를 중복 없이 기록한다 (방어적: 비문자열/오류는 무시). */
10014
+ recordOpenDocument(p) {
10015
+ if (typeof p !== "string" || p.trim() === "") return;
10016
+ if (!this.openDocuments.includes(p)) {
10017
+ this.openDocuments.push(p);
10018
+ }
10019
+ }
10012
10020
  /** approval-required 이벤트를 run() 스트림에 전달하기 위한 큐 */
10013
10021
  pendingApprovalEvents = [];
10014
10022
  /**
@@ -10064,6 +10072,18 @@ var AgentSession = class {
10064
10072
  args: part.input,
10065
10073
  callId: part.toolCallId
10066
10074
  };
10075
+ try {
10076
+ const inp = part.input;
10077
+ if (part.toolName === "read_document") {
10078
+ this.recordOpenDocument(inp.path);
10079
+ } else if (part.toolName === "compare_documents") {
10080
+ this.recordOpenDocument(inp.pathA);
10081
+ this.recordOpenDocument(inp.pathB);
10082
+ } else if (part.toolName === "write_new_document") {
10083
+ this.recordOpenDocument(inp.path);
10084
+ }
10085
+ } catch {
10086
+ }
10067
10087
  break;
10068
10088
  }
10069
10089
  case "tool-result": {
@@ -10806,26 +10826,29 @@ import { realpath } from "fs/promises";
10806
10826
  import { basename, dirname as dirname2, isAbsolute, join as join3, normalize, relative, resolve } from "path";
10807
10827
  import { copyFile, mkdir as mkdir3, readdir as readdir2, readFile as readFile3, rename, rm, stat as stat2, writeFile as writeFile2 } from "fs/promises";
10808
10828
  import { basename as basename2, dirname as dirname22, extname, join as join22 } from "path";
10829
+ import { createTwoFilesPatch } from "diff";
10830
+ import { readdir as readdir22, readFile as readFile22, stat as stat22 } from "fs/promises";
10831
+ import { basename as basename3, extname as extname2, join as join32 } from "path";
10809
10832
  var import_jszip = __toESM(require_lib3(), 1);
10810
10833
  var import_jszip2 = __toESM(require_lib3(), 1);
10811
10834
  var import_jszip3 = __toESM(require_lib3(), 1);
10812
- import { createTwoFilesPatch } from "diff";
10813
- import { readFile as readFile22 } from "fs/promises";
10814
- import { blocksToMarkdown, compare } from "@clazic/kordoc";
10815
10835
  import { z as z3 } from "zod";
10816
10836
  import { readFile as readFile32 } from "fs/promises";
10817
- import { extname as extname2 } from "path";
10837
+ import { blocksToMarkdown, compare } from "@clazic/kordoc";
10818
10838
  import { z as z22 } from "zod";
10819
- import { readdir as readdir22, stat as stat22 } from "fs/promises";
10820
- import { extname as extname3, join as join32, relative as relative2 } from "path";
10821
- import { z as z32 } from "zod";
10822
10839
  import { readFile as readFile4 } from "fs/promises";
10823
- import { extname as extname4 } from "path";
10840
+ import { extname as extname3 } from "path";
10841
+ import { z as z32 } from "zod";
10842
+ import { readdir as readdir3, stat as stat3 } from "fs/promises";
10843
+ import { extname as extname4, join as join4, relative as relative2 } from "path";
10824
10844
  import { z as z4 } from "zod";
10825
10845
  import { readFile as readFile5 } from "fs/promises";
10826
10846
  import { extname as extname5 } from "path";
10827
- import { compare as compare2, markdownToHwpx, parse } from "@clazic/kordoc";
10828
10847
  import { z as z5 } from "zod";
10848
+ import { readFile as readFile6 } from "fs/promises";
10849
+ import { extname as extname6 } from "path";
10850
+ import { compare as compare2, markdownToHwpx, parse } from "@clazic/kordoc";
10851
+ import { z as z6 } from "zod";
10829
10852
  import {
10830
10853
  Document,
10831
10854
  HeadingLevel,
@@ -10837,37 +10860,37 @@ import {
10837
10860
  TextRun,
10838
10861
  WidthType
10839
10862
  } from "docx";
10840
- import { readFile as readFile6 } from "fs/promises";
10841
- import { extname as extname6 } from "path";
10842
- import { parse as parse2 } from "@clazic/kordoc";
10843
- import { z as z6 } from "zod";
10844
10863
  import { readFile as readFile7 } from "fs/promises";
10845
10864
  import { extname as extname7 } from "path";
10846
- import { extractFormFields, markdownToHwpx as markdownToHwpx2, parse as parse3 } from "@clazic/kordoc";
10847
- var import_jszip4 = __toESM(require_lib3(), 1);
10865
+ import { parse as parse2 } from "@clazic/kordoc";
10848
10866
  import { z as z7 } from "zod";
10849
10867
  import { readFile as readFile8 } from "fs/promises";
10850
10868
  import { extname as extname8 } from "path";
10851
- import ExcelJS from "exceljs";
10869
+ import { extractFormFields, markdownToHwpx as markdownToHwpx2, parse as parse3 } from "@clazic/kordoc";
10870
+ var import_jszip4 = __toESM(require_lib3(), 1);
10852
10871
  import { z as z8 } from "zod";
10853
10872
  import { readFile as readFile9 } from "fs/promises";
10854
10873
  import { extname as extname9 } from "path";
10855
- import { parse as parse4 } from "@clazic/kordoc";
10874
+ import ExcelJS from "exceljs";
10856
10875
  import { z as z9 } from "zod";
10857
- import { readFile as readFile10, stat as stat3 } from "fs/promises";
10876
+ import { readFile as readFile10 } from "fs/promises";
10858
10877
  import { extname as extname10 } from "path";
10859
- import { parse as parse5 } from "@clazic/kordoc";
10878
+ import { parse as parse4 } from "@clazic/kordoc";
10860
10879
  import { z as z10 } from "zod";
10861
- import { readFile as fsReadFile, stat as stat4 } from "fs/promises";
10862
- import { z as z11 } from "zod";
10863
- import { stat as stat5 } from "fs/promises";
10880
+ import { readFile as readFile11, stat as stat4 } from "fs/promises";
10864
10881
  import { extname as extname11 } from "path";
10865
- import { markdownToHwpx as markdownToHwpx3 } from "@clazic/kordoc";
10882
+ import { parse as parse5 } from "@clazic/kordoc";
10883
+ import { z as z11 } from "zod";
10884
+ import { readFile as fsReadFile, stat as stat5 } from "fs/promises";
10866
10885
  import { z as z12 } from "zod";
10867
10886
  import { stat as stat6 } from "fs/promises";
10868
10887
  import { extname as extname12 } from "path";
10869
- import ExcelJS2 from "exceljs";
10888
+ import { markdownToHwpx as markdownToHwpx3 } from "@clazic/kordoc";
10870
10889
  import { z as z13 } from "zod";
10890
+ import { stat as stat7 } from "fs/promises";
10891
+ import { extname as extname13 } from "path";
10892
+ import ExcelJS2 from "exceljs";
10893
+ import { z as z14 } from "zod";
10871
10894
  async function resolveSafePath(cwd, p) {
10872
10895
  const normalizedCwd = normalize(cwd).normalize("NFC");
10873
10896
  const normalizedP = p.normalize("NFC");
@@ -10999,16 +11022,227 @@ async function cleanOldBackups(maxAgeDays = 30, baseDir) {
10999
11022
  function resolveOutputPath(targetPath) {
11000
11023
  const ext = extname(targetPath).toLowerCase();
11001
11024
  if (ext === ".hwp") {
11002
- const outputPath = targetPath.slice(0, -4) + ".hwpx";
11003
- return { outputPath, willConvertFormat: ".hwp \u2192 .hwpx" };
11025
+ return { outputPath: targetPath.slice(0, -4) + ".hwpx", willConvertFormat: ".hwp \u2192 .hwpx" };
11026
+ }
11027
+ if (ext === ".xls") {
11028
+ return { outputPath: targetPath.slice(0, -4) + ".xlsx", willConvertFormat: ".xls \u2192 .xlsx" };
11004
11029
  }
11005
11030
  return { outputPath: targetPath, willConvertFormat: void 0 };
11006
11031
  }
11032
+ function parseBackupFilename(filename) {
11033
+ const m = filename.match(/^(\d{4}-\d{2}-\d{2}T\d{2}-\d{2}-\d{2}-\d{3}Z)-(.+)$/);
11034
+ if (!m) return null;
11035
+ return { tsToken: m[1], origBasename: m[2] };
11036
+ }
11037
+ function formatTimestamp(tsToken) {
11038
+ const restored = tsToken.replace(
11039
+ /^(\d{4}-\d{2}-\d{2})T(\d{2})-(\d{2})-(\d{2})-(\d{3})Z$/,
11040
+ "$1T$2:$3:$4.$5Z"
11041
+ );
11042
+ try {
11043
+ const d = new Date(restored);
11044
+ if (Number.isNaN(d.getTime())) return tsToken;
11045
+ return d.toISOString().replace("T", " ").slice(0, 19);
11046
+ } catch {
11047
+ return tsToken;
11048
+ }
11049
+ }
11050
+ var MAX_BACKUP_LIST = 50;
11051
+ var listBackupsSchema = z3.object({
11052
+ path: z3.string().optional().describe("\uD2B9\uC815 \uD30C\uC77C\uC758 \uBC31\uC5C5\uB9CC \uBCF4\uB824\uBA74 \uADF8 \uD30C\uC77C \uACBD\uB85C (\uBBF8\uC9C0\uC815 \uC2DC \uC804\uCCB4 \uBC31\uC5C5)")
11053
+ });
11054
+ var listBackupsTool = {
11055
+ name: "list_backups",
11056
+ description: "\uBC31\uC5C5 \uB514\uB809\uD130\uB9AC\uC758 \uBC31\uC5C5 \uBAA9\uB85D\uC744 \uBC18\uD658\uD569\uB2C8\uB2E4. path\uB97C \uC9C0\uC815\uD558\uBA74 \uD574\uB2F9 \uD30C\uC77C\uC758 \uBC31\uC5C5\uB9CC, \uBBF8\uC9C0\uC815 \uC2DC \uC804\uCCB4 \uBC31\uC5C5\uC744 \uD45C\uC2DC\uD569\uB2C8\uB2E4. \uCD5C\uB300 50\uAC74, \uCD5C\uC2E0\uC21C \uC815\uB82C.",
11057
+ inputSchema: listBackupsSchema,
11058
+ requiresApproval: false,
11059
+ execute: async ({
11060
+ input
11061
+ }) => {
11062
+ const backupsDir = KODOC_PATHS.backups;
11063
+ let allEntries;
11064
+ try {
11065
+ allEntries = await readdir22(backupsDir);
11066
+ } catch {
11067
+ return "\uBC31\uC5C5\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.";
11068
+ }
11069
+ if (allEntries.length === 0) {
11070
+ return "\uBC31\uC5C5\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.";
11071
+ }
11072
+ const parsed = [];
11073
+ for (const filename of allEntries) {
11074
+ const info = parseBackupFilename(filename);
11075
+ if (!info) continue;
11076
+ const fullPath = join32(backupsDir, filename);
11077
+ let mtimeMs = 0;
11078
+ try {
11079
+ const s = await stat22(fullPath);
11080
+ mtimeMs = s.mtimeMs;
11081
+ } catch {
11082
+ mtimeMs = 0;
11083
+ }
11084
+ parsed.push({ filename, fullPath, ...info, mtimeMs });
11085
+ }
11086
+ let filtered = parsed;
11087
+ if (input.path) {
11088
+ const targetBase = basename3(input.path);
11089
+ filtered = parsed.filter((e) => e.origBasename === targetBase);
11090
+ }
11091
+ if (filtered.length === 0) {
11092
+ if (input.path) {
11093
+ return `\uD574\uB2F9 \uD30C\uC77C\uC758 \uBC31\uC5C5\uC774 \uC5C6\uC2B5\uB2C8\uB2E4: ${basename3(input.path)}`;
11094
+ }
11095
+ return "\uBC31\uC5C5\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.";
11096
+ }
11097
+ filtered.sort((a, b) => {
11098
+ if (b.mtimeMs !== a.mtimeMs) return b.mtimeMs - a.mtimeMs;
11099
+ return b.tsToken.localeCompare(a.tsToken);
11100
+ });
11101
+ const total = filtered.length;
11102
+ const truncated = filtered.length > MAX_BACKUP_LIST;
11103
+ const display = filtered.slice(0, MAX_BACKUP_LIST);
11104
+ const lines = display.map(
11105
+ (e, i) => `${i + 1}. ${e.origBasename} [${formatTimestamp(e.tsToken)}] ${e.fullPath}`
11106
+ );
11107
+ const notice = truncated ? `
11108
+ (\uCD1D ${total}\uAC74 \uC911 \uCD5C\uC2E0 ${MAX_BACKUP_LIST}\uAC74\uB9CC \uD45C\uC2DC\uB429\uB2C8\uB2E4.)` : "";
11109
+ return lines.join("\n") + notice;
11110
+ }
11111
+ };
11112
+ var restoreBackupSchema = z3.object({
11113
+ path: z3.string().describe("\uBCF5\uC6D0\uD560 \uB300\uC0C1 \uD30C\uC77C \uACBD\uB85C"),
11114
+ backup: z3.string().optional().describe("\uBCF5\uC6D0\uD560 \uD2B9\uC815 \uBC31\uC5C5 \uD30C\uC77C\uBA85 (list_backups \uACB0\uACFC\uC758 \uD30C\uC77C\uBA85; \uBBF8\uC9C0\uC815 \uC2DC \uAC00\uC7A5 \uCD5C\uADFC \uBC31\uC5C5)"),
11115
+ summary: z3.string().optional().describe("\uBCF5\uC6D0 \uC0AC\uC720/\uC694\uC57D")
11116
+ });
11117
+ var restoreBackupTool = {
11118
+ name: "restore_backup",
11119
+ description: "\uBC31\uC5C5 \uD30C\uC77C\uB85C \uB300\uC0C1 \uD30C\uC77C\uC744 \uBCF5\uC6D0\uD569\uB2C8\uB2E4. backup\uC744 \uBBF8\uC9C0\uC815 \uC2DC \uAC00\uC7A5 \uCD5C\uADFC \uBC31\uC5C5\uC744 \uC0AC\uC6A9\uD569\uB2C8\uB2E4. \uBCF5\uC6D0 \uC804 \uD604\uC7AC \uD30C\uC77C\uB3C4 \uC790\uB3D9 \uBC31\uC5C5\uB429\uB2C8\uB2E4(\uBCF5\uC6D0\uB3C4 \uB418\uB3CC\uB9B4 \uC218 \uC788\uC74C). \uC0AC\uC6A9\uC790 \uC2B9\uC778\uC774 \uD544\uC694\uD569\uB2C8\uB2E4.",
11120
+ inputSchema: restoreBackupSchema,
11121
+ requiresApproval: true,
11122
+ propose: async ({
11123
+ input,
11124
+ ctx
11125
+ }) => {
11126
+ let safePath;
11127
+ try {
11128
+ safePath = await resolveSafePath(ctx.cwd, input.path);
11129
+ } catch (e) {
11130
+ return `\uACBD\uB85C \uC624\uB958: ${e instanceof Error ? e.message : String(e)}`;
11131
+ }
11132
+ const targetBase = basename3(safePath);
11133
+ const backupsDir = KODOC_PATHS.backups;
11134
+ let allEntries;
11135
+ try {
11136
+ allEntries = await readdir22(backupsDir);
11137
+ } catch {
11138
+ return `\uBC31\uC5C5\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${targetBase}. list_backups\uB85C \uC0AC\uC6A9 \uAC00\uB2A5\uD55C \uBC31\uC5C5\uC744 \uBA3C\uC800 \uD655\uC778\uD558\uC138\uC694.`;
11139
+ }
11140
+ const candidates = [];
11141
+ for (const filename of allEntries) {
11142
+ const info = parseBackupFilename(filename);
11143
+ if (!info) continue;
11144
+ if (info.origBasename !== targetBase) continue;
11145
+ const fullPath = join32(backupsDir, filename);
11146
+ let mtimeMs = 0;
11147
+ try {
11148
+ const s = await stat22(fullPath);
11149
+ mtimeMs = s.mtimeMs;
11150
+ } catch {
11151
+ mtimeMs = 0;
11152
+ }
11153
+ candidates.push({ filename, fullPath, tsToken: info.tsToken, mtimeMs });
11154
+ }
11155
+ if (candidates.length === 0) {
11156
+ return `\uBC31\uC5C5\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${targetBase}. list_backups\uB85C \uC0AC\uC6A9 \uAC00\uB2A5\uD55C \uBC31\uC5C5\uC744 \uBA3C\uC800 \uD655\uC778\uD558\uC138\uC694.`;
11157
+ }
11158
+ let chosen;
11159
+ if (input.backup) {
11160
+ const found = candidates.find(
11161
+ (c) => c.filename === input.backup || c.fullPath.endsWith(input.backup)
11162
+ );
11163
+ if (!found) {
11164
+ return `\uC9C0\uC815\uD55C \uBC31\uC5C5\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${input.backup}. list_backups\uB85C \uC0AC\uC6A9 \uAC00\uB2A5\uD55C \uBC31\uC5C5\uC744 \uBA3C\uC800 \uD655\uC778\uD558\uC138\uC694.`;
11165
+ }
11166
+ chosen = found;
11167
+ } else {
11168
+ candidates.sort(
11169
+ (a, b) => b.mtimeMs !== a.mtimeMs ? b.mtimeMs - a.mtimeMs : b.tsToken.localeCompare(a.tsToken)
11170
+ );
11171
+ chosen = candidates[0];
11172
+ }
11173
+ let backupBytes;
11174
+ try {
11175
+ backupBytes = await readFile22(chosen.fullPath);
11176
+ } catch {
11177
+ return `\uBC31\uC5C5 \uD30C\uC77C\uC744 \uC77D\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${chosen.fullPath}`;
11178
+ }
11179
+ const stagedPath = await stageFile(ctx.sessionId, targetBase, backupBytes);
11180
+ const ext = extname2(targetBase).toLowerCase();
11181
+ const isText = ext === ".md" || ext === ".txt";
11182
+ let diff;
11183
+ if (isText) {
11184
+ let currentText = "";
11185
+ try {
11186
+ currentText = await readFile22(safePath, "utf-8");
11187
+ } catch {
11188
+ currentText = "";
11189
+ }
11190
+ const backupText = backupBytes.toString("utf-8");
11191
+ diff = markdownDiff(currentText, backupText, targetBase);
11192
+ } else {
11193
+ let currentSize = "\uD30C\uC77C \uC5C6\uC74C";
11194
+ try {
11195
+ const s = await stat22(safePath);
11196
+ currentSize = `${s.size} bytes`;
11197
+ } catch {
11198
+ currentSize = "\uD30C\uC77C \uC5C6\uC74C";
11199
+ }
11200
+ const backupSize = `${backupBytes.length} bytes`;
11201
+ diff = [
11202
+ `\uBCF5\uC6D0 \uB300\uC0C1: ${safePath}`,
11203
+ `\uD604\uC7AC \uD30C\uC77C: ${currentSize}`,
11204
+ `\uBC31\uC5C5 \uD30C\uC77C: ${chosen.fullPath}`,
11205
+ `\uBC31\uC5C5 \uC2DC\uAC01: ${formatTimestamp(chosen.tsToken)}`,
11206
+ `\uBC31\uC5C5 \uD06C\uAE30: ${backupSize}`,
11207
+ `\u2192 \uD604\uC7AC \uD30C\uC77C\uC744 \uC704 \uBC31\uC5C5\uC73C\uB85C \uB418\uB3CC\uB9BD\uB2C8\uB2E4.`
11208
+ ].join("\n");
11209
+ }
11210
+ const warnings = [];
11211
+ const autoSelected = !input.backup && candidates.length > 1;
11212
+ if (autoSelected) {
11213
+ warnings.push(
11214
+ `\uBC31\uC5C5\uC744 \uC9C0\uC815\uD558\uC9C0 \uC54A\uC544 \uAC00\uC7A5 \uCD5C\uADFC \uBC31\uC5C5(${formatTimestamp(chosen.tsToken)})\uC744 \uC790\uB3D9\uC73C\uB85C \uC120\uD0DD\uD588\uC2B5\uB2C8\uB2E4. \uB2E4\uB978 \uBC31\uC5C5\uC744 \uC6D0\uD558\uBA74 list_backups\uB85C \uD655\uC778 \uD6C4 backup \uD30C\uB77C\uBBF8\uD130\uB97C \uC9C0\uC815\uD558\uC138\uC694.`
11215
+ );
11216
+ }
11217
+ warnings.push(
11218
+ "\uBCF5\uC6D0\uC744 \uC2E4\uD589\uD558\uBA74 \uD604\uC7AC \uD30C\uC77C\uB3C4 \uBC31\uC5C5\uB41C \uB4A4 \uB36E\uC5B4\uC4F0\uC5EC\uC9D1\uB2C8\uB2E4(\uBCF5\uC6D0 \uC790\uCCB4\uB3C4 \uB418\uB3CC\uB9B4 \uC218 \uC788\uC74C)."
11219
+ );
11220
+ const summary = input.summary ?? `\uBC31\uC5C5\uC73C\uB85C \uB418\uB3CC\uB9AC\uAE30: ${targetBase}`;
11221
+ const chosenBackupPath = chosen.fullPath;
11222
+ return {
11223
+ proposal: {
11224
+ id: crypto.randomUUID(),
11225
+ kind: "restore",
11226
+ targetPath: safePath,
11227
+ stagedPath,
11228
+ summary,
11229
+ diff,
11230
+ warnings
11231
+ },
11232
+ commit: async () => {
11233
+ const safetyBackup = await backupFile(safePath);
11234
+ await commitStaged(stagedPath, safePath);
11235
+ const safetyNote = safetyBackup ? ` (\uBCF5\uC6D0 \uC804 \uD604\uC7AC \uC0C1\uD0DC \uBC31\uC5C5: ${safetyBackup})` : "";
11236
+ return `\uBCF5\uC6D0 \uC644\uB8CC: ${safePath} \u2190 ${chosenBackupPath}${safetyNote}`;
11237
+ }
11238
+ };
11239
+ }
11240
+ };
11007
11241
  var MAX_MARKDOWN_LENGTH = 8e4;
11008
11242
  var MAX_BLOCK_TEXT_LENGTH = 200;
11009
- var compareDocumentsSchema = z3.object({
11010
- pathA: z3.string().describe("\uBE44\uAD50\uD560 \uCCAB \uBC88\uC9F8 \uBB38\uC11C \uACBD\uB85C (cwd \uAE30\uC900 \uC0C1\uB300 \uACBD\uB85C \uB610\uB294 \uC808\uB300 \uACBD\uB85C)"),
11011
- pathB: z3.string().describe("\uBE44\uAD50\uD560 \uB450 \uBC88\uC9F8 \uBB38\uC11C \uACBD\uB85C (cwd \uAE30\uC900 \uC0C1\uB300 \uACBD\uB85C \uB610\uB294 \uC808\uB300 \uACBD\uB85C)")
11243
+ var compareDocumentsSchema = z22.object({
11244
+ pathA: z22.string().describe("\uBE44\uAD50\uD560 \uCCAB \uBC88\uC9F8 \uBB38\uC11C \uACBD\uB85C (cwd \uAE30\uC900 \uC0C1\uB300 \uACBD\uB85C \uB610\uB294 \uC808\uB300 \uACBD\uB85C)"),
11245
+ pathB: z22.string().describe("\uBE44\uAD50\uD560 \uB450 \uBC88\uC9F8 \uBB38\uC11C \uACBD\uB85C (cwd \uAE30\uC900 \uC0C1\uB300 \uACBD\uB85C \uB610\uB294 \uC808\uB300 \uACBD\uB85C)")
11012
11246
  });
11013
11247
  function bufferToArrayBuffer(buf) {
11014
11248
  return buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength);
@@ -11046,13 +11280,13 @@ var compareDocumentsTool = {
11046
11280
  let bufA;
11047
11281
  let bufB;
11048
11282
  try {
11049
- bufA = await readFile22(safePathA);
11283
+ bufA = await readFile32(safePathA);
11050
11284
  } catch (e) {
11051
11285
  const msg = e instanceof Error ? e.message : String(e);
11052
11286
  return `\uC624\uB958: \uCCAB \uBC88\uC9F8 \uD30C\uC77C\uC744 \uC77D\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4 (${input.pathA}): ${msg}`;
11053
11287
  }
11054
11288
  try {
11055
- bufB = await readFile22(safePathB);
11289
+ bufB = await readFile32(safePathB);
11056
11290
  } catch (e) {
11057
11291
  const msg = e instanceof Error ? e.message : String(e);
11058
11292
  return `\uC624\uB958: \uB450 \uBC88\uC9F8 \uD30C\uC77C\uC744 \uC77D\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4 (${input.pathB}): ${msg}`;
@@ -11500,14 +11734,14 @@ function validateHwpxBuffer(ext, buffer) {
11500
11734
  }
11501
11735
  return null;
11502
11736
  }
11503
- var listFormObjectsSchema = z22.object({
11504
- path: z22.string().describe("\uC77D\uC744 .hwpx \uD30C\uC77C \uACBD\uB85C")
11737
+ var listFormObjectsSchema = z32.object({
11738
+ path: z32.string().describe("\uC77D\uC744 .hwpx \uD30C\uC77C \uACBD\uB85C")
11505
11739
  });
11506
- var formEditSetSchema = z22.object({
11507
- caption: z22.string().optional().describe("PushButton \uCEA1\uC158 \uD14D\uC2A4\uD2B8"),
11508
- checked: z22.boolean().optional().describe("CheckBox/RadioButton \uCCB4\uD06C \uC0C1\uD0DC (true=CHECKED)"),
11509
- selected: z22.string().optional().describe("ComboBox \uC120\uD0DD \uAC12 (listItem \uC911 \uD558\uB098\uC5EC\uC57C \uD568)"),
11510
- text: z22.string().optional().describe("Edit \uD14D\uC2A4\uD2B8 \uB0B4\uC6A9")
11740
+ var formEditSetSchema = z32.object({
11741
+ caption: z32.string().optional().describe("PushButton \uCEA1\uC158 \uD14D\uC2A4\uD2B8"),
11742
+ checked: z32.boolean().optional().describe("CheckBox/RadioButton \uCCB4\uD06C \uC0C1\uD0DC (true=CHECKED)"),
11743
+ selected: z32.string().optional().describe("ComboBox \uC120\uD0DD \uAC12 (listItem \uC911 \uD558\uB098\uC5EC\uC57C \uD568)"),
11744
+ text: z32.string().optional().describe("Edit \uD14D\uC2A4\uD2B8 \uB0B4\uC6A9")
11511
11745
  }).refine(
11512
11746
  (v) => {
11513
11747
  const keys = ["caption", "checked", "selected", "text"].filter(
@@ -11517,24 +11751,24 @@ var formEditSetSchema = z22.object({
11517
11751
  },
11518
11752
  { message: "set \uD544\uB4DC\uB294 caption/checked/selected/text \uC911 \uC815\uD655\uD788 \uD558\uB098\uB9CC \uC9C0\uC815\uD574\uC57C \uD569\uB2C8\uB2E4." }
11519
11753
  );
11520
- var formEditExpectedSchema = z22.object({
11521
- caption: z22.string().optional(),
11522
- checked: z22.boolean().optional(),
11523
- selected: z22.string().optional(),
11524
- text: z22.string().optional()
11754
+ var formEditExpectedSchema = z32.object({
11755
+ caption: z32.string().optional(),
11756
+ checked: z32.boolean().optional(),
11757
+ selected: z32.string().optional(),
11758
+ text: z32.string().optional()
11525
11759
  }).optional();
11526
- var formEditItemSchema = z22.object({
11527
- name: z22.string().describe("\uC591\uC2DD \uAC1C\uCCB4\uC758 name \uC18D\uC131 \uAC12"),
11528
- index: z22.number().int().nonnegative().optional().describe("\uB3D9\uC77C name\uC774 \uC5EC\uB7FF\uC778 \uACBD\uC6B0 \uBB38\uC11C \uC804\uCCB4 0-based \uC778\uB371\uC2A4\uB85C \uAD6C\uBD84"),
11760
+ var formEditItemSchema = z32.object({
11761
+ name: z32.string().describe("\uC591\uC2DD \uAC1C\uCCB4\uC758 name \uC18D\uC131 \uAC12"),
11762
+ index: z32.number().int().nonnegative().optional().describe("\uB3D9\uC77C name\uC774 \uC5EC\uB7FF\uC778 \uACBD\uC6B0 \uBB38\uC11C \uC804\uCCB4 0-based \uC778\uB371\uC2A4\uB85C \uAD6C\uBD84"),
11529
11763
  set: formEditSetSchema.describe("\uBCC0\uACBD\uD560 \uAC12 (caption/checked/selected/text \uC911 \uD558\uB098)"),
11530
11764
  expected: formEditExpectedSchema.describe(
11531
11765
  "\uD604\uC7AC \uAC12 \uC0AC\uC804 \uAC80\uC99D (\uC548\uC804 \uC635\uC158). \uC2E4\uC81C \uAC12\uC774 \uB2E4\uB974\uBA74 \uC774 \uD3B8\uC9D1\uC744 \uCDE8\uC18C\uD558\uACE0 \uC624\uB958 \uBC18\uD658."
11532
11766
  )
11533
11767
  });
11534
- var proposeFormObjectSchema = z22.object({
11535
- path: z22.string().describe("\uC218\uC815\uD560 .hwpx \uD30C\uC77C \uACBD\uB85C"),
11536
- edits: z22.array(formEditItemSchema).min(1).describe("\uC591\uC2DD \uAC1C\uCCB4 \uD3B8\uC9D1 \uBAA9\uB85D"),
11537
- summary: z22.string().describe("\uBCC0\uACBD \uC694\uC57D (\uD55C\uAD6D\uC5B4 1-2\uBB38\uC7A5)")
11768
+ var proposeFormObjectSchema = z32.object({
11769
+ path: z32.string().describe("\uC218\uC815\uD560 .hwpx \uD30C\uC77C \uACBD\uB85C"),
11770
+ edits: z32.array(formEditItemSchema).min(1).describe("\uC591\uC2DD \uAC1C\uCCB4 \uD3B8\uC9D1 \uBAA9\uB85D"),
11771
+ summary: z32.string().describe("\uBCC0\uACBD \uC694\uC57D (\uD55C\uAD6D\uC5B4 1-2\uBB38\uC7A5)")
11538
11772
  });
11539
11773
  var listFormObjectsTool = {
11540
11774
  name: "list_form_objects",
@@ -11546,10 +11780,10 @@ var listFormObjectsTool = {
11546
11780
  ctx
11547
11781
  }) => {
11548
11782
  const safePath = await resolveSafePath(ctx.cwd, input.path);
11549
- const ext = extname2(safePath).toLowerCase();
11783
+ const ext = extname3(safePath).toLowerCase();
11550
11784
  let buffer;
11551
11785
  try {
11552
- buffer = await readFile32(safePath);
11786
+ buffer = await readFile4(safePath);
11553
11787
  } catch {
11554
11788
  return `\uC624\uB958: \uD30C\uC77C\uC744 \uC77D\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${input.path}`;
11555
11789
  }
@@ -11601,10 +11835,10 @@ var proposeFormObjectTool = {
11601
11835
  ctx
11602
11836
  }) => {
11603
11837
  const safePath = await resolveSafePath(ctx.cwd, input.path);
11604
- const ext = extname2(safePath).toLowerCase();
11838
+ const ext = extname3(safePath).toLowerCase();
11605
11839
  let originalBuffer;
11606
11840
  try {
11607
- originalBuffer = await readFile32(safePath);
11841
+ originalBuffer = await readFile4(safePath);
11608
11842
  } catch {
11609
11843
  return `\uC624\uB958: \uD30C\uC77C\uC744 \uC77D\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${input.path}`;
11610
11844
  }
@@ -11791,14 +12025,14 @@ var SKIP_DIRS = /* @__PURE__ */ new Set([
11791
12025
  ]);
11792
12026
  var MAX_DEPTH = 4;
11793
12027
  var MAX_FILES = 500;
11794
- var listFilesSchema = z32.object({
11795
- dir: z32.string().optional().describe("\uBAA9\uB85D\uC744 \uC870\uD68C\uD560 \uB514\uB809\uD130\uB9AC (\uBBF8\uC9C0\uC815 \uC2DC cwd \uC804\uCCB4)")
12028
+ var listFilesSchema = z4.object({
12029
+ dir: z4.string().optional().describe("\uBAA9\uB85D\uC744 \uC870\uD68C\uD560 \uB514\uB809\uD130\uB9AC (\uBBF8\uC9C0\uC815 \uC2DC cwd \uC804\uCCB4)")
11796
12030
  });
11797
12031
  async function collectFiles(dir, cwd, depth, entries) {
11798
12032
  if (depth > MAX_DEPTH || entries.length >= MAX_FILES) return;
11799
12033
  let items;
11800
12034
  try {
11801
- items = await readdir22(dir);
12035
+ items = await readdir3(dir);
11802
12036
  } catch {
11803
12037
  return;
11804
12038
  }
@@ -11806,15 +12040,15 @@ async function collectFiles(dir, cwd, depth, entries) {
11806
12040
  if (entries.length >= MAX_FILES) break;
11807
12041
  if (item.startsWith(".")) continue;
11808
12042
  if (SKIP_DIRS.has(item)) continue;
11809
- const fullPath = join32(dir, item);
12043
+ const fullPath = join4(dir, item);
11810
12044
  let info;
11811
12045
  try {
11812
- info = await stat22(fullPath);
12046
+ info = await stat3(fullPath);
11813
12047
  } catch {
11814
12048
  continue;
11815
12049
  }
11816
12050
  const relPath = relative2(cwd, fullPath);
11817
- const ext = extname3(item).toLowerCase();
12051
+ const ext = extname4(item).toLowerCase();
11818
12052
  const isDoc = DOC_EXTENSIONS.has(ext);
11819
12053
  if (info.isDirectory()) {
11820
12054
  entries.push({ path: relPath + "/", isDir: true, isDoc: false });
@@ -11852,29 +12086,29 @@ var listFilesTool = {
11852
12086
  return lines.join("\n") + truncateNotice;
11853
12087
  }
11854
12088
  };
11855
- var cellEditItemSchema = z4.object({
11856
- tableIndex: z4.number().int().nonnegative().optional().describe(
12089
+ var cellEditItemSchema = z5.object({
12090
+ tableIndex: z5.number().int().nonnegative().optional().describe(
11857
12091
  "\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)."
11858
12092
  ),
11859
- row: z4.number().int().nonnegative().optional().describe("\uC88C\uD45C \uBAA8\uB4DC: \uC140\uC758 rowAddr (0-based)"),
11860
- col: z4.number().int().nonnegative().optional().describe("\uC88C\uD45C \uBAA8\uB4DC: \uC140\uC758 colAddr (0-based)"),
11861
- label: z4.string().optional().describe(
12093
+ row: z5.number().int().nonnegative().optional().describe("\uC88C\uD45C \uBAA8\uB4DC: \uC140\uC758 rowAddr (0-based)"),
12094
+ col: z5.number().int().nonnegative().optional().describe("\uC88C\uD45C \uBAA8\uB4DC: \uC140\uC758 colAddr (0-based)"),
12095
+ label: z5.string().optional().describe(
11862
12096
  "\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."
11863
12097
  ),
11864
- 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."),
11865
- newText: z4.string().describe("\uC140\uC5D0 \uC4F8 \uC0C8 \uD14D\uC2A4\uD2B8"),
11866
- expectedText: z4.string().optional().describe(
12098
+ direction: z5.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."),
12099
+ newText: z5.string().describe("\uC140\uC5D0 \uC4F8 \uC0C8 \uD14D\uC2A4\uD2B8"),
12100
+ expectedText: z5.string().optional().describe(
11867
12101
  "\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."
11868
12102
  )
11869
12103
  }).describe(
11870
12104
  "\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."
11871
12105
  );
11872
- var proposeCellEditSchema = z4.object({
11873
- 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)"),
11874
- edits: z4.array(cellEditItemSchema).min(1).describe(
12106
+ var proposeCellEditSchema = z5.object({
12107
+ path: z5.string().describe("\uC218\uC815\uD560 .hwpx \uD30C\uC77C \uACBD\uB85C (cwd \uAE30\uC900 \uC0C1\uB300 \uACBD\uB85C \uB610\uB294 \uC808\uB300 \uACBD\uB85C)"),
12108
+ edits: z5.array(cellEditItemSchema).min(1).describe(
11875
12109
  "\uD3B8\uC9D1 \uBAA9\uB85D. \uAC01 \uD56D\uBAA9\uC740 \uC88C\uD45C \uBAA8\uB4DC(tableIndex+row+col) \uB610\uB294 \uB808\uC774\uBE14 \uBAA8\uB4DC(label+direction) \uC911 \uD558\uB098"
11876
12110
  ),
11877
- summary: z4.string().describe("\uBCC0\uACBD \uC694\uC57D (\uD55C\uAD6D\uC5B4 1-2\uBB38\uC7A5)")
12111
+ summary: z5.string().describe("\uBCC0\uACBD \uC694\uC57D (\uD55C\uAD6D\uC5B4 1-2\uBB38\uC7A5)")
11878
12112
  });
11879
12113
  function tokenizeHwpxXml(xml) {
11880
12114
  const tokens = [];
@@ -12209,7 +12443,7 @@ var proposeCellEditTool = {
12209
12443
  ctx
12210
12444
  }) => {
12211
12445
  const safePath = await resolveSafePath(ctx.cwd, input.path);
12212
- const ext = extname4(safePath).toLowerCase();
12446
+ const ext = extname5(safePath).toLowerCase();
12213
12447
  if (ext === ".hwp") {
12214
12448
  return "\uC624\uB958: propose_cell_edit\uC740 .hwpx \uD30C\uC77C\uB9CC \uC9C0\uC6D0\uD569\uB2C8\uB2E4. .hwp(\uAD6C\uD615 OLE \uBC14\uC774\uB108\uB9AC)\uB294 \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. \uB610\uB294 propose_edit\uC744 \uC0AC\uC6A9\uD560 \uC218 \uC788\uC73C\uB098, \uBCD1\uD569 \uC140\uC774 \uC18C\uC2E4\uB420 \uC218 \uC788\uC2B5\uB2C8\uB2E4.";
12215
12449
  }
@@ -12218,7 +12452,7 @@ var proposeCellEditTool = {
12218
12452
  }
12219
12453
  let originalBuffer;
12220
12454
  try {
12221
- originalBuffer = await readFile4(safePath);
12455
+ originalBuffer = await readFile5(safePath);
12222
12456
  } catch {
12223
12457
  return `\uC624\uB958: \uD30C\uC77C\uC744 \uC77D\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${input.path}. \uACBD\uB85C\uB97C \uD655\uC778\uD558\uAC70\uB098 read_document\uB85C \uBA3C\uC800 \uD655\uC778\uD558\uC138\uC694.`;
12224
12458
  }
@@ -12547,10 +12781,10 @@ async function markdownToDocx(markdown) {
12547
12781
  });
12548
12782
  return Packer.toBuffer(doc);
12549
12783
  }
12550
- var proposeEditSchema = z5.object({
12551
- path: z5.string().describe("\uC218\uC815\uD560 \uBB38\uC11C \uACBD\uB85C (cwd \uAE30\uC900 \uC0C1\uB300 \uACBD\uB85C \uB610\uB294 \uC808\uB300 \uACBD\uB85C)"),
12552
- newMarkdown: z5.string().describe("\uC0C8 \uBB38\uC11C \uB0B4\uC6A9 (\uB9C8\uD06C\uB2E4\uC6B4 \uD615\uC2DD). read_document\uB85C \uC6D0\uBCF8\uC744 \uBA3C\uC800 \uC77D\uC5B4\uC57C \uD568"),
12553
- summary: z5.string().describe("\uBCC0\uACBD \uC694\uC57D (\uD55C\uAD6D\uC5B4 1-2\uBB38\uC7A5)")
12784
+ var proposeEditSchema = z6.object({
12785
+ path: z6.string().describe("\uC218\uC815\uD560 \uBB38\uC11C \uACBD\uB85C (cwd \uAE30\uC900 \uC0C1\uB300 \uACBD\uB85C \uB610\uB294 \uC808\uB300 \uACBD\uB85C)"),
12786
+ newMarkdown: z6.string().describe("\uC0C8 \uBB38\uC11C \uB0B4\uC6A9 (\uB9C8\uD06C\uB2E4\uC6B4 \uD615\uC2DD). read_document\uB85C \uC6D0\uBCF8\uC744 \uBA3C\uC800 \uC77D\uC5B4\uC57C \uD568"),
12787
+ summary: z6.string().describe("\uBCC0\uACBD \uC694\uC57D (\uD55C\uAD6D\uC5B4 1-2\uBB38\uC7A5)")
12554
12788
  });
12555
12789
  var proposeEditTool = {
12556
12790
  name: "propose_edit",
@@ -12562,10 +12796,10 @@ var proposeEditTool = {
12562
12796
  ctx
12563
12797
  }) => {
12564
12798
  const safePath = await resolveSafePath(ctx.cwd, input.path);
12565
- const ext = extname5(safePath).toLowerCase();
12799
+ const ext = extname6(safePath).toLowerCase();
12566
12800
  let originalBuffer;
12567
12801
  try {
12568
- originalBuffer = await readFile5(safePath);
12802
+ originalBuffer = await readFile6(safePath);
12569
12803
  } catch {
12570
12804
  return `\uC624\uB958: \uD30C\uC77C\uC744 \uC77D\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${input.path}. \uACBD\uB85C\uB97C \uD655\uC778\uD558\uAC70\uB098 read_document\uB85C \uBA3C\uC800 \uD655\uC778\uD558\uC138\uC694.`;
12571
12805
  }
@@ -12636,17 +12870,48 @@ ${diff}`;
12636
12870
  };
12637
12871
  }
12638
12872
  };
12639
- var proposeFindReplaceSchema = z6.object({
12640
- 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)"),
12641
- find: z6.string().min(1).describe("\uCC3E\uC744 \uD14D\uC2A4\uD2B8"),
12642
- replace: z6.string().describe("\uBC14\uAFC0 \uD14D\uC2A4\uD2B8"),
12643
- caseSensitive: z6.boolean().optional().default(false).describe("\uB300\uC18C\uBB38\uC790 \uAD6C\uBD84 (\uAE30\uBCF8\uAC12: false)"),
12644
- 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"),
12645
- summary: z6.string().describe("\uBCC0\uACBD \uC694\uC57D (\uD55C\uAD6D\uC5B4 1-2\uBB38\uC7A5)")
12873
+ var proposeFindReplaceSchema = z7.object({
12874
+ path: z7.string().describe("\uC218\uC815\uD560 .hwpx \uD30C\uC77C \uACBD\uB85C (cwd \uAE30\uC900 \uC0C1\uB300 \uACBD\uB85C \uB610\uB294 \uC808\uB300 \uACBD\uB85C)"),
12875
+ find: z7.string().min(1).describe("\uCC3E\uC744 \uD14D\uC2A4\uD2B8"),
12876
+ replace: z7.string().describe("\uBC14\uAFC0 \uD14D\uC2A4\uD2B8"),
12877
+ caseSensitive: z7.boolean().optional().default(false).describe("\uB300\uC18C\uBB38\uC790 \uAD6C\uBD84 (\uAE30\uBCF8\uAC12: false)"),
12878
+ all: z7.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"),
12879
+ summary: z7.string().describe("\uBCC0\uACBD \uC694\uC57D (\uD55C\uAD6D\uC5B4 1-2\uBB38\uC7A5)")
12646
12880
  });
12881
+ var MAX_DIFF_SAMPLES = 20;
12647
12882
  function escapeXml3(text3) {
12648
12883
  return text3.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
12649
12884
  }
12885
+ function unescapeXml(text3) {
12886
+ return text3.replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&amp;/g, "&");
12887
+ }
12888
+ function makeChangeSnippet(before, after, ctx = 24) {
12889
+ let p = 0;
12890
+ while (p < before.length && p < after.length && before[p] === after[p]) p++;
12891
+ const maxSuffix = Math.min(before.length - p, after.length - p);
12892
+ let s = 0;
12893
+ while (s < maxSuffix && before[before.length - 1 - s] === after[after.length - 1 - s]) s++;
12894
+ const slice = (str) => {
12895
+ const start = Math.max(0, p - ctx);
12896
+ const end = Math.min(str.length, str.length - s + ctx);
12897
+ return (start > 0 ? "\u2026" : "") + str.slice(start, end) + (end < str.length ? "\u2026" : "");
12898
+ };
12899
+ return { before: slice(before), after: slice(after) };
12900
+ }
12901
+ function collectChangedSnippets(beforeXml, afterXml, maxSamples) {
12902
+ const re = /<hp:t>([\s\S]*?)<\/hp:t>/g;
12903
+ const beforeNodes = [...beforeXml.matchAll(re)].map((m) => m[1] ?? "");
12904
+ const afterNodes = [...afterXml.matchAll(re)].map((m) => m[1] ?? "");
12905
+ const out = [];
12906
+ const n = Math.min(beforeNodes.length, afterNodes.length);
12907
+ for (let i = 0; i < n && out.length < maxSamples; i++) {
12908
+ if (beforeNodes[i] !== afterNodes[i]) {
12909
+ const snip = makeChangeSnippet(unescapeXml(beforeNodes[i]), unescapeXml(afterNodes[i]));
12910
+ out.push(snip);
12911
+ }
12912
+ }
12913
+ return out;
12914
+ }
12650
12915
  function replaceInSectionXml(xml, find, replace, caseSensitive, replaceAll, alreadyReplaced) {
12651
12916
  if (!replaceAll && alreadyReplaced > 0) {
12652
12917
  return { xml, count: 0 };
@@ -12754,7 +13019,19 @@ async function applyFindReplaceToHwpx(hwpxBuffer, find, replace, caseSensitive,
12754
13019
  }
12755
13020
  }
12756
13021
  if (totalCount === 0) {
12757
- return { buffer: hwpxBuffer, count: 0 };
13022
+ return { buffer: hwpxBuffer, count: 0, samples: [] };
13023
+ }
13024
+ const samples = [];
13025
+ for (let si = 0; si < sectionFiles.length && samples.length < MAX_DIFF_SAMPLES; si++) {
13026
+ const remaining = MAX_DIFF_SAMPLES - samples.length;
13027
+ const snippets = collectChangedSnippets(
13028
+ sectionXmls[si] ?? "",
13029
+ newSectionXmls[si] ?? "",
13030
+ remaining
13031
+ );
13032
+ for (const snip of snippets) {
13033
+ samples.push(snip);
13034
+ }
12758
13035
  }
12759
13036
  const out = new import_jszip3.default();
12760
13037
  const mimetypeEntry = zip.file("mimetype");
@@ -12773,7 +13050,8 @@ async function applyFindReplaceToHwpx(hwpxBuffer, find, replace, caseSensitive,
12773
13050
  const buf = await out.generateAsync({ type: "nodebuffer", compression: "DEFLATE" });
12774
13051
  return {
12775
13052
  buffer: new Uint8Array(buf),
12776
- count: totalCount
13053
+ count: totalCount,
13054
+ samples
12777
13055
  };
12778
13056
  }
12779
13057
  var proposeFindReplaceTool = {
@@ -12786,7 +13064,7 @@ var proposeFindReplaceTool = {
12786
13064
  ctx
12787
13065
  }) => {
12788
13066
  const safePath = await resolveSafePath(ctx.cwd, input.path);
12789
- const ext = extname6(safePath).toLowerCase();
13067
+ const ext = extname7(safePath).toLowerCase();
12790
13068
  if (ext === ".hwp") {
12791
13069
  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.";
12792
13070
  }
@@ -12795,7 +13073,7 @@ var proposeFindReplaceTool = {
12795
13073
  }
12796
13074
  let originalBuf;
12797
13075
  try {
12798
- originalBuf = await readFile6(safePath);
13076
+ originalBuf = await readFile7(safePath);
12799
13077
  } catch {
12800
13078
  return `\uC624\uB958: \uD30C\uC77C\uC744 \uC77D\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${input.path}. \uACBD\uB85C\uB97C \uD655\uC778\uD558\uC138\uC694.`;
12801
13079
  }
@@ -12809,6 +13087,7 @@ var proposeFindReplaceTool = {
12809
13087
  );
12810
13088
  let newBytes;
12811
13089
  let replacedCount;
13090
+ let diffSamples;
12812
13091
  try {
12813
13092
  const result = await applyFindReplaceToHwpx(
12814
13093
  originalBytes,
@@ -12819,6 +13098,7 @@ var proposeFindReplaceTool = {
12819
13098
  );
12820
13099
  newBytes = result.buffer;
12821
13100
  replacedCount = result.count;
13101
+ diffSamples = result.samples;
12822
13102
  } catch (e) {
12823
13103
  return `\uC624\uB958: \uCE58\uD658 \uC911 \uC624\uB958\uAC00 \uBC1C\uC0DD\uD588\uC2B5\uB2C8\uB2E4. ${String(e)}`;
12824
13104
  }
@@ -12838,7 +13118,7 @@ var proposeFindReplaceTool = {
12838
13118
  const remaining = countOccurrences(normAfter, normFind);
12839
13119
  if (remaining > 0) {
12840
13120
  warnings.push(
12841
- `\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.`
13121
+ `\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. \uB0A8\uC740 ${remaining}\uACF3\uC740 \uD45C \uC548\uC758 \uC140\uC774\uBA74 propose_cell_edit\uC73C\uB85C \uD55C \uACF3\uC529 \uC218\uC815\uD558\uAC70\uB098, \uD55C\uAE00 \uD504\uB85C\uADF8\uB7A8\uC5D0\uC11C \uC9C1\uC811 \uCC3E\uAE30\xB7\uBC14\uAFB8\uAE30\uB85C \uCC98\uB9AC\uD558\uC138\uC694.`
12842
13122
  );
12843
13123
  }
12844
13124
  }
@@ -12847,7 +13127,23 @@ var proposeFindReplaceTool = {
12847
13127
  }
12848
13128
  const { outputPath, willConvertFormat } = resolveOutputPath(safePath);
12849
13129
  const allLabel = input.all ?? true ? `${replacedCount}\uACF3` : "\uCCAB \uBC88\uC9F8 1\uACF3";
12850
- const diff = `\uCC3E\uAE30: "${input.find}" \u2192 \uBC14\uAFB8\uAE30: "${input.replace}" (${allLabel} \uAD50\uCCB4\uB428)`;
13130
+ let diff;
13131
+ if (diffSamples.length === 0) {
13132
+ diff = `\uCC3E\uAE30: "${input.find}" \u2192 \uBC14\uAFB8\uAE30: "${input.replace}" (${allLabel} \uAD50\uCCB4\uB428)`;
13133
+ } else {
13134
+ const lines = [`${replacedCount}\uACF3 \uAD50\uCCB4: "${input.find}" \u2192 "${input.replace}"`];
13135
+ for (let i = 0; i < diffSamples.length; i++) {
13136
+ const sample = diffSamples[i];
13137
+ lines.push(` ${i + 1}. - ${sample.before}`);
13138
+ lines.push(` + ${sample.after}`);
13139
+ }
13140
+ if (replacedCount > diffSamples.length) {
13141
+ lines.push(
13142
+ ` \u2026 \uC678 ${replacedCount - diffSamples.length}\uACF3 (\uBBF8\uB9AC\uBCF4\uAE30\uB294 \uCD5C\uB300 ${MAX_DIFF_SAMPLES}\uACF3)`
13143
+ );
13144
+ }
13145
+ diff = lines.join("\n");
13146
+ }
12851
13147
  const stagedPath = await stageFile(ctx.sessionId, outputPath, newBytes);
12852
13148
  const proposalId = crypto.randomUUID();
12853
13149
  return {
@@ -12870,10 +13166,10 @@ var proposeFindReplaceTool = {
12870
13166
  };
12871
13167
  }
12872
13168
  };
12873
- var proposeFormFillSchema = z7.object({
12874
- path: z7.string().describe("\uC591\uC2DD \uBB38\uC11C \uACBD\uB85C (cwd \uAE30\uC900 \uC0C1\uB300 \uACBD\uB85C \uB610\uB294 \uC808\uB300 \uACBD\uB85C)"),
12875
- 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"),
12876
- summary: z7.string().describe("\uBCC0\uACBD \uC694\uC57D (\uD55C\uAD6D\uC5B4 1-2\uBB38\uC7A5)")
13169
+ var proposeFormFillSchema = z8.object({
13170
+ path: z8.string().describe("\uC591\uC2DD \uBB38\uC11C \uACBD\uB85C (cwd \uAE30\uC900 \uC0C1\uB300 \uACBD\uB85C \uB610\uB294 \uC808\uB300 \uACBD\uB85C)"),
13171
+ fields: z8.record(z8.string(), z8.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"),
13172
+ summary: z8.string().describe("\uBCC0\uACBD \uC694\uC57D (\uD55C\uAD6D\uC5B4 1-2\uBB38\uC7A5)")
12877
13173
  });
12878
13174
  var proposeFormFillTool = {
12879
13175
  name: "propose_form_fill",
@@ -12885,13 +13181,13 @@ var proposeFormFillTool = {
12885
13181
  ctx
12886
13182
  }) => {
12887
13183
  const safePath = await resolveSafePath(ctx.cwd, input.path);
12888
- const ext = extname7(safePath).toLowerCase();
13184
+ const ext = extname8(safePath).toLowerCase();
12889
13185
  if (ext !== ".hwpx" && ext !== ".hwp") {
12890
13186
  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.`;
12891
13187
  }
12892
13188
  let originalBuffer;
12893
13189
  try {
12894
- originalBuffer = await readFile7(safePath);
13190
+ originalBuffer = await readFile8(safePath);
12895
13191
  } catch {
12896
13192
  return `\uC624\uB958: \uD30C\uC77C\uC744 \uC77D\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${input.path}. \uACBD\uB85C\uB97C \uD655\uC778\uD558\uC138\uC694.`;
12897
13193
  }
@@ -12959,16 +13255,16 @@ var proposeFormFillTool = {
12959
13255
  };
12960
13256
  }
12961
13257
  };
12962
- var proposeSheetEditSchema = z8.object({
12963
- 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)"),
12964
- updates: z8.array(
12965
- z8.object({
12966
- sheet: z8.string().describe("\uC2DC\uD2B8 \uC774\uB984"),
12967
- cell: z8.string().describe("\uC140 \uC8FC\uC18C (\uC608: A1, B3)"),
12968
- value: z8.union([z8.string(), z8.number()]).describe("\uC0C8 \uAC12")
13258
+ var proposeSheetEditSchema = z9.object({
13259
+ path: z9.string().describe("\uC218\uC815\uD560 XLSX \uD30C\uC77C \uACBD\uB85C (cwd \uAE30\uC900 \uC0C1\uB300 \uACBD\uB85C \uB610\uB294 \uC808\uB300 \uACBD\uB85C)"),
13260
+ updates: z9.array(
13261
+ z9.object({
13262
+ sheet: z9.string().describe("\uC2DC\uD2B8 \uC774\uB984"),
13263
+ cell: z9.string().describe("\uC140 \uC8FC\uC18C (\uC608: A1, B3)"),
13264
+ value: z9.union([z9.string(), z9.number()]).describe("\uC0C8 \uAC12")
12969
13265
  })
12970
13266
  ).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"),
12971
- summary: z8.string().describe("\uBCC0\uACBD \uC694\uC57D (\uD55C\uAD6D\uC5B4 1-2\uBB38\uC7A5)")
13267
+ summary: z9.string().describe("\uBCC0\uACBD \uC694\uC57D (\uD55C\uAD6D\uC5B4 1-2\uBB38\uC7A5)")
12972
13268
  });
12973
13269
  var proposeSheetEditTool = {
12974
13270
  name: "propose_sheet_edit",
@@ -12980,13 +13276,13 @@ var proposeSheetEditTool = {
12980
13276
  ctx
12981
13277
  }) => {
12982
13278
  const safePath = await resolveSafePath(ctx.cwd, input.path);
12983
- const ext = extname8(safePath).toLowerCase();
13279
+ const ext = extname9(safePath).toLowerCase();
12984
13280
  if (ext !== ".xlsx" && ext !== ".xls") {
12985
13281
  return `\uC624\uB958: propose_sheet_edit\uC740 .xlsx/.xls \uD30C\uC77C\uB9CC \uC9C0\uC6D0\uD569\uB2C8\uB2E4. \uD604\uC7AC \uD30C\uC77C: ${ext}.`;
12986
13282
  }
12987
13283
  let originalBuffer;
12988
13284
  try {
12989
- originalBuffer = await readFile8(safePath);
13285
+ originalBuffer = await readFile9(safePath);
12990
13286
  } catch {
12991
13287
  return `\uC624\uB958: \uD30C\uC77C\uC744 \uC77D\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${input.path}. \uACBD\uB85C\uB97C \uD655\uC778\uD558\uC138\uC694.`;
12992
13288
  }
@@ -13025,23 +13321,25 @@ var proposeSheetEditTool = {
13025
13321
  return `\uC624\uB958: \uC6CC\uD06C\uBD81 \uC800\uC7A5 \uC911 \uC624\uB958\uAC00 \uBC1C\uC0DD\uD588\uC2B5\uB2C8\uB2E4: ${msg}`;
13026
13322
  }
13027
13323
  const stagedData = modifiedBuffer;
13028
- const stagedPath = await stageFile(ctx.sessionId, safePath, stagedData);
13324
+ const { outputPath, willConvertFormat } = resolveOutputPath(safePath);
13325
+ const stagedPath = await stageFile(ctx.sessionId, outputPath, stagedData);
13029
13326
  const proposalId = crypto.randomUUID();
13030
13327
  return {
13031
13328
  proposal: {
13032
13329
  id: proposalId,
13033
13330
  kind: "sheet-edit",
13034
- targetPath: safePath,
13331
+ targetPath: outputPath,
13035
13332
  stagedPath,
13036
13333
  summary: input.summary,
13037
13334
  diff,
13038
- warnings: []
13335
+ warnings: [],
13336
+ willConvertFormat
13039
13337
  },
13040
13338
  commit: async () => {
13041
- const backupPath = await backupFile(safePath);
13042
- await commitStaged(stagedPath, safePath);
13339
+ const backupPath = await backupFile(outputPath);
13340
+ await commitStaged(stagedPath, outputPath);
13043
13341
  const backupInfo = backupPath ? ` (\uBC31\uC5C5: ${backupPath})` : "";
13044
- return `\uC800\uC7A5 \uC644\uB8CC: ${safePath}${backupInfo}`;
13342
+ return `\uC800\uC7A5 \uC644\uB8CC: ${outputPath}${backupInfo}`;
13045
13343
  }
13046
13344
  };
13047
13345
  }
@@ -13077,47 +13375,47 @@ function detectStructuralLoss(beforeBlocks, afterBlocks) {
13077
13375
  }
13078
13376
  return { lost: false, detail: "" };
13079
13377
  }
13080
- var insertRowOpSchema = z9.object({
13081
- type: z9.literal("insertRow"),
13082
- row: z9.number().int().nonnegative().describe("\uAE30\uC900 \uD589 \uC778\uB371\uC2A4 (0-based)"),
13083
- position: z9.enum(["above", "below"]).describe("\uC0BD\uC785 \uC704\uCE58: above=row \uC704\uC5D0, below=row \uC544\uB798\uC5D0")
13378
+ var insertRowOpSchema = z10.object({
13379
+ type: z10.literal("insertRow"),
13380
+ row: z10.number().int().nonnegative().describe("\uAE30\uC900 \uD589 \uC778\uB371\uC2A4 (0-based)"),
13381
+ position: z10.enum(["above", "below"]).describe("\uC0BD\uC785 \uC704\uCE58: above=row \uC704\uC5D0, below=row \uC544\uB798\uC5D0")
13084
13382
  });
13085
- var deleteRowOpSchema = z9.object({
13086
- type: z9.literal("deleteRow"),
13087
- row: z9.number().int().nonnegative().describe("\uC0AD\uC81C\uD560 \uD589 \uC778\uB371\uC2A4 (0-based)")
13383
+ var deleteRowOpSchema = z10.object({
13384
+ type: z10.literal("deleteRow"),
13385
+ row: z10.number().int().nonnegative().describe("\uC0AD\uC81C\uD560 \uD589 \uC778\uB371\uC2A4 (0-based)")
13088
13386
  });
13089
- var insertColumnOpSchema = z9.object({
13090
- type: z9.literal("insertColumn"),
13091
- col: z9.number().int().nonnegative().describe("\uAE30\uC900 \uC5F4 \uC778\uB371\uC2A4 (0-based)"),
13092
- position: z9.enum(["left", "right"]).describe("\uC0BD\uC785 \uC704\uCE58: left=col \uC67C\uCABD\uC5D0, right=col \uC624\uB978\uCABD\uC5D0")
13387
+ var insertColumnOpSchema = z10.object({
13388
+ type: z10.literal("insertColumn"),
13389
+ col: z10.number().int().nonnegative().describe("\uAE30\uC900 \uC5F4 \uC778\uB371\uC2A4 (0-based)"),
13390
+ position: z10.enum(["left", "right"]).describe("\uC0BD\uC785 \uC704\uCE58: left=col \uC67C\uCABD\uC5D0, right=col \uC624\uB978\uCABD\uC5D0")
13093
13391
  });
13094
- var deleteColumnOpSchema = z9.object({
13095
- type: z9.literal("deleteColumn"),
13096
- col: z9.number().int().nonnegative().describe("\uC0AD\uC81C\uD560 \uC5F4 \uC778\uB371\uC2A4 (0-based)")
13392
+ var deleteColumnOpSchema = z10.object({
13393
+ type: z10.literal("deleteColumn"),
13394
+ col: z10.number().int().nonnegative().describe("\uC0AD\uC81C\uD560 \uC5F4 \uC778\uB371\uC2A4 (0-based)")
13097
13395
  });
13098
- var mergeCellsOpSchema = z9.object({
13099
- type: z9.literal("mergeCells"),
13100
- startRow: z9.number().int().nonnegative().describe("\uBCD1\uD569 \uC2DC\uC791 \uD589 (0-based)"),
13101
- startCol: z9.number().int().nonnegative().describe("\uBCD1\uD569 \uC2DC\uC791 \uC5F4 (0-based)"),
13102
- endRow: z9.number().int().nonnegative().describe("\uBCD1\uD569 \uB05D \uD589 (0-based, \uD3EC\uD568)"),
13103
- endCol: z9.number().int().nonnegative().describe("\uBCD1\uD569 \uB05D \uC5F4 (0-based, \uD3EC\uD568)")
13396
+ var mergeCellsOpSchema = z10.object({
13397
+ type: z10.literal("mergeCells"),
13398
+ startRow: z10.number().int().nonnegative().describe("\uBCD1\uD569 \uC2DC\uC791 \uD589 (0-based)"),
13399
+ startCol: z10.number().int().nonnegative().describe("\uBCD1\uD569 \uC2DC\uC791 \uC5F4 (0-based)"),
13400
+ endRow: z10.number().int().nonnegative().describe("\uBCD1\uD569 \uB05D \uD589 (0-based, \uD3EC\uD568)"),
13401
+ endCol: z10.number().int().nonnegative().describe("\uBCD1\uD569 \uB05D \uC5F4 (0-based, \uD3EC\uD568)")
13104
13402
  });
13105
- var operationSchema = z9.discriminatedUnion("type", [
13403
+ var operationSchema = z10.discriminatedUnion("type", [
13106
13404
  insertRowOpSchema,
13107
13405
  deleteRowOpSchema,
13108
13406
  insertColumnOpSchema,
13109
13407
  deleteColumnOpSchema,
13110
13408
  mergeCellsOpSchema
13111
13409
  ]).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.");
13112
- var proposeTableStructureSchema = z9.object({
13113
- 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)"),
13114
- anchor: z9.string().min(1).describe(
13410
+ var proposeTableStructureSchema = z10.object({
13411
+ path: z10.string().describe("\uC218\uC815\uD560 .hwpx \uD30C\uC77C \uACBD\uB85C (cwd \uAE30\uC900 \uC0C1\uB300 \uACBD\uB85C \uB610\uB294 \uC808\uB300 \uACBD\uB85C)"),
13412
+ anchor: z10.string().min(1).describe(
13115
13413
  "\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."
13116
13414
  ),
13117
- operations: z9.array(operationSchema).min(1).describe(
13415
+ operations: z10.array(operationSchema).min(1).describe(
13118
13416
  "\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."
13119
13417
  ),
13120
- summary: z9.string().describe("\uBCC0\uACBD \uC694\uC57D (\uD55C\uAD6D\uC5B4 1-2\uBB38\uC7A5)")
13418
+ summary: z10.string().describe("\uBCC0\uACBD \uC694\uC57D (\uD55C\uAD6D\uC5B4 1-2\uBB38\uC7A5)")
13121
13419
  });
13122
13420
  function tokenizeHwpxXml2(xml) {
13123
13421
  const tokens = [];
@@ -13792,7 +14090,7 @@ var proposeTableStructureTool = {
13792
14090
  ctx
13793
14091
  }) => {
13794
14092
  const safePath = await resolveSafePath(ctx.cwd, input.path);
13795
- const ext = extname9(safePath).toLowerCase();
14093
+ const ext = extname10(safePath).toLowerCase();
13796
14094
  if (ext === ".hwp") {
13797
14095
  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.";
13798
14096
  }
@@ -13801,7 +14099,7 @@ var proposeTableStructureTool = {
13801
14099
  }
13802
14100
  let originalBuf;
13803
14101
  try {
13804
- originalBuf = await readFile9(safePath);
14102
+ originalBuf = await readFile10(safePath);
13805
14103
  } catch {
13806
14104
  return `\uC624\uB958: \uD30C\uC77C\uC744 \uC77D\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${input.path}. \uACBD\uB85C\uB97C \uD655\uC778\uD558\uC138\uC694.`;
13807
14105
  }
@@ -13997,11 +14295,11 @@ function searchExcerpts(markdown, query) {
13997
14295
  }
13998
14296
  return result;
13999
14297
  }
14000
- var readDocumentSchema = z10.object({
14001
- path: z10.string().describe("\uC77D\uC744 \uBB38\uC11C \uACBD\uB85C (cwd \uAE30\uC900 \uC0C1\uB300 \uACBD\uB85C \uB610\uB294 \uC808\uB300 \uACBD\uB85C)"),
14002
- 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'),
14003
- 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)"),
14004
- 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)")
14298
+ var readDocumentSchema = z11.object({
14299
+ path: z11.string().describe("\uC77D\uC744 \uBB38\uC11C \uACBD\uB85C (cwd \uAE30\uC900 \uC0C1\uB300 \uACBD\uB85C \uB610\uB294 \uC808\uB300 \uACBD\uB85C)"),
14300
+ pages: z11.string().optional().describe('\uC77D\uC744 \uD398\uC774\uC9C0 \uBC94\uC704 (\uC608: "1-3", "1,3,5") \u2014 \uBBF8\uC9C0\uC815 \uC2DC \uC804\uCCB4'),
14301
+ outline: z11.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)"),
14302
+ search: z11.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)")
14005
14303
  });
14006
14304
  function applyReadMode(body, outline, search) {
14007
14305
  if (outline === true) {
@@ -14023,18 +14321,18 @@ var readDocumentTool = {
14023
14321
  }) => {
14024
14322
  const safePath = await resolveSafePath(ctx.cwd, input.path);
14025
14323
  try {
14026
- const fileStat = await stat3(safePath);
14324
+ const fileStat = await stat4(safePath);
14027
14325
  const guardMsg = fileSizeGuardMessage(fileStat.size, MAX_FILE_SIZE_BYTES);
14028
14326
  if (guardMsg !== null) return guardMsg;
14029
14327
  } catch (e) {
14030
14328
  const msg = e instanceof Error ? e.message : String(e);
14031
14329
  return `\uC624\uB958: \uD30C\uC77C\uC744 \uC77D\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${msg}`;
14032
14330
  }
14033
- const ext = extname10(safePath).toLowerCase();
14331
+ const ext = extname11(safePath).toLowerCase();
14034
14332
  if (PLAIN_TEXT_EXTS.has(ext)) {
14035
14333
  let raw;
14036
14334
  try {
14037
- raw = await readFile10(safePath, "utf-8");
14335
+ raw = await readFile11(safePath, "utf-8");
14038
14336
  } catch (e) {
14039
14337
  const msg = e instanceof Error ? e.message : String(e);
14040
14338
  return `\uC624\uB958: \uD30C\uC77C\uC744 \uC77D\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${msg}`;
@@ -14134,8 +14432,8 @@ var TEXT_EXTENSIONS = /* @__PURE__ */ new Set([
14134
14432
  ".csv",
14135
14433
  ".log"
14136
14434
  ]);
14137
- var readFileSchema = z11.object({
14138
- path: z11.string().describe("\uC77D\uC744 \uD30C\uC77C \uACBD\uB85C (cwd \uAE30\uC900 \uC0C1\uB300 \uACBD\uB85C \uB610\uB294 \uC808\uB300 \uACBD\uB85C)")
14435
+ var readFileSchema = z12.object({
14436
+ path: z12.string().describe("\uC77D\uC744 \uD30C\uC77C \uACBD\uB85C (cwd \uAE30\uC900 \uC0C1\uB300 \uACBD\uB85C \uB610\uB294 \uC808\uB300 \uACBD\uB85C)")
14139
14437
  });
14140
14438
  var readFileTool = {
14141
14439
  name: "read_file",
@@ -14149,7 +14447,7 @@ var readFileTool = {
14149
14447
  const safePath = await resolveSafePath(ctx.cwd, input.path);
14150
14448
  let info;
14151
14449
  try {
14152
- info = await stat4(safePath);
14450
+ info = await stat5(safePath);
14153
14451
  } catch {
14154
14452
  return `\uC624\uB958: \uD30C\uC77C\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${input.path}`;
14155
14453
  }
@@ -14159,8 +14457,8 @@ var readFileTool = {
14159
14457
  if (info.size > MAX_SIZE) {
14160
14458
  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.`;
14161
14459
  }
14162
- const { extname: extname13 } = await import("path");
14163
- const ext = extname13(safePath).toLowerCase();
14460
+ const { extname: extname14 } = await import("path");
14461
+ const ext = extname14(safePath).toLowerCase();
14164
14462
  if (!TEXT_EXTENSIONS.has(ext) && ext !== "") {
14165
14463
  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.`;
14166
14464
  }
@@ -14175,9 +14473,9 @@ var readFileTool = {
14175
14473
  }
14176
14474
  };
14177
14475
  var MAX_PREVIEW_CHARS = 1e4;
14178
- var writeNewDocumentSchema = z12.object({
14179
- path: z12.string().describe("\uC0DD\uC131\uD560 \uBB38\uC11C \uACBD\uB85C (cwd \uAE30\uC900 \uC0C1\uB300 \uACBD\uB85C \uB610\uB294 \uC808\uB300 \uACBD\uB85C)"),
14180
- markdown: z12.string().describe("\uC0C8 \uBB38\uC11C \uB0B4\uC6A9 (\uB9C8\uD06C\uB2E4\uC6B4 \uD615\uC2DD)")
14476
+ var writeNewDocumentSchema = z13.object({
14477
+ path: z13.string().describe("\uC0DD\uC131\uD560 \uBB38\uC11C \uACBD\uB85C (cwd \uAE30\uC900 \uC0C1\uB300 \uACBD\uB85C \uB610\uB294 \uC808\uB300 \uACBD\uB85C)"),
14478
+ markdown: z13.string().describe("\uC0C8 \uBB38\uC11C \uB0B4\uC6A9 (\uB9C8\uD06C\uB2E4\uC6B4 \uD615\uC2DD)")
14181
14479
  });
14182
14480
  var writeNewDocumentTool = {
14183
14481
  name: "write_new_document",
@@ -14189,9 +14487,9 @@ var writeNewDocumentTool = {
14189
14487
  ctx
14190
14488
  }) => {
14191
14489
  const safePath = await resolveSafePath(ctx.cwd, input.path);
14192
- const ext = extname11(safePath).toLowerCase();
14490
+ const ext = extname12(safePath).toLowerCase();
14193
14491
  try {
14194
- await stat5(safePath);
14492
+ await stat6(safePath);
14195
14493
  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.`;
14196
14494
  } catch {
14197
14495
  }
@@ -14239,12 +14537,12 @@ ${preview}`,
14239
14537
  };
14240
14538
  }
14241
14539
  };
14242
- var writeNewSpreadsheetSchema = z13.object({
14243
- 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)"),
14244
- sheets: z13.array(
14245
- z13.object({
14246
- name: z13.string().describe("\uC2DC\uD2B8 \uC774\uB984"),
14247
- rows: z13.array(z13.array(z13.string())).describe("\uD589 \uB370\uC774\uD130 \uBC30\uC5F4 (\uAC01 \uD589\uC740 \uC140 \uAC12 \uBC30\uC5F4)")
14540
+ var writeNewSpreadsheetSchema = z14.object({
14541
+ path: z14.string().describe("\uC0DD\uC131\uD560 XLSX \uD30C\uC77C \uACBD\uB85C (cwd \uAE30\uC900 \uC0C1\uB300 \uACBD\uB85C \uB610\uB294 \uC808\uB300 \uACBD\uB85C)"),
14542
+ sheets: z14.array(
14543
+ z14.object({
14544
+ name: z14.string().describe("\uC2DC\uD2B8 \uC774\uB984"),
14545
+ rows: z14.array(z14.array(z14.string())).describe("\uD589 \uB370\uC774\uD130 \uBC30\uC5F4 (\uAC01 \uD589\uC740 \uC140 \uAC12 \uBC30\uC5F4)")
14248
14546
  })
14249
14547
  ).min(1).describe("\uC0DD\uC131\uD560 \uC2DC\uD2B8 \uBAA9\uB85D")
14250
14548
  });
@@ -14258,12 +14556,12 @@ var writeNewSpreadsheetTool = {
14258
14556
  ctx
14259
14557
  }) => {
14260
14558
  const safePath = await resolveSafePath(ctx.cwd, input.path);
14261
- const ext = extname12(safePath).toLowerCase();
14559
+ const ext = extname13(safePath).toLowerCase();
14262
14560
  if (ext !== ".xlsx") {
14263
14561
  return `\uC624\uB958: write_new_spreadsheet\uC740 .xlsx \uD30C\uC77C\uB9CC \uC9C0\uC6D0\uD569\uB2C8\uB2E4. \uD604\uC7AC \uD655\uC7A5\uC790: ${ext}.`;
14264
14562
  }
14265
14563
  try {
14266
- await stat6(safePath);
14564
+ await stat7(safePath);
14267
14565
  return `\uC624\uB958: \uD30C\uC77C\uC774 \uC774\uBBF8 \uC874\uC7AC\uD569\uB2C8\uB2E4: ${input.path}. \uAE30\uC874 \uD30C\uC77C \uC140 \uC218\uC815\uC740 propose_sheet_edit\uC744 \uC0AC\uC6A9\uD558\uC138\uC694.`;
14268
14566
  } catch {
14269
14567
  }
@@ -14316,6 +14614,7 @@ function createDocTools(_ctx) {
14316
14614
  readDocumentTool,
14317
14615
  compareDocumentsTool,
14318
14616
  listFilesTool,
14617
+ listBackupsTool,
14319
14618
  readFileTool,
14320
14619
  proposeEditTool,
14321
14620
  proposeFormFillTool,
@@ -14326,7 +14625,8 @@ function createDocTools(_ctx) {
14326
14625
  listFormObjectsTool,
14327
14626
  proposeFormObjectTool,
14328
14627
  writeNewDocumentTool,
14329
- writeNewSpreadsheetTool
14628
+ writeNewSpreadsheetTool,
14629
+ restoreBackupTool
14330
14630
  ];
14331
14631
  }
14332
14632
 
@@ -14415,6 +14715,14 @@ function createCliApprovalHandler() {
14415
14715
 
14416
14716
  // src/chat.ts
14417
14717
  var HELP_TEXT = `
14718
+ \uD560 \uC218 \uC788\uB294 \uC77C:
14719
+ \u2022 \uBB38\uC11C \uC77D\uAE30\xB7\uC694\uC57D\xB7\uAC80\uD1A0 \u2014 .hwp/.hwpx/.docx/.xlsx/.pdf (\uC608: "\uC774 \uBCF4\uB3C4\uC790\uB8CC \uC694\uC57D\uD574\uC918")
14720
+ \u2022 \uD45C\xB7\uC591\uC2DD \uC218\uC815 \u2014 \uC140 \uAC12, \uC591\uC2DD \uBE48\uCE78, \uD589\xB7\uC5F4 \uCD94\uAC00/\uC0AD\uC81C (\uC608: "\uC774 \uD45C\uC758 \uAE08\uC561\uC744 30000\uC73C\uB85C \uACE0\uCCD0\uC918")
14721
+ \u2022 \uBB38\uC11C \uC804\uCCB4 \uCC3E\uAE30\xB7\uBC14\uAFB8\uAE30 (\uC608: "'\uAD6D\uBBFC\uC8FC\uAD8C'\uC744 '\uAD6D\uBBFC\uC911\uC2EC'\uC73C\uB85C \uB2E4 \uBC14\uAFD4\uC918")
14722
+ \u2022 \uB418\uB3CC\uB9AC\uAE30 \u2014 \uC9C1\uC804 \uBCC0\uACBD\uC744 \uBC31\uC5C5\uC73C\uB85C \uBCF5\uC6D0 (\uC608: "\uBC29\uAE08 \uC218\uC815 \uB418\uB3CC\uB824\uC918")
14723
+ \u2022 \uD55C\uAD6D \uBC95\uB839 \uAE30\uBC18 \uAC80\uD1A0 (\uC608: "\uC774 \uCDE8\uC5C5\uADDC\uCE59\uC774 \uADFC\uB85C\uAE30\uC900\uBC95\uC5D0 \uB9DE\uB294\uC9C0 \uBD10\uC918")
14724
+ \u203B .hwp\uB294 \uD55C\uAE00\uC5D0\uC11C .hwpx\uB85C \uC800\uC7A5\uD55C \uD6C4 \uC218\uC815\uD560 \uC218 \uC788\uC2B5\uB2C8\uB2E4.
14725
+
14418
14726
  \uC2AC\uB798\uC2DC \uBA85\uB839:
14419
14727
  /model \u2014 \uD504\uB85C\uBC14\uC774\uB354/\uBAA8\uB378 \uC804\uD658
14420
14728
  /context \u2014 \uD604\uC7AC \uCEE8\uD14D\uC2A4\uD2B8 \uC0AC\uC6A9\uB7C9 \uD45C\uC2DC
@@ -14537,6 +14845,11 @@ async function runChat(opts) {
14537
14845
 
14538
14846
  `)
14539
14847
  );
14848
+ process.stdout.write(
14849
+ chalk2.dim(
14850
+ '\uBB38\uC11C\uB97C \uC77D\uACE0 \uD45C\xB7\uC591\uC2DD \uC218\uC815, \uCC3E\uAE30\xB7\uBC14\uAFB8\uAE30, \uB418\uB3CC\uB9AC\uAE30\uAE4C\uC9C0 \uC790\uC5F0\uC5B4\uB85C \uC694\uCCAD\uD558\uC138\uC694. \uC608: "\uC774 \uD45C\uC758 \uD569\uACC4\uB97C \uB2E4\uC2DC \uACC4\uC0B0\uD574\uC918", "\uBC29\uAE08 \uC218\uC815 \uB418\uB3CC\uB824\uC918". \uC790\uC138\uD788: /help\n'
14851
+ )
14852
+ );
14540
14853
  while (true) {
14541
14854
  let clearActiveSpinner2 = function() {
14542
14855
  if (!sharedActiveInterval) return;
@@ -15130,7 +15443,7 @@ async function runOnboarding() {
15130
15443
 
15131
15444
  // src/update.ts
15132
15445
  import { spawn } from "child_process";
15133
- import { mkdir as mkdir4, readFile as readFile11, writeFile as writeFile3 } from "fs/promises";
15446
+ import { mkdir as mkdir4, readFile as readFile12, writeFile as writeFile3 } from "fs/promises";
15134
15447
  import { dirname as dirname3 } from "path";
15135
15448
  function compareSemver(a, b) {
15136
15449
  const parse6 = (v) => {
@@ -15151,7 +15464,7 @@ function compareSemver(a, b) {
15151
15464
  var CACHE_TTL_MS = 24 * 60 * 60 * 1e3;
15152
15465
  async function readCache(cachePath) {
15153
15466
  try {
15154
- const raw = await readFile11(cachePath, "utf-8");
15467
+ const raw = await readFile12(cachePath, "utf-8");
15155
15468
  const parsed = JSON.parse(raw);
15156
15469
  if (parsed !== null && typeof parsed === "object" && "checkedAt" in parsed && "latest" in parsed && typeof parsed.checkedAt === "string" && typeof parsed.latest === "string") {
15157
15470
  return parsed;
@@ -15434,7 +15747,9 @@ async function runSingleTurn(prompt) {
15434
15747
  });
15435
15748
  const tools = new ToolRegistry();
15436
15749
  for (const tool2 of createDocTools({ cwd })) {
15437
- tools.register(tool2);
15750
+ const t = tool2;
15751
+ if (t.requiresApproval) continue;
15752
+ tools.register(t);
15438
15753
  }
15439
15754
  for (const mcpTool of mcpManager.getToolDefinitions()) {
15440
15755
  tools.register(mcpTool);