@bifos/dooray-cli 0.7.0 → 0.8.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/README.md +23 -0
- package/dist/index.js +91 -18
- package/package.json +1 -1
- package/skills/dooray-cli/SKILL.md +44 -1
package/README.md
CHANGED
|
@@ -163,6 +163,29 @@ dooray post comment add <project> <post-number> --body "..." --link-task <projec
|
|
|
163
163
|
> `--mention` / `--mention-group` / `--link-task` / `--dry-run` 은 `post create`, `post edit`, `post comment add`, `post comment edit` 4 명령 모두 지원.
|
|
164
164
|
> 이전 버전 캐시는 orgId가 없으므로 첫 호출 시 자동 갱신됩니다 (또는 `dooray cache clear`).
|
|
165
165
|
|
|
166
|
+
#### 참조자(cc) / 담당자(to) 변경 (ADR-025)
|
|
167
|
+
|
|
168
|
+
```bash
|
|
169
|
+
# 멤버/그룹 추가 (기존 참조자 유지 + dedupe)
|
|
170
|
+
dooray post edit <project> <post-number> \
|
|
171
|
+
--cc 홍길동 --cc-group dev-team \
|
|
172
|
+
--to 김철수
|
|
173
|
+
|
|
174
|
+
# 기존 참조자 전부 비우고 신규만
|
|
175
|
+
dooray post edit <project> <post-number> --cc-clear --cc 홍길동
|
|
176
|
+
|
|
177
|
+
# 신규 업무 생성 시 그룹 cc 동봉
|
|
178
|
+
dooray post create <project> --title "주간 audit" --cc-group dev-team
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
interactive ($EDITOR) 모드에서는 위 6개 옵션이 무시되고 stderr 경고가 출력됩니다.
|
|
182
|
+
`post edit --dry-run --json` 사용 시 출력에 `users: { to, cc }` 가 포함되어 API 호출 없이 변경 결과 미리보기 가능. (`post create --dry-run` 은 본문만 출력하며 `users` 는 포함하지 않음.)
|
|
183
|
+
|
|
184
|
+
```bash
|
|
185
|
+
dooray post edit <project> <post-number> --cc-group dev-team --dry-run --json | jq '.users.cc'
|
|
186
|
+
# 출력 예: [{ "type": "group", "group": { "projectMemberGroupId": "...", "members": [] } }, ...]
|
|
187
|
+
```
|
|
188
|
+
|
|
166
189
|
#### comment list 필터 옵션
|
|
167
190
|
|
|
168
191
|
```bash
|
package/dist/index.js
CHANGED
|
@@ -1817,6 +1817,56 @@ async function resolveTaskLinks(client, linkInputs) {
|
|
|
1817
1817
|
);
|
|
1818
1818
|
}
|
|
1819
1819
|
|
|
1820
|
+
// src/resolvers/post-users.ts
|
|
1821
|
+
function parseUserSpec(memberIds, groupIds) {
|
|
1822
|
+
const users = [];
|
|
1823
|
+
for (const id of memberIds) {
|
|
1824
|
+
users.push({ type: "member", member: { organizationMemberId: id } });
|
|
1825
|
+
}
|
|
1826
|
+
for (const gid of groupIds) {
|
|
1827
|
+
users.push({ type: "group", group: { projectMemberGroupId: gid, members: [] } });
|
|
1828
|
+
}
|
|
1829
|
+
return users;
|
|
1830
|
+
}
|
|
1831
|
+
function mergeUsers(existing, additions, clear) {
|
|
1832
|
+
const base = clear ? [] : existing;
|
|
1833
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1834
|
+
const result = [];
|
|
1835
|
+
for (const u of [...base, ...additions]) {
|
|
1836
|
+
const key = userKey(u);
|
|
1837
|
+
if (key && seen.has(key)) continue;
|
|
1838
|
+
if (key) seen.add(key);
|
|
1839
|
+
result.push(u);
|
|
1840
|
+
}
|
|
1841
|
+
return result;
|
|
1842
|
+
}
|
|
1843
|
+
function userKey(u) {
|
|
1844
|
+
if (u.member?.organizationMemberId) return `m:${u.member.organizationMemberId}`;
|
|
1845
|
+
if (u.group?.projectMemberGroupId) return `g:${u.group.projectMemberGroupId}`;
|
|
1846
|
+
if (u.emailUser?.emailAddress) return `e:${u.emailUser.emailAddress}`;
|
|
1847
|
+
return null;
|
|
1848
|
+
}
|
|
1849
|
+
async function resolveUserAdditions(client, projectId, names, groupCodes) {
|
|
1850
|
+
const memberIds = await Promise.all(
|
|
1851
|
+
names.map(
|
|
1852
|
+
(n) => resolveMember(client, projectId, n).catch((err) => {
|
|
1853
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
1854
|
+
throw new Error(`\uBA64\uBC84 '${n}' \uC870\uD68C \uC2E4\uD328: ${msg}`);
|
|
1855
|
+
})
|
|
1856
|
+
)
|
|
1857
|
+
);
|
|
1858
|
+
const groups = await Promise.all(
|
|
1859
|
+
groupCodes.map(
|
|
1860
|
+
(c) => resolveMemberGroup(client, projectId, c).catch((err) => {
|
|
1861
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
1862
|
+
throw new Error(`\uADF8\uB8F9 '${c}' \uC870\uD68C \uC2E4\uD328: ${msg}`);
|
|
1863
|
+
})
|
|
1864
|
+
)
|
|
1865
|
+
);
|
|
1866
|
+
const groupIds = groups.map((g) => g.id);
|
|
1867
|
+
return parseUserSpec(memberIds, groupIds);
|
|
1868
|
+
}
|
|
1869
|
+
|
|
1820
1870
|
// src/editor/index.ts
|
|
1821
1871
|
var import_node_child_process = require("child_process");
|
|
1822
1872
|
var import_promises6 = __toESM(require("fs/promises"));
|
|
@@ -2050,12 +2100,16 @@ async function resolveUsers(client, projectId, names) {
|
|
|
2050
2100
|
}
|
|
2051
2101
|
return users;
|
|
2052
2102
|
}
|
|
2053
|
-
var postEditCommand = new import_commander13.Command("edit").description("\uC5C5\uBB34 \uC218\uC815 ($EDITOR \uB610\uB294 --title/--body \uC635\uC158)").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)").option("--title <title>", "\uC81C\uBAA9 \uBCC0\uACBD (non-interactive)").option("--subject <subject>", "--title\uC758 deprecated alias").option("--body <text>", "\uBCF8\uBB38 \uBCC0\uACBD (- \uC785\uB825 \uC2DC stdin, non-interactive)").option("--body-file <path>", "\uBCF8\uBB38 \uD30C\uC77C \uACBD\uB85C (- \uC785\uB825 \uC2DC stdin, non-interactive)").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("--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)").option("--no-confirm", "\uB204\uB77D attachment \uACBD\uACE0 \uC2DC confirm \uC5C6\uC774 \uC9C4\uD589 (\uC790\uB3D9\uD654\uC6A9)").action(async (project, postNumberStr, opts) => {
|
|
2103
|
+
var postEditCommand = new import_commander13.Command("edit").description("\uC5C5\uBB34 \uC218\uC815 ($EDITOR \uB610\uB294 --title/--body \uC635\uC158)").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)").option("--title <title>", "\uC81C\uBAA9 \uBCC0\uACBD (non-interactive)").option("--subject <subject>", "--title\uC758 deprecated alias").option("--body <text>", "\uBCF8\uBB38 \uBCC0\uACBD (- \uC785\uB825 \uC2DC stdin, non-interactive)").option("--body-file <path>", "\uBCF8\uBB38 \uD30C\uC77C \uACBD\uB85C (- \uC785\uB825 \uC2DC stdin, non-interactive)").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("--cc <name>", "\uCC38\uC870\uC790(cc) \uBA64\uBC84 \uCD94\uAC00 (\uBC18\uBCF5 \uAC00\uB2A5, \uC774\uB984 \uBD80\uBD84\uC77C\uCE58)", (v, prev) => [...prev, v], []).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("--cc-clear", "\uAE30\uC874 \uCC38\uC870\uC790 \uC804\uBD80 \uC81C\uAC70 \uD6C4 \uC2E0\uADDC\uB9CC \uC801\uC6A9 (--cc/--cc-group \uC640 \uC870\uD569 \uAC00\uB2A5)").option("--to <name>", "\uB2F4\uB2F9\uC790(to) \uBA64\uBC84 \uCD94\uAC00 (\uBC18\uBCF5 \uAC00\uB2A5, \uC774\uB984 \uBD80\uBD84\uC77C\uCE58)", (v, prev) => [...prev, v], []).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("--to-clear", "\uAE30\uC874 \uB2F4\uB2F9\uC790 \uC804\uBD80 \uC81C\uAC70 \uD6C4 \uC2E0\uADDC\uB9CC \uC801\uC6A9").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)").option("--no-confirm", "\uB204\uB77D attachment \uACBD\uACE0 \uC2DC confirm \uC5C6\uC774 \uC9C4\uD589 (\uC790\uB3D9\uD654\uC6A9)").action(async (project, postNumberStr, opts) => {
|
|
2054
2104
|
const config = await getConfigOrThrow();
|
|
2055
2105
|
const client = new DoorayApiClient(config.apiKey, config.baseUrl);
|
|
2056
2106
|
const mentionInputs = (opts.mention ?? []).filter((s) => s.length > 0);
|
|
2057
2107
|
const groupInputs = (opts.mentionGroup ?? []).filter((s) => s.length > 0);
|
|
2058
2108
|
const linkInputs = (opts.linkTask ?? []).filter((s) => s.length > 0);
|
|
2109
|
+
const ccNames = (opts.cc ?? []).filter((s) => s.length > 0);
|
|
2110
|
+
const ccGroups = (opts.ccGroup ?? []).filter((s) => s.length > 0);
|
|
2111
|
+
const toNames = (opts.to ?? []).filter((s) => s.length > 0);
|
|
2112
|
+
const toGroups = (opts.toGroup ?? []).filter((s) => s.length > 0);
|
|
2059
2113
|
startSpinner("\uC5C5\uBB34 \uC870\uD68C \uC911...");
|
|
2060
2114
|
const { projectId, postId, postNumber, projectCode } = await resolvePostInput(client, {
|
|
2061
2115
|
projectArg: project,
|
|
@@ -2105,30 +2159,38 @@ var postEditCommand = new import_commander13.Command("edit").description("\uC5C5
|
|
|
2105
2159
|
const links = await resolveTaskLinks(client, linkInputs);
|
|
2106
2160
|
newBody = appendTaskLinks(effectiveBody, links, me);
|
|
2107
2161
|
}
|
|
2162
|
+
let toUsers = post.users.to.map((u) => ({
|
|
2163
|
+
type: u.type,
|
|
2164
|
+
member: u.member,
|
|
2165
|
+
emailUser: u.emailUser,
|
|
2166
|
+
group: u.group
|
|
2167
|
+
}));
|
|
2168
|
+
let ccUsers = post.users.cc.map((u) => ({
|
|
2169
|
+
type: u.type,
|
|
2170
|
+
member: u.member,
|
|
2171
|
+
emailUser: u.emailUser,
|
|
2172
|
+
group: u.group
|
|
2173
|
+
}));
|
|
2174
|
+
if (toNames.length > 0 || toGroups.length > 0 || opts.toClear) {
|
|
2175
|
+
const additions = await resolveUserAdditions(client, projectId, toNames, toGroups);
|
|
2176
|
+
toUsers = mergeUsers(toUsers, additions, !!opts.toClear);
|
|
2177
|
+
}
|
|
2178
|
+
if (ccNames.length > 0 || ccGroups.length > 0 || opts.ccClear) {
|
|
2179
|
+
const additions = await resolveUserAdditions(client, projectId, ccNames, ccGroups);
|
|
2180
|
+
ccUsers = mergeUsers(ccUsers, additions, !!opts.ccClear);
|
|
2181
|
+
}
|
|
2108
2182
|
if (opts.dryRun) {
|
|
2109
2183
|
stopSpinner(false);
|
|
2110
2184
|
const globalOpts = postEditCommand.optsWithGlobals();
|
|
2111
2185
|
const previewBody = newBody ?? post.body.content;
|
|
2112
2186
|
if (globalOpts.json) {
|
|
2113
|
-
process.stdout.write(JSON.stringify({ body: previewBody }) + "\n");
|
|
2187
|
+
process.stdout.write(JSON.stringify({ body: previewBody, users: { to: toUsers, cc: ccUsers } }) + "\n");
|
|
2114
2188
|
} else {
|
|
2115
2189
|
process.stdout.write(previewBody + "\n");
|
|
2116
2190
|
}
|
|
2117
2191
|
return;
|
|
2118
2192
|
}
|
|
2119
2193
|
startSpinner("\uC5C5\uBB34 \uC218\uC815 \uC911...");
|
|
2120
|
-
const toUsers = post.users.to.map((u) => ({
|
|
2121
|
-
type: u.type,
|
|
2122
|
-
member: u.member,
|
|
2123
|
-
emailUser: u.emailUser,
|
|
2124
|
-
group: u.group
|
|
2125
|
-
}));
|
|
2126
|
-
const ccUsers = post.users.cc.map((u) => ({
|
|
2127
|
-
type: u.type,
|
|
2128
|
-
member: u.member,
|
|
2129
|
-
emailUser: u.emailUser,
|
|
2130
|
-
group: u.group
|
|
2131
|
-
}));
|
|
2132
2194
|
await client.updatePost(projectId, postId, {
|
|
2133
2195
|
subject: title ?? post.subject,
|
|
2134
2196
|
body: {
|
|
@@ -2152,6 +2214,11 @@ var postEditCommand = new import_commander13.Command("edit").description("\uC5C5
|
|
|
2152
2214
|
"\u26A0 --link-task \uB294 --title/--body \uC640 \uD568\uAED8 \uC0AC\uC6A9 \uC2DC\uC5D0\uB9CC \uC801\uC6A9\uB429\uB2C8\uB2E4.\n"
|
|
2153
2215
|
);
|
|
2154
2216
|
}
|
|
2217
|
+
if (ccNames.length > 0 || ccGroups.length > 0 || opts.ccClear || toNames.length > 0 || toGroups.length > 0 || opts.toClear) {
|
|
2218
|
+
process.stderr.write(
|
|
2219
|
+
"\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"
|
|
2220
|
+
);
|
|
2221
|
+
}
|
|
2155
2222
|
const original = serializePostFrontmatter(post, members);
|
|
2156
2223
|
const edited = await openInEditor(original);
|
|
2157
2224
|
if (original === edited) {
|
|
@@ -2234,7 +2301,7 @@ async function resolveUsers2(client, projectId, inputs) {
|
|
|
2234
2301
|
}
|
|
2235
2302
|
return users;
|
|
2236
2303
|
}
|
|
2237
|
-
var postCreateCommand = new import_commander14.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("--cc <members...>", "\uCC38\uC870\uC790 (\uC774\uB984 \uB610\uB294 \uC774\uBA54\uC77C, \uC5EC\uB7EC \uBA85 \uAC00\uB2A5)").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("--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) => {
|
|
2304
|
+
var postCreateCommand = new import_commander14.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("--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) => {
|
|
2238
2305
|
const globalOpts = postCreateCommand.optsWithGlobals();
|
|
2239
2306
|
const config = await getConfigOrThrow();
|
|
2240
2307
|
const client = new DoorayApiClient(config.apiKey, config.baseUrl);
|
|
@@ -2299,8 +2366,14 @@ var postCreateCommand = new import_commander14.Command("create").description("\u
|
|
|
2299
2366
|
}
|
|
2300
2367
|
return;
|
|
2301
2368
|
}
|
|
2302
|
-
const
|
|
2303
|
-
const
|
|
2369
|
+
const ccGroupCodes = (opts.ccGroup ?? []).filter((s) => s.length > 0);
|
|
2370
|
+
const toGroupCodes = (opts.toGroup ?? []).filter((s) => s.length > 0);
|
|
2371
|
+
const toUsersMembers = opts.to ? await resolveUsers2(client, projectId, opts.to) : [];
|
|
2372
|
+
const ccUsersMembers = opts.cc ? await resolveUsers2(client, projectId, opts.cc) : [];
|
|
2373
|
+
const toUsersGroups = toGroupCodes.length > 0 ? await resolveUserAdditions(client, projectId, [], toGroupCodes) : [];
|
|
2374
|
+
const ccUsersGroups = ccGroupCodes.length > 0 ? await resolveUserAdditions(client, projectId, [], ccGroupCodes) : [];
|
|
2375
|
+
const toUsers = [...toUsersMembers, ...toUsersGroups];
|
|
2376
|
+
const ccUsers = [...ccUsersMembers, ...ccUsersGroups];
|
|
2304
2377
|
const tagInputs = (opts.tag ?? []).filter((s) => s.length > 0);
|
|
2305
2378
|
const [tagIds, parentPostId, milestoneId] = await Promise.all([
|
|
2306
2379
|
tagInputs.length > 0 ? resolveTags(client, projectId, tagInputs) : validateMandatoryTags(client, projectId).then(() => void 0),
|
|
@@ -4401,7 +4474,7 @@ function sanitizeArgv(argv) {
|
|
|
4401
4474
|
|
|
4402
4475
|
// src/index.ts
|
|
4403
4476
|
var program = new import_commander47.Command();
|
|
4404
|
-
program.name("dooray").description("Dooray REST API CLI").version("0.
|
|
4477
|
+
program.name("dooray").description("Dooray REST API CLI").version("0.8.0").option("--json", "JSON \uD615\uC2DD\uC73C\uB85C \uCD9C\uB825").option("--quiet", "ID\uB9CC \uCD9C\uB825").option("--no-color", "\uC0C9\uC0C1 \uBE44\uD65C\uC131\uD654");
|
|
4405
4478
|
program.hook("preAction", () => {
|
|
4406
4479
|
const opts = program.opts();
|
|
4407
4480
|
if (opts.color === false || process.env.NO_COLOR) {
|
package/package.json
CHANGED
|
@@ -92,6 +92,9 @@ dooray doctor # 설정 검증
|
|
|
92
92
|
| 댓글 파일 업로드 | `dooray post comment file upload <project> <number> <comment-id> <path>` |
|
|
93
93
|
| 댓글 파일 다운로드 | `dooray post comment file download <project> <number> <comment-id> <file-id>` |
|
|
94
94
|
| 댓글 파일 삭제 | `dooray post comment file delete <project> <number> <comment-id> <file-id> --yes` |
|
|
95
|
+
| 참조자(cc) 멤버/그룹 추가 | `dooray post edit <project> <number> --cc-group <code>` — 기존 참조자 유지 + 그룹 추가 (dedupe, ADR-025) |
|
|
96
|
+
| 참조자 전체 교체 | `dooray post edit <project> <number> --cc-clear --cc <name>` — 기존 참조자 비우고 신규 멤버만 |
|
|
97
|
+
| 신규 업무 + 그룹 cc | `dooray post create <project> --title "..." --cc-group <code>` — 생성 시 그룹 참조자 포함 |
|
|
95
98
|
|
|
96
99
|
> **제목 옵션 네이밍**: `post` 와 `wiki page` 모두 `--title` 표준. `post`의 `--subject`는 deprecated alias로 당분간 동작하되, 새 코드에서는 `--title` 사용을 권장.
|
|
97
100
|
|
|
@@ -103,7 +106,7 @@ CLI로 처리 **불가능한** 작업. 아래 항목을 요청받으면 웹 UI
|
|
|
103
106
|
|
|
104
107
|
| 작업 | 대체 경로 | 근거 |
|
|
105
108
|
|---|---|---|
|
|
106
|
-
| 위키 페이지 **삭제** | 웹 UI (`https://{tenant}.dooray.com/wiki/...`) | Dooray REST API에 해당 엔드포인트 없음 (위키 댓글·첨부파일 삭제는 있지만 페이지 자체는
|
|
109
|
+
| 위키 페이지 **삭제** | 웹 UI (`https://{tenant}.dooray.com/wiki/...`) | Dooray REST API에 해당 엔드포인트 없음 (위키 댓글·첨부파일 삭제는 있지만 페이지 자체는 없음 — ADR 또는 Dooray 공식 API 문서 미제공) |
|
|
107
110
|
| 프로젝트 삭제 | 웹 UI (admin 페이지) | API 미지원 |
|
|
108
111
|
|
|
109
112
|
위키 페이지를 잘못 만든 경우(테스트/중복) **soft delete(빈 제목·본문) 우회 금지** — 페이지가 트리에 남아 사용자 혼란 유발.
|
|
@@ -279,6 +282,46 @@ dooray post edit <project> <number> --body "새 본문"
|
|
|
279
282
|
dooray post edit <project> <number> --title "새 제목" --body-file ./updated.md
|
|
280
283
|
```
|
|
281
284
|
|
|
285
|
+
### 참조자(cc) / 담당자(to) 변경 — 멤버 · 그룹 (ADR-025)
|
|
286
|
+
|
|
287
|
+
```bash
|
|
288
|
+
# 기존 참조자 유지 + 그룹 추가 (dedupe: organizationMemberId / projectMemberGroupId)
|
|
289
|
+
dooray post edit <project> <number> --cc-group dev-team
|
|
290
|
+
|
|
291
|
+
# 기존 참조자 전부 비우고 신규 멤버만
|
|
292
|
+
dooray post edit <project> <number> --cc-clear --cc 홍길동
|
|
293
|
+
|
|
294
|
+
# 담당자(to)도 동일 패턴: --to / --to-group / --to-clear
|
|
295
|
+
dooray post edit <project> <number> --to 김철수 --to-group qa-team
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
dry-run 으로 변경 결과 미리보기 (API 호출 없음):
|
|
299
|
+
|
|
300
|
+
```bash
|
|
301
|
+
dooray post edit --id "$POST_ID" --cc-group qa-team --dry-run --json \
|
|
302
|
+
| jq '.users.cc'
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
> interactive (`$EDITOR`) 모드에서는 위 옵션이 무시되고 stderr 경고가 출력됩니다.
|
|
306
|
+
|
|
307
|
+
## 신규 업무 생성 후 그룹 cc 첨부 (ADR-025)
|
|
308
|
+
|
|
309
|
+
audit 리포트 분석 → 신규 업무 생성 → 후속으로 특정 그룹을 참조에 추가하는 자동화 패턴:
|
|
310
|
+
|
|
311
|
+
```bash
|
|
312
|
+
# 1. 신규 업무 생성 (그룹 cc 포함)
|
|
313
|
+
POST_ID=$(dooray post create <project> \
|
|
314
|
+
--title "주간 audit 리포트" \
|
|
315
|
+
--body-file ./report.md \
|
|
316
|
+
--cc-group dev-team \
|
|
317
|
+
--json | jq -r '.id')
|
|
318
|
+
|
|
319
|
+
# 2. (필요 시) 후속으로 cc 추가
|
|
320
|
+
dooray post edit --id "$POST_ID" --cc-group qa-team
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
---
|
|
324
|
+
|
|
282
325
|
### 댓글 추가 (non-interactive)
|
|
283
326
|
|
|
284
327
|
```bash
|