@thanaen/ado-cli 0.4.0 → 0.6.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/cli.js +95 -25
- package/package.json +1 -1
- package/skills/ado-workflows/SKILL.md +12 -9
package/dist/cli.js
CHANGED
|
@@ -57228,6 +57228,9 @@ function loadLocalConfig() {
|
|
|
57228
57228
|
return {};
|
|
57229
57229
|
}
|
|
57230
57230
|
}
|
|
57231
|
+
function isInteractive() {
|
|
57232
|
+
return Boolean(process.stdin.isTTY && process.stdout.isTTY);
|
|
57233
|
+
}
|
|
57231
57234
|
function censorPat(pat) {
|
|
57232
57235
|
if (pat.length <= 8) {
|
|
57233
57236
|
return "****";
|
|
@@ -57237,18 +57240,26 @@ function censorPat(pat) {
|
|
|
57237
57240
|
function getConfig() {
|
|
57238
57241
|
const fileConfig = loadFileConfig();
|
|
57239
57242
|
const localConfig = loadLocalConfig();
|
|
57240
|
-
const pat = process.env.DEVOPS_PAT ?? localConfig.pat ?? fileConfig.pat;
|
|
57243
|
+
const pat = process.env.ADO_PAT ?? process.env.DEVOPS_PAT ?? localConfig.pat ?? fileConfig.pat;
|
|
57241
57244
|
if (!pat) {
|
|
57242
|
-
|
|
57243
|
-
|
|
57245
|
+
if (isInteractive()) {
|
|
57246
|
+
console.error("Missing ADO_PAT (or DEVOPS_PAT) environment variable or pat in config file.");
|
|
57247
|
+
console.error(`Run "ado init" to create a config file at ${getConfigFilePath()}`);
|
|
57248
|
+
} else {
|
|
57249
|
+
console.error("Error: ADO_PAT environment variable is not set.");
|
|
57250
|
+
console.error('Please set it in your environment or in your Claude Desktop config (claude_desktop_config.json) under the "env" key.');
|
|
57251
|
+
console.error("Never paste your PAT directly in the chat.");
|
|
57252
|
+
}
|
|
57244
57253
|
process.exit(1);
|
|
57245
57254
|
}
|
|
57246
57255
|
const collectionUrl = process.env.ADO_COLLECTION_URL ?? localConfig.collectionUrl ?? fileConfig.collectionUrl ?? DEFAULT_COLLECTION_URL;
|
|
57247
57256
|
const project = process.env.ADO_PROJECT ?? localConfig.project ?? fileConfig.project ?? DEFAULT_PROJECT;
|
|
57248
57257
|
const repo = process.env.ADO_REPO ?? localConfig.repo ?? fileConfig.repo ?? DEFAULT_REPO;
|
|
57249
57258
|
if (isDefaultPlaceholder(collectionUrl) || isDefaultPlaceholder(project) || isDefaultPlaceholder(repo)) {
|
|
57250
|
-
console.error("ADO configuration is incomplete. Set ADO_COLLECTION_URL, ADO_PROJECT, and ADO_REPO.");
|
|
57251
|
-
|
|
57259
|
+
console.error("ADO configuration is incomplete. Set ADO_COLLECTION_URL, ADO_PROJECT, and ADO_REPO environment variables.");
|
|
57260
|
+
if (isInteractive()) {
|
|
57261
|
+
console.error(`You can also run "ado init" to create a config file at ${getConfigFilePath()}`);
|
|
57262
|
+
}
|
|
57252
57263
|
process.exit(1);
|
|
57253
57264
|
}
|
|
57254
57265
|
const insecure = process.env.ADO_INSECURE === "1" || localConfig.insecure === true || fileConfig.insecure === true;
|
|
@@ -57412,6 +57423,7 @@ var import_GitInterfaces = __toESM(require_GitInterfaces(), 1);
|
|
|
57412
57423
|
var import_WorkItemTrackingInterfaces = __toESM(require_WorkItemTrackingInterfaces(), 1);
|
|
57413
57424
|
var import_BuildInterfaces = __toESM(require_BuildInterfaces(), 1);
|
|
57414
57425
|
var import_VSSInterfaces = __toESM(require_VSSInterfaces(), 1);
|
|
57426
|
+
process.removeAllListeners("warning");
|
|
57415
57427
|
function pickRepo(config, value) {
|
|
57416
57428
|
return value || config.repo;
|
|
57417
57429
|
}
|
|
@@ -57467,9 +57479,9 @@ async function cmdSmoke(config) {
|
|
|
57467
57479
|
async function cmdStatus() {
|
|
57468
57480
|
const localConfig = loadLocalConfig();
|
|
57469
57481
|
const fileConfig = loadFileConfig();
|
|
57470
|
-
const pat = process.env.DEVOPS_PAT ?? localConfig.pat ?? fileConfig.pat;
|
|
57482
|
+
const pat = process.env.ADO_PAT ?? process.env.DEVOPS_PAT ?? localConfig.pat ?? fileConfig.pat;
|
|
57471
57483
|
if (!pat) {
|
|
57472
|
-
console.error("Not ready: DEVOPS_PAT is not set");
|
|
57484
|
+
console.error("Not ready: ADO_PAT (or DEVOPS_PAT) is not set");
|
|
57473
57485
|
process.exit(1);
|
|
57474
57486
|
}
|
|
57475
57487
|
const collectionUrl = process.env.ADO_COLLECTION_URL ?? localConfig.collectionUrl ?? fileConfig.collectionUrl ?? "";
|
|
@@ -57498,17 +57510,28 @@ async function cmdStatus() {
|
|
|
57498
57510
|
process.exit(1);
|
|
57499
57511
|
}
|
|
57500
57512
|
}
|
|
57501
|
-
async function cmdRepos(config) {
|
|
57513
|
+
async function cmdRepos(config, args = []) {
|
|
57514
|
+
const json = args.includes("--json");
|
|
57502
57515
|
const gitApi = await config.connection.getGitApi();
|
|
57503
57516
|
const repos = await gitApi.getRepositories(config.project);
|
|
57517
|
+
if (json) {
|
|
57518
|
+
console.log(JSON.stringify(repos.map((r) => ({ id: r.id, name: r.name })), null, 2));
|
|
57519
|
+
return;
|
|
57520
|
+
}
|
|
57504
57521
|
for (const repo of repos) {
|
|
57505
57522
|
console.log(`${repo.id} ${repo.name}`);
|
|
57506
57523
|
}
|
|
57507
57524
|
}
|
|
57508
|
-
async function cmdBranches(config,
|
|
57525
|
+
async function cmdBranches(config, args = []) {
|
|
57526
|
+
const json = args.includes("--json");
|
|
57527
|
+
const repoArg = args.find((a) => !a.startsWith("--"));
|
|
57509
57528
|
const gitApi = await config.connection.getGitApi();
|
|
57510
57529
|
const repo = pickRepo(config, repoArg);
|
|
57511
57530
|
const refs = await gitApi.getRefs(repo, config.project, "heads/");
|
|
57531
|
+
if (json) {
|
|
57532
|
+
console.log(JSON.stringify(refs.map((r) => ({ name: String(r.name || "").replace("refs/heads/", "") })), null, 2));
|
|
57533
|
+
return;
|
|
57534
|
+
}
|
|
57512
57535
|
for (const ref of refs) {
|
|
57513
57536
|
const name = String(ref.name || "").replace("refs/heads/", "");
|
|
57514
57537
|
console.log(name);
|
|
@@ -57553,18 +57576,25 @@ async function cmdWorkItemGet(config, idRaw, args = []) {
|
|
|
57553
57576
|
}, null, 2));
|
|
57554
57577
|
}
|
|
57555
57578
|
async function cmdWorkItemsRecent(config, args = []) {
|
|
57579
|
+
const json = args.includes("--json");
|
|
57580
|
+
const filteredArgs = args.filter((a) => a !== "--json");
|
|
57556
57581
|
let parsedArgs;
|
|
57557
57582
|
try {
|
|
57558
|
-
parsedArgs = parseWorkItemsRecentArgs(
|
|
57583
|
+
parsedArgs = parseWorkItemsRecentArgs(filteredArgs);
|
|
57559
57584
|
} catch (error) {
|
|
57560
57585
|
console.error(error instanceof Error ? error.message : String(error));
|
|
57561
|
-
console.error("Usage: workitems-recent [top] [--tag=<tag>] [--type=<work-item-type>] [--state=<state>]");
|
|
57586
|
+
console.error("Usage: workitems-recent [top] [--tag=<tag>] [--type=<work-item-type>] [--state=<state>] [--json]");
|
|
57562
57587
|
process.exit(1);
|
|
57563
57588
|
}
|
|
57564
57589
|
const witApi = await config.connection.getWorkItemTrackingApi();
|
|
57565
57590
|
const wiqlResult = await witApi.queryByWiql({ query: buildRecentWorkItemsWiql(parsedArgs.filters) }, { project: config.project }, undefined, parsedArgs.top);
|
|
57566
|
-
|
|
57567
|
-
|
|
57591
|
+
const ids = (wiqlResult.workItems ?? []).map((wi) => wi.id);
|
|
57592
|
+
if (json) {
|
|
57593
|
+
console.log(JSON.stringify(ids, null, 2));
|
|
57594
|
+
return;
|
|
57595
|
+
}
|
|
57596
|
+
for (const id of ids) {
|
|
57597
|
+
console.log(id);
|
|
57568
57598
|
}
|
|
57569
57599
|
}
|
|
57570
57600
|
async function cmdWorkItemComments(config, idRaw, args = []) {
|
|
@@ -57670,12 +57700,26 @@ async function cmdWorkItemCommentUpdate(config, idRaw, commentIdRaw, args = [])
|
|
|
57670
57700
|
text: result.text ?? text
|
|
57671
57701
|
}, null, 2));
|
|
57672
57702
|
}
|
|
57673
|
-
async function cmdPrs(config,
|
|
57703
|
+
async function cmdPrs(config, args = []) {
|
|
57704
|
+
const json = args.includes("--json");
|
|
57705
|
+
const positionals = args.filter((a) => !a.startsWith("--"));
|
|
57706
|
+
const status = positionals[0] ?? "active";
|
|
57707
|
+
const topRaw = positionals[1] ?? "10";
|
|
57708
|
+
const repoArg = positionals[2];
|
|
57674
57709
|
const top = Number(topRaw);
|
|
57675
57710
|
const boundedTop = Number.isFinite(top) && top > 0 ? Math.min(top, 50) : 10;
|
|
57676
57711
|
const repo = pickRepo(config, repoArg);
|
|
57677
57712
|
const gitApi = await config.connection.getGitApi();
|
|
57678
57713
|
const prs = await gitApi.getPullRequests(repo, { status: mapPrStatus(status) }, config.project, undefined, undefined, boundedTop);
|
|
57714
|
+
if (json) {
|
|
57715
|
+
console.log(JSON.stringify(prs.map((pr) => ({
|
|
57716
|
+
id: pr.pullRequestId,
|
|
57717
|
+
status: prStatusName(pr.status),
|
|
57718
|
+
title: pr.title,
|
|
57719
|
+
createdBy: pr.createdBy?.displayName ?? null
|
|
57720
|
+
})), null, 2));
|
|
57721
|
+
return;
|
|
57722
|
+
}
|
|
57679
57723
|
for (const pr of prs) {
|
|
57680
57724
|
const createdBy = pr.createdBy?.displayName ?? "unknown";
|
|
57681
57725
|
console.log(`#${pr.pullRequestId} [${prStatusName(pr.status)}] ${pr.title} (${createdBy})`);
|
|
@@ -57772,11 +57816,24 @@ async function cmdPrAutocomplete(config, idRaw, repoArg) {
|
|
|
57772
57816
|
console.log(`Enabled auto-complete for PR #${id}`);
|
|
57773
57817
|
}
|
|
57774
57818
|
}
|
|
57775
|
-
async function cmdBuilds(config,
|
|
57819
|
+
async function cmdBuilds(config, args = []) {
|
|
57820
|
+
const json = args.includes("--json");
|
|
57821
|
+
const positionals = args.filter((a) => !a.startsWith("--"));
|
|
57822
|
+
const topRaw = positionals[0] ?? "10";
|
|
57776
57823
|
const top = Number(topRaw);
|
|
57777
57824
|
const boundedTop = Number.isFinite(top) && top > 0 ? Math.min(top, 50) : 10;
|
|
57778
57825
|
const buildApi = await config.connection.getBuildApi();
|
|
57779
57826
|
const builds = await buildApi.getBuilds(config.project, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, boundedTop, undefined, undefined, undefined, import_BuildInterfaces.BuildQueryOrder.QueueTimeDescending);
|
|
57827
|
+
if (json) {
|
|
57828
|
+
console.log(JSON.stringify(builds.map((b) => ({
|
|
57829
|
+
id: b.id,
|
|
57830
|
+
status: b.status,
|
|
57831
|
+
result: b.result ?? null,
|
|
57832
|
+
definition: b.definition?.name ?? null,
|
|
57833
|
+
sourceBranch: b.sourceBranch ?? null
|
|
57834
|
+
})), null, 2));
|
|
57835
|
+
return;
|
|
57836
|
+
}
|
|
57780
57837
|
for (const b of builds) {
|
|
57781
57838
|
console.log(`#${b.id} ${b.status}/${b.result ?? "n/a"} ${b.definition?.name ?? "unknown"} ${b.sourceBranch ?? ""}`);
|
|
57782
57839
|
}
|
|
@@ -57941,6 +57998,11 @@ async function cmdPrCherryPick(config, args) {
|
|
|
57941
57998
|
}
|
|
57942
57999
|
}
|
|
57943
58000
|
async function cmdInit(args) {
|
|
58001
|
+
if (!isInteractive()) {
|
|
58002
|
+
console.error("The init command requires an interactive terminal.");
|
|
58003
|
+
console.error("For non-interactive usage, set environment variables: ADO_PAT, ADO_COLLECTION_URL, ADO_PROJECT, ADO_REPO");
|
|
58004
|
+
process.exit(1);
|
|
58005
|
+
}
|
|
57944
58006
|
const isLocal = args.includes("--local");
|
|
57945
58007
|
const { createInterface } = await import("node:readline/promises");
|
|
57946
58008
|
const { mkdirSync, writeFileSync, existsSync: existsSync2 } = await import("node:fs");
|
|
@@ -57999,7 +58061,7 @@ Configuration saved to ${configPath}`);
|
|
|
57999
58061
|
function cmdConfig() {
|
|
58000
58062
|
const fileConfig = loadFileConfig();
|
|
58001
58063
|
const localConfig = loadLocalConfig();
|
|
58002
|
-
const pat = process.env.DEVOPS_PAT ?? localConfig.pat ?? fileConfig.pat;
|
|
58064
|
+
const pat = process.env.ADO_PAT ?? process.env.DEVOPS_PAT ?? localConfig.pat ?? fileConfig.pat;
|
|
58003
58065
|
const collectionUrl = process.env.ADO_COLLECTION_URL ?? localConfig.collectionUrl ?? fileConfig.collectionUrl;
|
|
58004
58066
|
const project = process.env.ADO_PROJECT ?? localConfig.project ?? fileConfig.project;
|
|
58005
58067
|
const repo = process.env.ADO_REPO ?? localConfig.repo ?? fileConfig.repo;
|
|
@@ -58025,21 +58087,29 @@ Commands:
|
|
|
58025
58087
|
config
|
|
58026
58088
|
status
|
|
58027
58089
|
smoke
|
|
58028
|
-
repos
|
|
58029
|
-
branches [repo]
|
|
58090
|
+
repos [--json]
|
|
58091
|
+
branches [repo] [--json]
|
|
58030
58092
|
workitem-get <id> [--raw] [--expand=all|fields|links|relations]
|
|
58031
|
-
workitems-recent [top] [--tag=<tag>] [--type=<work-item-type>] [--state=<state>]
|
|
58093
|
+
workitems-recent [top] [--tag=<tag>] [--type=<work-item-type>] [--state=<state>] [--json]
|
|
58032
58094
|
workitem-comments <id> [top] [--top=<n>] [--order=asc|desc]
|
|
58033
58095
|
workitem-comment-add <id> --text="..." [--file=path]
|
|
58034
58096
|
workitem-comment-update <id> <commentId> --text="..." [--file=path]
|
|
58035
|
-
prs [status] [top] [repo]
|
|
58097
|
+
prs [status] [top] [repo] [--json]
|
|
58036
58098
|
pr-get <id> [repo]
|
|
58037
58099
|
pr-create --title=... --source=... --target=... [--description=...] [--repo=...] [--work-items=123,456] [--tags=tag-a,tag-b]
|
|
58038
58100
|
pr-update <id> [--title=...] [--description=...] [--repo=...] [--work-items=123,456] [--tags=tag-a,tag-b]
|
|
58039
58101
|
pr-cherry-pick <id> --target=... [--topic=branch-name] [--repo=...]
|
|
58040
58102
|
pr-approve <id> [repo]
|
|
58041
58103
|
pr-autocomplete <id> [repo]
|
|
58042
|
-
builds [top]
|
|
58104
|
+
builds [top] [--json]
|
|
58105
|
+
|
|
58106
|
+
Environment variables:
|
|
58107
|
+
ADO_PAT Personal Access Token (primary)
|
|
58108
|
+
DEVOPS_PAT Personal Access Token (fallback, deprecated)
|
|
58109
|
+
ADO_COLLECTION_URL Azure DevOps collection URL
|
|
58110
|
+
ADO_PROJECT Default project
|
|
58111
|
+
ADO_REPO Default repository
|
|
58112
|
+
ADO_INSECURE Set to "1" to disable TLS verification
|
|
58043
58113
|
`);
|
|
58044
58114
|
}
|
|
58045
58115
|
async function printVersion() {
|
|
@@ -58075,10 +58145,10 @@ async function main() {
|
|
|
58075
58145
|
await cmdSmoke(config);
|
|
58076
58146
|
break;
|
|
58077
58147
|
case "repos":
|
|
58078
|
-
await cmdRepos(config);
|
|
58148
|
+
await cmdRepos(config, args);
|
|
58079
58149
|
break;
|
|
58080
58150
|
case "branches":
|
|
58081
|
-
await cmdBranches(config, args
|
|
58151
|
+
await cmdBranches(config, args);
|
|
58082
58152
|
break;
|
|
58083
58153
|
case "workitem-get":
|
|
58084
58154
|
await cmdWorkItemGet(config, args[0], args.slice(1));
|
|
@@ -58096,7 +58166,7 @@ async function main() {
|
|
|
58096
58166
|
await cmdWorkItemCommentUpdate(config, args[0], args[1], args.slice(2));
|
|
58097
58167
|
break;
|
|
58098
58168
|
case "prs":
|
|
58099
|
-
await cmdPrs(config, args
|
|
58169
|
+
await cmdPrs(config, args);
|
|
58100
58170
|
break;
|
|
58101
58171
|
case "pr-get":
|
|
58102
58172
|
await cmdPrGet(config, args[0], args[1]);
|
|
@@ -58117,7 +58187,7 @@ async function main() {
|
|
|
58117
58187
|
await cmdPrAutocomplete(config, args[0], args[1]);
|
|
58118
58188
|
break;
|
|
58119
58189
|
case "builds":
|
|
58120
|
-
await cmdBuilds(config, args
|
|
58190
|
+
await cmdBuilds(config, args);
|
|
58121
58191
|
break;
|
|
58122
58192
|
default:
|
|
58123
58193
|
console.error(`Unknown command: ${command}`);
|
package/package.json
CHANGED
|
@@ -10,33 +10,36 @@ Use the `ado` CLI instead of ad-hoc curl commands.
|
|
|
10
10
|
## Preflight
|
|
11
11
|
|
|
12
12
|
Run `ado status` before any other command. It checks token, configuration, and connectivity in one step.
|
|
13
|
-
If it reports an issue, guide the user to
|
|
13
|
+
If it reports an issue, guide the user to set the required environment variables (`ADO_PAT`, `ADO_COLLECTION_URL`, `ADO_PROJECT`, `ADO_REPO`).
|
|
14
|
+
Do **not** suggest `ado init` — it requires an interactive terminal and cannot run in this context.
|
|
14
15
|
|
|
15
16
|
## Core commands
|
|
16
17
|
|
|
17
|
-
- Initialize global config: `ado init`
|
|
18
|
-
- Initialize local (per-project) config: `ado init --local`
|
|
19
18
|
- Show resolved config: `ado config`
|
|
20
|
-
- List repos: `ado repos`
|
|
21
|
-
- List branches: `ado branches "MyRepo"`
|
|
19
|
+
- List repos: `ado repos` (or `ado repos --json` for structured output)
|
|
20
|
+
- List branches: `ado branches "MyRepo"` (or `--json`)
|
|
22
21
|
- Get work item: `ado workitem-get <id>`
|
|
23
22
|
- Get full raw work item payload: `ado workitem-get <id> --raw --expand=all`
|
|
24
|
-
- List recent work items: `ado workitems-recent 10`
|
|
23
|
+
- List recent work items: `ado workitems-recent 10` (or `--json`)
|
|
25
24
|
- List recent work items filtered by tag/type/state: `ado workitems-recent 20 --type=Bug --tag=bot --state=New`
|
|
26
25
|
- List comments on a work item: `ado workitem-comments <id> --top=100 --order=desc`
|
|
27
26
|
- Add a comment on a work item: `ado workitem-comment-add <id> --text="..."`
|
|
28
27
|
- Update an existing comment on a work item: `ado workitem-comment-update <id> <commentId> --text="..."`
|
|
29
|
-
- List PRs: `ado prs active 10 "MyRepo"`
|
|
28
|
+
- List PRs: `ado prs active 10 "MyRepo"` (or `--json`)
|
|
30
29
|
- Get PR: `ado pr-get <id> "MyRepo"`
|
|
31
30
|
- Create PR: `ado pr-create --title="..." --source="feature/x" --target="develop" --description="..." --repo="MyRepo" --work-items=123,456 --tags=backend,release-1`
|
|
32
31
|
- Update PR: `ado pr-update <id> --title="..." --description="..." --repo="MyRepo" --work-items=123,456 --tags=backend,release-1`
|
|
33
32
|
- Cherry-pick PR onto another branch: `ado pr-cherry-pick <id> --target="main" --topic="cherry-pick-branch" --repo="MyRepo"`
|
|
34
33
|
- Approve PR: `ado pr-approve <id> "MyRepo"`
|
|
35
34
|
- Enable auto-complete: `ado pr-autocomplete <id> "MyRepo"`
|
|
36
|
-
- List builds: `ado builds 10`
|
|
35
|
+
- List builds: `ado builds 10` (or `--json`)
|
|
36
|
+
|
|
37
|
+
## Structured output
|
|
38
|
+
|
|
39
|
+
Add `--json` to listing commands (`repos`, `branches`, `prs`, `builds`, `workitems-recent`) to get JSON arrays instead of tab-delimited text. Commands like `workitem-get`, `pr-get`, `workitem-comments`, and `config` already output JSON by default.
|
|
37
40
|
|
|
38
41
|
## Safety rules
|
|
39
42
|
|
|
40
|
-
- Never print `DEVOPS_PAT`.
|
|
43
|
+
- Never print `ADO_PAT` or `DEVOPS_PAT`.
|
|
41
44
|
- Prefer CLI commands over direct API calls.
|
|
42
45
|
- Use `ADO_INSECURE=1` only when needed for trusted self-signed endpoints.
|