azdo-cli 0.5.0-010-work-item-comments.149 → 0.5.0-develop.148
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 +0 -39
- package/dist/index.js +2 -169
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -13,7 +13,6 @@ Azure DevOps CLI focused on work item read/write workflows.
|
|
|
13
13
|
- Assign and unassign work items (`assign`)
|
|
14
14
|
- Set any work item field by reference name (`set-field`)
|
|
15
15
|
- Create or update work items from markdown documents (`upsert`)
|
|
16
|
-
- Read and post work item comments (`comments`)
|
|
17
16
|
- Read rich-text fields as markdown (`get-md-field`)
|
|
18
17
|
- Set rich-text fields as markdown from inline text, file, or stdin (`set-md-field`)
|
|
19
18
|
- Check branch pull request status, open PRs to `develop`, and review active comments (`pr`)
|
|
@@ -68,10 +67,6 @@ azdo set-state 12345 "Active"
|
|
|
68
67
|
|
|
69
68
|
# 4) Create or update a work item from markdown
|
|
70
69
|
azdo upsert --type "User Story" --content $'---\nTitle: Improve markdown import UX\nState: New\n---'
|
|
71
|
-
|
|
72
|
-
# 5) Review work item discussion and post an update
|
|
73
|
-
azdo comments list 12345
|
|
74
|
-
azdo comments add 12345 "Investigating the root cause now."
|
|
75
70
|
```
|
|
76
71
|
|
|
77
72
|
## Command Cheat Sheet
|
|
@@ -83,7 +78,6 @@ azdo comments add 12345 "Investigating the root cause now."
|
|
|
83
78
|
| `azdo assign <id> [name]` | Assign or unassign owner | `--unassign`, `--json`, `--org`, `--project` |
|
|
84
79
|
| `azdo set-field <id> <field> <value>` | Update any field | `--json`, `--org`, `--project` |
|
|
85
80
|
| `azdo upsert [id]` | Create or update a work item from markdown | `--content`, `--file`, `--type`, `--json`, `--org`, `--project` |
|
|
86
|
-
| `azdo comments <subcommand>` | Read or add work item comments | `list`, `add`, `--json`, `--org`, `--project` |
|
|
87
81
|
| `azdo get-md-field <id> <field>` | Get field as markdown | `--org`, `--project` |
|
|
88
82
|
| `azdo set-md-field <id> <field> [content]` | Set markdown field | `--file`, `--json`, `--org`, `--project` |
|
|
89
83
|
| `azdo list-fields <id>` | List all fields of a work item | `--json`, `--org`, `--project` |
|
|
@@ -193,38 +187,6 @@ azdo pr comments
|
|
|
193
187
|
- Prints `Pull request #<id> has no active comments.` when the PR has no active comment threads
|
|
194
188
|
- Fails instead of guessing when no active PR or multiple active PRs exist
|
|
195
189
|
|
|
196
|
-
### Work Item Comment Commands
|
|
197
|
-
|
|
198
|
-
The `comments` command group works on a specific work item ID. It requires a PAT with `Work Items (read)` scope for listing comments and `Work Items (Read & Write)` to add a new comment.
|
|
199
|
-
|
|
200
|
-
```bash
|
|
201
|
-
# Read the visible comment history for a work item
|
|
202
|
-
azdo comments list 12345
|
|
203
|
-
|
|
204
|
-
# Read the same history as JSON
|
|
205
|
-
azdo comments list 12345 --json
|
|
206
|
-
|
|
207
|
-
# Post a progress update
|
|
208
|
-
azdo comments add 12345 "Investigation complete. Working on the fix next."
|
|
209
|
-
|
|
210
|
-
# Post the update and return JSON
|
|
211
|
-
azdo comments add 12345 "Queued validation run." --json
|
|
212
|
-
```
|
|
213
|
-
|
|
214
|
-
`azdo comments list`
|
|
215
|
-
|
|
216
|
-
- Resolves the target work item directly from the provided ID
|
|
217
|
-
- Retrieves the full visible comment history and follows Azure DevOps pagination internally
|
|
218
|
-
- Prints comments newest first with comment ID, author, timestamp, and body text
|
|
219
|
-
- Prints `Work item #<id> has no comments.` when the work item has no visible comments
|
|
220
|
-
|
|
221
|
-
`azdo comments add`
|
|
222
|
-
|
|
223
|
-
- Requires a non-empty `<text>` positional argument
|
|
224
|
-
- Preserves the supplied comment text as the submitted body
|
|
225
|
-
- Prints `Added comment #<commentId> to work item #<id>` on success
|
|
226
|
-
- Fails locally before any API call when the text is empty or whitespace-only
|
|
227
|
-
|
|
228
190
|
## azdo upsert
|
|
229
191
|
|
|
230
192
|
`azdo upsert` accepts a single markdown work-item document and either creates a new Azure DevOps work item or updates an existing one. Omit `[id]` to create; pass `[id]` to update that work item in place. Create mode defaults to `Task`, and `--type <work item type>` lets you create `Bug`, `User Story`, `Feature`, `Epic`, and other Azure DevOps work item types.
|
|
@@ -350,7 +312,6 @@ These commands support `--json` for machine-readable output:
|
|
|
350
312
|
- `set-field`
|
|
351
313
|
- `set-md-field`
|
|
352
314
|
- `upsert`
|
|
353
|
-
- `comments list|add`
|
|
354
315
|
- `pr status|open|comments`
|
|
355
316
|
- `config set|get|list|unset`
|
|
356
317
|
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/index.ts
|
|
4
|
-
import { Command as
|
|
4
|
+
import { Command as Command12 } from "commander";
|
|
5
5
|
|
|
6
6
|
// src/version.ts
|
|
7
7
|
import { readFileSync } from "fs";
|
|
@@ -96,42 +96,6 @@ function writeHeaders(pat) {
|
|
|
96
96
|
"Content-Type": "application/json-patch+json"
|
|
97
97
|
};
|
|
98
98
|
}
|
|
99
|
-
function buildWorkItemCommentsListUrl(context, id, continuationToken) {
|
|
100
|
-
const url = new URL(
|
|
101
|
-
`https://dev.azure.com/${encodeURIComponent(context.org)}/${encodeURIComponent(context.project)}/_apis/wit/workItems/${id}/comments`
|
|
102
|
-
);
|
|
103
|
-
url.searchParams.set("api-version", "7.1-preview.4");
|
|
104
|
-
url.searchParams.set("order", "desc");
|
|
105
|
-
if (continuationToken) {
|
|
106
|
-
url.searchParams.set("continuationToken", continuationToken);
|
|
107
|
-
}
|
|
108
|
-
return url;
|
|
109
|
-
}
|
|
110
|
-
function buildWorkItemCommentsUrl(context, id) {
|
|
111
|
-
const url = new URL(
|
|
112
|
-
`https://dev.azure.com/${encodeURIComponent(context.org)}/${encodeURIComponent(context.project)}/_apis/wit/workItems/${id}/comments`
|
|
113
|
-
);
|
|
114
|
-
url.searchParams.set("api-version", "7.1-preview.4");
|
|
115
|
-
return url;
|
|
116
|
-
}
|
|
117
|
-
function mapWorkItemComment(comment, fallbackWorkItemId) {
|
|
118
|
-
return {
|
|
119
|
-
id: comment.id ?? comment.commentId ?? 0,
|
|
120
|
-
workItemId: comment.workItemId ?? fallbackWorkItemId,
|
|
121
|
-
text: typeof comment.text === "string" ? comment.text : "",
|
|
122
|
-
author: comment.createdBy?.displayName ?? null,
|
|
123
|
-
createdAt: comment.createdDate ?? null,
|
|
124
|
-
modifiedAt: comment.modifiedDate ?? null,
|
|
125
|
-
isDeleted: comment.isDeleted === true
|
|
126
|
-
};
|
|
127
|
-
}
|
|
128
|
-
function readContinuationToken(response, data) {
|
|
129
|
-
if (typeof data.continuationToken === "string" && data.continuationToken.trim() !== "") {
|
|
130
|
-
return data.continuationToken;
|
|
131
|
-
}
|
|
132
|
-
const headerToken = response.headers?.get("x-ms-continuationtoken") ?? response.headers?.get("continuationtoken") ?? null;
|
|
133
|
-
return headerToken && headerToken.trim() !== "" ? headerToken : null;
|
|
134
|
-
}
|
|
135
99
|
async function readWriteResponse(response, errorCode) {
|
|
136
100
|
if (response.status === 400) {
|
|
137
101
|
const serverMessage = await readResponseMessage(response) ?? "Unknown error";
|
|
@@ -240,55 +204,6 @@ async function getWorkItemFieldValue(context, id, pat, fieldName) {
|
|
|
240
204
|
}
|
|
241
205
|
return stringifyFieldValue(value);
|
|
242
206
|
}
|
|
243
|
-
async function listWorkItemComments(context, id, pat) {
|
|
244
|
-
const comments = [];
|
|
245
|
-
let continuationToken = null;
|
|
246
|
-
do {
|
|
247
|
-
const response = await fetchWithErrors(
|
|
248
|
-
buildWorkItemCommentsListUrl(context, id, continuationToken ?? void 0).toString(),
|
|
249
|
-
{ headers: authHeaders(pat) }
|
|
250
|
-
);
|
|
251
|
-
if (!response.ok) {
|
|
252
|
-
throw new Error(`HTTP_${response.status}`);
|
|
253
|
-
}
|
|
254
|
-
const data = await response.json();
|
|
255
|
-
comments.push(
|
|
256
|
-
...(data.comments ?? []).map((comment) => mapWorkItemComment(comment, id)).filter((comment) => !comment.isDeleted)
|
|
257
|
-
);
|
|
258
|
-
continuationToken = readContinuationToken(response, data);
|
|
259
|
-
} while (continuationToken !== null);
|
|
260
|
-
return {
|
|
261
|
-
workItemId: id,
|
|
262
|
-
count: comments.length,
|
|
263
|
-
comments
|
|
264
|
-
};
|
|
265
|
-
}
|
|
266
|
-
async function addWorkItemComment(context, id, pat, text) {
|
|
267
|
-
const response = await fetchWithErrors(buildWorkItemCommentsUrl(context, id).toString(), {
|
|
268
|
-
method: "POST",
|
|
269
|
-
headers: {
|
|
270
|
-
...authHeaders(pat),
|
|
271
|
-
"Content-Type": "application/json"
|
|
272
|
-
},
|
|
273
|
-
body: JSON.stringify({ text })
|
|
274
|
-
});
|
|
275
|
-
if (response.status === 400) {
|
|
276
|
-
const serverMessage = await readResponseMessage(response) ?? "Unknown error";
|
|
277
|
-
throw new Error(`BAD_REQUEST: ${serverMessage}`);
|
|
278
|
-
}
|
|
279
|
-
if (!response.ok) {
|
|
280
|
-
throw new Error(`HTTP_${response.status}`);
|
|
281
|
-
}
|
|
282
|
-
const data = await response.json();
|
|
283
|
-
return {
|
|
284
|
-
workItemId: data.workItemId ?? id,
|
|
285
|
-
commentId: data.commentId ?? data.id ?? 0,
|
|
286
|
-
text: typeof data.text === "string" ? data.text : text,
|
|
287
|
-
author: data.createdBy?.displayName ?? null,
|
|
288
|
-
createdAt: data.createdDate ?? null,
|
|
289
|
-
url: data.url ?? null
|
|
290
|
-
};
|
|
291
|
-
}
|
|
292
207
|
async function updateWorkItem(context, id, pat, fieldName, operations) {
|
|
293
208
|
const result = await applyWorkItemPatch(context, id, pat, operations);
|
|
294
209
|
const title = result.fields["System.Title"];
|
|
@@ -1965,89 +1880,8 @@ function createPrCommand() {
|
|
|
1965
1880
|
return command;
|
|
1966
1881
|
}
|
|
1967
1882
|
|
|
1968
|
-
// src/commands/comments.ts
|
|
1969
|
-
import { Command as Command12 } from "commander";
|
|
1970
|
-
function writeError2(message) {
|
|
1971
|
-
process.stderr.write(`Error: ${message}
|
|
1972
|
-
`);
|
|
1973
|
-
process.exit(1);
|
|
1974
|
-
}
|
|
1975
|
-
function formatCommentHeader(comment) {
|
|
1976
|
-
const author = comment.author ?? "Unknown";
|
|
1977
|
-
const timestamp = comment.modifiedAt ?? comment.createdAt ?? "Unknown time";
|
|
1978
|
-
return `Comment #${comment.id} by ${author} at ${timestamp}`;
|
|
1979
|
-
}
|
|
1980
|
-
function formatComments(result) {
|
|
1981
|
-
const lines = [`Comments for work item #${result.workItemId}`];
|
|
1982
|
-
for (const comment of result.comments) {
|
|
1983
|
-
lines.push("", formatCommentHeader(comment), comment.text);
|
|
1984
|
-
}
|
|
1985
|
-
return lines.join("\n");
|
|
1986
|
-
}
|
|
1987
|
-
function createCommentsListCommand() {
|
|
1988
|
-
const command = new Command12("list");
|
|
1989
|
-
command.description("List visible comments for a work item").argument("<id>", "work item ID").option("--org <org>", "Azure DevOps organization").option("--project <project>", "Azure DevOps project").option("--json", "output JSON").action(async (idStr, options) => {
|
|
1990
|
-
validateOrgProjectPair(options);
|
|
1991
|
-
const id = parseWorkItemId(idStr);
|
|
1992
|
-
let context;
|
|
1993
|
-
try {
|
|
1994
|
-
context = resolveContext(options);
|
|
1995
|
-
const credential = await resolvePat();
|
|
1996
|
-
const result = await listWorkItemComments(context, id, credential.pat);
|
|
1997
|
-
if (options.json) {
|
|
1998
|
-
process.stdout.write(`${JSON.stringify(result, null, 2)}
|
|
1999
|
-
`);
|
|
2000
|
-
return;
|
|
2001
|
-
}
|
|
2002
|
-
if (result.comments.length === 0) {
|
|
2003
|
-
process.stdout.write(`Work item #${id} has no comments.
|
|
2004
|
-
`);
|
|
2005
|
-
return;
|
|
2006
|
-
}
|
|
2007
|
-
process.stdout.write(`${formatComments(result)}
|
|
2008
|
-
`);
|
|
2009
|
-
} catch (err) {
|
|
2010
|
-
handleCommandError(err, id, context, "read");
|
|
2011
|
-
}
|
|
2012
|
-
});
|
|
2013
|
-
return command;
|
|
2014
|
-
}
|
|
2015
|
-
function createCommentsAddCommand() {
|
|
2016
|
-
const command = new Command12("add");
|
|
2017
|
-
command.description("Add a comment to a work item").argument("<id>", "work item ID").argument("<text>", "comment text").option("--org <org>", "Azure DevOps organization").option("--project <project>", "Azure DevOps project").option("--json", "output JSON").action(async (idStr, text, options) => {
|
|
2018
|
-
validateOrgProjectPair(options);
|
|
2019
|
-
const id = parseWorkItemId(idStr);
|
|
2020
|
-
if (text.trim() === "") {
|
|
2021
|
-
writeError2("Comment text must be a non-empty string.");
|
|
2022
|
-
}
|
|
2023
|
-
let context;
|
|
2024
|
-
try {
|
|
2025
|
-
context = resolveContext(options);
|
|
2026
|
-
const credential = await resolvePat();
|
|
2027
|
-
const result = await addWorkItemComment(context, id, credential.pat, text);
|
|
2028
|
-
if (options.json) {
|
|
2029
|
-
process.stdout.write(`${JSON.stringify(result, null, 2)}
|
|
2030
|
-
`);
|
|
2031
|
-
return;
|
|
2032
|
-
}
|
|
2033
|
-
process.stdout.write(`Added comment #${result.commentId} to work item #${result.workItemId}
|
|
2034
|
-
`);
|
|
2035
|
-
} catch (err) {
|
|
2036
|
-
handleCommandError(err, id, context, "write");
|
|
2037
|
-
}
|
|
2038
|
-
});
|
|
2039
|
-
return command;
|
|
2040
|
-
}
|
|
2041
|
-
function createCommentsCommand() {
|
|
2042
|
-
const command = new Command12("comments");
|
|
2043
|
-
command.description("Manage Azure DevOps work item comments");
|
|
2044
|
-
command.addCommand(createCommentsListCommand());
|
|
2045
|
-
command.addCommand(createCommentsAddCommand());
|
|
2046
|
-
return command;
|
|
2047
|
-
}
|
|
2048
|
-
|
|
2049
1883
|
// src/index.ts
|
|
2050
|
-
var program = new
|
|
1884
|
+
var program = new Command12();
|
|
2051
1885
|
program.name("azdo").description("Azure DevOps CLI tool").version(version, "-v, --version");
|
|
2052
1886
|
program.addCommand(createGetItemCommand());
|
|
2053
1887
|
program.addCommand(createClearPatCommand());
|
|
@@ -2060,7 +1894,6 @@ program.addCommand(createSetMdFieldCommand());
|
|
|
2060
1894
|
program.addCommand(createUpsertCommand());
|
|
2061
1895
|
program.addCommand(createListFieldsCommand());
|
|
2062
1896
|
program.addCommand(createPrCommand());
|
|
2063
|
-
program.addCommand(createCommentsCommand());
|
|
2064
1897
|
program.showHelpAfterError();
|
|
2065
1898
|
program.parse();
|
|
2066
1899
|
if (process.argv.length <= 2) {
|