@bifos/dooray-cli 0.15.1 → 0.16.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.16.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
  }
@@ -3235,6 +3235,7 @@ var postEditCommand = new import_commander15.Command("edit").description("\uC5C5
3235
3235
  const toGroups = (opts.toGroup ?? []).filter((s) => s.length > 0);
3236
3236
  const tagAdditions = (opts.tag ?? []).filter((s) => s.length > 0);
3237
3237
  const tagRemovals = (opts.tagRemove ?? []).filter((s) => s.length > 0);
3238
+ const hasParticipantChange = ccNames.length > 0 || ccGroups.length > 0 || !!opts.ccClear || toNames.length > 0 || toGroups.length > 0 || !!opts.toClear;
3238
3239
  const hasTagChange = tagAdditions.length > 0 || tagRemovals.length > 0 || !!opts.tagClear;
3239
3240
  startSpinner("\uC5C5\uBB34 \uC870\uD68C \uC911...");
3240
3241
  const { projectId, postId, postNumber, projectCode } = await resolvePostInput(client, {
@@ -3253,7 +3254,7 @@ var postEditCommand = new import_commander15.Command("edit").description("\uC5C5
3253
3254
  "\u26A0 --subject\uB294 deprecated\uC785\uB2C8\uB2E4. \uB300\uC2E0 --title\uC744 \uC0AC\uC6A9\uD574\uC8FC\uC138\uC694.\n"
3254
3255
  );
3255
3256
  }
3256
- const nonInteractive = title || opts.body || opts.bodyFile || hasTagChange;
3257
+ const nonInteractive = title || opts.body || opts.bodyFile || hasTagChange || hasParticipantChange;
3257
3258
  if (nonInteractive) {
3258
3259
  let newBody = await readBodyInputOrNull(opts);
3259
3260
  if (newBody != null && !opts.dryRun) {
@@ -3368,11 +3369,6 @@ var postEditCommand = new import_commander15.Command("edit").description("\uC5C5
3368
3369
  "\u26A0 --link-task \uB294 --title/--body \uC640 \uD568\uAED8 \uC0AC\uC6A9 \uC2DC\uC5D0\uB9CC \uC801\uC6A9\uB429\uB2C8\uB2E4.\n"
3369
3370
  );
3370
3371
  }
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
3372
  if (opts.parent) {
3377
3373
  process.stderr.write(
3378
3374
  "\u26A0 --parent \uB294 --title/--body \uC640 \uD568\uAED8 \uC0AC\uC6A9 \uC2DC\uC5D0\uB9CC \uC801\uC6A9\uB429\uB2C8\uB2E4.\n"
@@ -3451,7 +3447,7 @@ function postUserToCreate(u) {
3451
3447
  group: u.group
3452
3448
  };
3453
3449
  }
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) => {
3450
+ 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
3451
  const globalOpts = postCreateCommand.optsWithGlobals();
3456
3452
  const config = await getConfigOrThrow();
3457
3453
  const client = new DoorayApiClient(config.apiKey, config.baseUrl);
@@ -4034,7 +4030,36 @@ var commentEditCommand = new import_commander22.Command("edit").description("\uB
4034
4030
 
4035
4031
  // src/commands/post/comment/delete.ts
4036
4032
  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) => {
4033
+
4034
+ // src/utils/delete-confirmation.ts
4035
+ async function authorizeDeletion(skipConfirmation, isTTY, confirmDeletion) {
4036
+ if (skipConfirmation) {
4037
+ return true;
4038
+ }
4039
+ if (!isTTY) {
4040
+ throw new DoorayCliError(
4041
+ "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.",
4042
+ EXIT_PARAM_ERROR
4043
+ );
4044
+ }
4045
+ return confirmDeletion();
4046
+ }
4047
+ async function promptDeletion(message) {
4048
+ const { confirm: confirm2 } = await import("@inquirer/prompts");
4049
+ return confirm2({ message, default: false });
4050
+ }
4051
+
4052
+ // src/commands/post/comment/delete.ts
4053
+ 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) => {
4054
+ const confirmed = await authorizeDeletion(
4055
+ !!opts.yes,
4056
+ !!process.stdin.isTTY,
4057
+ () => promptDeletion("\uC5C5\uBB34 \uB313\uAE00\uC744 \uC601\uAD6C \uC0AD\uC81C\uD560\uAE4C\uC694?")
4058
+ );
4059
+ if (!confirmed) {
4060
+ process.stderr.write("\uCDE8\uC18C\uB418\uC5C8\uC2B5\uB2C8\uB2E4.\n");
4061
+ return;
4062
+ }
4038
4063
  const config = await getConfigOrThrow();
4039
4064
  const client = new DoorayApiClient(config.apiKey, config.baseUrl);
4040
4065
  let projectArg;
@@ -4315,9 +4340,11 @@ var import_commander26 = require("commander");
4315
4340
  var import_node_path7 = require("path");
4316
4341
 
4317
4342
  // src/utils/comment-files.ts
4343
+ var IMAGE_FILE_EXTENSION_RE = /\.(png|jpe?g|gif|webp|bmp|svg|avif|heic)$/i;
4318
4344
  function appendFileReference(body, fileName, fileId) {
4319
4345
  const safeName = fileName.replace(/[\[\]]/g, "");
4320
- const ref = `![${safeName}](/files/${fileId})`;
4346
+ const prefix = IMAGE_FILE_EXTENSION_RE.test(safeName) ? "!" : "";
4347
+ const ref = `${prefix}[${safeName}](/files/${fileId})`;
4321
4348
  if (body.length === 0) return ref;
4322
4349
  const trailing = body.endsWith("\n") ? "" : "\n";
4323
4350
  return `${body}${trailing}
@@ -4325,14 +4352,14 @@ ${ref}`;
4325
4352
  }
4326
4353
  function removeFileReference(body, fileId) {
4327
4354
  const escaped = fileId.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
4328
- const refSource = `!\\[[^\\]]*\\]\\(/files/${escaped}\\)`;
4355
+ const refSource = `!?\\[[^\\]]*\\]\\(/files/${escaped}\\)`;
4329
4356
  const lineRe = new RegExp(`^[ \\t]*${refSource}[ \\t]*$\\n?`, "gm");
4330
4357
  const inlineRe = new RegExp(refSource, "g");
4331
4358
  return body.replace(lineRe, "").replace(inlineRe, "");
4332
4359
  }
4333
4360
 
4334
4361
  // 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) => {
4362
+ var uploadCommentFileCommand = new import_commander26.Command("upload").description("\uB313\uAE00\uC5D0 \uD30C\uC77C \uC5C5\uB85C\uB4DC (\uBCF8\uBB38\uC5D0 reference \uC790\uB3D9 \uCD94\uAC00)").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
4363
  const globalOpts = uploadCommentFileCommand.optsWithGlobals();
4337
4364
  const config = await getConfigOrThrow();
4338
4365
  const client = new DoorayApiClient(config.apiKey, config.baseUrl);
@@ -4413,7 +4440,18 @@ var downloadCommentFileCommand = new import_commander27.Command("download").desc
4413
4440
 
4414
4441
  // src/commands/post/comment/file/delete.ts
4415
4442
  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) => {
4443
+ 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) => {
4444
+ const confirmed = await authorizeDeletion(
4445
+ !!opts.yes,
4446
+ !!process.stdin.isTTY,
4447
+ () => promptDeletion(
4448
+ "\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?"
4449
+ )
4450
+ );
4451
+ if (!confirmed) {
4452
+ process.stderr.write("\uCDE8\uC18C\uB418\uC5C8\uC2B5\uB2C8\uB2E4.\n");
4453
+ return;
4454
+ }
4417
4455
  const config = await getConfigOrThrow();
4418
4456
  const client = new DoorayApiClient(config.apiKey, config.baseUrl);
4419
4457
  const { projectId, postId, commentId, secondary: fileId } = await resolveCommentFileInput(client, {
@@ -4428,16 +4466,6 @@ var deleteCommentFileCommand = new import_commander28.Command("delete").descript
4428
4466
  requireSecondary: true,
4429
4467
  secondaryLabel: FILE_ID_SECONDARY_LABEL
4430
4468
  });
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
4469
  startSpinner("\uB313\uAE00 \uBCF8\uBB38 reference \uC81C\uAC70 \uC911...");
4442
4470
  try {
4443
4471
  const commentRes = await client.getPostComment(projectId, postId, commentId);
@@ -4470,7 +4498,7 @@ var deleteCommentFileCommand = new import_commander28.Command("delete").descript
4470
4498
  });
4471
4499
 
4472
4500
  // 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);
4501
+ 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
4502
 
4475
4503
  // src/commands/post/file/list.ts
4476
4504
  var import_commander30 = require("commander");
@@ -4725,7 +4753,16 @@ var fileUploadCommand = new import_commander33.Command("upload").description("\u
4725
4753
 
4726
4754
  // src/commands/post/file/delete.ts
4727
4755
  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) => {
4756
+ 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) => {
4757
+ const confirmed = await authorizeDeletion(
4758
+ !!opts.yes,
4759
+ !!process.stdin.isTTY,
4760
+ () => promptDeletion("\uC5C5\uBB34\uC758 \uCCA8\uBD80 \uD30C\uC77C\uC744 \uC601\uAD6C \uC0AD\uC81C\uD560\uAE4C\uC694?")
4761
+ );
4762
+ if (!confirmed) {
4763
+ process.stderr.write("\uCDE8\uC18C\uB418\uC5C8\uC2B5\uB2C8\uB2E4.\n");
4764
+ return;
4765
+ }
4729
4766
  const globalOpts = fileDeleteCommand.optsWithGlobals();
4730
4767
  const config = await getConfigOrThrow();
4731
4768
  const client = new DoorayApiClient(config.apiKey, config.baseUrl);
@@ -5057,7 +5094,7 @@ var wikiPageEditCommand = new import_commander40.Command("edit").description("\u
5057
5094
  var import_commander41 = require("commander");
5058
5095
 
5059
5096
  // 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>";
5097
+ 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
5098
  async function resolveWikiPageInput(client, args) {
5062
5099
  const { projectArg, pageIdArg, idOpt, urlOpt, project } = args;
5063
5100
  const hasPositional = !!projectArg || !!pageIdArg;
@@ -5112,6 +5149,17 @@ async function resolveWikiPageInput(client, args) {
5112
5149
 
5113
5150
  // src/commands/wiki/page-delete.ts
5114
5151
  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) => {
5152
+ const confirmed = await authorizeDeletion(
5153
+ !!opts.yes,
5154
+ !!process.stdin.isTTY,
5155
+ () => promptDeletion(
5156
+ "\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."
5157
+ )
5158
+ );
5159
+ if (!confirmed) {
5160
+ process.stderr.write("\uCDE8\uC18C\uB418\uC5C8\uC2B5\uB2C8\uB2E4.\n");
5161
+ return;
5162
+ }
5115
5163
  const globalOpts = wikiPageDeleteCommand.optsWithGlobals();
5116
5164
  const config = await getConfigOrThrow();
5117
5165
  const client = new DoorayApiClient(config.apiKey, config.baseUrl);
@@ -5122,25 +5170,6 @@ var wikiPageDeleteCommand = new import_commander41.Command("delete").description
5122
5170
  urlOpt: opts.url,
5123
5171
  project: opts.project
5124
5172
  });
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
5173
  startSpinner("\uD398\uC774\uC9C0 \uC0AD\uC81C \uC911...");
5145
5174
  try {
5146
5175
  await client.deleteWikiPage(wikiId, pageId);
@@ -5205,7 +5234,7 @@ function wikiInlineImageSnippet(wikiId, attachFileId, name) {
5205
5234
  }
5206
5235
 
5207
5236
  // 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) => {
5237
+ 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
5238
  const globalOpts = wikiPageFileUploadCommand.optsWithGlobals();
5210
5239
  const config = await getConfigOrThrow();
5211
5240
  const client = new DoorayApiClient(config.apiKey, config.baseUrl);
@@ -5426,7 +5455,16 @@ var wikiPageFileDownloadAllCommand = new import_commander45.Command("download-al
5426
5455
 
5427
5456
  // src/commands/wiki/page-file/delete.ts
5428
5457
  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) => {
5458
+ 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) => {
5459
+ const confirmed = await authorizeDeletion(
5460
+ !!opts.yes,
5461
+ !!process.stdin.isTTY,
5462
+ () => promptDeletion("\uC704\uD0A4 \uD398\uC774\uC9C0\uC758 \uCCA8\uBD80 \uD30C\uC77C\uC744 \uC601\uAD6C \uC0AD\uC81C\uD560\uAE4C\uC694?")
5463
+ );
5464
+ if (!confirmed) {
5465
+ process.stderr.write("\uCDE8\uC18C\uB418\uC5C8\uC2B5\uB2C8\uB2E4.\n");
5466
+ return;
5467
+ }
5430
5468
  const globalOpts = wikiPageFileDeleteCommand.optsWithGlobals();
5431
5469
  const config = await getConfigOrThrow();
5432
5470
  const client = new DoorayApiClient(config.apiKey, config.baseUrl);
@@ -5485,7 +5523,7 @@ var wikiPageFileDeleteCommand = new import_commander46.Command("delete").descrip
5485
5523
  });
5486
5524
 
5487
5525
  // 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)");
5526
+ var wikiPageFileCommand = new import_commander47.Command("file").description("\uC704\uD0A4 \uD398\uC774\uC9C0 \uCCA8\uBD80\uD30C\uC77C \uAD00\uB828 \uBA85\uB839");
5489
5527
  wikiPageFileCommand.addCommand(wikiPageFileListCommand);
5490
5528
  wikiPageFileCommand.addCommand(wikiPageFileUploadCommand);
5491
5529
  wikiPageFileCommand.addCommand(wikiPageFileDownloadCommand);
@@ -5766,7 +5804,16 @@ var wikiPageCommentEditCommand = new import_commander52.Command("edit").descript
5766
5804
 
5767
5805
  // src/commands/wiki/page-comment/delete.ts
5768
5806
  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) => {
5807
+ 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) => {
5808
+ const confirmed = await authorizeDeletion(
5809
+ !!opts.yes,
5810
+ !!process.stdin.isTTY,
5811
+ () => promptDeletion("\uC704\uD0A4 \uD398\uC774\uC9C0\uC758 \uB313\uAE00\uC744 \uC601\uAD6C \uC0AD\uC81C\uD560\uAE4C\uC694?")
5812
+ );
5813
+ if (!confirmed) {
5814
+ process.stderr.write("\uCDE8\uC18C\uB418\uC5C8\uC2B5\uB2C8\uB2E4.\n");
5815
+ return;
5816
+ }
5770
5817
  const parsed = parseWikiCommentArgs(arg1, arg2, arg3, opts);
5771
5818
  const config = await getConfigOrThrow();
5772
5819
  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.16.0",
4
4
  "description": "CLI tool for Dooray project management — AI agent & terminal friendly",
5
5
  "keywords": [
6
6
  "dooray",
@@ -5,50 +5,207 @@ description: Dooray 업무 관리 CLI. 프로젝트/업무/댓글/위키 조회
5
5
 
6
6
  # dooray-cli
7
7
 
8
- NHN Dooray REST API를 래핑한 CLI 도구다.
9
- 이 파일은 router다.
10
- 작업하려는 영역에 맞는 reference먼저 읽고, 자연어 요청은 [intent-map.md](references/intent-map.md) 표로 커맨드를 찾는다.
11
-
12
- ## 먼저 읽을
13
-
14
- | 상황 | 읽을 reference |
15
- |------|----------------|
16
- | 자연어 요청 커맨드 매핑 (마스터 표) | [intent-map.md](references/intent-map.md) |
17
- | 설치, 초기 설정, 출력 모드, API 제약사항, 피드백 등록, 에러 핸들링, 캐시 | [common.md](references/common.md) |
18
- | 업무(post) 식별·생성·수정, 참조자/담당자 변경, 본문 attachment 보호, 부모 지정, 태그 자동화 | [post.md](references/post.md) |
19
- | 업무 댓글 추가/필터/단일 조회 | [comment.md](references/comment.md) |
20
- | 위키 페이지 조회(트리 포함), 삭제, 첨부파일, 인라인 이미지, 위키 댓글 | [wiki.md](references/wiki.md) |
21
- | 그룹 멘션/cc 판단, 멘션·링크 자동 삽입, Dooray 마크다운 링크 형식 | [mention-link.md](references/mention-link.md) |
22
- | 워크플로우 판단 기준, 정형 task 자동화, 체이닝 시나리오 | [workflow.md](references/workflow.md) |
23
-
24
- ## 공통 우선 규칙
25
-
26
- - 자연어 요청이 오면 먼저 [intent-map.md](references/intent-map.md) 표에서 대응 커맨드를 찾는다.
27
- - 사용자가 Dooray URL을 줬으면 그대로 인자로 전달하는 것이 가장 빠른 경로다 (resolve 단계 단축).
28
- - 구조화 출력이 필요하면 `--json`을 우선 사용한다.
29
- - 결과를 다음 액션에 넘길 때는 `--quiet`로 ID만 뽑아 스크립팅한다.
30
- - 이름 기반 조회(멤버/그룹/워크플로우/태그)는 부분일치를 지원하되, 모호하면 에러와 후보 목록이 출력된다 — 임의 선택하지 말고 사용자에게 확인한다.
31
- - 파괴적 명령(위키 페이지 삭제, 파일 삭제 등)은 기본적으로 confirm이 있거나 즉시 실행됨이 reference마다 다르니 해당 reference에서 먼저 확인한다.
32
-
33
- ## 빠른 시작
34
-
35
- ```bash
36
- npm install -g @bifos/dooray-cli
37
- dooray setup # API endpoint, API key, 메일 설정까지 대화형 진행
38
- dooray skill status
39
- dooray skill install
40
- dooray post list <project> --json
41
- ```
42
-
43
- 전역 npm 패키지를 갱신한 뒤에는 `dooray skill update`를 실행해 Claude Code 스킬을 현재 CLI 버전으로 맞춘다.
44
- 스킬 활성 링크는 Node 설치 경로가 아니라 dooray-cli 관리 저장소를 가리킨다.
45
- `modified`, `corrupt`, `unmanaged` 상태는 내용을 확인한 뒤 `dooray skill update --force`로 백업 후 복구한다.
46
-
47
- ## 안전한 탐색 순서
48
-
49
- 1. [common.md](references/common.md)에서 설치·설정·출력 모드를 확인한다.
50
- 2. 자연어 요청이면 [intent-map.md](references/intent-map.md)에서 커맨드를 찾는다.
51
- 3. 대상 영역(post/comment/wiki/mention-link/workflow) reference를 읽는다.
52
- 4. 조회 명령은 `--json`으로 먼저 실행해 구조를 확인한다.
53
- 5. 쓰기 명령은 대상 id, `--dry-run`(지원 시), confirm 옵션을 명시한다.
54
- 6. 실패하면 [common.md](references/common.md)의 에러 핸들링 표와 대조한다.
8
+ NHN Dooray REST API 를 래핑한 CLI 다. 이 파일은 라우터이므로, 작업 영역에 맞는 reference 를 먼저 읽는다.
9
+
10
+ ## 어느 reference읽을지
11
+
12
+ | 하려는 | reference |
13
+ | --- | --- |
14
+ | 설치·초기 설정, 출력 모드, API 제약, 에러 처리, 캐시, 피드백 등록 | [common.md](references/common.md) |
15
+ | 업무 식별·생성·수정·삭제, 참조자·담당자 변경, 첨부 보호, 부모 지정, 태그 | [post.md](references/post.md) |
16
+ | 업무 댓글 추가·필터·조회 | [comment.md](references/comment.md) |
17
+ | 위키 페이지 조회·트리·삭제, 첨부와 인라인 이미지, 위키 댓글 | [wiki.md](references/wiki.md) |
18
+ | 그룹 멘션·cc 판단, 멘션·링크 자동 삽입, Dooray 마크다운 링크 | [mention-link.md](references/mention-link.md) |
19
+ | 워크플로우 판단 기준, 정형 task 자동화, 명령 체이닝 | [workflow.md](references/workflow.md) |
20
+
21
+ ## 대상 지정 방법
22
+
23
+ `post get`/`edit`/`done`/`workflow`, `post comment` 전체, `post file` 전체, `post comment file` 전체,
24
+ `wiki page file` 과 `wiki page comment` 전체, 그리고 `wiki page delete` 가 네 가지 형태를 모두 받는다.
25
+
26
+ - `<project> <number>` 업무는 번호, 위키는 `<project> <page-id>`
27
+ - `--id <postId>` / `--id <pageId>` 위키는 `--project` 함께 줘야 한다 (API page 단독 조회를 지원하지 않는다)
28
+ - `--url <url>`
29
+ - 인자에 Dooray URL 직접
30
+
31
+ 받아들이는 URL 형식은 셋이다.
32
+
33
+ - `https://*.dooray.com/task/to/<postId>`
34
+ - `https://*.dooray.com/task/<projectId>/<postId>` — 브라우저 주소창 복사본
35
+ - `https://*.dooray.com/project/tasks/<postId>` — 업무 목록에서 업무를 열었을 때
36
+
37
+ ## 실행 규칙
38
+
39
+ - 사용자가 Dooray URL 을 줬으면 그대로 첫 인자로 넘긴다 — resolve 단계를 건너뛰어 가장 빠르다
40
+ - 구조화 결과가 필요하면 `--json`, 다음 명령에 ID 만 넘길 때는 `--quiet` 를 쓴다
41
+ - 조회는 `--json` 으로 먼저 실행해 응답 구조를 확인한 뒤 쓰기 명령으로 넘어간다
42
+ - 쓰기 명령은 대상 ID 를 명시하고, 지원하면 `--dry-run` 으로 먼저 확인한다
43
+ - 이름 기반 조회(멤버·그룹·워크플로우·태그)는 부분일치를 지원한다. 모호하면 에러와 후보 목록이 나오므로 임의로 고르지 말고 사용자에게 확인한다
44
+ - 실패하면 [common.md](references/common.md) 에러 처리 표와 대조한다
45
+
46
+ ## 파일 명령의 `--json` 스키마
47
+
48
+ `post file` 과 `wiki page file` 이 같은 스키마를 쓴다. 한쪽 파싱 코드를 다른 쪽에 그대로 쓸 수 있다.
49
+
50
+ | 명령 | `--json` | `--quiet` |
51
+ | --- | --- | --- |
52
+ | `upload` | API 응답의 `result` 원형 | `id` |
53
+ | `download` | `{outputPath, fileName, size}` | `outputPath` |
54
+ | `download-all` | `{count, succeeded: [{path, fileName}], failed: [{fileId, error}]}` | — |
55
+ | `delete` | `{fileId, status: "deleted"}` | `fileId` |
56
+
57
+ `download-all` 은 일부만 실패해도 나머지를 계속 내려받고 **종료 코드 1** 을 반환한다.
58
+ 성공과 실패를 갈라 처리해야 하므로 종료 코드만 보고 전체 실패로 판단하지 않는다.
59
+
60
+ `wiki page file upload --type inline_image` 는 `--json` 에 `markdownSnippet` 이 더 붙는다.
61
+ 본문에 그대로 넣을 수 있는 markdown 이며, `general` 타입과 `--quiet` 에는 없다.
62
+
63
+ ## 삭제 명령의 확인 동작
64
+
65
+ 여섯 삭제 명령은 같은 안전 확인 정책을 따른다.
66
+
67
+ | 명령 | 확인 | 자동화 |
68
+ | --- | --- | --- |
69
+ | `wiki page delete` | 있음 | `-y`, `--yes` |
70
+ | `post comment file delete` | 있음 | `-y`, `--yes` |
71
+ | `post file delete` | 있음 | `-y`, `--yes` |
72
+ | `wiki page file delete` | 있음 | `-y`, `--yes` |
73
+ | `post comment delete` | 있음 | `-y`, `--yes` |
74
+ | `wiki page comment delete` | 있음 | `-y`, `--yes` |
75
+
76
+ - TTY 확인은 기본값이 아니오다.
77
+ - non-TTY에서 플래그가 없으면 설정 조회나 삭제 API 호출 전에 종료 코드 3으로 중단한다.
78
+ - 자동화에서는 `-y` 또는 `--yes`를 반드시 붙인다.
79
+
80
+ # 의도별 커맨드
81
+
82
+ 자연어 요청을 커맨드로 옮길 때 해당 영역의 절만 본다.
83
+
84
+ ## 설정
85
+
86
+ | 의도 | 커맨드 |
87
+ | --- | --- |
88
+ | 초기 설정 (대화형) | `dooray setup` |
89
+
90
+ ## 프로젝트와 멤버
91
+
92
+ | 의도 | 커맨드 |
93
+ | --- | --- |
94
+ | 프로젝트 찾기 | `dooray project list --search <keyword>` |
95
+ | 개인 프로젝트 목록 | `dooray project list --type private` |
96
+ | 프로젝트 멤버 보기 | `dooray project members <project>` 또는 `dooray member list <project>` |
97
+ | 프로젝트 멤버 그룹 목록 | `dooray project groups <project>` |
98
+ | 프로젝트 태그 목록 | `dooray project tags <project>` |
99
+ | 프로젝트 템플릿 목록 | `dooray project templates <project>` |
100
+ | 멤버 상세 | `dooray member get <organizationMemberId>` (캐시 우회) |
101
+ | organization 전체 멤버 검색 | `dooray member search <keyword>` — 옵션은 [common.md](references/common.md) |
102
+
103
+ ## 업무 조회와 생성
104
+
105
+ | 의도 | 커맨드 |
106
+ | --- | --- |
107
+ | 업무 목록 | `dooray post list <project>` |
108
+ | 업무 검색 | `dooray post search <project> "<keyword>"` — projectId(15자리 이상 numeric) 를 넣으면 캐시를 우회한다 |
109
+ | 업무 상세 | `dooray post get <project> <number>` 또는 `dooray post get --id <postId>` |
110
+ | 업무 생성 | `dooray post create <project> --title "..." [--body "..." \| --body-file <path>]` — 담당자는 `--to <name\|email>`, 참조자는 `--cc`, 둘 다 여러 명 가능 |
111
+ | 템플릿으로 생성 | `dooray post create <project> --template <name\|id>` — 본문·담당자·태그가 채워지고 사용자 옵션이 우선한다 |
112
+ | 제목·본문 수정 | `dooray post edit <project> <number> --title "..." --body "..."` |
113
+ | 완료 처리 | `dooray post done <project> <number>` |
114
+ | 워크플로우 변경 | `dooray post workflow <project> <number> <workflow>` |
115
+
116
+ ## 업무 메타 변경
117
+
118
+ 자세한 동작은 [post.md](references/post.md) 를 읽는다.
119
+
120
+ | 의도 | 커맨드 |
121
+ | --- | --- |
122
+ | 참조자에 그룹 추가 | `dooray post edit <project> <number> --cc-group <code>` — 기존 참조자를 유지하고 추가한다 |
123
+ | 참조자 전체 교체 | `dooray post edit <project> <number> --cc-clear --cc <name>` |
124
+ | 생성 시 그룹 참조자 | `dooray post create <project> --title "..." --cc-group <code>` |
125
+ | 상위 업무 지정·변경 | `dooray post edit <project> <number> --title "<원제목>" --parent <ref>` — `--title` 이 필수이고 해제는 지원하지 않는다 |
126
+ | 태그 추가 | `dooray post edit --id <postId> --tag <name>` (반복 가능, 중복 제거) |
127
+ | 태그 전체 교체 | `dooray post edit --id <postId> --tag-clear --tag <name>` |
128
+ | 태그 제거 | `dooray post edit --id <postId> --tag-remove <name>` |
129
+
130
+ 참조자·담당자 옵션만 지정하면 `$EDITOR`를 열지 않고 기존 제목·본문·태그를 보존한 채 참여자만 바꾼다.
131
+
132
+ 그룹 지정(`--cc-group`, `--mention-group`)은 15자리 이상 numeric 이면 ID 로, 그 외에는 code 부분일치로 찾는다.
133
+
134
+ ## 업무 댓글
135
+
136
+ | 의도 | 커맨드 |
137
+ | --- | --- |
138
+ | 댓글 조회 | `dooray post comment list <project> <number>` — 필터는 [comment.md](references/comment.md) |
139
+ | 최신 댓글 | `dooray post comment latest <project> <number>` (`-n <N>` 으로 개수 지정) |
140
+ | 단일 댓글 | `dooray post comment get <project> <number> <comment-id>` |
141
+ | 댓글 추가 | `dooray post comment add <project> <number> --body "..."` |
142
+ | 댓글 수정 | `dooray post comment edit <project> <number> <comment-id> --body "..."` |
143
+ | 댓글 삭제 | `dooray post comment delete <project> <number> <comment-id>` — 확인 있음, `-y`/`--yes`로 생략 |
144
+
145
+ ## 업무 첨부
146
+
147
+ `--json` 출력 스키마는 [post.md](references/post.md) 를 읽는다.
148
+
149
+ | 의도 | 커맨드 |
150
+ | --- | --- |
151
+ | 첨부 목록 | `dooray post file list <project> <number>` |
152
+ | 첨부 다운로드 | `dooray post file download <project> <number> <file-id>` |
153
+ | 첨부 일괄 다운로드 | `dooray post file download-all <project> <number>` |
154
+ | 첨부 업로드 | `dooray post file upload <project> <number> <file-path>` |
155
+ | 첨부 삭제 | `dooray post file delete <project> <number> <file-id>` — 확인 있음, `-y`/`--yes`로 생략 |
156
+ | 댓글 첨부 목록 | `dooray post comment file list <project> <number> <comment-id>` |
157
+ | 댓글 첨부 업로드 | `dooray post comment file upload <project> <number> <comment-id> <path>` |
158
+ | 댓글 첨부 다운로드 | `dooray post comment file download <project> <number> <comment-id> <file-id>` |
159
+ | 댓글 첨부 삭제 | `dooray post comment file delete <project> <number> <comment-id> <file-id>` — 확인 있음, `-y`/`--yes`로 생략 |
160
+
161
+ - 댓글 파일 업로드는 이미지 확장자면 이미지 마크다운을, 그 외에는 일반 링크를 만든다.
162
+ - `comment file list`가 비어도 웹 UI 첨부가 없다고 단정하지 말고 `post file list`로 확인한다.
163
+
164
+ ## 위키
165
+
166
+ | 의도 | 커맨드 |
167
+ | --- | --- |
168
+ | 위키 목록 | `dooray wiki list` |
169
+ | 페이지 목록 | `dooray wiki pages <project>` |
170
+ | 페이지 트리 | `dooray wiki tree <project>` (`--depth N` 으로 상한, `--json` 은 flat) |
171
+ | 페이지 상세 | `dooray wiki page get <project> <page-id>` |
172
+ | 페이지 생성 | `dooray wiki page create <project> --title "..." [--parent <page-id>] [--body "..."]` — `--parent` 를 생략하면 위키 home 아래에 만든다 |
173
+ | 페이지 제목 수정 | `dooray wiki page edit <project> <page-id> --title "..."` |
174
+ | 페이지 본문 수정 | `dooray wiki page edit <project> <page-id> --body "..."` 또는 `--body-file ./new.md` |
175
+ | 페이지 에디터로 수정 | `dooray wiki page edit <project> <page-id>` — 플래그가 없으면 `$EDITOR` 가 열린다 |
176
+ | 페이지 삭제 | `dooray wiki page delete <project> <page-id>` — 확인 있음, `-y`/`--yes`로 생략. 하위 페이지는 삭제한 페이지의 부모 아래로 재부착되어 orphan 이 생기지 않는다 |
177
+ | 첨부 목록 | `dooray wiki page file list <project> <page-id>` — general 과 inline 을 합쳐 보여준다 |
178
+ | 첨부 업로드 | `dooray wiki page file upload <project> <page-id> --file <path> [--type inline_image]` |
179
+ | 첨부 다운로드 | `dooray wiki page file download <project> <page-id> --file-id <id> -o <dir>` |
180
+ | 첨부 일괄 다운로드 | `dooray wiki page file download-all <project> <page-id> -o <dir>` |
181
+ | 첨부 삭제 | `dooray wiki page file delete <project> <page-id> --file-id <id>` — 확인 있음, `-y`/`--yes`로 생략 |
182
+ | 댓글 목록 | `dooray wiki page comment list <project> <page-id> [--latest N]` (최신순) |
183
+ | 최신 댓글 | `dooray wiki page comment latest <project> <page-id>` |
184
+ | 단일 댓글 | `dooray wiki page comment get <project> <page-id> <comment-id>` |
185
+ | 댓글 추가 | `dooray wiki page comment add <project> <page-id> --body "..."` (`$EDITOR` fallback) |
186
+ | 댓글 수정 | `dooray wiki page comment edit <project> <page-id> <comment-id> --body "..."` |
187
+ | 댓글 삭제 | `dooray wiki page comment delete <project> <page-id> <comment-id>` — 확인 있음, `-y`/`--yes`로 생략 |
188
+
189
+ ## 메일
190
+
191
+ | 의도 | 커맨드 |
192
+ | --- | --- |
193
+ | 메일 목록 | `dooray mail list` |
194
+ | 안 읽은 메일 | `dooray mail list --unread` |
195
+ | 제목 검색 | `dooray mail list --search "<keyword>"` |
196
+ | 메일 상세 | `dooray mail get <uid>` |
197
+ | 메일 발송 | `dooray mail send --to "..." --subject "..." --body "..."` |
198
+ | 메일 답장 | `dooray mail reply <uid> --body "..."` |
199
+ | 저장된 인증정보 제거 | `dooray mail logout` (비대화형 환경은 `--yes`) |
200
+
201
+ ## 메신저
202
+
203
+ | 의도 | 커맨드 |
204
+ | --- | --- |
205
+ | 1:1 다이렉트 메시지 | `dooray messenger send --to "<id\|email>" --body "..."` — `--to` 는 ID 나 이메일만 받고 이름은 지원하지 않는다 |
206
+ | 대화방 메시지 | `dooray messenger channel-send --channel "<channelId\|이름>" --body "..."` — 이름으로는 자신이 속한 방만 찾는다 |
207
+
208
+ ## 옵션 이름
209
+
210
+ `post` 와 `wiki page` 모두 제목은 `--title` 이다.
211
+ `post` 의 `--subject` 는 deprecated alias 로 아직 동작하지만 경고가 나온다.