azdo-cli 0.2.0-develop.65 → 0.2.0-develop.71
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 +18 -1
- package/dist/index.js +92 -71
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -53,7 +53,7 @@ azdo set-state 12345 "Active"
|
|
|
53
53
|
|
|
54
54
|
| Command | Purpose | Common Flags |
|
|
55
55
|
| --- | --- | --- |
|
|
56
|
-
| `azdo get-item <id>` | Read a work item | `--short`, `--fields`, `--org`, `--project` |
|
|
56
|
+
| `azdo get-item <id>` | Read a work item | `--short`, `--fields`, `--markdown`, `--org`, `--project` |
|
|
57
57
|
| `azdo set-state <id> <state>` | Change work item state | `--json`, `--org`, `--project` |
|
|
58
58
|
| `azdo assign <id> [name]` | Assign or unassign owner | `--unassign`, `--json`, `--org`, `--project` |
|
|
59
59
|
| `azdo set-field <id> <field> <value>` | Update any field | `--json`, `--org`, `--project` |
|
|
@@ -75,6 +75,12 @@ azdo get-item 12345 --short
|
|
|
75
75
|
|
|
76
76
|
# Include extra fields for this call
|
|
77
77
|
azdo get-item 12345 --fields "System.Tags,Microsoft.VSTS.Common.Priority"
|
|
78
|
+
|
|
79
|
+
# Convert rich text fields to markdown
|
|
80
|
+
azdo get-item 12345 --markdown
|
|
81
|
+
|
|
82
|
+
# Disable markdown even if config is on
|
|
83
|
+
azdo get-item 12345 --no-markdown
|
|
78
84
|
```
|
|
79
85
|
|
|
80
86
|
```bash
|
|
@@ -89,6 +95,14 @@ azdo assign 12345 --unassign
|
|
|
89
95
|
azdo set-field 12345 System.Title "Updated title"
|
|
90
96
|
```
|
|
91
97
|
|
|
98
|
+
### Markdown Display
|
|
99
|
+
|
|
100
|
+
The `get-item` command can convert HTML rich-text fields to readable markdown. Resolution order:
|
|
101
|
+
|
|
102
|
+
1. `--markdown` / `--no-markdown` flag (highest priority)
|
|
103
|
+
2. Config setting: `azdo config set markdown true`
|
|
104
|
+
3. Default: off (HTML stripped to plain text)
|
|
105
|
+
|
|
92
106
|
### Markdown Field Commands
|
|
93
107
|
|
|
94
108
|
```bash
|
|
@@ -114,6 +128,9 @@ azdo config list
|
|
|
114
128
|
# Interactive setup
|
|
115
129
|
azdo config wizard
|
|
116
130
|
|
|
131
|
+
# Enable markdown display for all get-item calls
|
|
132
|
+
azdo config set markdown true
|
|
133
|
+
|
|
117
134
|
# Set/get/unset values
|
|
118
135
|
azdo config set fields "System.Tags,Custom.Priority"
|
|
119
136
|
azdo config get fields
|
package/dist/index.js
CHANGED
|
@@ -336,6 +336,13 @@ var SETTINGS = [
|
|
|
336
336
|
type: "string[]",
|
|
337
337
|
example: "System.Tags,Custom.Priority",
|
|
338
338
|
required: false
|
|
339
|
+
},
|
|
340
|
+
{
|
|
341
|
+
key: "markdown",
|
|
342
|
+
description: "Convert rich text fields to markdown on display",
|
|
343
|
+
type: "boolean",
|
|
344
|
+
example: "true",
|
|
345
|
+
required: false
|
|
339
346
|
}
|
|
340
347
|
];
|
|
341
348
|
var VALID_KEYS = SETTINGS.map((s) => s.key);
|
|
@@ -369,7 +376,7 @@ function saveConfig(config) {
|
|
|
369
376
|
}
|
|
370
377
|
function validateKey(key) {
|
|
371
378
|
if (!VALID_KEYS.includes(key)) {
|
|
372
|
-
throw new Error(`Unknown setting key "${key}". Valid keys:
|
|
379
|
+
throw new Error(`Unknown setting key "${key}". Valid keys: ${VALID_KEYS.join(", ")}`);
|
|
373
380
|
}
|
|
374
381
|
}
|
|
375
382
|
function getConfigValue(key) {
|
|
@@ -382,6 +389,11 @@ function setConfigValue(key, value) {
|
|
|
382
389
|
const config = loadConfig();
|
|
383
390
|
if (value === "") {
|
|
384
391
|
delete config[key];
|
|
392
|
+
} else if (key === "markdown") {
|
|
393
|
+
if (value !== "true" && value !== "false") {
|
|
394
|
+
throw new Error(`Invalid value "${value}" for markdown. Must be "true" or "false".`);
|
|
395
|
+
}
|
|
396
|
+
config.markdown = value === "true";
|
|
385
397
|
} else if (key === "fields") {
|
|
386
398
|
config.fields = value.split(",").map((s) => s.trim());
|
|
387
399
|
} else {
|
|
@@ -420,6 +432,61 @@ function resolveContext(options) {
|
|
|
420
432
|
);
|
|
421
433
|
}
|
|
422
434
|
|
|
435
|
+
// src/services/md-convert.ts
|
|
436
|
+
import { NodeHtmlMarkdown } from "node-html-markdown";
|
|
437
|
+
|
|
438
|
+
// src/services/html-detect.ts
|
|
439
|
+
var HTML_TAG_REGEX = /<\/?([a-z][a-z0-9]*)\b/gi;
|
|
440
|
+
var HTML_TAGS = /* @__PURE__ */ new Set([
|
|
441
|
+
"p",
|
|
442
|
+
"br",
|
|
443
|
+
"div",
|
|
444
|
+
"span",
|
|
445
|
+
"strong",
|
|
446
|
+
"em",
|
|
447
|
+
"b",
|
|
448
|
+
"i",
|
|
449
|
+
"u",
|
|
450
|
+
"a",
|
|
451
|
+
"ul",
|
|
452
|
+
"ol",
|
|
453
|
+
"li",
|
|
454
|
+
"h1",
|
|
455
|
+
"h2",
|
|
456
|
+
"h3",
|
|
457
|
+
"h4",
|
|
458
|
+
"h5",
|
|
459
|
+
"h6",
|
|
460
|
+
"table",
|
|
461
|
+
"tr",
|
|
462
|
+
"td",
|
|
463
|
+
"th",
|
|
464
|
+
"img",
|
|
465
|
+
"pre",
|
|
466
|
+
"code"
|
|
467
|
+
]);
|
|
468
|
+
function isHtml(content) {
|
|
469
|
+
let match;
|
|
470
|
+
HTML_TAG_REGEX.lastIndex = 0;
|
|
471
|
+
while ((match = HTML_TAG_REGEX.exec(content)) !== null) {
|
|
472
|
+
if (HTML_TAGS.has(match[1].toLowerCase())) {
|
|
473
|
+
return true;
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
return false;
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
// src/services/md-convert.ts
|
|
480
|
+
function htmlToMarkdown(html) {
|
|
481
|
+
return NodeHtmlMarkdown.translate(html);
|
|
482
|
+
}
|
|
483
|
+
function toMarkdown(content) {
|
|
484
|
+
if (isHtml(content)) {
|
|
485
|
+
return htmlToMarkdown(content);
|
|
486
|
+
}
|
|
487
|
+
return content;
|
|
488
|
+
}
|
|
489
|
+
|
|
423
490
|
// src/services/command-helpers.ts
|
|
424
491
|
function parseWorkItemId(idStr) {
|
|
425
492
|
const id = Number.parseInt(idStr, 10);
|
|
@@ -508,7 +575,24 @@ function stripHtml(html) {
|
|
|
508
575
|
text = text.replace(/\n{3,}/g, "\n\n");
|
|
509
576
|
return text.trim();
|
|
510
577
|
}
|
|
511
|
-
function
|
|
578
|
+
function convertRichText(html, markdown) {
|
|
579
|
+
if (!html) return "";
|
|
580
|
+
return markdown ? toMarkdown(html) : stripHtml(html);
|
|
581
|
+
}
|
|
582
|
+
function formatExtraFields(extraFields, markdown) {
|
|
583
|
+
return Object.entries(extraFields).map(([refName, value]) => {
|
|
584
|
+
const fieldLabel = refName.includes(".") ? refName.split(".").pop() : refName;
|
|
585
|
+
const displayValue = markdown ? toMarkdown(value) : value;
|
|
586
|
+
return `${fieldLabel.padEnd(13)}${displayValue}`;
|
|
587
|
+
});
|
|
588
|
+
}
|
|
589
|
+
function summarizeDescription(text, label) {
|
|
590
|
+
const descLines = text.split("\n").filter((l) => l.trim() !== "");
|
|
591
|
+
const firstThree = descLines.slice(0, 3);
|
|
592
|
+
const suffix = descLines.length > 3 ? "\n..." : "";
|
|
593
|
+
return [`${label("Description:")}${firstThree.join("\n")}${suffix}`];
|
|
594
|
+
}
|
|
595
|
+
function formatWorkItem(workItem, short, markdown = false) {
|
|
512
596
|
const lines = [];
|
|
513
597
|
const label = (name) => name.padEnd(13);
|
|
514
598
|
lines.push(`${label("ID:")}${workItem.id}`);
|
|
@@ -522,19 +606,12 @@ function formatWorkItem(workItem, short) {
|
|
|
522
606
|
}
|
|
523
607
|
lines.push(`${label("URL:")}${workItem.url}`);
|
|
524
608
|
if (workItem.extraFields) {
|
|
525
|
-
|
|
526
|
-
const fieldLabel = refName.includes(".") ? refName.split(".").pop() : refName;
|
|
527
|
-
lines.push(`${fieldLabel.padEnd(13)}${value}`);
|
|
528
|
-
}
|
|
609
|
+
lines.push(...formatExtraFields(workItem.extraFields, markdown));
|
|
529
610
|
}
|
|
530
611
|
lines.push("");
|
|
531
|
-
const descriptionText = workItem.description
|
|
612
|
+
const descriptionText = convertRichText(workItem.description, markdown);
|
|
532
613
|
if (short) {
|
|
533
|
-
|
|
534
|
-
const firstThree = descLines.slice(0, 3);
|
|
535
|
-
const truncated = descLines.length > 3;
|
|
536
|
-
const descSummary = firstThree.join("\n") + (truncated ? "\n..." : "");
|
|
537
|
-
lines.push(`${label("Description:")}${descSummary}`);
|
|
614
|
+
lines.push(...summarizeDescription(descriptionText, label));
|
|
538
615
|
} else {
|
|
539
616
|
lines.push("Description:");
|
|
540
617
|
lines.push(descriptionText);
|
|
@@ -543,7 +620,7 @@ function formatWorkItem(workItem, short) {
|
|
|
543
620
|
}
|
|
544
621
|
function createGetItemCommand() {
|
|
545
622
|
const command = new Command("get-item");
|
|
546
|
-
command.description("Retrieve an Azure DevOps work item by ID").argument("<id>", "work item ID").option("--org <org>", "Azure DevOps organization").option("--project <project>", "Azure DevOps project").option("--short", "show abbreviated output").option("--fields <fields>", "comma-separated additional field reference names").action(
|
|
623
|
+
command.description("Retrieve an Azure DevOps work item by ID").argument("<id>", "work item ID").option("--org <org>", "Azure DevOps organization").option("--project <project>", "Azure DevOps project").option("--short", "show abbreviated output").option("--fields <fields>", "comma-separated additional field reference names").option("--markdown", "convert rich text fields to markdown").action(
|
|
547
624
|
async (idStr, options) => {
|
|
548
625
|
const id = parseWorkItemId(idStr);
|
|
549
626
|
validateOrgProjectPair(options);
|
|
@@ -553,7 +630,8 @@ function createGetItemCommand() {
|
|
|
553
630
|
const credential = await resolvePat();
|
|
554
631
|
const fieldsList = options.fields !== void 0 ? parseRequestedFields(options.fields) : parseRequestedFields(loadConfig().fields);
|
|
555
632
|
const workItem = await getWorkItem(context, id, credential.pat, fieldsList);
|
|
556
|
-
const
|
|
633
|
+
const markdownEnabled = options.markdown ?? loadConfig().markdown ?? false;
|
|
634
|
+
const output = formatWorkItem(workItem, options.short ?? false, markdownEnabled);
|
|
557
635
|
process.stdout.write(output + "\n");
|
|
558
636
|
} catch (err) {
|
|
559
637
|
handleCommandError(err, id, context, "read", false);
|
|
@@ -857,63 +935,6 @@ function createSetFieldCommand() {
|
|
|
857
935
|
|
|
858
936
|
// src/commands/get-md-field.ts
|
|
859
937
|
import { Command as Command7 } from "commander";
|
|
860
|
-
|
|
861
|
-
// src/services/md-convert.ts
|
|
862
|
-
import { NodeHtmlMarkdown } from "node-html-markdown";
|
|
863
|
-
|
|
864
|
-
// src/services/html-detect.ts
|
|
865
|
-
var HTML_TAG_REGEX = /<\/?([a-z][a-z0-9]*)\b/gi;
|
|
866
|
-
var HTML_TAGS = /* @__PURE__ */ new Set([
|
|
867
|
-
"p",
|
|
868
|
-
"br",
|
|
869
|
-
"div",
|
|
870
|
-
"span",
|
|
871
|
-
"strong",
|
|
872
|
-
"em",
|
|
873
|
-
"b",
|
|
874
|
-
"i",
|
|
875
|
-
"u",
|
|
876
|
-
"a",
|
|
877
|
-
"ul",
|
|
878
|
-
"ol",
|
|
879
|
-
"li",
|
|
880
|
-
"h1",
|
|
881
|
-
"h2",
|
|
882
|
-
"h3",
|
|
883
|
-
"h4",
|
|
884
|
-
"h5",
|
|
885
|
-
"h6",
|
|
886
|
-
"table",
|
|
887
|
-
"tr",
|
|
888
|
-
"td",
|
|
889
|
-
"th",
|
|
890
|
-
"img",
|
|
891
|
-
"pre",
|
|
892
|
-
"code"
|
|
893
|
-
]);
|
|
894
|
-
function isHtml(content) {
|
|
895
|
-
let match;
|
|
896
|
-
HTML_TAG_REGEX.lastIndex = 0;
|
|
897
|
-
while ((match = HTML_TAG_REGEX.exec(content)) !== null) {
|
|
898
|
-
if (HTML_TAGS.has(match[1].toLowerCase())) {
|
|
899
|
-
return true;
|
|
900
|
-
}
|
|
901
|
-
}
|
|
902
|
-
return false;
|
|
903
|
-
}
|
|
904
|
-
|
|
905
|
-
// src/services/md-convert.ts
|
|
906
|
-
function htmlToMarkdown(html) {
|
|
907
|
-
return NodeHtmlMarkdown.translate(html);
|
|
908
|
-
}
|
|
909
|
-
function toMarkdown(content) {
|
|
910
|
-
if (isHtml(content)) {
|
|
911
|
-
return htmlToMarkdown(content);
|
|
912
|
-
}
|
|
913
|
-
return content;
|
|
914
|
-
}
|
|
915
|
-
|
|
916
|
-
// src/commands/get-md-field.ts
|
|
917
938
|
function createGetMdFieldCommand() {
|
|
918
939
|
const command = new Command7("get-md-field");
|
|
919
940
|
command.description("Get a work item field value, converting HTML to markdown").argument("<id>", "work item ID").argument("<field>", "field reference name (e.g., System.Description)").option("--org <org>", "Azure DevOps organization").option("--project <project>", "Azure DevOps project").action(
|