@floh-solutions/pharos-cli 0.3.0 → 0.12.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.d.ts.map +1 -1
- package/dist/cli.js +392 -15
- package/dist/cli.js.map +1 -1
- package/dist/commands/attach.d.ts +30 -0
- package/dist/commands/attach.d.ts.map +1 -0
- package/dist/commands/attach.js +55 -0
- package/dist/commands/attach.js.map +1 -0
- package/dist/commands/create.d.ts +35 -0
- package/dist/commands/create.d.ts.map +1 -0
- package/dist/commands/create.js +82 -0
- package/dist/commands/create.js.map +1 -0
- package/dist/commands/detach.d.ts +34 -0
- package/dist/commands/detach.d.ts.map +1 -0
- package/dist/commands/detach.js +117 -0
- package/dist/commands/detach.js.map +1 -0
- package/dist/commands/discover.d.ts +66 -0
- package/dist/commands/discover.d.ts.map +1 -0
- package/dist/commands/discover.js +128 -0
- package/dist/commands/discover.js.map +1 -0
- package/dist/commands/hooks.d.ts +24 -0
- package/dist/commands/hooks.d.ts.map +1 -0
- package/dist/commands/hooks.js +184 -0
- package/dist/commands/hooks.js.map +1 -0
- package/dist/commands/image.d.ts +44 -0
- package/dist/commands/image.d.ts.map +1 -0
- package/dist/commands/image.js +114 -0
- package/dist/commands/image.js.map +1 -0
- package/dist/commands/import.d.ts +51 -0
- package/dist/commands/import.d.ts.map +1 -0
- package/dist/commands/import.js +190 -0
- package/dist/commands/import.js.map +1 -0
- package/dist/commands/link.d.ts +40 -0
- package/dist/commands/link.d.ts.map +1 -0
- package/dist/commands/link.js +150 -0
- package/dist/commands/link.js.map +1 -0
- package/dist/commands/people.d.ts +39 -0
- package/dist/commands/people.d.ts.map +1 -0
- package/dist/commands/people.js +64 -0
- package/dist/commands/people.js.map +1 -0
- package/dist/commands/query.d.ts +43 -0
- package/dist/commands/query.d.ts.map +1 -0
- package/dist/commands/query.js +202 -0
- package/dist/commands/query.js.map +1 -0
- package/dist/commands/recycle.d.ts +10 -0
- package/dist/commands/recycle.d.ts.map +1 -0
- package/dist/commands/recycle.js +118 -0
- package/dist/commands/recycle.js.map +1 -0
- package/dist/commands/setup.d.ts +4 -1
- package/dist/commands/setup.d.ts.map +1 -1
- package/dist/commands/setup.js +22 -21
- package/dist/commands/setup.js.map +1 -1
- package/dist/commands/update.d.ts +52 -0
- package/dist/commands/update.d.ts.map +1 -0
- package/dist/commands/update.js +181 -0
- package/dist/commands/update.js.map +1 -0
- package/dist/commands/wiki.d.ts.map +1 -1
- package/dist/commands/wiki.js +133 -2
- package/dist/commands/wiki.js.map +1 -1
- package/dist/output.d.ts.map +1 -1
- package/dist/output.js +32 -2
- package/dist/output.js.map +1 -1
- package/dist/wiql-build.d.ts +92 -0
- package/dist/wiql-build.d.ts.map +1 -0
- package/dist/wiql-build.js +147 -0
- package/dist/wiql-build.js.map +1 -0
- package/package.json +3 -3
- package/skill/SKILL.md +315 -65
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { type ExitCode, type Io } from "../output.js";
|
|
2
|
+
import type { Session } from "../session.js";
|
|
3
|
+
/**
|
|
4
|
+
* `pharos link` and `pharos unlink` — the relations between work items.
|
|
5
|
+
*
|
|
6
|
+
* **`az boards work-item relation add|remove` already exists, so this is only
|
|
7
|
+
* worth building for the guard** — which is the scope test (#590) applied
|
|
8
|
+
* honestly rather than a claim of coverage.
|
|
9
|
+
*
|
|
10
|
+
* The guard is not decoration. Azure DevOps removes a relation by its POSITION
|
|
11
|
+
* in the array (`/relations/3`), so an index from a stale read removes a
|
|
12
|
+
* DIFFERENT link, the request succeeds, and nothing anywhere says so. `unlink`
|
|
13
|
+
* therefore reads the item, finds the relation by identity, and applies the
|
|
14
|
+
* removal under a `test` op on the revision that read came from — so if the
|
|
15
|
+
* array moved in between, the whole patch aborts instead of cutting the wrong
|
|
16
|
+
* link. `ado-core`'s `removeRelationPatch` is where that lives, and it throws
|
|
17
|
+
* rather than returning an empty patch when the relation is absent: "already
|
|
18
|
+
* gone" and "your read was wrong" are different answers and a caller needs to
|
|
19
|
+
* know which it got.
|
|
20
|
+
*/
|
|
21
|
+
/**
|
|
22
|
+
* The link kinds, and the direction each one means **from the item named on the
|
|
23
|
+
* command line**.
|
|
24
|
+
*
|
|
25
|
+
* The forward/reverse naming is Azure DevOps' and it is a reliable source of
|
|
26
|
+
* mistakes: on item X, a `Hierarchy-Reverse` relation points at X's PARENT.
|
|
27
|
+
* Naming the flags after the relationship rather than the direction is what
|
|
28
|
+
* stops somebody linking a child where they meant a parent.
|
|
29
|
+
*/
|
|
30
|
+
export declare const LINK_KINDS: Record<string, {
|
|
31
|
+
rel: string;
|
|
32
|
+
describes: string;
|
|
33
|
+
}>;
|
|
34
|
+
export interface LinkCommandOptions {
|
|
35
|
+
kinds: Record<string, number | undefined>;
|
|
36
|
+
comment: string | undefined;
|
|
37
|
+
}
|
|
38
|
+
export declare function runLink(io: Io, session: Session, positionals: readonly string[], options: LinkCommandOptions): Promise<ExitCode>;
|
|
39
|
+
export declare function runUnlink(io: Io, session: Session, positionals: readonly string[], options: LinkCommandOptions): Promise<ExitCode>;
|
|
40
|
+
//# sourceMappingURL=link.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"link.d.ts","sourceRoot":"","sources":["../../src/commands/link.ts"],"names":[],"mappings":"AAQA,OAAO,EAAuC,KAAK,QAAQ,EAAE,KAAK,EAAE,EAAE,MAAM,cAAc,CAAC;AAC3F,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAE7C;;;;;;;;;;;;;;;;;GAiBG;AAEH;;;;;;;;GAQG;AACH,eAAO,MAAM,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAOzE,CAAC;AAEF,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;IAC1C,OAAO,EAAE,MAAM,GAAG,SAAS,CAAC;CAC7B;AA6BD,wBAAsB,OAAO,CAC3B,EAAE,EAAE,EAAE,EACN,OAAO,EAAE,OAAO,EAChB,WAAW,EAAE,SAAS,MAAM,EAAE,EAC9B,OAAO,EAAE,kBAAkB,GAC1B,OAAO,CAAC,QAAQ,CAAC,CAyDnB;AAED,wBAAsB,SAAS,CAC7B,EAAE,EAAE,EAAE,EACN,OAAO,EAAE,OAAO,EAChB,WAAW,EAAE,SAAS,MAAM,EAAE,EAC9B,OAAO,EAAE,kBAAkB,GAC1B,OAAO,CAAC,QAAQ,CAAC,CA+BnB"}
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import { RelationNotFoundError, addRelation, removeRelationPatch, } from "@floh-solutions/ado-core";
|
|
2
|
+
import { emit, emitText, refusal, usageError } from "../output.js";
|
|
3
|
+
/**
|
|
4
|
+
* `pharos link` and `pharos unlink` — the relations between work items.
|
|
5
|
+
*
|
|
6
|
+
* **`az boards work-item relation add|remove` already exists, so this is only
|
|
7
|
+
* worth building for the guard** — which is the scope test (#590) applied
|
|
8
|
+
* honestly rather than a claim of coverage.
|
|
9
|
+
*
|
|
10
|
+
* The guard is not decoration. Azure DevOps removes a relation by its POSITION
|
|
11
|
+
* in the array (`/relations/3`), so an index from a stale read removes a
|
|
12
|
+
* DIFFERENT link, the request succeeds, and nothing anywhere says so. `unlink`
|
|
13
|
+
* therefore reads the item, finds the relation by identity, and applies the
|
|
14
|
+
* removal under a `test` op on the revision that read came from — so if the
|
|
15
|
+
* array moved in between, the whole patch aborts instead of cutting the wrong
|
|
16
|
+
* link. `ado-core`'s `removeRelationPatch` is where that lives, and it throws
|
|
17
|
+
* rather than returning an empty patch when the relation is absent: "already
|
|
18
|
+
* gone" and "your read was wrong" are different answers and a caller needs to
|
|
19
|
+
* know which it got.
|
|
20
|
+
*/
|
|
21
|
+
/**
|
|
22
|
+
* The link kinds, and the direction each one means **from the item named on the
|
|
23
|
+
* command line**.
|
|
24
|
+
*
|
|
25
|
+
* The forward/reverse naming is Azure DevOps' and it is a reliable source of
|
|
26
|
+
* mistakes: on item X, a `Hierarchy-Reverse` relation points at X's PARENT.
|
|
27
|
+
* Naming the flags after the relationship rather than the direction is what
|
|
28
|
+
* stops somebody linking a child where they meant a parent.
|
|
29
|
+
*/
|
|
30
|
+
export const LINK_KINDS = {
|
|
31
|
+
parent: { rel: "System.LinkTypes.Hierarchy-Reverse", describes: "the parent of" },
|
|
32
|
+
child: { rel: "System.LinkTypes.Hierarchy-Forward", describes: "a child of" },
|
|
33
|
+
related: { rel: "System.LinkTypes.Related", describes: "related to" },
|
|
34
|
+
predecessor: { rel: "System.LinkTypes.Dependency-Reverse", describes: "a predecessor of" },
|
|
35
|
+
successor: { rel: "System.LinkTypes.Dependency-Forward", describes: "a successor of" },
|
|
36
|
+
duplicate: { rel: "System.LinkTypes.Duplicate-Forward", describes: "a duplicate of" },
|
|
37
|
+
};
|
|
38
|
+
/** Exactly one link kind, or a refusal that lists them. */
|
|
39
|
+
function soleKind(kinds) {
|
|
40
|
+
const given = Object.entries(kinds).filter(([, id]) => id !== undefined);
|
|
41
|
+
if (given.length === 0) {
|
|
42
|
+
throw usageError(`Which relationship? One of ${Object.keys(LINK_KINDS)
|
|
43
|
+
.map((kind) => `--${kind} <id>`)
|
|
44
|
+
.join(", ")}.`);
|
|
45
|
+
}
|
|
46
|
+
if (given.length > 1) {
|
|
47
|
+
// Not silently applied one at a time: each is its own revision, so a
|
|
48
|
+
// partial failure would leave half the links made and no way to tell which.
|
|
49
|
+
throw usageError(`One relationship at a time; got ${given.map(([flag]) => `--${flag}`).join(" and ")}.`);
|
|
50
|
+
}
|
|
51
|
+
const [flag, target] = given[0];
|
|
52
|
+
return { flag, target: target };
|
|
53
|
+
}
|
|
54
|
+
function id(raw, usage) {
|
|
55
|
+
if (raw === undefined)
|
|
56
|
+
throw usageError(usage);
|
|
57
|
+
if (!/^\d+$/.test(raw))
|
|
58
|
+
throw usageError(`"${raw}" is not a work item id.`, { given: raw });
|
|
59
|
+
return Number(raw);
|
|
60
|
+
}
|
|
61
|
+
export async function runLink(io, session, positionals, options) {
|
|
62
|
+
const from = id(positionals[0], "Which work item? `pharos link <id> --parent <id>`.");
|
|
63
|
+
const { flag, target } = soleKind(options.kinds);
|
|
64
|
+
const kind = LINK_KINDS[flag];
|
|
65
|
+
if (target === from) {
|
|
66
|
+
throw usageError(`A work item cannot be ${kind.describes} itself (#${from}).`);
|
|
67
|
+
}
|
|
68
|
+
// Resolved rather than composed: `addRelation` wants the target's canonical
|
|
69
|
+
// url, and a target that does not exist fails here by name instead of as an
|
|
70
|
+
// opaque 400 about a relation.
|
|
71
|
+
const to = await session.client.workItems.get(target);
|
|
72
|
+
const url = to.url;
|
|
73
|
+
if (typeof url !== "string" || url === "") {
|
|
74
|
+
throw usageError(`Work item #${target} did not report a url, so it cannot be linked.`);
|
|
75
|
+
}
|
|
76
|
+
const item = await session.client.workItems.get(from, { expand: "relations" });
|
|
77
|
+
const existing = relationsOf(item).find((relation) => relation.rel === kind.rel && relation.url === url);
|
|
78
|
+
if (existing !== undefined) {
|
|
79
|
+
return report(io, session, {
|
|
80
|
+
applied: false,
|
|
81
|
+
unchanged: true,
|
|
82
|
+
from,
|
|
83
|
+
to: target,
|
|
84
|
+
relationship: flag,
|
|
85
|
+
message: `#${target} is already ${kind.describes} #${from}.`,
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
if (session.previewOnly) {
|
|
89
|
+
return report(io, session, { applied: false, dryRun: true, from, to: target, relationship: flag });
|
|
90
|
+
}
|
|
91
|
+
await session.client.workItems.updateWithRev(from, item.rev, [
|
|
92
|
+
addRelation(kind.rel, url, options.comment === undefined ? undefined : { comment: options.comment }),
|
|
93
|
+
], { suppressNotifications: true });
|
|
94
|
+
return report(io, session, {
|
|
95
|
+
applied: true,
|
|
96
|
+
from,
|
|
97
|
+
to: target,
|
|
98
|
+
relationship: flag,
|
|
99
|
+
title: titleOf(to),
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
export async function runUnlink(io, session, positionals, options) {
|
|
103
|
+
const from = id(positionals[0], "Which work item? `pharos unlink <id> --parent <id>`.");
|
|
104
|
+
const { flag, target } = soleKind(options.kinds);
|
|
105
|
+
const kind = LINK_KINDS[flag];
|
|
106
|
+
const item = await session.client.workItems.get(from, { expand: "relations" });
|
|
107
|
+
let ops;
|
|
108
|
+
try {
|
|
109
|
+
ops = removeRelationPatch(relationsOf(item), item.rev, {
|
|
110
|
+
kind: "workItem",
|
|
111
|
+
id: target,
|
|
112
|
+
rel: kind.rel,
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
catch (error) {
|
|
116
|
+
if (error instanceof RelationNotFoundError) {
|
|
117
|
+
// Exit 3 rather than 1: nothing is wrong with the connection or the call,
|
|
118
|
+
// a guard here declined to act on an assumption. The message already says
|
|
119
|
+
// why removing by position would be worse.
|
|
120
|
+
throw refusal(error.message, { from, to: target, relationship: flag });
|
|
121
|
+
}
|
|
122
|
+
throw error;
|
|
123
|
+
}
|
|
124
|
+
if (session.previewOnly) {
|
|
125
|
+
return report(io, session, { applied: false, dryRun: true, from, to: target, relationship: flag });
|
|
126
|
+
}
|
|
127
|
+
await session.client.workItems.update(from, ops, { suppressNotifications: true });
|
|
128
|
+
return report(io, session, { applied: true, removed: true, from, to: target, relationship: flag });
|
|
129
|
+
}
|
|
130
|
+
function relationsOf(item) {
|
|
131
|
+
const relations = item.relations;
|
|
132
|
+
return relations ?? [];
|
|
133
|
+
}
|
|
134
|
+
function titleOf(item) {
|
|
135
|
+
const title = item.fields?.["System.Title"];
|
|
136
|
+
return typeof title === "string" ? title : null;
|
|
137
|
+
}
|
|
138
|
+
function report(io, session, result) {
|
|
139
|
+
if (!session.pretty)
|
|
140
|
+
return emit(io, result);
|
|
141
|
+
// The unchanged case carries its own sentence and must use it. Falling through
|
|
142
|
+
// to the verb table produced "#224 is would be a predecessor of #331", which
|
|
143
|
+
// is the kind of line that makes somebody doubt what actually happened.
|
|
144
|
+
if (typeof result["message"] === "string")
|
|
145
|
+
return emitText(io, result["message"]);
|
|
146
|
+
const describes = LINK_KINDS[result.relationship]?.describes ?? result.relationship;
|
|
147
|
+
const verb = result["removed"] === true ? "no longer" : result.applied ? "now" : "would be";
|
|
148
|
+
return emitText(io, `#${result.to} is ${verb} ${describes} #${result.from}`);
|
|
149
|
+
}
|
|
150
|
+
//# sourceMappingURL=link.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"link.js","sourceRoot":"","sources":["../../src/commands/link.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { type ExitCode, type Io } from "../output.js";
|
|
2
|
+
import type { Session } from "../session.js";
|
|
3
|
+
/**
|
|
4
|
+
* `pharos people [query]` — who can be mentioned, and the form that mentions
|
|
5
|
+
* them.
|
|
6
|
+
*
|
|
7
|
+
* ## Why this exists, and why its absence was invisible
|
|
8
|
+
*
|
|
9
|
+
* **A mention's wire form is `@<{guid}>` and NOTHING ELSE NOTIFIES.** Writing
|
|
10
|
+
* `@Ada Lovelace` into a comment produces text that reads exactly like a mention
|
|
11
|
+
* to every human who sees it, links to nobody, and sends no notification. There
|
|
12
|
+
* is no error and no warning — the comment posts, looks right, and quietly
|
|
13
|
+
* reaches no one.
|
|
14
|
+
*
|
|
15
|
+
* So a session that wanted to mention somebody had two ways to fail and no way
|
|
16
|
+
* to succeed: guess the guid, or write the readable form and never learn it did
|
|
17
|
+
* nothing. This verb is the missing half.
|
|
18
|
+
*
|
|
19
|
+
* ## Where the names come from
|
|
20
|
+
*
|
|
21
|
+
* **The board's own work items**, not an identity endpoint. That is deliberate:
|
|
22
|
+
* the identity APIs live on a different host (`vssps.dev.azure.com`) and want
|
|
23
|
+
* scopes a work-scoped PAT does not have, so a `pharos` configured exactly as
|
|
24
|
+
* the setup instructions say would get a 401 from a verb that looks like it
|
|
25
|
+
* should work. Every person who has been assigned work, created it or changed it
|
|
26
|
+
* is already in the items themselves, at no extra scope and no extra call
|
|
27
|
+
* beyond the query.
|
|
28
|
+
*
|
|
29
|
+
* The consequence is worth stating rather than discovering: **somebody who has
|
|
30
|
+
* never touched an item on this board will not be listed.** For mentioning
|
|
31
|
+
* people about work, that is nearly always the right set — and when it is not,
|
|
32
|
+
* the guid from any Azure DevOps url works just as well in `@<…>`.
|
|
33
|
+
*/
|
|
34
|
+
export interface PeopleCommandOptions {
|
|
35
|
+
/** Cap the items scanned. More items, more people, slower. */
|
|
36
|
+
top: number | undefined;
|
|
37
|
+
}
|
|
38
|
+
export declare function runPeople(io: Io, session: Session, positionals: readonly string[], options: PeopleCommandOptions): Promise<ExitCode>;
|
|
39
|
+
//# sourceMappingURL=people.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"people.d.ts","sourceRoot":"","sources":["../../src/commands/people.ts"],"names":[],"mappings":"AAAA,OAAO,EAAkB,KAAK,QAAQ,EAAE,KAAK,EAAE,EAAE,MAAM,cAAc,CAAC;AACtE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAE7C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAEH,MAAM,WAAW,oBAAoB;IACnC,8DAA8D;IAC9D,GAAG,EAAE,MAAM,GAAG,SAAS,CAAC;CACzB;AAYD,wBAAsB,SAAS,CAC7B,EAAE,EAAE,EAAE,EACN,OAAO,EAAE,OAAO,EAChB,WAAW,EAAE,SAAS,MAAM,EAAE,EAC9B,OAAO,EAAE,oBAAoB,GAC5B,OAAO,CAAC,QAAQ,CAAC,CAmDnB"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { emit, emitText } from "../output.js";
|
|
2
|
+
export async function runPeople(io, session, positionals, options) {
|
|
3
|
+
const query = positionals[0]?.trim().toLowerCase() ?? "";
|
|
4
|
+
const top = options.top ?? 200;
|
|
5
|
+
// Every item, including finished work: a person who closed everything they
|
|
6
|
+
// ever had is still a person worth mentioning.
|
|
7
|
+
const ids = await session.client.wiql.queryIds(`SELECT [System.Id] FROM WorkItems WHERE [System.TeamProject] = @project `
|
|
8
|
+
+ `ORDER BY [System.ChangedDate] DESC`);
|
|
9
|
+
const wanted = ids.slice(0, top);
|
|
10
|
+
const found = new Map();
|
|
11
|
+
if (wanted.length > 0) {
|
|
12
|
+
const items = await session.client.workItems.getBatch(wanted, {
|
|
13
|
+
fields: ["System.AssignedTo", "System.CreatedBy", "System.ChangedBy"],
|
|
14
|
+
});
|
|
15
|
+
for (const item of items) {
|
|
16
|
+
record(found, item.fields?.["System.AssignedTo"], "assigned");
|
|
17
|
+
record(found, item.fields?.["System.CreatedBy"], "created");
|
|
18
|
+
record(found, item.fields?.["System.ChangedBy"], "changed");
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
const people = [...found.values()]
|
|
22
|
+
.filter((person) => query === ""
|
|
23
|
+
|| person.displayName.toLowerCase().includes(query)
|
|
24
|
+
|| person.uniqueName.toLowerCase().includes(query))
|
|
25
|
+
.sort((a, b) => a.displayName.localeCompare(b.displayName));
|
|
26
|
+
if (session.pretty) {
|
|
27
|
+
if (people.length === 0) {
|
|
28
|
+
emitText(io, query === "" ? "Nobody on this board yet." : `Nobody matching "${query}".`);
|
|
29
|
+
return 0;
|
|
30
|
+
}
|
|
31
|
+
for (const person of people) {
|
|
32
|
+
emitText(io, `${person.mention} ${person.displayName} <${person.uniqueName}>`);
|
|
33
|
+
}
|
|
34
|
+
emitText(io, "");
|
|
35
|
+
emitText(io, "Paste the @<…> form into a comment or a description. The readable form "
|
|
36
|
+
+ "(@Name) notifies nobody.");
|
|
37
|
+
return 0;
|
|
38
|
+
}
|
|
39
|
+
emit(io, { scanned: wanted.length, people });
|
|
40
|
+
return 0;
|
|
41
|
+
}
|
|
42
|
+
/** One identity field, folded into the set. */
|
|
43
|
+
function record(into, value, seenAs) {
|
|
44
|
+
if (value === null || typeof value !== "object")
|
|
45
|
+
return;
|
|
46
|
+
const identity = value;
|
|
47
|
+
const id = identity.id;
|
|
48
|
+
if (id === undefined || id === "")
|
|
49
|
+
return;
|
|
50
|
+
const existing = into.get(id);
|
|
51
|
+
if (existing !== undefined) {
|
|
52
|
+
if (!existing.seenAs.includes(seenAs))
|
|
53
|
+
existing.seenAs.push(seenAs);
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
into.set(id, {
|
|
57
|
+
displayName: identity.displayName ?? id,
|
|
58
|
+
uniqueName: identity.uniqueName ?? "",
|
|
59
|
+
id,
|
|
60
|
+
mention: `@<${id}>`,
|
|
61
|
+
seenAs: [seenAs],
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
//# sourceMappingURL=people.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"people.js","sourceRoot":"","sources":["../../src/commands/people.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { type ExitCode, type Io } from "../output.js";
|
|
2
|
+
import type { Session } from "../session.js";
|
|
3
|
+
/**
|
|
4
|
+
* `pharos query` — the verb whose absence made every session start with `curl`.
|
|
5
|
+
*
|
|
6
|
+
* A real session was asked what was assigned to @user. It spent 1m16s and five
|
|
7
|
+
* calls — two on `--help`, one checking environment variables setup had already
|
|
8
|
+
* written, two more on per-command help — and then gave up on this CLI and hit
|
|
9
|
+
* the WIQL endpoint directly, because `task` takes one id and nothing here
|
|
10
|
+
* answered "what is assigned to me". That is the most common opening question
|
|
11
|
+
* anybody asks a board.
|
|
12
|
+
*
|
|
13
|
+
* ## Why the output is hydrated items rather than ids
|
|
14
|
+
*
|
|
15
|
+
* WIQL returns **references only** — ids, never field values, however many
|
|
16
|
+
* columns the SELECT names. The curl recipe this replaces therefore handed back
|
|
17
|
+
* sixteen bare numbers, which is not an answer to any question somebody asked.
|
|
18
|
+
* `queryAndFetch` runs the query and then hydrates through `workitemsbatch` in
|
|
19
|
+
* pages of 200; both routes are read-shaped POSTs and neither counts against
|
|
20
|
+
* the write budget.
|
|
21
|
+
*
|
|
22
|
+
* ## Why it says how it decided "open"
|
|
23
|
+
*
|
|
24
|
+
* Because the answer is uninterpretable without it. "12 open" means nothing
|
|
25
|
+
* until you know which states were counted as finished, and Azure DevOps lets a
|
|
26
|
+
* process template rename every one of them. See {@link Openness}.
|
|
27
|
+
*/
|
|
28
|
+
export interface QueryCommandOptions {
|
|
29
|
+
mine: boolean;
|
|
30
|
+
assignee: string | undefined;
|
|
31
|
+
states: readonly string[];
|
|
32
|
+
types: readonly string[];
|
|
33
|
+
sprint: string | undefined;
|
|
34
|
+
area: string | undefined;
|
|
35
|
+
tags: readonly string[];
|
|
36
|
+
all: boolean;
|
|
37
|
+
team: string | undefined;
|
|
38
|
+
top: number | undefined;
|
|
39
|
+
/** The escape hatch: a statement written by hand, used verbatim. */
|
|
40
|
+
wiql: string | undefined;
|
|
41
|
+
}
|
|
42
|
+
export declare function runQuery(io: Io, session: Session, options: QueryCommandOptions): Promise<ExitCode>;
|
|
43
|
+
//# sourceMappingURL=query.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"query.d.ts","sourceRoot":"","sources":["../../src/commands/query.ts"],"names":[],"mappings":"AAEA,OAAO,EAA8B,KAAK,QAAQ,EAAE,KAAK,EAAE,EAAE,MAAM,cAAc,CAAC;AAClF,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAG7C;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,OAAO,CAAC;IACd,QAAQ,EAAE,MAAM,GAAG,SAAS,CAAC;IAC7B,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC;IAC1B,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;IACzB,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,IAAI,EAAE,MAAM,GAAG,SAAS,CAAC;IACzB,IAAI,EAAE,SAAS,MAAM,EAAE,CAAC;IACxB,GAAG,EAAE,OAAO,CAAC;IACb,IAAI,EAAE,MAAM,GAAG,SAAS,CAAC;IACzB,GAAG,EAAE,MAAM,GAAG,SAAS,CAAC;IACxB,oEAAoE;IACpE,IAAI,EAAE,MAAM,GAAG,SAAS,CAAC;CAC1B;AAgBD,wBAAsB,QAAQ,CAC5B,EAAE,EAAE,EAAE,EACN,OAAO,EAAE,OAAO,EAChB,OAAO,EAAE,mBAAmB,GAC3B,OAAO,CAAC,QAAQ,CAAC,CAsEnB"}
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
import { authenticatedIdentity } from "@floh-solutions/ado-core";
|
|
2
|
+
import { emit, emitText, usageError } from "../output.js";
|
|
3
|
+
import { buildWiql, opennessFor } from "../wiql-build.js";
|
|
4
|
+
/** What the hydrate step asks for. Anything a type does not have is simply absent. */
|
|
5
|
+
const FIELDS = [
|
|
6
|
+
"System.Id",
|
|
7
|
+
"System.WorkItemType",
|
|
8
|
+
"System.Title",
|
|
9
|
+
"System.State",
|
|
10
|
+
"System.AssignedTo",
|
|
11
|
+
"System.IterationPath",
|
|
12
|
+
"System.AreaPath",
|
|
13
|
+
"System.Tags",
|
|
14
|
+
"System.ChangedDate",
|
|
15
|
+
"Microsoft.VSTS.Common.Priority",
|
|
16
|
+
];
|
|
17
|
+
export async function runQuery(io, session, options) {
|
|
18
|
+
if (options.mine && options.assignee !== undefined) {
|
|
19
|
+
throw usageError("Pass either --mine or --assignee, not both — they filter the same field and would AND "
|
|
20
|
+
+ "together into a query that can never match.");
|
|
21
|
+
}
|
|
22
|
+
const filters = {
|
|
23
|
+
mine: options.mine,
|
|
24
|
+
assignee: options.assignee,
|
|
25
|
+
states: options.states,
|
|
26
|
+
types: options.types,
|
|
27
|
+
sprint: options.sprint,
|
|
28
|
+
area: options.area,
|
|
29
|
+
tags: options.tags,
|
|
30
|
+
all: options.all,
|
|
31
|
+
...(session.client.project === undefined ? {} : { project: session.client.project }),
|
|
32
|
+
};
|
|
33
|
+
let statement;
|
|
34
|
+
let openness;
|
|
35
|
+
if (options.wiql !== undefined) {
|
|
36
|
+
if (describesFilters(options)) {
|
|
37
|
+
throw usageError("--wiql is the whole statement, so the other filters would be silently ignored. Pass "
|
|
38
|
+
+ "--wiql alone, or drop it and use the flags.");
|
|
39
|
+
}
|
|
40
|
+
statement = options.wiql;
|
|
41
|
+
openness = { source: "none", terminal: [], note: "Your statement, used verbatim." };
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
// Only read the catalogue when the answer depends on it. `--all` and an
|
|
45
|
+
// explicit `--state` both decide openness without asking the project, and a
|
|
46
|
+
// request nobody needs is a request that can fail for nobody's benefit.
|
|
47
|
+
let catalog;
|
|
48
|
+
let failure;
|
|
49
|
+
if (!options.all && options.states.length === 0) {
|
|
50
|
+
try {
|
|
51
|
+
catalog = await session.client.workItemTypes.states();
|
|
52
|
+
}
|
|
53
|
+
catch (error) {
|
|
54
|
+
// NOT fatal. A token without the work-item-type read still queries fine;
|
|
55
|
+
// it just cannot be told what "finished" means here, and saying so and
|
|
56
|
+
// guessing beats refusing to answer at all.
|
|
57
|
+
failure = error instanceof Error ? error.message : String(error);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
openness = opennessFor(filters, catalog, failure);
|
|
61
|
+
statement = buildWiql(filters, openness, catalog);
|
|
62
|
+
}
|
|
63
|
+
const items = await session.client.wiql.queryAndFetch(statement, {
|
|
64
|
+
fields: FIELDS,
|
|
65
|
+
...(options.top === undefined ? {} : { top: options.top }),
|
|
66
|
+
...(options.team === undefined ? {} : { team: options.team }),
|
|
67
|
+
});
|
|
68
|
+
const result = {
|
|
69
|
+
count: items.length,
|
|
70
|
+
...(items.length === 0 ? { emptyBecause: silentFilters(options) } : {}),
|
|
71
|
+
// Emitted so a caller can see exactly what ran — and edit it into --wiql
|
|
72
|
+
// when the flags do not reach far enough.
|
|
73
|
+
wiql: statement,
|
|
74
|
+
openness,
|
|
75
|
+
assignedTo: options.mine ? await resolveMe(session) : (options.assignee ?? null),
|
|
76
|
+
items: items.map(summarise),
|
|
77
|
+
};
|
|
78
|
+
return session.pretty ? emitText(io, render(result)) : emit(io, result);
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Which filters could have emptied this result **without anybody being told**.
|
|
82
|
+
*
|
|
83
|
+
* An empty list and exit 0 is a successful query that matched nothing, and a
|
|
84
|
+
* session reports it as a fact about the board. That is right for most filters
|
|
85
|
+
* and dangerous for the ones Azure DevOps does not validate:
|
|
86
|
+
*
|
|
87
|
+
* - `--sprint` and `--area` are SAFE and it is worth saying so, because it is
|
|
88
|
+
* the opposite of what you would guess: a path that does not exist answers
|
|
89
|
+
* `TF51011` naming the path, so a wrong one fails loudly. Measured.
|
|
90
|
+
* - `--assignee` is the silent one. An identity Azure DevOps has never heard of
|
|
91
|
+
* is not an error — it simply matches nothing, so "0 items assigned to
|
|
92
|
+
* ada@contoso.com" and "you spelt it wrong" are the same output.
|
|
93
|
+
* - `--tag` is a substring match against a semicolon-joined string, so a
|
|
94
|
+
* plausible-looking tag that is not on anything reads the same way.
|
|
95
|
+
* - `--state` is checked on `update` but not here, because a query naming
|
|
96
|
+
* several states across several types is legitimate.
|
|
97
|
+
*
|
|
98
|
+
* Named rather than counted: telling somebody "this might be wrong" without
|
|
99
|
+
* saying which flag is the one to re-read is a warning they cannot act on.
|
|
100
|
+
*/
|
|
101
|
+
function silentFilters(options) {
|
|
102
|
+
const suspects = [];
|
|
103
|
+
if (options.assignee !== undefined) {
|
|
104
|
+
suspects.push(`--assignee "${options.assignee}" — Azure DevOps does not reject an identity it does not `
|
|
105
|
+
+ "know; it matches nothing. `pharos whoami` shows the form your own identity takes.");
|
|
106
|
+
}
|
|
107
|
+
for (const tag of options.tags) {
|
|
108
|
+
suspects.push(`--tag "${tag}" — a substring match; no error if nothing carries it.`);
|
|
109
|
+
}
|
|
110
|
+
for (const state of options.states) {
|
|
111
|
+
suspects.push(`--state "${state}" — run \`pharos types\` to see what each type accepts.`);
|
|
112
|
+
}
|
|
113
|
+
return suspects.length === 0 ? undefined : suspects;
|
|
114
|
+
}
|
|
115
|
+
/** Whether anything other than `--wiql` was asked for. */
|
|
116
|
+
function describesFilters(options) {
|
|
117
|
+
return (options.mine
|
|
118
|
+
|| options.assignee !== undefined
|
|
119
|
+
|| options.states.length > 0
|
|
120
|
+
|| options.types.length > 0
|
|
121
|
+
|| options.tags.length > 0
|
|
122
|
+
|| options.sprint !== undefined
|
|
123
|
+
|| options.area !== undefined);
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Who `@Me` actually was.
|
|
127
|
+
*
|
|
128
|
+
* The macro resolves server-side against whoever owns `ADO_PAT`, which is not
|
|
129
|
+
* necessarily the person reading the output — a shared or service token makes
|
|
130
|
+
* "assigned to me" quietly mean somebody else. One GET, and it never fails the
|
|
131
|
+
* query: an unnameable identity is reported as such.
|
|
132
|
+
*/
|
|
133
|
+
async function resolveMe(session) {
|
|
134
|
+
try {
|
|
135
|
+
const identity = await authenticatedIdentity(session.client.http);
|
|
136
|
+
return identity.displayName ?? identity.uniqueName ?? "@Me (unnamed)";
|
|
137
|
+
}
|
|
138
|
+
catch {
|
|
139
|
+
return "@Me — whoever owns ADO_PAT; the identity could not be read";
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
function summarise(item) {
|
|
143
|
+
const fields = item.fields ?? {};
|
|
144
|
+
const text = (name) => {
|
|
145
|
+
const value = fields[name];
|
|
146
|
+
return typeof value === "string" && value !== "" ? value : undefined;
|
|
147
|
+
};
|
|
148
|
+
const tags = text("System.Tags");
|
|
149
|
+
const priority = fields["Microsoft.VSTS.Common.Priority"];
|
|
150
|
+
return {
|
|
151
|
+
id: item.id,
|
|
152
|
+
type: text("System.WorkItemType"),
|
|
153
|
+
title: text("System.Title"),
|
|
154
|
+
state: text("System.State"),
|
|
155
|
+
assignedTo: displayName(fields["System.AssignedTo"]),
|
|
156
|
+
iteration: text("System.IterationPath"),
|
|
157
|
+
tags: tags === undefined ? [] : tags.split(";").map((tag) => tag.trim()).filter((tag) => tag !== ""),
|
|
158
|
+
priority: typeof priority === "number" ? priority : undefined,
|
|
159
|
+
changed: text("System.ChangedDate"),
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
/** `null` rather than absent: unassigned is an answer, and a common one. */
|
|
163
|
+
function displayName(value) {
|
|
164
|
+
if (typeof value === "string" && value !== "")
|
|
165
|
+
return value;
|
|
166
|
+
if (typeof value === "object" && value !== null) {
|
|
167
|
+
const display = value.displayName;
|
|
168
|
+
if (typeof display === "string")
|
|
169
|
+
return display;
|
|
170
|
+
}
|
|
171
|
+
return null;
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* The human rendering.
|
|
175
|
+
*
|
|
176
|
+
* The openness note is at the BOTTOM rather than the top: it is the thing to
|
|
177
|
+
* check when the list looks wrong, and putting a paragraph of caveat above the
|
|
178
|
+
* answer buries the answer.
|
|
179
|
+
*/
|
|
180
|
+
function render(result) {
|
|
181
|
+
const lines = [];
|
|
182
|
+
const who = result.assignedTo === null ? "" : ` assigned to ${result.assignedTo}`;
|
|
183
|
+
lines.push(`${result.count} work item${result.count === 1 ? "" : "s"}${who}`, "");
|
|
184
|
+
if (result.items.length === 0) {
|
|
185
|
+
lines.push(" (nothing matched — this is a successful query, not a failure)");
|
|
186
|
+
for (const suspect of result.emptyBecause ?? [])
|
|
187
|
+
lines.push(` ${suspect}`);
|
|
188
|
+
}
|
|
189
|
+
for (const item of result.items) {
|
|
190
|
+
const assignee = item.assignedTo ?? "unassigned";
|
|
191
|
+
lines.push(` #${String(item.id ?? "?").padEnd(6)} ${(item.type ?? "").padEnd(10)} `
|
|
192
|
+
+ `${(item.state ?? "").padEnd(12)} ${(item.title ?? "(untitled)").slice(0, 60).padEnd(60)} ${assignee}`);
|
|
193
|
+
}
|
|
194
|
+
if (result.openness.note !== undefined) {
|
|
195
|
+
lines.push("", `openness: ${result.openness.note}`);
|
|
196
|
+
}
|
|
197
|
+
if (result.openness.terminal.length > 0) {
|
|
198
|
+
lines.push(`treated as finished: ${result.openness.terminal.join(", ")}`);
|
|
199
|
+
}
|
|
200
|
+
return lines.join("\n");
|
|
201
|
+
}
|
|
202
|
+
//# sourceMappingURL=query.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"query.js","sourceRoot":"","sources":["../../src/commands/query.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { type ExitCode, type Io } from "../output.js";
|
|
2
|
+
import { type Session } from "../session.js";
|
|
3
|
+
export declare function runDelete(io: Io, session: Session, positionals: readonly string[]): Promise<ExitCode>;
|
|
4
|
+
export declare function runRestore(io: Io, session: Session, positionals: readonly string[]): Promise<ExitCode>;
|
|
5
|
+
export interface DeletedCommandOptions {
|
|
6
|
+
/** How many to name. The bin is not usually small. */
|
|
7
|
+
top: number;
|
|
8
|
+
}
|
|
9
|
+
export declare function runDeleted(io: Io, session: Session, options: DeletedCommandOptions): Promise<ExitCode>;
|
|
10
|
+
//# sourceMappingURL=recycle.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"recycle.d.ts","sourceRoot":"","sources":["../../src/commands/recycle.ts"],"names":[],"mappings":"AAAA,OAAO,EAA8B,KAAK,QAAQ,EAAE,KAAK,EAAE,EAAE,MAAM,cAAc,CAAC;AAClF,OAAO,EAAW,KAAK,OAAO,EAAE,MAAM,eAAe,CAAC;AA0CtD,wBAAsB,SAAS,CAC7B,EAAE,EAAE,EAAE,EACN,OAAO,EAAE,OAAO,EAChB,WAAW,EAAE,SAAS,MAAM,EAAE,GAC7B,OAAO,CAAC,QAAQ,CAAC,CA8BnB;AAED,wBAAsB,UAAU,CAC9B,EAAE,EAAE,EAAE,EACN,OAAO,EAAE,OAAO,EAChB,WAAW,EAAE,SAAS,MAAM,EAAE,GAC7B,OAAO,CAAC,QAAQ,CAAC,CAYnB;AAED,MAAM,WAAW,qBAAqB;IACpC,sDAAsD;IACtD,GAAG,EAAE,MAAM,CAAC;CACb;AAED,wBAAsB,UAAU,CAC9B,EAAE,EAAE,EAAE,EACN,OAAO,EAAE,OAAO,EAChB,OAAO,EAAE,qBAAqB,GAC7B,OAAO,CAAC,QAAQ,CAAC,CAyCnB"}
|