@thanaen/ado-cli 0.5.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 +94 -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;
|
|
@@ -57468,9 +57479,9 @@ async function cmdSmoke(config) {
|
|
|
57468
57479
|
async function cmdStatus() {
|
|
57469
57480
|
const localConfig = loadLocalConfig();
|
|
57470
57481
|
const fileConfig = loadFileConfig();
|
|
57471
|
-
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;
|
|
57472
57483
|
if (!pat) {
|
|
57473
|
-
console.error("Not ready: DEVOPS_PAT is not set");
|
|
57484
|
+
console.error("Not ready: ADO_PAT (or DEVOPS_PAT) is not set");
|
|
57474
57485
|
process.exit(1);
|
|
57475
57486
|
}
|
|
57476
57487
|
const collectionUrl = process.env.ADO_COLLECTION_URL ?? localConfig.collectionUrl ?? fileConfig.collectionUrl ?? "";
|
|
@@ -57499,17 +57510,28 @@ async function cmdStatus() {
|
|
|
57499
57510
|
process.exit(1);
|
|
57500
57511
|
}
|
|
57501
57512
|
}
|
|
57502
|
-
async function cmdRepos(config) {
|
|
57513
|
+
async function cmdRepos(config, args = []) {
|
|
57514
|
+
const json = args.includes("--json");
|
|
57503
57515
|
const gitApi = await config.connection.getGitApi();
|
|
57504
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
|
+
}
|
|
57505
57521
|
for (const repo of repos) {
|
|
57506
57522
|
console.log(`${repo.id} ${repo.name}`);
|
|
57507
57523
|
}
|
|
57508
57524
|
}
|
|
57509
|
-
async function cmdBranches(config,
|
|
57525
|
+
async function cmdBranches(config, args = []) {
|
|
57526
|
+
const json = args.includes("--json");
|
|
57527
|
+
const repoArg = args.find((a) => !a.startsWith("--"));
|
|
57510
57528
|
const gitApi = await config.connection.getGitApi();
|
|
57511
57529
|
const repo = pickRepo(config, repoArg);
|
|
57512
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
|
+
}
|
|
57513
57535
|
for (const ref of refs) {
|
|
57514
57536
|
const name = String(ref.name || "").replace("refs/heads/", "");
|
|
57515
57537
|
console.log(name);
|
|
@@ -57554,18 +57576,25 @@ async function cmdWorkItemGet(config, idRaw, args = []) {
|
|
|
57554
57576
|
}, null, 2));
|
|
57555
57577
|
}
|
|
57556
57578
|
async function cmdWorkItemsRecent(config, args = []) {
|
|
57579
|
+
const json = args.includes("--json");
|
|
57580
|
+
const filteredArgs = args.filter((a) => a !== "--json");
|
|
57557
57581
|
let parsedArgs;
|
|
57558
57582
|
try {
|
|
57559
|
-
parsedArgs = parseWorkItemsRecentArgs(
|
|
57583
|
+
parsedArgs = parseWorkItemsRecentArgs(filteredArgs);
|
|
57560
57584
|
} catch (error) {
|
|
57561
57585
|
console.error(error instanceof Error ? error.message : String(error));
|
|
57562
|
-
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]");
|
|
57563
57587
|
process.exit(1);
|
|
57564
57588
|
}
|
|
57565
57589
|
const witApi = await config.connection.getWorkItemTrackingApi();
|
|
57566
57590
|
const wiqlResult = await witApi.queryByWiql({ query: buildRecentWorkItemsWiql(parsedArgs.filters) }, { project: config.project }, undefined, parsedArgs.top);
|
|
57567
|
-
|
|
57568
|
-
|
|
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);
|
|
57569
57598
|
}
|
|
57570
57599
|
}
|
|
57571
57600
|
async function cmdWorkItemComments(config, idRaw, args = []) {
|
|
@@ -57671,12 +57700,26 @@ async function cmdWorkItemCommentUpdate(config, idRaw, commentIdRaw, args = [])
|
|
|
57671
57700
|
text: result.text ?? text
|
|
57672
57701
|
}, null, 2));
|
|
57673
57702
|
}
|
|
57674
|
-
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];
|
|
57675
57709
|
const top = Number(topRaw);
|
|
57676
57710
|
const boundedTop = Number.isFinite(top) && top > 0 ? Math.min(top, 50) : 10;
|
|
57677
57711
|
const repo = pickRepo(config, repoArg);
|
|
57678
57712
|
const gitApi = await config.connection.getGitApi();
|
|
57679
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
|
+
}
|
|
57680
57723
|
for (const pr of prs) {
|
|
57681
57724
|
const createdBy = pr.createdBy?.displayName ?? "unknown";
|
|
57682
57725
|
console.log(`#${pr.pullRequestId} [${prStatusName(pr.status)}] ${pr.title} (${createdBy})`);
|
|
@@ -57773,11 +57816,24 @@ async function cmdPrAutocomplete(config, idRaw, repoArg) {
|
|
|
57773
57816
|
console.log(`Enabled auto-complete for PR #${id}`);
|
|
57774
57817
|
}
|
|
57775
57818
|
}
|
|
57776
|
-
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";
|
|
57777
57823
|
const top = Number(topRaw);
|
|
57778
57824
|
const boundedTop = Number.isFinite(top) && top > 0 ? Math.min(top, 50) : 10;
|
|
57779
57825
|
const buildApi = await config.connection.getBuildApi();
|
|
57780
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
|
+
}
|
|
57781
57837
|
for (const b of builds) {
|
|
57782
57838
|
console.log(`#${b.id} ${b.status}/${b.result ?? "n/a"} ${b.definition?.name ?? "unknown"} ${b.sourceBranch ?? ""}`);
|
|
57783
57839
|
}
|
|
@@ -57942,6 +57998,11 @@ async function cmdPrCherryPick(config, args) {
|
|
|
57942
57998
|
}
|
|
57943
57999
|
}
|
|
57944
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
|
+
}
|
|
57945
58006
|
const isLocal = args.includes("--local");
|
|
57946
58007
|
const { createInterface } = await import("node:readline/promises");
|
|
57947
58008
|
const { mkdirSync, writeFileSync, existsSync: existsSync2 } = await import("node:fs");
|
|
@@ -58000,7 +58061,7 @@ Configuration saved to ${configPath}`);
|
|
|
58000
58061
|
function cmdConfig() {
|
|
58001
58062
|
const fileConfig = loadFileConfig();
|
|
58002
58063
|
const localConfig = loadLocalConfig();
|
|
58003
|
-
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;
|
|
58004
58065
|
const collectionUrl = process.env.ADO_COLLECTION_URL ?? localConfig.collectionUrl ?? fileConfig.collectionUrl;
|
|
58005
58066
|
const project = process.env.ADO_PROJECT ?? localConfig.project ?? fileConfig.project;
|
|
58006
58067
|
const repo = process.env.ADO_REPO ?? localConfig.repo ?? fileConfig.repo;
|
|
@@ -58026,21 +58087,29 @@ Commands:
|
|
|
58026
58087
|
config
|
|
58027
58088
|
status
|
|
58028
58089
|
smoke
|
|
58029
|
-
repos
|
|
58030
|
-
branches [repo]
|
|
58090
|
+
repos [--json]
|
|
58091
|
+
branches [repo] [--json]
|
|
58031
58092
|
workitem-get <id> [--raw] [--expand=all|fields|links|relations]
|
|
58032
|
-
workitems-recent [top] [--tag=<tag>] [--type=<work-item-type>] [--state=<state>]
|
|
58093
|
+
workitems-recent [top] [--tag=<tag>] [--type=<work-item-type>] [--state=<state>] [--json]
|
|
58033
58094
|
workitem-comments <id> [top] [--top=<n>] [--order=asc|desc]
|
|
58034
58095
|
workitem-comment-add <id> --text="..." [--file=path]
|
|
58035
58096
|
workitem-comment-update <id> <commentId> --text="..." [--file=path]
|
|
58036
|
-
prs [status] [top] [repo]
|
|
58097
|
+
prs [status] [top] [repo] [--json]
|
|
58037
58098
|
pr-get <id> [repo]
|
|
58038
58099
|
pr-create --title=... --source=... --target=... [--description=...] [--repo=...] [--work-items=123,456] [--tags=tag-a,tag-b]
|
|
58039
58100
|
pr-update <id> [--title=...] [--description=...] [--repo=...] [--work-items=123,456] [--tags=tag-a,tag-b]
|
|
58040
58101
|
pr-cherry-pick <id> --target=... [--topic=branch-name] [--repo=...]
|
|
58041
58102
|
pr-approve <id> [repo]
|
|
58042
58103
|
pr-autocomplete <id> [repo]
|
|
58043
|
-
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
|
|
58044
58113
|
`);
|
|
58045
58114
|
}
|
|
58046
58115
|
async function printVersion() {
|
|
@@ -58076,10 +58145,10 @@ async function main() {
|
|
|
58076
58145
|
await cmdSmoke(config);
|
|
58077
58146
|
break;
|
|
58078
58147
|
case "repos":
|
|
58079
|
-
await cmdRepos(config);
|
|
58148
|
+
await cmdRepos(config, args);
|
|
58080
58149
|
break;
|
|
58081
58150
|
case "branches":
|
|
58082
|
-
await cmdBranches(config, args
|
|
58151
|
+
await cmdBranches(config, args);
|
|
58083
58152
|
break;
|
|
58084
58153
|
case "workitem-get":
|
|
58085
58154
|
await cmdWorkItemGet(config, args[0], args.slice(1));
|
|
@@ -58097,7 +58166,7 @@ async function main() {
|
|
|
58097
58166
|
await cmdWorkItemCommentUpdate(config, args[0], args[1], args.slice(2));
|
|
58098
58167
|
break;
|
|
58099
58168
|
case "prs":
|
|
58100
|
-
await cmdPrs(config, args
|
|
58169
|
+
await cmdPrs(config, args);
|
|
58101
58170
|
break;
|
|
58102
58171
|
case "pr-get":
|
|
58103
58172
|
await cmdPrGet(config, args[0], args[1]);
|
|
@@ -58118,7 +58187,7 @@ async function main() {
|
|
|
58118
58187
|
await cmdPrAutocomplete(config, args[0], args[1]);
|
|
58119
58188
|
break;
|
|
58120
58189
|
case "builds":
|
|
58121
|
-
await cmdBuilds(config, args
|
|
58190
|
+
await cmdBuilds(config, args);
|
|
58122
58191
|
break;
|
|
58123
58192
|
default:
|
|
58124
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.
|