@floh-solutions/pharos-cli 0.23.0 → 0.25.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/README.md +409 -5
- package/dist/capabilities.d.ts.map +1 -1
- package/dist/capabilities.js +23 -0
- package/dist/capabilities.js.map +1 -1
- package/dist/cli.d.ts +4 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +362 -36
- package/dist/cli.js.map +1 -1
- package/dist/commands/comment-github.d.ts +138 -0
- package/dist/commands/comment-github.d.ts.map +1 -0
- package/dist/commands/comment-github.js +427 -0
- package/dist/commands/comment-github.js.map +1 -0
- package/dist/commands/comment.d.ts.map +1 -1
- package/dist/commands/comment.js +114 -19
- package/dist/commands/comment.js.map +1 -1
- package/dist/commands/doctor.d.ts +71 -2
- package/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/doctor.js +414 -8
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/issue.d.ts +75 -0
- package/dist/commands/issue.d.ts.map +1 -0
- package/dist/commands/issue.js +2466 -0
- package/dist/commands/issue.js.map +1 -0
- package/dist/commands/setup.d.ts +6 -0
- package/dist/commands/setup.d.ts.map +1 -1
- package/dist/commands/setup.js +114 -0
- package/dist/commands/setup.js.map +1 -1
- package/dist/half-link.d.ts +157 -0
- package/dist/half-link.d.ts.map +1 -0
- package/dist/half-link.js +257 -0
- package/dist/half-link.js.map +1 -0
- package/dist/issue-scan.d.ts +383 -0
- package/dist/issue-scan.d.ts.map +1 -0
- package/dist/issue-scan.js +455 -0
- package/dist/issue-scan.js.map +1 -0
- package/dist/output.d.ts +18 -1
- package/dist/output.d.ts.map +1 -1
- package/dist/output.js +293 -0
- package/dist/output.js.map +1 -1
- package/dist/session.d.ts +101 -2
- package/dist/session.d.ts.map +1 -1
- package/dist/session.js +132 -11
- package/dist/session.js.map +1 -1
- package/dist/target.d.ts +40 -4
- package/dist/target.d.ts.map +1 -1
- package/dist/target.js +14 -3
- package/dist/target.js.map +1 -1
- package/package.json +3 -2
- package/skill/SKILL.md +422 -11
package/dist/session.js
CHANGED
|
@@ -1,30 +1,132 @@
|
|
|
1
1
|
import { AdoClient, WikiApi } from "@floh-solutions/ado-core";
|
|
2
2
|
import { WriteBudget } from "./budget.js";
|
|
3
|
+
import { HalfLinkJournal } from "./half-link.js";
|
|
3
4
|
import { EXIT_OK, emit, refusal, usageError } from "./output.js";
|
|
4
5
|
export class Session {
|
|
5
|
-
client;
|
|
6
|
-
wiki;
|
|
7
6
|
budget;
|
|
8
7
|
apply;
|
|
9
8
|
previewOnly;
|
|
10
9
|
pretty;
|
|
10
|
+
/**
|
|
11
|
+
* Adoptions this machine started and did not finish — see `half-link.ts`.
|
|
12
|
+
*
|
|
13
|
+
* Built from the invocation's environment rather than reached for inside the
|
|
14
|
+
* command, so a run whose env names no `HOME` has no journal instead of
|
|
15
|
+
* quietly writing into whichever home directory the process happens to have.
|
|
16
|
+
*/
|
|
17
|
+
halfLinks;
|
|
18
|
+
#env;
|
|
19
|
+
#organization;
|
|
20
|
+
#project;
|
|
11
21
|
#wikiHint;
|
|
22
|
+
#client;
|
|
23
|
+
#wikiApi;
|
|
12
24
|
#resolvedWiki;
|
|
25
|
+
#github;
|
|
13
26
|
constructor(options) {
|
|
14
27
|
this.budget = options.budget;
|
|
15
28
|
this.apply = options.apply;
|
|
16
29
|
this.previewOnly = options.previewOnly;
|
|
17
30
|
this.pretty = options.pretty;
|
|
31
|
+
this.#env = options.env;
|
|
32
|
+
this.#organization = options.organization;
|
|
33
|
+
this.#project = options.project;
|
|
18
34
|
this.#wikiHint = options.wiki;
|
|
19
|
-
this
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
35
|
+
this.#client = options.client;
|
|
36
|
+
this.#github = options.github;
|
|
37
|
+
this.halfLinks = new HalfLinkJournal(options.env);
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* The Azure DevOps client — **resolved on first use, not in the constructor.**
|
|
41
|
+
*
|
|
42
|
+
* It was built eagerly, and that made `ADO_PAT` the price of entry to the
|
|
43
|
+
* whole CLI: `pharos comment list contoso/widgets#45` reads a GitHub issue
|
|
44
|
+
* through `gh-core` with the `gh` credential and never touches Azure DevOps,
|
|
45
|
+
* and it answered `{"kind":"config","message":"ADO_PAT is not set…"}` (#839).
|
|
46
|
+
* Somebody with `gh` signed in and no Azure DevOps PAT — a state
|
|
47
|
+
* `pharos doctor` reports as fine, and which the separate Keychain entries
|
|
48
|
+
* exist to make possible — could not use a single GitHub verb.
|
|
49
|
+
*
|
|
50
|
+
* {@link github} was made lazy for exactly this reason (#782) and this half
|
|
51
|
+
* was not. The fix belongs here rather than in the `comment` dispatch: any
|
|
52
|
+
* verb family that grows a GitHub target inherits it, and a special case in
|
|
53
|
+
* one command is a special case the next one has to remember.
|
|
54
|
+
*
|
|
55
|
+
* **Resolution failure is deferred, never softened.** The `AdoConfigError`
|
|
56
|
+
* still names the missing variable and still exits 2 — it arrives when
|
|
57
|
+
* something actually needs Azure DevOps, which is where it is an answer
|
|
58
|
+
* rather than an obstacle. See {@link approve} for the one place that
|
|
59
|
+
* ordering has to be forced.
|
|
60
|
+
*/
|
|
61
|
+
get client() {
|
|
62
|
+
this.#client ??= AdoClient.fromEnv(this.#env, {
|
|
63
|
+
...(this.#organization === undefined ? {} : { organization: this.#organization }),
|
|
64
|
+
...(this.#project === undefined ? {} : { project: this.#project }),
|
|
65
|
+
// Every write goes through here, whoever issued it.
|
|
66
|
+
fetch: this.budget.wrap((input, init) => globalThis.fetch(input, init)),
|
|
67
|
+
});
|
|
68
|
+
return this.#client;
|
|
69
|
+
}
|
|
70
|
+
/** The wiki API, over {@link client}'s transport — and lazy for its sake. */
|
|
71
|
+
get wiki() {
|
|
72
|
+
this.#wikiApi ??= new WikiApi(this.client.http);
|
|
73
|
+
return this.#wikiApi;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* The GitHub client, resolved once, from `~/.config/pharos/repos.json`.
|
|
77
|
+
*
|
|
78
|
+
* Three things about this are deliberate.
|
|
79
|
+
*
|
|
80
|
+
* **Its fetch is wrapped by the same {@link WriteBudget} the Azure DevOps
|
|
81
|
+
* client uses.** `--max-writes 0` promises that an invocation cannot change
|
|
82
|
+
* anything, and that promise is worthless if it covers one platform of a
|
|
83
|
+
* two-platform verb: `pharos issue adopt` posts a comment on GitHub, and a cap
|
|
84
|
+
* that let that through would be a cap in name only. `gh-core`'s transport
|
|
85
|
+
* already recognises a refusal thrown by a fetch wrapper and rethrows it
|
|
86
|
+
* unretried rather than reporting it as a dead socket.
|
|
87
|
+
*
|
|
88
|
+
* **It is loaded lazily, and imported dynamically.** Twenty-seven of the
|
|
89
|
+
* twenty-eight verbs here never touch GitHub, and `gh-core` reaches for
|
|
90
|
+
* `node:child_process` at import time to talk to `gh`. `doctor` does the same
|
|
91
|
+
* thing for the same reason.
|
|
92
|
+
*
|
|
93
|
+
* **There is no repository default.** `GitHubClient` has no current repo and
|
|
94
|
+
* no current account by construction (plan §2) — every call names both, and an
|
|
95
|
+
* unbound repository is an error naming `repos.json` rather than a guess at
|
|
96
|
+
* whichever `gh` account happens to be active.
|
|
97
|
+
*/
|
|
98
|
+
async github() {
|
|
99
|
+
if (this.#github !== undefined)
|
|
100
|
+
return this.#github;
|
|
101
|
+
const { GhTokenSource, GitHubClient } = await import("@floh-solutions/gh-core");
|
|
102
|
+
this.#github = await GitHubClient.fromConfig({
|
|
103
|
+
env: this.#env,
|
|
104
|
+
// The invocation's environment, not `process.env` — `gh` is spawned with
|
|
105
|
+
// it (`GhTokenSource` scrubs the ambient GH_TOKEN out on the way), and the
|
|
106
|
+
// PATH it is found on is the PATH this run was given. Same reason `doctor`
|
|
107
|
+
// hands it the path it resolved.
|
|
108
|
+
tokens: new GhTokenSource({ env: this.#env }),
|
|
109
|
+
fetch: this.budget.wrap((input, init) => globalThis.fetch(input, init)),
|
|
110
|
+
});
|
|
111
|
+
return this.#github;
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* The web address of a work item — what a person clicks, not what the API
|
|
115
|
+
* reads.
|
|
116
|
+
*
|
|
117
|
+
* `WorkItem.url` is the REST url and opening it in a browser gives JSON, so a
|
|
118
|
+
* comment on a public GitHub issue carrying it would be worse than carrying
|
|
119
|
+
* nothing. `_links.html.href` is what Azure DevOps itself calls the human
|
|
120
|
+
* address; it is only present when the read asked for links, so the composed
|
|
121
|
+
* form is the fallback rather than the other way round.
|
|
122
|
+
*/
|
|
123
|
+
workItemUrl(item) {
|
|
124
|
+
const html = item._links?.["html"]?.href;
|
|
125
|
+
if (typeof html === "string" && html !== "")
|
|
126
|
+
return html;
|
|
127
|
+
const organization = encodeURIComponent(this.client.organization);
|
|
128
|
+
const project = encodeURIComponent(this.client.project ?? "");
|
|
129
|
+
return `${this.client.http.baseUrl}/${organization}/${project}/_workitems/edit/${item.id}`;
|
|
28
130
|
}
|
|
29
131
|
/**
|
|
30
132
|
* The wiki to act on, resolved once.
|
|
@@ -96,9 +198,28 @@ export class Session {
|
|
|
96
198
|
* is how a half-finished job gets reported as done (`docs/pharos-cli-plan.md`
|
|
97
199
|
* §2.5). `--dry-run` exits 0 because there the preview IS the deliverable.
|
|
98
200
|
*
|
|
201
|
+
* ## The credential is checked before the gate, and that ordering is the point
|
|
202
|
+
*
|
|
203
|
+
* {@link Session.client} resolves on first use (#839), and every arm below
|
|
204
|
+
* runs *before* the verb touches it. So on a machine with no `ADO_PAT`,
|
|
205
|
+
* `pharos comment delete 210 5` would answer "Refusing to delete comment 5 on
|
|
206
|
+
* #210 without --yes" — a refusal that invites a retry with `--yes` which then
|
|
207
|
+
* fails on the credential anyway. Two refusals, the useful one second.
|
|
208
|
+
*
|
|
209
|
+
* Touching the client here puts the credential failure back in front, exactly
|
|
210
|
+
* where it was when the client was eager. A `--dry-run` preview of an Azure
|
|
211
|
+
* DevOps write is refused for the same reason: a preview of something this
|
|
212
|
+
* machine cannot do is a preview of nothing.
|
|
213
|
+
*
|
|
214
|
+
* A GitHub action says so and is exempt — that is the whole bug this ordering
|
|
215
|
+
* has to avoid recreating.
|
|
216
|
+
*
|
|
99
217
|
* @returns `undefined` to proceed with the write, or the exit code to return.
|
|
100
218
|
*/
|
|
101
219
|
export function approve(io, session, action) {
|
|
220
|
+
// Raises `AdoConfigError` naming the missing variable, or does nothing.
|
|
221
|
+
if (action.platform !== "github")
|
|
222
|
+
void session.client;
|
|
102
223
|
if (session.previewOnly) {
|
|
103
224
|
emit(io, { applied: false, dryRun: true, ...action.would });
|
|
104
225
|
return EXIT_OK;
|
package/dist/session.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session.js","sourceRoot":"","sources":["../src/session.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,OAAO,EAAgB,MAAM,0BAA0B,CAAC;
|
|
1
|
+
{"version":3,"file":"session.js","sourceRoot":"","sources":["../src/session.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,OAAO,EAAgB,MAAM,0BAA0B,CAAC;AAG5E,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACjD,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,UAAU,EAA0B,MAAM,aAAa,CAAC;AAyBzF,MAAM,OAAO,OAAO;IACT,MAAM,CAAc;IACpB,KAAK,CAAU;IACf,WAAW,CAAU;IACrB,MAAM,CAAU;IACzB;;;;;;OAMG;IACM,SAAS,CAAkB;IAE3B,IAAI,CAAoB;IACxB,aAAa,CAAqB;IAClC,QAAQ,CAAqB;IAC7B,SAAS,CAAqB;IACvC,OAAO,CAAwB;IAC/B,QAAQ,CAAsB;IAC9B,aAAa,CAAsB;IACnC,OAAO,CAA2B;IAElC,YAAY,OAAuB;QACjC,IAAI,CAAC,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;QAC7B,IAAI,CAAC,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC;QAC3B,IAAI,CAAC,WAAW,GAAG,OAAO,CAAC,WAAW,CAAC;QACvC,IAAI,CAAC,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;QAC7B,IAAI,CAAC,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC;QACxB,IAAI,CAAC,aAAa,GAAG,OAAO,CAAC,YAAY,CAAC;QAC1C,IAAI,CAAC,QAAQ,GAAG,OAAO,CAAC,OAAO,CAAC;QAChC,IAAI,CAAC,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC;QAC9B,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC;QAC9B,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC;QAC9B,IAAI,CAAC,SAAS,GAAG,IAAI,eAAe,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IACpD,CAAC;IAED;;;;;;;;;;;;;;;;;;;;;OAqBG;IACH,IAAI,MAAM;QACR,IAAI,CAAC,OAAO,KAAK,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,EAAE;YAC5C,GAAG,CAAC,IAAI,CAAC,aAAa,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,YAAY,EAAE,IAAI,CAAC,aAAa,EAAE,CAAC;YACjF,GAAG,CAAC,IAAI,CAAC,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,IAAI,CAAC,QAAQ,EAAE,CAAC;YAClE,oDAAoD;YACpD,KAAK,EAAE,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE,CAAC,UAAU,CAAC,KAAK,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;SACxE,CAAC,CAAC;QACH,OAAO,IAAI,CAAC,OAAO,CAAC;IACtB,CAAC;IAED,6EAA6E;IAC7E,IAAI,IAAI;QACN,IAAI,CAAC,QAAQ,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QAChD,OAAO,IAAI,CAAC,QAAQ,CAAC;IACvB,CAAC;IAED;;;;;;;;;;;;;;;;;;;;;;OAsBG;IACH,KAAK,CAAC,MAAM;QACV,IAAI,IAAI,CAAC,OAAO,KAAK,SAAS;YAAE,OAAO,IAAI,CAAC,OAAO,CAAC;QACpD,MAAM,EAAE,aAAa,EAAE,YAAY,EAAE,GAAG,MAAM,MAAM,CAAC,yBAAyB,CAAC,CAAC;QAChF,IAAI,CAAC,OAAO,GAAG,MAAM,YAAY,CAAC,UAAU,CAAC;YAC3C,GAAG,EAAE,IAAI,CAAC,IAAI;YACd,yEAAyE;YACzE,2EAA2E;YAC3E,2EAA2E;YAC3E,iCAAiC;YACjC,MAAM,EAAE,IAAI,aAAa,CAAC,EAAE,GAAG,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC;YAC7C,KAAK,EAAE,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE,CAAC,UAAU,CAAC,KAAK,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;SACxE,CAAC,CAAC;QACH,OAAO,IAAI,CAAC,OAAO,CAAC;IACtB,CAAC;IAED;;;;;;;;;OASG;IACH,WAAW,CAAC,IAAsD;QAChE,MAAM,IAAI,GAAI,IAAI,CAAC,MAAM,EAAE,CAAC,MAAM,CAAoC,EAAE,IAAI,CAAC;QAC7E,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,KAAK,EAAE;YAAE,OAAO,IAAI,CAAC;QACzD,MAAM,YAAY,GAAG,kBAAkB,CAAC,IAAI,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC;QAClE,MAAM,OAAO,GAAG,kBAAkB,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC;QAC9D,OAAO,GAAG,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,IAAI,YAAY,IAAI,OAAO,oBAAoB,IAAI,CAAC,EAAE,EAAE,CAAC;IAC7F,CAAC;IAED;;;;;;;;;OASG;IACH,KAAK,CAAC,cAAc;QAClB,OAAO,CAAC,MAAM,IAAI,CAAC,YAAY,EAAE,CAAC,CAAC,IAAI,CAAC;IAC1C,CAAC;IAED,wEAAwE;IACxE,KAAK,CAAC,cAAc;QAClB,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,YAAY,EAAE,CAAC;QACvC,IAAI,IAAI,CAAC,SAAS,KAAK,SAAS,IAAI,IAAI,CAAC,SAAS,KAAK,EAAE,EAAE,CAAC;YAC1D,MAAM,UAAU,CACd,aAAa,IAAI,CAAC,IAAI,qEAAqE;kBACvF,wFAAwF;kBACxF,0CAA0C,CAC/C,CAAC;QACJ,CAAC;QACD,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,CAAC,SAAS,EAAE,CAAC;IACzE,CAAC;IAED,KAAK,CAAC,YAAY;QAChB,IAAI,IAAI,CAAC,aAAa,KAAK,SAAS;YAAE,OAAO,IAAI,CAAC,aAAa,CAAC;QAEhE,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC;QAE1C,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACvB,MAAM,UAAU,CAAC,2BAA2B,CAAC,CAAC;QAChD,CAAC;QAED,IAAI,IAAI,CAAC,SAAS,KAAK,SAAS,IAAI,IAAI,CAAC,SAAS,KAAK,EAAE,EAAE,CAAC;YAC1D,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC;YAC5B,MAAM,KAAK,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,SAAS,CAAC,IAAI,KAAK,IAAI,IAAI,SAAS,CAAC,EAAE,KAAK,IAAI,CAAC,CAAC;YAC1F,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;gBACxB,MAAM,UAAU,CAAC,mBAAmB,IAAI,oBAAoB,EAAE;oBAC5D,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC;iBAChD,CAAC,CAAC;YACL,CAAC;YACD,IAAI,CAAC,aAAa,GAAG,KAAK,CAAC;YAC3B,OAAO,KAAK,CAAC;QACf,CAAC;QAED,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACvB,IAAI,CAAC,aAAa,GAAG,KAAK,CAAC,CAAC,CAAE,CAAC;YAC/B,OAAO,IAAI,CAAC,aAAa,CAAC;QAC5B,CAAC;QAED,MAAM,YAAY,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,SAAS,CAAC,IAAI,KAAK,aAAa,CAAC,CAAC;QACnF,IAAI,YAAY,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC9B,IAAI,CAAC,aAAa,GAAG,YAAY,CAAC,CAAC,CAAE,CAAC;YACtC,OAAO,IAAI,CAAC,aAAa,CAAC;QAC5B,CAAC;QAED,MAAM,UAAU,CACd,oBAAoB,KAAK,CAAC,MAAM,yDAAyD,EACzF,EAAE,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,CACpD,CAAC;IACJ,CAAC;CACF;AAoBD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,MAAM,UAAU,OAAO,CAAC,EAAM,EAAE,OAAgB,EAAE,MAAyB;IACzE,wEAAwE;IACxE,IAAI,MAAM,CAAC,QAAQ,KAAK,QAAQ;QAAE,KAAK,OAAO,CAAC,MAAM,CAAC;IAEtD,IAAI,OAAO,CAAC,WAAW,EAAE,CAAC;QACxB,IAAI,CAAC,EAAE,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC;QAC5D,OAAO,OAAO,CAAC;IACjB,CAAC;IACD,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;QACnB,MAAM,OAAO,CAAC,eAAe,MAAM,CAAC,IAAI,sCAAsC,EAAE;YAC9E,GAAG,MAAM,CAAC,KAAK;YACf,IAAI,EAAE,8EAA8E;SACrF,CAAC,CAAC;IACL,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC"}
|
package/dist/target.d.ts
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
|
+
import { type IssueCoordinate } from "@floh-solutions/gh-core";
|
|
1
2
|
/**
|
|
2
|
-
* What a comment verb is addressing: a work item,
|
|
3
|
+
* What a comment verb is addressing: a work item, a wiki page, or a GitHub
|
|
4
|
+
* issue.
|
|
3
5
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
6
|
+
* Three comment surfaces, three APIs with different quirks, but from where an
|
|
7
|
+
* agent stands they are one idea — "the discussion about this thing" — so
|
|
8
|
+
* `pharos comment` takes any of them and the target says which.
|
|
7
9
|
*
|
|
8
10
|
* **The rule is that a wiki page is an absolute path, and that is not a
|
|
9
11
|
* convention invented here.** The Azure DevOps wiki API answers 404 for a page
|
|
@@ -12,6 +14,20 @@
|
|
|
12
14
|
* spelling that disambiguates is also the only spelling that works, and a
|
|
13
15
|
* target that omits it is rejected with the reason rather than quietly fixed —
|
|
14
16
|
* somebody passing `Plans/Foo` has a bug somewhere upstream worth finding.
|
|
17
|
+
*
|
|
18
|
+
* **A GitHub issue is `owner/name#45`** — the form plan §8 already chose for
|
|
19
|
+
* `pharos issue`, so one spelling addresses an issue everywhere in this CLI.
|
|
20
|
+
* The three grammars cannot collide: a work item is all digits, a wiki page
|
|
21
|
+
* opens with `/`, and a repository name may contain neither a `#` nor a second
|
|
22
|
+
* `/`. `gh-core` owns the parse (it also accepts `owner/name/45` and a full
|
|
23
|
+
* issue URL, because all three get pasted), so there is no second grammar here.
|
|
24
|
+
*
|
|
25
|
+
* **A GitHub target carries no account, deliberately.** The account is resolved
|
|
26
|
+
* from `repos.json` at the point of use — plan §2's whole finding is that
|
|
27
|
+
* letting anything else decide returns a cheerful 200 from the wrong
|
|
28
|
+
* repository. A `Target` is what the caller *said*; the binding is what this
|
|
29
|
+
* machine is configured to do about it, and conflating them is how the wrong
|
|
30
|
+
* one gets used.
|
|
15
31
|
*/
|
|
16
32
|
export type Target = {
|
|
17
33
|
kind: "workItem";
|
|
@@ -19,7 +35,27 @@ export type Target = {
|
|
|
19
35
|
} | {
|
|
20
36
|
kind: "wikiPage";
|
|
21
37
|
path: string;
|
|
38
|
+
} | {
|
|
39
|
+
kind: "githubIssue";
|
|
40
|
+
issue: IssueCoordinate;
|
|
22
41
|
};
|
|
42
|
+
/**
|
|
43
|
+
* The two Azure DevOps surfaces, as their own type.
|
|
44
|
+
*
|
|
45
|
+
* Named rather than left implicit because the arms that handle them are written
|
|
46
|
+
* as "work item, else wiki page" — which was total when there were two kinds
|
|
47
|
+
* and silently stopped being when GitHub arrived. Taking this type makes the
|
|
48
|
+
* compiler carry that guarantee instead of a reader having to.
|
|
49
|
+
*/
|
|
50
|
+
export type AdoTarget = Extract<Target, {
|
|
51
|
+
kind: "workItem";
|
|
52
|
+
} | {
|
|
53
|
+
kind: "wikiPage";
|
|
54
|
+
}>;
|
|
55
|
+
/** A GitHub issue target, for the arms that only accept one. */
|
|
56
|
+
export type GitHubTarget = Extract<Target, {
|
|
57
|
+
kind: "githubIssue";
|
|
58
|
+
}>;
|
|
23
59
|
export declare function parseTarget(raw: string): Target;
|
|
24
60
|
export declare function describeTarget(target: Target): string;
|
|
25
61
|
//# sourceMappingURL=target.d.ts.map
|
package/dist/target.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"target.d.ts","sourceRoot":"","sources":["../src/target.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"target.d.ts","sourceRoot":"","sources":["../src/target.ts"],"names":[],"mappings":"AAAA,OAAO,EAAwB,KAAK,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAIrF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,MAAM,MAAM,MAAM,GACd;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,GAChC;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAClC;IAAE,IAAI,EAAE,aAAa,CAAC;IAAC,KAAK,EAAE,eAAe,CAAA;CAAE,CAAC;AAEpD;;;;;;;GAOG;AACH,MAAM,MAAM,SAAS,GAAG,OAAO,CAAC,MAAM,EAAE;IAAE,IAAI,EAAE,UAAU,CAAA;CAAE,GAAG;IAAE,IAAI,EAAE,UAAU,CAAA;CAAE,CAAC,CAAC;AAErF,gEAAgE;AAChE,MAAM,MAAM,YAAY,GAAG,OAAO,CAAC,MAAM,EAAE;IAAE,IAAI,EAAE,aAAa,CAAA;CAAE,CAAC,CAAC;AAEpE,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAyB/C;AAED,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CASrD"}
|
package/dist/target.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { parseIssueCoordinate } from "@floh-solutions/gh-core";
|
|
1
2
|
import { usageError } from "./output.js";
|
|
2
3
|
export function parseTarget(raw) {
|
|
3
4
|
const value = raw.trim();
|
|
@@ -13,11 +14,21 @@ export function parseTarget(raw) {
|
|
|
13
14
|
}
|
|
14
15
|
return { kind: "wikiPage", path: value };
|
|
15
16
|
}
|
|
17
|
+
const issue = parseIssueCoordinate(value);
|
|
18
|
+
if (issue !== undefined)
|
|
19
|
+
return { kind: "githubIssue", issue };
|
|
16
20
|
throw usageError(`Cannot tell what "${raw}" is. A work item is its number (210); a wiki page is an absolute `
|
|
17
|
-
+ `path (/Plans/Foo)
|
|
18
|
-
+
|
|
21
|
+
+ `path (/Plans/Foo); a GitHub issue is owner/name#45. The leading slash on a wiki page is `
|
|
22
|
+
+ `required by the wiki API itself, not by this tool — it answers 404 without one.`, { target: raw });
|
|
19
23
|
}
|
|
20
24
|
export function describeTarget(target) {
|
|
21
|
-
|
|
25
|
+
switch (target.kind) {
|
|
26
|
+
case "workItem":
|
|
27
|
+
return `#${target.id}`;
|
|
28
|
+
case "wikiPage":
|
|
29
|
+
return target.path;
|
|
30
|
+
case "githubIssue":
|
|
31
|
+
return `${target.issue.repo}#${target.issue.number}`;
|
|
32
|
+
}
|
|
22
33
|
}
|
|
23
34
|
//# sourceMappingURL=target.js.map
|
package/dist/target.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"target.js","sourceRoot":"","sources":["../src/target.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"target.js","sourceRoot":"","sources":["../src/target.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAwB,MAAM,yBAAyB,CAAC;AAErF,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAkDzC,MAAM,UAAU,WAAW,CAAC,GAAW;IACrC,MAAM,KAAK,GAAG,GAAG,CAAC,IAAI,EAAE,CAAC;IAEzB,IAAI,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;QACxB,MAAM,EAAE,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;QACzB,IAAI,EAAE,IAAI,CAAC;YAAE,MAAM,UAAU,CAAC,IAAI,GAAG,0BAA0B,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC;QAClF,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,EAAE,EAAE,CAAC;IAClC,CAAC;IAED,IAAI,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;QAC1B,IAAI,KAAK,KAAK,GAAG,EAAE,CAAC;YAClB,MAAM,UAAU,CAAC,8CAA8C,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC;QACpF,CAAC;QACD,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC;IAC3C,CAAC;IAED,MAAM,KAAK,GAAG,oBAAoB,CAAC,KAAK,CAAC,CAAC;IAC1C,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,EAAE,IAAI,EAAE,aAAa,EAAE,KAAK,EAAE,CAAC;IAE/D,MAAM,UAAU,CACd,qBAAqB,GAAG,oEAAoE;UACxF,0FAA0F;UAC1F,iFAAiF,EACrF,EAAE,MAAM,EAAE,GAAG,EAAE,CAChB,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,MAAc;IAC3C,QAAQ,MAAM,CAAC,IAAI,EAAE,CAAC;QACpB,KAAK,UAAU;YACb,OAAO,IAAI,MAAM,CAAC,EAAE,EAAE,CAAC;QACzB,KAAK,UAAU;YACb,OAAO,MAAM,CAAC,IAAI,CAAC;QACrB,KAAK,aAAa;YAChB,OAAO,GAAG,MAAM,CAAC,KAAK,CAAC,IAAI,IAAI,MAAM,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC;IACzD,CAAC;AACH,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@floh-solutions/pharos-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.25.0",
|
|
4
4
|
"description": "Azure DevOps from a headless shell, for an agent: the whole context of a task in one call, and the wiki/comment verbs Microsoft's MCP server does not ship.",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"author": "FLOH Solutions",
|
|
@@ -25,7 +25,8 @@
|
|
|
25
25
|
},
|
|
26
26
|
"dependencies": {
|
|
27
27
|
"@floh-solutions/ado-core": "0.9.1",
|
|
28
|
-
"@floh-solutions/plan-to-board": "0.1.1"
|
|
28
|
+
"@floh-solutions/plan-to-board": "0.1.1",
|
|
29
|
+
"@floh-solutions/gh-core": "0.2.0"
|
|
29
30
|
},
|
|
30
31
|
"devDependencies": {
|
|
31
32
|
"@types/node": "^22.10.2",
|