@bifos/dooray-cli 0.15.1 → 0.17.0

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
@@ -385,7 +385,7 @@ var import_node_path3 = __toESM(require("path"));
385
385
  var import_node_os3 = require("os");
386
386
 
387
387
  // src/version.ts
388
- var CLI_VERSION = true ? "0.15.1" : "0.0.0-dev";
388
+ var CLI_VERSION = true ? "0.17.0" : "0.0.0-dev";
389
389
 
390
390
  // src/skill/context.ts
391
391
  function resolveSkillDataRoot(homeDir, xdgDataHome = process.env.XDG_DATA_HOME) {
@@ -2300,7 +2300,7 @@ async function resolveMemberGroup(client, projectId, input2) {
2300
2300
  }
2301
2301
  const adapter = valid.map((g) => ({ name: g.code, id: g.id, code: g.code }));
2302
2302
  const match = matchByName(adapter, input2, "\uADF8\uB8F9", (g) => `${g.code} (${g.id})`, {
2303
- helpHint: "\uC804\uCCB4 \uBAA9\uB85D: `dooray project groups <project>` / id \uC9C1\uC811 \uC785\uB825\uB3C4 \uAC00\uB2A5 (15+\uC790\uB9AC numeric \u2014 code \uB204\uB77D \uADF8\uB8F9\uB3C4 \uB9E4\uCE6D, ADR-028)"
2303
+ helpHint: "\uC804\uCCB4 \uBAA9\uB85D: `dooray project groups <project>` / id \uC9C1\uC811 \uC785\uB825\uB3C4 \uAC00\uB2A5 (15+\uC790\uB9AC numeric \u2014 code \uB204\uB77D \uADF8\uB8F9\uB3C4 \uB9E4\uCE6D)"
2304
2304
  });
2305
2305
  return { id: match.id, code: match.code };
2306
2306
  }
@@ -3151,14 +3151,21 @@ async function readStdin() {
3151
3151
 
3152
3152
  // src/utils/attachment-check.ts
3153
3153
  var import_node_readline = __toESM(require("readline"));
3154
- function extractAttachmentFileIds(body) {
3155
- const ids = /* @__PURE__ */ new Set();
3156
- const re = /!?\[[^\]]*\]\(\/files\/([^\s)?#]+)/g;
3154
+ function extractAttachmentReferences(body) {
3155
+ const references = [];
3156
+ const seen = /* @__PURE__ */ new Set();
3157
+ const re = /!?\[([^\]]*)\]\(\/files\/([^\s)?#]+)/g;
3157
3158
  let m;
3158
3159
  while ((m = re.exec(body)) !== null) {
3159
- ids.add(m[1]);
3160
+ const id = m[2];
3161
+ if (seen.has(id)) continue;
3162
+ seen.add(id);
3163
+ references.push({ id, label: m[1] });
3160
3164
  }
3161
- return ids;
3165
+ return references;
3166
+ }
3167
+ function extractAttachmentFileIds(body) {
3168
+ return new Set(extractAttachmentReferences(body).map((reference) => reference.id));
3162
3169
  }
3163
3170
  function findDroppedAttachments(oldBody, newBody, attachments) {
3164
3171
  const oldIds = extractAttachmentFileIds(oldBody);
@@ -3235,6 +3242,7 @@ var postEditCommand = new import_commander15.Command("edit").description("\uC5C5
3235
3242
  const toGroups = (opts.toGroup ?? []).filter((s) => s.length > 0);
3236
3243
  const tagAdditions = (opts.tag ?? []).filter((s) => s.length > 0);
3237
3244
  const tagRemovals = (opts.tagRemove ?? []).filter((s) => s.length > 0);
3245
+ const hasParticipantChange = ccNames.length > 0 || ccGroups.length > 0 || !!opts.ccClear || toNames.length > 0 || toGroups.length > 0 || !!opts.toClear;
3238
3246
  const hasTagChange = tagAdditions.length > 0 || tagRemovals.length > 0 || !!opts.tagClear;
3239
3247
  startSpinner("\uC5C5\uBB34 \uC870\uD68C \uC911...");
3240
3248
  const { projectId, postId, postNumber, projectCode } = await resolvePostInput(client, {
@@ -3253,7 +3261,7 @@ var postEditCommand = new import_commander15.Command("edit").description("\uC5C5
3253
3261
  "\u26A0 --subject\uB294 deprecated\uC785\uB2C8\uB2E4. \uB300\uC2E0 --title\uC744 \uC0AC\uC6A9\uD574\uC8FC\uC138\uC694.\n"
3254
3262
  );
3255
3263
  }
3256
- const nonInteractive = title || opts.body || opts.bodyFile || hasTagChange;
3264
+ const nonInteractive = title || opts.body || opts.bodyFile || hasTagChange || hasParticipantChange;
3257
3265
  if (nonInteractive) {
3258
3266
  let newBody = await readBodyInputOrNull(opts);
3259
3267
  if (newBody != null && !opts.dryRun) {
@@ -3368,11 +3376,6 @@ var postEditCommand = new import_commander15.Command("edit").description("\uC5C5
3368
3376
  "\u26A0 --link-task \uB294 --title/--body \uC640 \uD568\uAED8 \uC0AC\uC6A9 \uC2DC\uC5D0\uB9CC \uC801\uC6A9\uB429\uB2C8\uB2E4.\n"
3369
3377
  );
3370
3378
  }
3371
- if (ccNames.length > 0 || ccGroups.length > 0 || opts.ccClear || toNames.length > 0 || toGroups.length > 0 || opts.toClear) {
3372
- process.stderr.write(
3373
- "\u26A0 --cc/--cc-group/--cc-clear/--to/--to-group/--to-clear \uB294 --title/--body \uC640 \uD568\uAED8 \uC0AC\uC6A9 \uC2DC\uC5D0\uB9CC \uC801\uC6A9\uB429\uB2C8\uB2E4.\n"
3374
- );
3375
- }
3376
3379
  if (opts.parent) {
3377
3380
  process.stderr.write(
3378
3381
  "\u26A0 --parent \uB294 --title/--body \uC640 \uD568\uAED8 \uC0AC\uC6A9 \uC2DC\uC5D0\uB9CC \uC801\uC6A9\uB429\uB2C8\uB2E4.\n"
@@ -3451,7 +3454,7 @@ function postUserToCreate(u) {
3451
3454
  group: u.group
3452
3455
  };
3453
3456
  }
3454
- var postCreateCommand = new import_commander16.Command("create").description("\uC5C5\uBB34 \uC0DD\uC131").argument("<project>", "\uD504\uB85C\uC81D\uD2B8 \uCF54\uB4DC \uB610\uB294 ID").option("--title <title>", "\uC5C5\uBB34 \uC81C\uBAA9").option("--subject <subject>", "--title\uC758 deprecated alias").option("--to <members...>", "\uB2F4\uB2F9\uC790 (\uC774\uB984 \uB610\uB294 \uC774\uBA54\uC77C, \uC5EC\uB7EC \uBA85 \uAC00\uB2A5)").option("--to-group <code>", "\uB2F4\uB2F9\uC790(to) \uADF8\uB8F9 \uCD94\uAC00 (\uBC18\uBCF5 \uAC00\uB2A5, \uADF8\uB8F9 \uCF54\uB4DC \uBD80\uBD84\uC77C\uCE58)", (v, prev) => [...prev, v], []).option("--cc <members...>", "\uCC38\uC870\uC790 (\uC774\uB984 \uB610\uB294 \uC774\uBA54\uC77C, \uC5EC\uB7EC \uBA85 \uAC00\uB2A5)").option("--cc-group <code>", "\uCC38\uC870\uC790(cc) \uADF8\uB8F9 \uCD94\uAC00 (\uBC18\uBCF5 \uAC00\uB2A5, \uADF8\uB8F9 \uCF54\uB4DC \uBD80\uBD84\uC77C\uCE58)", (v, prev) => [...prev, v], []).option("--body <text>", "\uBCF8\uBB38 \uD14D\uC2A4\uD2B8 (- \uC785\uB825 \uC2DC stdin\uC5D0\uC11C \uC77D\uAE30)").option("--body-file <path>", "\uBCF8\uBB38 \uD30C\uC77C \uACBD\uB85C (- \uC785\uB825 \uC2DC stdin\uC5D0\uC11C \uC77D\uAE30)").option("--priority <level>", "\uC6B0\uC120\uC21C\uC704 (highest, high, normal, low, lowest)", "normal").option("--due-date <date>", "\uB9C8\uAC10\uC77C (ISO 8601 \uD615\uC2DD)").option("--tag <name>", "\uD0DC\uADF8 \uC774\uB984 (\uBC18\uBCF5 \uAC00\uB2A5)", (value, prev) => [...prev, value], []).option("--mention <name>", "\uBA64\uBC84 \uBA58\uC158 (\uBC18\uBCF5 \uAC00\uB2A5, \uC774\uB984 \uBD80\uBD84\uC77C\uCE58)", (v, prev) => [...prev, v], []).option("--mention-group <code>", "\uADF8\uB8F9 \uBA58\uC158 (\uBC18\uBCF5 \uAC00\uB2A5, code \uBD80\uBD84\uC77C\uCE58)", (v, prev) => [...prev, v], []).option("--link-task <ref>", "\uB2E4\uB978 \uC5C5\uBB34 \uB9C1\uD06C \uCD94\uAC00 (<project>/<number> \uB610\uB294 postId, \uBC18\uBCF5 \uAC00\uB2A5)", (v, prev) => [...prev, v], []).option("--parent <ref>", "\uBD80\uBAA8 \uC5C5\uBB34 (project/number \uB610\uB294 postId)").option("--template <ref>", "\uD15C\uD50C\uB9BF \uC774\uB984 \uB610\uB294 ID \u2014 body/users/tags \uC790\uB3D9 \uCC44\uC6C0 (\uC0AC\uC6A9\uC790 \uC635\uC158 \uC6B0\uC120 override, ADR-027)").option("--workflow <name>", "\uCD08\uAE30 \uC6CC\uD06C\uD50C\uB85C\uC6B0 \uC774\uB984 \uB610\uB294 class").option("--milestone <name>", "\uB9C8\uC77C\uC2A4\uD1A4 \uC774\uB984").option("--dry-run", "API \uD638\uCD9C \uC5C6\uC774 \uD569\uC131\uB41C \uBCF8\uBB38\uB9CC stdout \uCD9C\uB825 (mention/link-task \uC801\uC6A9 \uACB0\uACFC \uBBF8\uB9AC\uBCF4\uAE30)").action(async (project, opts) => {
3457
+ var postCreateCommand = new import_commander16.Command("create").description("\uC5C5\uBB34 \uC0DD\uC131").argument("<project>", "\uD504\uB85C\uC81D\uD2B8 \uCF54\uB4DC \uB610\uB294 ID").option("--title <title>", "\uC5C5\uBB34 \uC81C\uBAA9").option("--subject <subject>", "--title\uC758 deprecated alias").option("--to <members...>", "\uB2F4\uB2F9\uC790 (\uC774\uB984 \uB610\uB294 \uC774\uBA54\uC77C, \uC5EC\uB7EC \uBA85 \uAC00\uB2A5)").option("--to-group <code>", "\uB2F4\uB2F9\uC790(to) \uADF8\uB8F9 \uCD94\uAC00 (\uBC18\uBCF5 \uAC00\uB2A5, \uADF8\uB8F9 \uCF54\uB4DC \uBD80\uBD84\uC77C\uCE58)", (v, prev) => [...prev, v], []).option("--cc <members...>", "\uCC38\uC870\uC790 (\uC774\uB984 \uB610\uB294 \uC774\uBA54\uC77C, \uC5EC\uB7EC \uBA85 \uAC00\uB2A5)").option("--cc-group <code>", "\uCC38\uC870\uC790(cc) \uADF8\uB8F9 \uCD94\uAC00 (\uBC18\uBCF5 \uAC00\uB2A5, \uADF8\uB8F9 \uCF54\uB4DC \uBD80\uBD84\uC77C\uCE58)", (v, prev) => [...prev, v], []).option("--body <text>", "\uBCF8\uBB38 \uD14D\uC2A4\uD2B8 (- \uC785\uB825 \uC2DC stdin\uC5D0\uC11C \uC77D\uAE30)").option("--body-file <path>", "\uBCF8\uBB38 \uD30C\uC77C \uACBD\uB85C (- \uC785\uB825 \uC2DC stdin\uC5D0\uC11C \uC77D\uAE30)").option("--priority <level>", "\uC6B0\uC120\uC21C\uC704 (highest, high, normal, low, lowest)", "normal").option("--due-date <date>", "\uB9C8\uAC10\uC77C (ISO 8601 \uD615\uC2DD)").option("--tag <name>", "\uD0DC\uADF8 \uC774\uB984 (\uBC18\uBCF5 \uAC00\uB2A5)", (value, prev) => [...prev, value], []).option("--mention <name>", "\uBA64\uBC84 \uBA58\uC158 (\uBC18\uBCF5 \uAC00\uB2A5, \uC774\uB984 \uBD80\uBD84\uC77C\uCE58)", (v, prev) => [...prev, v], []).option("--mention-group <code>", "\uADF8\uB8F9 \uBA58\uC158 (\uBC18\uBCF5 \uAC00\uB2A5, code \uBD80\uBD84\uC77C\uCE58)", (v, prev) => [...prev, v], []).option("--link-task <ref>", "\uB2E4\uB978 \uC5C5\uBB34 \uB9C1\uD06C \uCD94\uAC00 (<project>/<number> \uB610\uB294 postId, \uBC18\uBCF5 \uAC00\uB2A5)", (v, prev) => [...prev, v], []).option("--parent <ref>", "\uBD80\uBAA8 \uC5C5\uBB34 (project/number \uB610\uB294 postId)").option("--template <ref>", "\uD15C\uD50C\uB9BF \uC774\uB984 \uB610\uB294 ID \u2014 body/users/tags \uC790\uB3D9 \uCC44\uC6C0 (\uC0AC\uC6A9\uC790 \uC635\uC158 \uC6B0\uC120)").option("--workflow <name>", "\uCD08\uAE30 \uC6CC\uD06C\uD50C\uB85C\uC6B0 \uC774\uB984 \uB610\uB294 class").option("--milestone <name>", "\uB9C8\uC77C\uC2A4\uD1A4 \uC774\uB984").option("--dry-run", "API \uD638\uCD9C \uC5C6\uC774 \uD569\uC131\uB41C \uBCF8\uBB38\uB9CC stdout \uCD9C\uB825 (mention/link-task \uC801\uC6A9 \uACB0\uACFC \uBBF8\uB9AC\uBCF4\uAE30)").action(async (project, opts) => {
3455
3458
  const globalOpts = postCreateCommand.optsWithGlobals();
3456
3459
  const config = await getConfigOrThrow();
3457
3460
  const client = new DoorayApiClient(config.apiKey, config.baseUrl);
@@ -4034,7 +4037,36 @@ var commentEditCommand = new import_commander22.Command("edit").description("\uB
4034
4037
 
4035
4038
  // src/commands/post/comment/delete.ts
4036
4039
  var import_commander23 = require("commander");
4037
- var commentDeleteCommand = new import_commander23.Command("delete").description("\uB313\uAE00 \uC0AD\uC81C").argument("[arg1]", "\uD504\uB85C\uC81D\uD2B8 \uCF54\uB4DC / Dooray URL / \uB313\uAE00 ID (\uBAA8\uB4DC\uBCC4)").argument("[arg2]", "\uC5C5\uBB34 \uBC88\uD638 \uB610\uB294 \uB313\uAE00 ID (\uBAA8\uB4DC\uBCC4)").argument("[arg3]", "\uB313\uAE00 ID (positional 3\uAC1C \uBAA8\uB4DC)").option("--id <postId>", "Dooray post ID (project/post-number \uB300\uC2E0)").option("--url <url>", "Dooray \uC5C5\uBB34 URL (project/post-number \uB300\uC2E0)").option("--comment-id <commentId>", "\uB313\uAE00 ID (positional \uB300\uCCB4)").action(async (arg1, arg2, arg3, opts) => {
4040
+
4041
+ // src/utils/delete-confirmation.ts
4042
+ async function authorizeDeletion(skipConfirmation, isTTY, confirmDeletion) {
4043
+ if (skipConfirmation) {
4044
+ return true;
4045
+ }
4046
+ if (!isTTY) {
4047
+ throw new DoorayCliError(
4048
+ "non-TTY \uD658\uACBD\uC5D0\uC11C\uB294 \uC0AD\uC81C \uD655\uC778\uC744 \uC9C4\uD589\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. --yes(-y) \uD50C\uB798\uADF8\uB85C \uB2E4\uC2DC \uC2E4\uD589\uD558\uC138\uC694.",
4049
+ EXIT_PARAM_ERROR
4050
+ );
4051
+ }
4052
+ return confirmDeletion();
4053
+ }
4054
+ async function promptDeletion(message) {
4055
+ const { confirm: confirm2 } = await import("@inquirer/prompts");
4056
+ return confirm2({ message, default: false });
4057
+ }
4058
+
4059
+ // src/commands/post/comment/delete.ts
4060
+ var commentDeleteCommand = new import_commander23.Command("delete").description("\uB313\uAE00 \uC0AD\uC81C").argument("[arg1]", "\uD504\uB85C\uC81D\uD2B8 \uCF54\uB4DC / Dooray URL / \uB313\uAE00 ID (\uBAA8\uB4DC\uBCC4)").argument("[arg2]", "\uC5C5\uBB34 \uBC88\uD638 \uB610\uB294 \uB313\uAE00 ID (\uBAA8\uB4DC\uBCC4)").argument("[arg3]", "\uB313\uAE00 ID (positional 3\uAC1C \uBAA8\uB4DC)").option("--id <postId>", "Dooray post ID (project/post-number \uB300\uC2E0)").option("--url <url>", "Dooray \uC5C5\uBB34 URL (project/post-number \uB300\uC2E0)").option("--comment-id <commentId>", "\uB313\uAE00 ID (positional \uB300\uCCB4)").option("-y, --yes", "\uD655\uC778 \uC5C6\uC774 \uC0AD\uC81C (\uC790\uB3D9\uD654\uC6A9)").action(async (arg1, arg2, arg3, opts) => {
4061
+ const confirmed = await authorizeDeletion(
4062
+ !!opts.yes,
4063
+ !!process.stdin.isTTY,
4064
+ () => promptDeletion("\uC5C5\uBB34 \uB313\uAE00\uC744 \uC601\uAD6C \uC0AD\uC81C\uD560\uAE4C\uC694?")
4065
+ );
4066
+ if (!confirmed) {
4067
+ process.stderr.write("\uCDE8\uC18C\uB418\uC5C8\uC2B5\uB2C8\uB2E4.\n");
4068
+ return;
4069
+ }
4038
4070
  const config = await getConfigOrThrow();
4039
4071
  const client = new DoorayApiClient(config.apiKey, config.baseUrl);
4040
4072
  let projectArg;
@@ -4092,6 +4124,14 @@ var commentDeleteCommand = new import_commander23.Command("delete").description(
4092
4124
  // src/commands/post/comment/get.ts
4093
4125
  var import_commander24 = require("commander");
4094
4126
 
4127
+ // src/utils/format-size.ts
4128
+ function formatSize(bytes) {
4129
+ if (bytes == null) return "-";
4130
+ if (bytes < 1024) return `${bytes}B`;
4131
+ if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)}KB`;
4132
+ return `${(bytes / (1024 * 1024)).toFixed(1)}MB`;
4133
+ }
4134
+
4095
4135
  // src/formatters/comment.ts
4096
4136
  function formatCommentDetail(comment, opts) {
4097
4137
  if (opts.json) {
@@ -4115,7 +4155,7 @@ function formatCommentDetail(comment, opts) {
4115
4155
  process.stdout.write("\n[Attachments]\n");
4116
4156
  printTable(
4117
4157
  ["Name", "Size", "ID"],
4118
- comment.files.map((f) => [f.name, String(f.size), f.id])
4158
+ comment.files.map((f) => [f.name ?? "-", formatSize(f.size), f.id])
4119
4159
  );
4120
4160
  }
4121
4161
  }
@@ -4271,13 +4311,54 @@ async function resolveCommentFileInput(client, args) {
4271
4311
  return { projectId, postId, commentId, secondary };
4272
4312
  }
4273
4313
 
4314
+ // src/utils/comment-file-merge.ts
4315
+ function labelOrNull(label) {
4316
+ if (label === void 0 || label.length === 0) return null;
4317
+ return label;
4318
+ }
4319
+ function mergeCommentFiles(input2) {
4320
+ const postFilesById = new Map(input2.postFiles.map((file) => [file.id, file]));
4321
+ const bodyRefsById = /* @__PURE__ */ new Map();
4322
+ for (const reference of input2.bodyRefs) {
4323
+ if (!bodyRefsById.has(reference.id)) bodyRefsById.set(reference.id, reference);
4324
+ }
4325
+ const merged = [];
4326
+ const seen = /* @__PURE__ */ new Set();
4327
+ for (const commentFile of input2.commentFiles) {
4328
+ if (seen.has(commentFile.id)) continue;
4329
+ seen.add(commentFile.id);
4330
+ const postFile = postFilesById.get(commentFile.id);
4331
+ const bodyRef = bodyRefsById.get(commentFile.id);
4332
+ merged.push({
4333
+ id: commentFile.id,
4334
+ name: postFile?.name ?? commentFile.name ?? labelOrNull(bodyRef?.label),
4335
+ size: postFile?.size ?? null,
4336
+ mimeType: postFile?.mimeType ?? null,
4337
+ source: bodyRef ? "both" : "attachment"
4338
+ });
4339
+ }
4340
+ for (const bodyRef of input2.bodyRefs) {
4341
+ if (seen.has(bodyRef.id)) continue;
4342
+ seen.add(bodyRef.id);
4343
+ const postFile = postFilesById.get(bodyRef.id);
4344
+ merged.push({
4345
+ id: bodyRef.id,
4346
+ name: postFile?.name ?? labelOrNull(bodyRef.label),
4347
+ size: postFile?.size ?? null,
4348
+ mimeType: postFile?.mimeType ?? null,
4349
+ source: "body-link"
4350
+ });
4351
+ }
4352
+ return merged;
4353
+ }
4354
+
4274
4355
  // src/commands/post/comment/file/list.ts
4275
- function formatSize(bytes) {
4276
- if (bytes < 1024) return `${bytes}B`;
4277
- if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)}KB`;
4278
- return `${(bytes / (1024 * 1024)).toFixed(1)}MB`;
4356
+ function formatSource(source) {
4357
+ if (source === "attachment") return "\uCCA8\uBD80";
4358
+ if (source === "body-link") return "\uBCF8\uBB38 \uB9C1\uD06C";
4359
+ return "\uB458 \uB2E4";
4279
4360
  }
4280
- var listCommentFileCommand = new import_commander25.Command("list").description("\uB313\uAE00 \uCCA8\uBD80 \uD30C\uC77C \uBAA9\uB85D \uC870\uD68C").argument("[arg1]", "\uD504\uB85C\uC81D\uD2B8 \uCF54\uB4DC, Dooray URL, \uB610\uB294 (`--id`/`--url` \uBAA8\uB4DC\uC77C \uB54C) \uB313\uAE00 ID").argument("[arg2]", "\uC5C5\uBB34 \uBC88\uD638 (positional \uBAA8\uB4DC)").argument("[arg3]", "\uB313\uAE00 ID (positional 3\uAC1C \uBAA8\uB4DC)").option("--id <postId>", "Dooray post ID (project/post-number \uB300\uC2E0)").option("--url <url>", "Dooray \uC5C5\uBB34 URL (project/post-number \uB300\uC2E0)").option("--comment-id <logId>", "\uB313\uAE00 ID (positional \uB300\uCCB4)").action(async (arg1, arg2, arg3, opts) => {
4361
+ var listCommentFileCommand = new import_commander25.Command("list").description("\uB313\uAE00 \uCCA8\uBD80 \uD30C\uC77C\uACFC \uBCF8\uBB38 \uD30C\uC77C \uB9C1\uD06C \uBAA9\uB85D \uD1B5\uD569 \uC870\uD68C").argument("[arg1]", "\uD504\uB85C\uC81D\uD2B8 \uCF54\uB4DC, Dooray URL, \uB610\uB294 (`--id`/`--url` \uBAA8\uB4DC\uC77C \uB54C) \uB313\uAE00 ID").argument("[arg2]", "\uC5C5\uBB34 \uBC88\uD638 (positional \uBAA8\uB4DC)").argument("[arg3]", "\uB313\uAE00 ID (positional 3\uAC1C \uBAA8\uB4DC)").option("--id <postId>", "Dooray post ID (project/post-number \uB300\uC2E0)").option("--url <url>", "Dooray \uC5C5\uBB34 URL (project/post-number \uB300\uC2E0)").option("--comment-id <logId>", "\uB313\uAE00 ID (positional \uB300\uCCB4)").action(async (arg1, arg2, arg3, opts) => {
4281
4362
  const globalOpts = listCommentFileCommand.optsWithGlobals();
4282
4363
  const config = await getConfigOrThrow();
4283
4364
  const client = new DoorayApiClient(config.apiKey, config.baseUrl);
@@ -4291,23 +4372,50 @@ var listCommentFileCommand = new import_commander25.Command("list").description(
4291
4372
  requireSecondary: false
4292
4373
  });
4293
4374
  startSpinner("\uB313\uAE00 \uCCA8\uBD80 \uD30C\uC77C \uBAA9\uB85D \uC870\uD68C \uC911...");
4294
- const res = await client.getPostComment(projectId, postId, commentId);
4295
- const files = res.result.files ?? [];
4296
- stopSpinner(true, `\uCCA8\uBD80 \uD30C\uC77C ${files.length}\uAC1C`);
4297
- if (files.length === 0) {
4298
- if (globalOpts.json) {
4299
- process.stdout.write("[]\n");
4300
- } else if (!globalOpts.quiet) {
4301
- process.stdout.write("\uCCA8\uBD80 \uC5C6\uC74C\n");
4375
+ try {
4376
+ const res = await client.getPostComment(projectId, postId, commentId);
4377
+ const commentFiles = res.result.files ?? [];
4378
+ const bodyRefs = extractAttachmentReferences(res.result.body.content);
4379
+ if (commentFiles.length === 0 && bodyRefs.length === 0) {
4380
+ stopSpinner(true, "\uCCA8\uBD80 \uD30C\uC77C 0\uAC1C");
4381
+ if (globalOpts.json) {
4382
+ process.stdout.write("[]\n");
4383
+ } else if (!globalOpts.quiet) {
4384
+ process.stdout.write("\uCCA8\uBD80 \uC5C6\uC74C\n");
4385
+ }
4386
+ return;
4302
4387
  }
4303
- return;
4388
+ let postFiles = [];
4389
+ let metadataLookupError = null;
4390
+ try {
4391
+ const postFilesRes = await client.getPostFiles(projectId, postId);
4392
+ postFiles = postFilesRes.result;
4393
+ } catch (error) {
4394
+ metadataLookupError = error instanceof Error ? error.message : String(error);
4395
+ }
4396
+ const merged = mergeCommentFiles({ commentFiles, bodyRefs, postFiles });
4397
+ stopSpinner(true, `\uCCA8\uBD80 \uD30C\uC77C ${merged.length}\uAC1C`);
4398
+ if (metadataLookupError !== null) {
4399
+ process.stderr.write(
4400
+ `\u26A0 \uC5C5\uBB34 \uCCA8\uBD80 \uC774\uB984\xB7\uD06C\uAE30 \uBCF4\uAC15 \uC2E4\uD328 (${metadataLookupError}) \u2014 \uC77C\uBD80 \uD56D\uBAA9\uC758 \uD30C\uC77C\uBA85\xB7\uD06C\uAE30\uAC00 \uBE44\uC5B4 \uC788\uC2B5\uB2C8\uB2E4.
4401
+ `
4402
+ );
4403
+ }
4404
+ output(globalOpts, {
4405
+ headers: ["\uD30C\uC77C\uBA85", "\uD06C\uAE30", "\uCD9C\uCC98", "ID"],
4406
+ rows: merged.map((file) => [
4407
+ file.name == null ? "-" : sanitizeFileName(file.name),
4408
+ formatSize(file.size),
4409
+ formatSource(file.source),
4410
+ file.id
4411
+ ]),
4412
+ raw: merged,
4413
+ ids: merged.map((file) => file.id)
4414
+ });
4415
+ } catch (error) {
4416
+ stopSpinner(false);
4417
+ throw error;
4304
4418
  }
4305
- output(globalOpts, {
4306
- headers: ["\uD30C\uC77C\uBA85", "\uD06C\uAE30", "ID"],
4307
- rows: files.map((f) => [f.name, formatSize(f.size), f.id]),
4308
- raw: files,
4309
- ids: files.map((f) => f.id)
4310
- });
4311
4419
  });
4312
4420
 
4313
4421
  // src/commands/post/comment/file/upload.ts
@@ -4315,9 +4423,11 @@ var import_commander26 = require("commander");
4315
4423
  var import_node_path7 = require("path");
4316
4424
 
4317
4425
  // src/utils/comment-files.ts
4426
+ var IMAGE_FILE_EXTENSION_RE = /\.(png|jpe?g|gif|webp|bmp|svg|avif|heic)$/i;
4318
4427
  function appendFileReference(body, fileName, fileId) {
4319
4428
  const safeName = fileName.replace(/[\[\]]/g, "");
4320
- const ref = `![${safeName}](/files/${fileId})`;
4429
+ const prefix = IMAGE_FILE_EXTENSION_RE.test(safeName) ? "!" : "";
4430
+ const ref = `${prefix}[${safeName}](/files/${fileId})`;
4321
4431
  if (body.length === 0) return ref;
4322
4432
  const trailing = body.endsWith("\n") ? "" : "\n";
4323
4433
  return `${body}${trailing}
@@ -4325,14 +4435,14 @@ ${ref}`;
4325
4435
  }
4326
4436
  function removeFileReference(body, fileId) {
4327
4437
  const escaped = fileId.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
4328
- const refSource = `!\\[[^\\]]*\\]\\(/files/${escaped}\\)`;
4438
+ const refSource = `!?\\[[^\\]]*\\]\\(/files/${escaped}\\)`;
4329
4439
  const lineRe = new RegExp(`^[ \\t]*${refSource}[ \\t]*$\\n?`, "gm");
4330
4440
  const inlineRe = new RegExp(refSource, "g");
4331
4441
  return body.replace(lineRe, "").replace(inlineRe, "");
4332
4442
  }
4333
4443
 
4334
4444
  // src/commands/post/comment/file/upload.ts
4335
- var uploadCommentFileCommand = new import_commander26.Command("upload").description("\uB313\uAE00\uC5D0 \uD30C\uC77C \uC5C5\uB85C\uB4DC (post-level \uD30C\uC77C \uC5C5\uB85C\uB4DC + \uB313\uAE00 reference \uCD94\uAC00, ADR-024)").argument("[arg1]", "\uD504\uB85C\uC81D\uD2B8 \uCF54\uB4DC, Dooray URL, \uB610\uB294 (`--id`/`--url` \uBAA8\uB4DC\uC77C \uB54C) \uB313\uAE00 ID").argument("[arg2]", "\uC5C5\uBB34 \uBC88\uD638 \uB610\uB294 (`--id`/`--url` \uBAA8\uB4DC\uC77C \uB54C) \uD30C\uC77C \uACBD\uB85C").argument("[arg3]", "\uB313\uAE00 ID (positional \uBAA8\uB4DC)").argument("[arg4]", "\uD30C\uC77C \uACBD\uB85C (positional \uBAA8\uB4DC)").option("--id <postId>", "Dooray post ID (project/post-number \uB300\uC2E0)").option("--url <url>", "Dooray \uC5C5\uBB34 URL (project/post-number \uB300\uC2E0)").option("--comment-id <logId>", "\uB313\uAE00 ID (positional \uB300\uCCB4)").option("--file <path>", "\uC5C5\uB85C\uB4DC\uD560 \uD30C\uC77C \uACBD\uB85C (positional \uB300\uCCB4)").action(async (arg1, arg2, arg3, arg4, opts) => {
4445
+ var uploadCommentFileCommand = new import_commander26.Command("upload").description("\uB313\uAE00\uC5D0 \uD30C\uC77C \uC5C5\uB85C\uB4DC (\uCCA8\uBD80 \uCE74\uB4DC\uAC00 \uC544\uB2CC \uBCF8\uBB38 \uB9C1\uD06C\uB85C \uD45C\uC2DC)").argument("[arg1]", "\uD504\uB85C\uC81D\uD2B8 \uCF54\uB4DC, Dooray URL, \uB610\uB294 (`--id`/`--url` \uBAA8\uB4DC\uC77C \uB54C) \uB313\uAE00 ID").argument("[arg2]", "\uC5C5\uBB34 \uBC88\uD638 \uB610\uB294 (`--id`/`--url` \uBAA8\uB4DC\uC77C \uB54C) \uD30C\uC77C \uACBD\uB85C").argument("[arg3]", "\uB313\uAE00 ID (positional \uBAA8\uB4DC)").argument("[arg4]", "\uD30C\uC77C \uACBD\uB85C (positional \uBAA8\uB4DC)").option("--id <postId>", "Dooray post ID (project/post-number \uB300\uC2E0)").option("--url <url>", "Dooray \uC5C5\uBB34 URL (project/post-number \uB300\uC2E0)").option("--comment-id <logId>", "\uB313\uAE00 ID (positional \uB300\uCCB4)").option("--file <path>", "\uC5C5\uB85C\uB4DC\uD560 \uD30C\uC77C \uACBD\uB85C (positional \uB300\uCCB4)").action(async (arg1, arg2, arg3, arg4, opts) => {
4336
4446
  const globalOpts = uploadCommentFileCommand.optsWithGlobals();
4337
4447
  const config = await getConfigOrThrow();
4338
4448
  const client = new DoorayApiClient(config.apiKey, config.baseUrl);
@@ -4413,7 +4523,18 @@ var downloadCommentFileCommand = new import_commander27.Command("download").desc
4413
4523
 
4414
4524
  // src/commands/post/comment/file/delete.ts
4415
4525
  var import_commander28 = require("commander");
4416
- var deleteCommentFileCommand = new import_commander28.Command("delete").description("\uB313\uAE00 \uCCA8\uBD80 \uD30C\uC77C \uC0AD\uC81C (\uB313\uAE00 \uBCF8\uBB38 reference \uC81C\uAC70 + \uD30C\uC77C \uC0AD\uC81C, ADR-024)").argument("[arg1]", "\uD504\uB85C\uC81D\uD2B8 \uCF54\uB4DC, Dooray URL, \uB610\uB294 (`--id`/`--url` \uBAA8\uB4DC\uC77C \uB54C) \uB313\uAE00 ID").argument("[arg2]", "\uC5C5\uBB34 \uBC88\uD638 \uB610\uB294 (`--id`/`--url` \uBAA8\uB4DC\uC77C \uB54C) \uD30C\uC77C ID").argument("[arg3]", "\uB313\uAE00 ID (positional \uBAA8\uB4DC)").argument("[arg4]", "\uD30C\uC77C ID (positional \uBAA8\uB4DC)").option("--id <postId>", "Dooray post ID (project/post-number \uB300\uC2E0)").option("--url <url>", "Dooray \uC5C5\uBB34 URL (project/post-number \uB300\uC2E0)").option("--comment-id <logId>", "\uB313\uAE00 ID (positional \uB300\uCCB4)").option("--file-id <fileId>", "\uD30C\uC77C ID (positional \uB300\uCCB4)").option("--yes", "\uD655\uC778 \uC5C6\uC774 \uC0AD\uC81C").action(async (arg1, arg2, arg3, arg4, opts) => {
4526
+ var deleteCommentFileCommand = new import_commander28.Command("delete").description("\uB313\uAE00 \uCCA8\uBD80 \uD30C\uC77C \uC0AD\uC81C (\uBCF8\uBB38 reference \uC81C\uAC70\uC640 \uD30C\uC77C \uC0AD\uC81C\uB97C \uD568\uAED8 \uC218\uD589)").argument("[arg1]", "\uD504\uB85C\uC81D\uD2B8 \uCF54\uB4DC, Dooray URL, \uB610\uB294 (`--id`/`--url` \uBAA8\uB4DC\uC77C \uB54C) \uB313\uAE00 ID").argument("[arg2]", "\uC5C5\uBB34 \uBC88\uD638 \uB610\uB294 (`--id`/`--url` \uBAA8\uB4DC\uC77C \uB54C) \uD30C\uC77C ID").argument("[arg3]", "\uB313\uAE00 ID (positional \uBAA8\uB4DC)").argument("[arg4]", "\uD30C\uC77C ID (positional \uBAA8\uB4DC)").option("--id <postId>", "Dooray post ID (project/post-number \uB300\uC2E0)").option("--url <url>", "Dooray \uC5C5\uBB34 URL (project/post-number \uB300\uC2E0)").option("--comment-id <logId>", "\uB313\uAE00 ID (positional \uB300\uCCB4)").option("--file-id <fileId>", "\uD30C\uC77C ID (positional \uB300\uCCB4)").option("-y, --yes", "\uD655\uC778 \uC5C6\uC774 \uC0AD\uC81C (\uC790\uB3D9\uD654\uC6A9)").action(async (arg1, arg2, arg3, arg4, opts) => {
4527
+ const confirmed = await authorizeDeletion(
4528
+ !!opts.yes,
4529
+ !!process.stdin.isTTY,
4530
+ () => promptDeletion(
4531
+ "\uB313\uAE00 \uBCF8\uBB38\uC5D0\uC11C \uD30C\uC77C reference\uB97C \uC81C\uAC70\uD558\uACE0 \uCCA8\uBD80 \uD30C\uC77C\uC744 \uC0AD\uC81C\uD569\uB2C8\uB2E4. \uAC19\uC740 \uC5C5\uBB34\uC758 \uB2E4\uB978 \uB313\uAE00\uC774 \uAC19\uC740 \uD30C\uC77C ID\uB97C \uCC38\uC870\uD558\uBA74 \uB9C1\uD06C\uAC00 \uAE68\uC9C8 \uC218 \uC788\uC2B5\uB2C8\uB2E4. \uACC4\uC18D\uD558\uC2DC\uACA0\uC2B5\uB2C8\uAE4C?"
4532
+ )
4533
+ );
4534
+ if (!confirmed) {
4535
+ process.stderr.write("\uCDE8\uC18C\uB418\uC5C8\uC2B5\uB2C8\uB2E4.\n");
4536
+ return;
4537
+ }
4417
4538
  const config = await getConfigOrThrow();
4418
4539
  const client = new DoorayApiClient(config.apiKey, config.baseUrl);
4419
4540
  const { projectId, postId, commentId, secondary: fileId } = await resolveCommentFileInput(client, {
@@ -4428,16 +4549,6 @@ var deleteCommentFileCommand = new import_commander28.Command("delete").descript
4428
4549
  requireSecondary: true,
4429
4550
  secondaryLabel: FILE_ID_SECONDARY_LABEL
4430
4551
  });
4431
- if (!opts.yes) {
4432
- const { confirm: confirm2 } = await import("@inquirer/prompts");
4433
- const ok = await confirm2({
4434
- message: `\uB313\uAE00 \uBCF8\uBB38\uC5D0\uC11C reference \uB97C \uC81C\uAC70\uD558\uACE0 \uD30C\uC77C(${fileId})\uC744 \uC0AD\uC81C\uD569\uB2C8\uB2E4. \uAC19\uC740 post \uC758 \uB2E4\uB978 \uB313\uAE00\uC5D0\uC11C \uAC19\uC740 \uD30C\uC77C ID \uB97C \uCC38\uC870\uD558\uB294 \uACBD\uC6B0 broken \uB429\uB2C8\uB2E4. \uACC4\uC18D\uD558\uC2DC\uACA0\uC2B5\uB2C8\uAE4C?`
4435
- });
4436
- if (!ok) {
4437
- process.stdout.write("\uCDE8\uC18C\uB418\uC5C8\uC2B5\uB2C8\uB2E4.\n");
4438
- return;
4439
- }
4440
- }
4441
4552
  startSpinner("\uB313\uAE00 \uBCF8\uBB38 reference \uC81C\uAC70 \uC911...");
4442
4553
  try {
4443
4554
  const commentRes = await client.getPostComment(projectId, postId, commentId);
@@ -4470,15 +4581,10 @@ var deleteCommentFileCommand = new import_commander28.Command("delete").descript
4470
4581
  });
4471
4582
 
4472
4583
  // src/commands/post/comment/file/index.ts
4473
- var commentFileCommand = new import_commander29.Command("file").description("\uB313\uAE00 \uCCA8\uBD80 \uD30C\uC77C \uAD00\uB9AC (post-level files API + \uB313\uAE00 PUT \uD569\uC131, ADR-024)").addCommand(listCommentFileCommand).addCommand(uploadCommentFileCommand).addCommand(downloadCommentFileCommand).addCommand(deleteCommentFileCommand);
4584
+ var commentFileCommand = new import_commander29.Command("file").description("\uB313\uAE00 \uCCA8\uBD80 \uD30C\uC77C \uAD00\uB9AC").addCommand(listCommentFileCommand).addCommand(uploadCommentFileCommand).addCommand(downloadCommentFileCommand).addCommand(deleteCommentFileCommand);
4474
4585
 
4475
4586
  // src/commands/post/file/list.ts
4476
4587
  var import_commander30 = require("commander");
4477
- function formatSize2(bytes) {
4478
- if (bytes < 1024) return `${bytes}B`;
4479
- if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)}KB`;
4480
- return `${(bytes / (1024 * 1024)).toFixed(1)}MB`;
4481
- }
4482
4588
  var fileListCommand = new import_commander30.Command("list").description("\uC5C5\uBB34 \uCCA8\uBD80\uD30C\uC77C \uBAA9\uB85D \uC870\uD68C").argument("[project]", "\uD504\uB85C\uC81D\uD2B8 \uCF54\uB4DC (\uB610\uB294 \uCCAB \uC778\uC790\uC5D0 Dooray URL)").argument("[post-number]", "\uC5C5\uBB34 \uBC88\uD638 (project\uC640 \uD568\uAED8 \uC0AC\uC6A9)").option("--id <postId>", "Dooray post ID (project/post-number \uB300\uC2E0)").option("--url <url>", "Dooray \uC5C5\uBB34 URL (project/post-number \uB300\uC2E0)").action(async (project, postNumberStr, opts) => {
4483
4589
  const globalOpts = fileListCommand.optsWithGlobals();
4484
4590
  const config = await getConfigOrThrow();
@@ -4497,7 +4603,7 @@ var fileListCommand = new import_commander30.Command("list").description("\uC5C5
4497
4603
  rows: res.result.map((f) => [
4498
4604
  f.id,
4499
4605
  f.name,
4500
- formatSize2(f.size),
4606
+ formatSize(f.size),
4501
4607
  f.mimeType,
4502
4608
  f.createdAt
4503
4609
  ]),
@@ -4725,7 +4831,16 @@ var fileUploadCommand = new import_commander33.Command("upload").description("\u
4725
4831
 
4726
4832
  // src/commands/post/file/delete.ts
4727
4833
  var import_commander34 = require("commander");
4728
- var fileDeleteCommand = new import_commander34.Command("delete").description("\uCCA8\uBD80\uD30C\uC77C \uC0AD\uC81C").argument("[arg1]", "\uD504\uB85C\uC81D\uD2B8 \uCF54\uB4DC, Dooray URL, \uB610\uB294 (`--id`/`--url` \uBAA8\uB4DC\uC77C \uB54C) \uD30C\uC77C ID").argument("[arg2]", "\uC5C5\uBB34 \uBC88\uD638 \uB610\uB294 (`--id`/`--url` \uBAA8\uB4DC\uC77C \uB54C) \uD30C\uC77C ID").argument("[arg3]", "\uD30C\uC77C ID (positional 3\uAC1C \uBAA8\uB4DC)").option("--id <postId>", "Dooray post ID (project/post-number \uB300\uC2E0)").option("--url <url>", "Dooray \uC5C5\uBB34 URL (project/post-number \uB300\uC2E0)").option("--file-id <fileId>", "\uD30C\uC77C ID (positional \uB300\uCCB4)").action(async (arg1, arg2, arg3, opts) => {
4834
+ var fileDeleteCommand = new import_commander34.Command("delete").description("\uCCA8\uBD80\uD30C\uC77C \uC0AD\uC81C").argument("[arg1]", "\uD504\uB85C\uC81D\uD2B8 \uCF54\uB4DC, Dooray URL, \uB610\uB294 (`--id`/`--url` \uBAA8\uB4DC\uC77C \uB54C) \uD30C\uC77C ID").argument("[arg2]", "\uC5C5\uBB34 \uBC88\uD638 \uB610\uB294 (`--id`/`--url` \uBAA8\uB4DC\uC77C \uB54C) \uD30C\uC77C ID").argument("[arg3]", "\uD30C\uC77C ID (positional 3\uAC1C \uBAA8\uB4DC)").option("--id <postId>", "Dooray post ID (project/post-number \uB300\uC2E0)").option("--url <url>", "Dooray \uC5C5\uBB34 URL (project/post-number \uB300\uC2E0)").option("--file-id <fileId>", "\uD30C\uC77C ID (positional \uB300\uCCB4)").option("-y, --yes", "\uD655\uC778 \uC5C6\uC774 \uC0AD\uC81C (\uC790\uB3D9\uD654\uC6A9)").action(async (arg1, arg2, arg3, opts) => {
4835
+ const confirmed = await authorizeDeletion(
4836
+ !!opts.yes,
4837
+ !!process.stdin.isTTY,
4838
+ () => promptDeletion("\uC5C5\uBB34\uC758 \uCCA8\uBD80 \uD30C\uC77C\uC744 \uC601\uAD6C \uC0AD\uC81C\uD560\uAE4C\uC694?")
4839
+ );
4840
+ if (!confirmed) {
4841
+ process.stderr.write("\uCDE8\uC18C\uB418\uC5C8\uC2B5\uB2C8\uB2E4.\n");
4842
+ return;
4843
+ }
4729
4844
  const globalOpts = fileDeleteCommand.optsWithGlobals();
4730
4845
  const config = await getConfigOrThrow();
4731
4846
  const client = new DoorayApiClient(config.apiKey, config.baseUrl);
@@ -5057,7 +5172,7 @@ var wikiPageEditCommand = new import_commander40.Command("edit").description("\u
5057
5172
  var import_commander41 = require("commander");
5058
5173
 
5059
5174
  // src/resolvers/wiki-page-input.ts
5060
- var INPUT_HELP2 = "\uC704\uD0A4 \uD398\uC774\uC9C0\uB97C \uC2DD\uBCC4\uD560 \uC815\uBCF4\uAC00 \uBD80\uC871\uD569\uB2C8\uB2E4. \uB2E4\uC74C \uC911 \uD558\uB098\uB97C \uC785\uB825\uD558\uC138\uC694:\n - <project> <page-id> \uC608: my-project 4071828729722696495\n - --id <page-id> --project <project> (\uB610\uB294 --url)\n - <Dooray URL> \uC608: https://x.dooray.com/wiki/<wikiId>/<pageId>";
5175
+ var INPUT_HELP2 = "\uC704\uD0A4 \uD398\uC774\uC9C0\uB97C \uC2DD\uBCC4\uD560 \uC815\uBCF4\uAC00 \uBD80\uC871\uD569\uB2C8\uB2E4. \uB2E4\uC74C \uC911 \uD558\uB098\uB97C \uC785\uB825\uD558\uC138\uC694:\n - <project> <page-id> \uC608: my-project 9876543210987654321\n - --id <page-id> --project <project> (\uB610\uB294 --url)\n - <Dooray URL> \uC608: https://x.dooray.com/wiki/<wikiId>/<pageId>";
5061
5176
  async function resolveWikiPageInput(client, args) {
5062
5177
  const { projectArg, pageIdArg, idOpt, urlOpt, project } = args;
5063
5178
  const hasPositional = !!projectArg || !!pageIdArg;
@@ -5112,6 +5227,17 @@ async function resolveWikiPageInput(client, args) {
5112
5227
 
5113
5228
  // src/commands/wiki/page-delete.ts
5114
5229
  var wikiPageDeleteCommand = new import_commander41.Command("delete").description("\uC704\uD0A4 \uD398\uC774\uC9C0 \uC0AD\uC81C (\uBE44\uACF5\uC2DD endpoint)").argument("[arg1]", "\uD504\uB85C\uC81D\uD2B8 \uCF54\uB4DC, Dooray Wiki URL, \uB610\uB294 (`--id`/`--url` \uBAA8\uB4DC\uC77C \uB54C) \uBBF8\uC0AC\uC6A9").argument("[arg2]", "page-id (positional 2\uAC1C \uBAA8\uB4DC)").option("--id <pageId>", "\uC704\uD0A4 \uD398\uC774\uC9C0 ID").option("--url <url>", "Dooray Wiki URL").option("--project <code>", "\uD504\uB85C\uC81D\uD2B8 \uCF54\uB4DC (--id \uBAA8\uB4DC\uC5D0\uC11C wikiId \uD574\uC11D\uC6A9)").option("-y, --yes", "\uD655\uC778 \uC5C6\uC774 \uC0AD\uC81C (\uC790\uB3D9\uD654\uC6A9)").action(async (arg1, arg2, opts) => {
5230
+ const confirmed = await authorizeDeletion(
5231
+ !!opts.yes,
5232
+ !!process.stdin.isTTY,
5233
+ () => promptDeletion(
5234
+ "\uC704\uD0A4 \uD398\uC774\uC9C0\uB97C \uC0AD\uC81C\uD560\uAE4C\uC694? \uD558\uC704 \uD398\uC774\uC9C0\uAC00 \uC788\uC73C\uBA74 \uC0AD\uC81C\uD55C \uD398\uC774\uC9C0\uC758 \uBD80\uBAA8 \uC544\uB798\uB85C \uC7AC\uBD80\uCC29\uB429\uB2C8\uB2E4."
5235
+ )
5236
+ );
5237
+ if (!confirmed) {
5238
+ process.stderr.write("\uCDE8\uC18C\uB418\uC5C8\uC2B5\uB2C8\uB2E4.\n");
5239
+ return;
5240
+ }
5115
5241
  const globalOpts = wikiPageDeleteCommand.optsWithGlobals();
5116
5242
  const config = await getConfigOrThrow();
5117
5243
  const client = new DoorayApiClient(config.apiKey, config.baseUrl);
@@ -5122,25 +5248,6 @@ var wikiPageDeleteCommand = new import_commander41.Command("delete").description
5122
5248
  urlOpt: opts.url,
5123
5249
  project: opts.project
5124
5250
  });
5125
- if (!opts.yes) {
5126
- if (!process.stdin.isTTY) {
5127
- throw new DoorayCliError(
5128
- "non-TTY \uD658\uACBD\uC5D0\uC11C\uB294 \uD655\uC778 \uC5C6\uC774 \uC0AD\uC81C\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. --yes(-y) \uD50C\uB798\uADF8\uB85C \uB2E4\uC2DC \uC2E4\uD589\uD558\uC138\uC694.",
5129
- EXIT_PARAM_ERROR
5130
- );
5131
- }
5132
- const { confirm: confirm2 } = await import("@inquirer/prompts");
5133
- const ok = await confirm2({
5134
- message: `\uD398\uC774\uC9C0(${pageId})\uB97C \uC0AD\uC81C\uD560\uAE4C\uC694? \uD558\uC704 \uD398\uC774\uC9C0\uAC00 \uC788\uC73C\uBA74 \uC0C1\uC704 \uD398\uC774\uC9C0\uB85C \uC7AC\uBD80\uCC29\uB429\uB2C8\uB2E4.`,
5135
- default: false
5136
- });
5137
- if (!ok) {
5138
- if (!globalOpts.json && !globalOpts.quiet) {
5139
- process.stdout.write("\uCDE8\uC18C\uB418\uC5C8\uC2B5\uB2C8\uB2E4.\n");
5140
- }
5141
- return;
5142
- }
5143
- }
5144
5251
  startSpinner("\uD398\uC774\uC9C0 \uC0AD\uC81C \uC911...");
5145
5252
  try {
5146
5253
  await client.deleteWikiPage(wikiId, pageId);
@@ -5161,11 +5268,6 @@ var import_commander47 = require("commander");
5161
5268
 
5162
5269
  // src/commands/wiki/page-file/list.ts
5163
5270
  var import_commander42 = require("commander");
5164
- function formatSize3(bytes) {
5165
- if (bytes < 1024) return `${bytes}B`;
5166
- if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)}KB`;
5167
- return `${(bytes / (1024 * 1024)).toFixed(1)}MB`;
5168
- }
5169
5271
  var wikiPageFileListCommand = new import_commander42.Command("list").description("\uC704\uD0A4 \uD398\uC774\uC9C0 \uCCA8\uBD80\uD30C\uC77C \uBAA9\uB85D \uC870\uD68C (general + inline image)").argument("[project]", "\uD504\uB85C\uC81D\uD2B8 \uCF54\uB4DC (\uB610\uB294 \uCCAB \uC778\uC790\uC5D0 Dooray Wiki URL)").argument("[page-id]", "\uC704\uD0A4 \uD398\uC774\uC9C0 ID (project\uC640 \uD568\uAED8 \uC0AC\uC6A9)").option("--id <pageId>", "\uC704\uD0A4 \uD398\uC774\uC9C0 ID (--project \uB3D9\uBC18 \uD544\uC694)").option("--url <url>", "Dooray Wiki URL").option("--project <code>", "\uD504\uB85C\uC81D\uD2B8 \uCF54\uB4DC (--id \uBAA8\uB4DC\uC5D0\uC11C wikiId \uD574\uC11D\uC6A9)").action(async (project, pageIdArg, opts) => {
5170
5272
  const globalOpts = wikiPageFileListCommand.optsWithGlobals();
5171
5273
  const config = await getConfigOrThrow();
@@ -5186,7 +5288,7 @@ var wikiPageFileListCommand = new import_commander42.Command("list").description
5186
5288
  stopSpinner(true, `\uCCA8\uBD80\uD30C\uC77C ${merged.length}\uAC1C (general ${files.length} + inline ${images.length})`);
5187
5289
  output(globalOpts, {
5188
5290
  headers: ["ID", "Type", "\uD30C\uC77C\uBA85", "\uD06C\uAE30"],
5189
- rows: merged.map((f) => [f.id, f.type, f.name, formatSize3(f.size)]),
5291
+ rows: merged.map((f) => [f.id, f.type, f.name, formatSize(f.size)]),
5190
5292
  raw: merged,
5191
5293
  ids: merged.map((f) => f.id)
5192
5294
  });
@@ -5205,7 +5307,7 @@ function wikiInlineImageSnippet(wikiId, attachFileId, name) {
5205
5307
  }
5206
5308
 
5207
5309
  // src/commands/wiki/page-file/upload.ts
5208
- var wikiPageFileUploadCommand = new import_commander43.Command("upload").description("\uC704\uD0A4 \uD398\uC774\uC9C0 \uCCA8\uBD80\uD30C\uC77C \uC5C5\uB85C\uB4DC (multipart type \uC21C\uC11C \uAC15\uC81C, ADR-029)").argument("[arg1]", "\uD504\uB85C\uC81D\uD2B8 \uCF54\uB4DC, Dooray Wiki URL, \uB610\uB294 (`--id`/`--url` \uBAA8\uB4DC\uC77C \uB54C) \uD30C\uC77C \uACBD\uB85C").argument("[arg2]", "page-id \uB610\uB294 (`--id`/`--url` \uBAA8\uB4DC\uC77C \uB54C) \uD30C\uC77C \uACBD\uB85C").argument("[arg3]", "\uD30C\uC77C \uACBD\uB85C (positional 3\uAC1C \uBAA8\uB4DC)").option("--id <pageId>", "\uC704\uD0A4 \uD398\uC774\uC9C0 ID").option("--url <url>", "Dooray Wiki URL").option("--project <code>", "\uD504\uB85C\uC81D\uD2B8 \uCF54\uB4DC (--id \uBAA8\uB4DC\uC5D0\uC11C wikiId \uD574\uC11D\uC6A9)").option("--file <path>", "\uC5C5\uB85C\uB4DC\uD560 \uD30C\uC77C \uACBD\uB85C (positional \uB300\uCCB4)").option("--type <type>", "\uD30C\uC77C \uD0C0\uC785: general | inline_image (\uAE30\uBCF8 general)", "general").action(async (arg1, arg2, arg3, opts) => {
5310
+ var wikiPageFileUploadCommand = new import_commander43.Command("upload").description("\uC704\uD0A4 \uD398\uC774\uC9C0 \uCCA8\uBD80\uD30C\uC77C \uC5C5\uB85C\uB4DC").argument("[arg1]", "\uD504\uB85C\uC81D\uD2B8 \uCF54\uB4DC, Dooray Wiki URL, \uB610\uB294 (`--id`/`--url` \uBAA8\uB4DC\uC77C \uB54C) \uD30C\uC77C \uACBD\uB85C").argument("[arg2]", "page-id \uB610\uB294 (`--id`/`--url` \uBAA8\uB4DC\uC77C \uB54C) \uD30C\uC77C \uACBD\uB85C").argument("[arg3]", "\uD30C\uC77C \uACBD\uB85C (positional 3\uAC1C \uBAA8\uB4DC)").option("--id <pageId>", "\uC704\uD0A4 \uD398\uC774\uC9C0 ID").option("--url <url>", "Dooray Wiki URL").option("--project <code>", "\uD504\uB85C\uC81D\uD2B8 \uCF54\uB4DC (--id \uBAA8\uB4DC\uC5D0\uC11C wikiId \uD574\uC11D\uC6A9)").option("--file <path>", "\uC5C5\uB85C\uB4DC\uD560 \uD30C\uC77C \uACBD\uB85C (positional \uB300\uCCB4)").option("--type <type>", "\uD30C\uC77C \uD0C0\uC785: general | inline_image (\uAE30\uBCF8 general)", "general").action(async (arg1, arg2, arg3, opts) => {
5209
5311
  const globalOpts = wikiPageFileUploadCommand.optsWithGlobals();
5210
5312
  const config = await getConfigOrThrow();
5211
5313
  const client = new DoorayApiClient(config.apiKey, config.baseUrl);
@@ -5426,7 +5528,16 @@ var wikiPageFileDownloadAllCommand = new import_commander45.Command("download-al
5426
5528
 
5427
5529
  // src/commands/wiki/page-file/delete.ts
5428
5530
  var import_commander46 = require("commander");
5429
- var wikiPageFileDeleteCommand = new import_commander46.Command("delete").description("\uC704\uD0A4 \uD398\uC774\uC9C0 \uCCA8\uBD80\uD30C\uC77C \uC0AD\uC81C").argument("[arg1]", "\uD504\uB85C\uC81D\uD2B8 \uCF54\uB4DC, Dooray Wiki URL, \uB610\uB294 (`--id`/`--url` \uBAA8\uB4DC\uC77C \uB54C) \uD30C\uC77C ID").argument("[arg2]", "page-id \uB610\uB294 (`--id`/`--url` \uBAA8\uB4DC\uC77C \uB54C) \uD30C\uC77C ID").argument("[arg3]", "\uD30C\uC77C ID (positional 3\uAC1C \uBAA8\uB4DC)").option("--id <pageId>", "\uC704\uD0A4 \uD398\uC774\uC9C0 ID").option("--url <url>", "Dooray Wiki URL").option("--project <code>", "\uD504\uB85C\uC81D\uD2B8 \uCF54\uB4DC (--id \uBAA8\uB4DC\uC5D0\uC11C wikiId \uD574\uC11D\uC6A9)").option("--file-id <fileId>", "\uD30C\uC77C ID (positional \uB300\uCCB4)").action(async (arg1, arg2, arg3, opts) => {
5531
+ var wikiPageFileDeleteCommand = new import_commander46.Command("delete").description("\uC704\uD0A4 \uD398\uC774\uC9C0 \uCCA8\uBD80\uD30C\uC77C \uC0AD\uC81C").argument("[arg1]", "\uD504\uB85C\uC81D\uD2B8 \uCF54\uB4DC, Dooray Wiki URL, \uB610\uB294 (`--id`/`--url` \uBAA8\uB4DC\uC77C \uB54C) \uD30C\uC77C ID").argument("[arg2]", "page-id \uB610\uB294 (`--id`/`--url` \uBAA8\uB4DC\uC77C \uB54C) \uD30C\uC77C ID").argument("[arg3]", "\uD30C\uC77C ID (positional 3\uAC1C \uBAA8\uB4DC)").option("--id <pageId>", "\uC704\uD0A4 \uD398\uC774\uC9C0 ID").option("--url <url>", "Dooray Wiki URL").option("--project <code>", "\uD504\uB85C\uC81D\uD2B8 \uCF54\uB4DC (--id \uBAA8\uB4DC\uC5D0\uC11C wikiId \uD574\uC11D\uC6A9)").option("--file-id <fileId>", "\uD30C\uC77C ID (positional \uB300\uCCB4)").option("-y, --yes", "\uD655\uC778 \uC5C6\uC774 \uC0AD\uC81C (\uC790\uB3D9\uD654\uC6A9)").action(async (arg1, arg2, arg3, opts) => {
5532
+ const confirmed = await authorizeDeletion(
5533
+ !!opts.yes,
5534
+ !!process.stdin.isTTY,
5535
+ () => promptDeletion("\uC704\uD0A4 \uD398\uC774\uC9C0\uC758 \uCCA8\uBD80 \uD30C\uC77C\uC744 \uC601\uAD6C \uC0AD\uC81C\uD560\uAE4C\uC694?")
5536
+ );
5537
+ if (!confirmed) {
5538
+ process.stderr.write("\uCDE8\uC18C\uB418\uC5C8\uC2B5\uB2C8\uB2E4.\n");
5539
+ return;
5540
+ }
5430
5541
  const globalOpts = wikiPageFileDeleteCommand.optsWithGlobals();
5431
5542
  const config = await getConfigOrThrow();
5432
5543
  const client = new DoorayApiClient(config.apiKey, config.baseUrl);
@@ -5485,7 +5596,7 @@ var wikiPageFileDeleteCommand = new import_commander46.Command("delete").descrip
5485
5596
  });
5486
5597
 
5487
5598
  // src/commands/wiki/page-file/index.ts
5488
- var wikiPageFileCommand = new import_commander47.Command("file").description("\uC704\uD0A4 \uD398\uC774\uC9C0 \uCCA8\uBD80\uD30C\uC77C \uAD00\uB828 \uBA85\uB839 (Issue #70, ADR-029)");
5599
+ var wikiPageFileCommand = new import_commander47.Command("file").description("\uC704\uD0A4 \uD398\uC774\uC9C0 \uCCA8\uBD80\uD30C\uC77C \uAD00\uB828 \uBA85\uB839");
5489
5600
  wikiPageFileCommand.addCommand(wikiPageFileListCommand);
5490
5601
  wikiPageFileCommand.addCommand(wikiPageFileUploadCommand);
5491
5602
  wikiPageFileCommand.addCommand(wikiPageFileDownloadCommand);
@@ -5766,7 +5877,16 @@ var wikiPageCommentEditCommand = new import_commander52.Command("edit").descript
5766
5877
 
5767
5878
  // src/commands/wiki/page-comment/delete.ts
5768
5879
  var import_commander53 = require("commander");
5769
- var wikiPageCommentDeleteCommand = new import_commander53.Command("delete").description("\uC704\uD0A4 \uD398\uC774\uC9C0 \uB313\uAE00 \uC0AD\uC81C (confirm \uC5C6\uC774 \uC989\uC2DC)").argument("[arg1]", "\uD504\uB85C\uC81D\uD2B8 \uCF54\uB4DC / Dooray Wiki URL (\uBAA8\uB4DC\uBCC4)").argument("[arg2]", "\uC704\uD0A4 \uD398\uC774\uC9C0 ID (\uBAA8\uB4DC\uBCC4)").argument("[arg3]", "\uB313\uAE00 ID (positional 3\uAC1C \uBAA8\uB4DC)").option("--id <pageId>", "\uC704\uD0A4 \uD398\uC774\uC9C0 ID (positional \uB300\uC2E0)").option("--url <url>", "Dooray Wiki URL (positional \uB300\uC2E0)").option("--project <code>", "\uD504\uB85C\uC81D\uD2B8 \uCF54\uB4DC (--id \uBAA8\uB4DC\uC6A9)").option("--comment-id <commentId>", "\uB313\uAE00 ID (positional \uB300\uCCB4)").action(async (arg1, arg2, arg3, opts) => {
5880
+ var wikiPageCommentDeleteCommand = new import_commander53.Command("delete").description("\uC704\uD0A4 \uD398\uC774\uC9C0 \uB313\uAE00 \uC0AD\uC81C").argument("[arg1]", "\uD504\uB85C\uC81D\uD2B8 \uCF54\uB4DC / Dooray Wiki URL (\uBAA8\uB4DC\uBCC4)").argument("[arg2]", "\uC704\uD0A4 \uD398\uC774\uC9C0 ID (\uBAA8\uB4DC\uBCC4)").argument("[arg3]", "\uB313\uAE00 ID (positional 3\uAC1C \uBAA8\uB4DC)").option("--id <pageId>", "\uC704\uD0A4 \uD398\uC774\uC9C0 ID (positional \uB300\uC2E0)").option("--url <url>", "Dooray Wiki URL (positional \uB300\uC2E0)").option("--project <code>", "\uD504\uB85C\uC81D\uD2B8 \uCF54\uB4DC (--id \uBAA8\uB4DC\uC6A9)").option("--comment-id <commentId>", "\uB313\uAE00 ID (positional \uB300\uCCB4)").option("-y, --yes", "\uD655\uC778 \uC5C6\uC774 \uC0AD\uC81C (\uC790\uB3D9\uD654\uC6A9)").action(async (arg1, arg2, arg3, opts) => {
5881
+ const confirmed = await authorizeDeletion(
5882
+ !!opts.yes,
5883
+ !!process.stdin.isTTY,
5884
+ () => promptDeletion("\uC704\uD0A4 \uD398\uC774\uC9C0\uC758 \uB313\uAE00\uC744 \uC601\uAD6C \uC0AD\uC81C\uD560\uAE4C\uC694?")
5885
+ );
5886
+ if (!confirmed) {
5887
+ process.stderr.write("\uCDE8\uC18C\uB418\uC5C8\uC2B5\uB2C8\uB2E4.\n");
5888
+ return;
5889
+ }
5770
5890
  const parsed = parseWikiCommentArgs(arg1, arg2, arg3, opts);
5771
5891
  const config = await getConfigOrThrow();
5772
5892
  const client = new DoorayApiClient(config.apiKey, config.baseUrl);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bifos/dooray-cli",
3
- "version": "0.15.1",
3
+ "version": "0.17.0",
4
4
  "description": "CLI tool for Dooray project management — AI agent & terminal friendly",
5
5
  "keywords": [
6
6
  "dooray",