@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.
Files changed (49) hide show
  1. package/README.md +409 -5
  2. package/dist/capabilities.d.ts.map +1 -1
  3. package/dist/capabilities.js +23 -0
  4. package/dist/capabilities.js.map +1 -1
  5. package/dist/cli.d.ts +4 -1
  6. package/dist/cli.d.ts.map +1 -1
  7. package/dist/cli.js +362 -36
  8. package/dist/cli.js.map +1 -1
  9. package/dist/commands/comment-github.d.ts +138 -0
  10. package/dist/commands/comment-github.d.ts.map +1 -0
  11. package/dist/commands/comment-github.js +427 -0
  12. package/dist/commands/comment-github.js.map +1 -0
  13. package/dist/commands/comment.d.ts.map +1 -1
  14. package/dist/commands/comment.js +114 -19
  15. package/dist/commands/comment.js.map +1 -1
  16. package/dist/commands/doctor.d.ts +71 -2
  17. package/dist/commands/doctor.d.ts.map +1 -1
  18. package/dist/commands/doctor.js +414 -8
  19. package/dist/commands/doctor.js.map +1 -1
  20. package/dist/commands/issue.d.ts +75 -0
  21. package/dist/commands/issue.d.ts.map +1 -0
  22. package/dist/commands/issue.js +2466 -0
  23. package/dist/commands/issue.js.map +1 -0
  24. package/dist/commands/setup.d.ts +6 -0
  25. package/dist/commands/setup.d.ts.map +1 -1
  26. package/dist/commands/setup.js +114 -0
  27. package/dist/commands/setup.js.map +1 -1
  28. package/dist/half-link.d.ts +157 -0
  29. package/dist/half-link.d.ts.map +1 -0
  30. package/dist/half-link.js +257 -0
  31. package/dist/half-link.js.map +1 -0
  32. package/dist/issue-scan.d.ts +383 -0
  33. package/dist/issue-scan.d.ts.map +1 -0
  34. package/dist/issue-scan.js +455 -0
  35. package/dist/issue-scan.js.map +1 -0
  36. package/dist/output.d.ts +18 -1
  37. package/dist/output.d.ts.map +1 -1
  38. package/dist/output.js +293 -0
  39. package/dist/output.js.map +1 -1
  40. package/dist/session.d.ts +101 -2
  41. package/dist/session.d.ts.map +1 -1
  42. package/dist/session.js +132 -11
  43. package/dist/session.js.map +1 -1
  44. package/dist/target.d.ts +40 -4
  45. package/dist/target.d.ts.map +1 -1
  46. package/dist/target.js +14 -3
  47. package/dist/target.js.map +1 -1
  48. package/package.json +3 -2
  49. package/skill/SKILL.md +422 -11
package/README.md CHANGED
@@ -71,6 +71,10 @@ A bad token exits **2**, not 1 — retrying it is pure noise. A conflict carries
71
71
  both revisions, and says out loud that posting a comment bumps `System.Rev`, so
72
72
  a rev mismatch is *not* proof that somebody edited the same field.
73
73
 
74
+ A failure that came from GitHub says so — `"platform": "github"` — because
75
+ `pharos issue` touches two platforms in one call and which one refused is the
76
+ first thing to know. Its absence means Azure DevOps or this tool, as before.
77
+
74
78
  ## Guards
75
79
 
76
80
  Every verb here is one an agent can call in a loop against a board real people
@@ -98,6 +102,10 @@ something a caller can skip.
98
102
  runaway loop becomes a clean refusal rather than 500 work items. `--max-writes 0`
99
103
  is read-only: reads work, every write is refused before it is sent.
100
104
 
105
+ It counts **both platforms**. `pharos issue` writes to Azure DevOps and to
106
+ GitHub in one invocation, and a cap that covered one of them because the other
107
+ left by a different transport would be a cap in name only.
108
+
101
109
  It is **not security** — anything that can run this holds the token and can make
102
110
  the same REST calls directly. It shapes intent and catches accidents. A
103
111
  capability that must be impossible has to be withheld at the token.
@@ -108,13 +116,18 @@ capability that must be impossible has to be withheld at the token.
108
116
  task <id> the whole context, in one call
109
117
  wiki list | tree | read | write | delete
110
118
  comment list | add | edit | delete | react | unreact | reactors
119
+ comment hide | unhide | pin | unpin GitHub only
120
+ issue adopt | link | say | trail | drift | backfill | close
121
+ a GitHub issue and a work item, joined —
122
+ and kept honest
111
123
  plan <file> an implementation plan becomes a work item tree
112
124
  ```
113
125
 
114
- `comment` takes either kind of target: a work item is its number (`210`), a wiki
115
- page is an **absolute path** (`/Plans/Foo`). The leading slash is required by
116
- the wiki API itself it answers 404 without oneso the spelling that tells
117
- them apart is also the only spelling that works.
126
+ `comment` takes any of three targets: a work item is its number (`210`), a wiki
127
+ page is an **absolute path** (`/Plans/Foo`), a GitHub issue is
128
+ `owner/name#45`. The leading slash is required by the wiki API itself it
129
+ answers 404 without one — so the spelling that tells them apart is also the only
130
+ spelling that works, and the three grammars cannot collide.
118
131
 
119
132
  Long text does not belong on a command line:
120
133
 
@@ -123,6 +136,327 @@ pharos comment add 210 --file review.md
123
136
  pharos wiki write /Plans/Sprint-4 --stdin < plan.md
124
137
  ```
125
138
 
139
+ ### `comment` on a GitHub issue — the surface `gh` half has and half has not
140
+
141
+ ```bash
142
+ pharos comment list contoso/widgets#45 # ids, state, and what you MAY do
143
+ pharos comment edit contoso/widgets#45 900 --file fixed.md
144
+ pharos comment delete contoso/widgets#45 900 --yes
145
+ pharos comment react contoso/widgets#45 900 thumbs-up
146
+ pharos comment hide contoso/widgets#45 900 off-topic
147
+ pharos comment pin contoso/widgets#45 900
148
+ ```
149
+
150
+ This is the one place `pharos` reaches past the join, and it is two arguments
151
+ rather than one. **Editing or deleting a comment can destroy the link record**,
152
+ which nothing outside this repo knows to protect. And **hide and pin have no
153
+ `gh` verb at all** — they are GraphQL mutations with no REST route, so they pass
154
+ the same "nothing else can do this" test the `issue` verbs pass. Posting a *new*
155
+ comment is still `gh issue comment` or `pharos issue say`, and `comment add` on
156
+ a GitHub issue is refused with both of those named.
157
+
158
+ **`list` is the load-bearing verb**, and it is not one the macOS app needed: the
159
+ app has a window, an agent has this. Before it there was no way to learn a
160
+ GitHub comment id from this CLI at all, so nothing else here was addressable. It
161
+ prints both ids per comment — the REST number that edit, delete and reactions
162
+ address, and the node id that hide and pin take, because neither is derivable
163
+ from the other — plus a `may` object read from GitHub's own `viewerCan…` fields.
164
+ For a window those grey out a menu item; for an agent they turn a write that
165
+ would 403 into a fact known beforehand.
166
+
167
+ **An edit cannot double or strip the `pharos:v1` trailer.** Send prose with no
168
+ trailer and the existing one is restored, with `markerPreserved: true` saying
169
+ so; send two, or one naming a different work item, or one on a comment that
170
+ never had one, and the edit is refused with the reason and the repair. The rule
171
+ lives in `gh-core`, not here, because it is a rule about the link record rather
172
+ than about this command — and the macOS app edits through the same package.
173
+
174
+ **Deleting the comment that carries the trailer is allowed and never silent.**
175
+ The `--yes` refusal names the work item about to lose its GitHub end and hands
176
+ over the `pharos issue link` that puts it back; `--dry-run` prints the same
177
+ preview and exits 0. Both read the comment first, because a preview that could
178
+ not say which of the two deletes this is would be a preview of the wrong thing.
179
+
180
+ Reactions are **not one vocabulary across the two platforms**. Azure DevOps
181
+ takes `like dislike heart hooray smile confused`; GitHub takes `thumbs-up
182
+ thumbs-down laugh hooray confused heart rocket eyes`. Passing one platform's
183
+ name to the other is refused rather than translated. The CLI spelling is words
184
+ because GitHub's own names for the first two are `+1` and `-1`, and `-1` as a
185
+ positional is read as a flag by every argument parser including this one.
186
+
187
+ ### `issue` — the GitHub edge, and only the edge
188
+
189
+ ```bash
190
+ pharos issue adopt contoso/widgets#45 --parent 39
191
+ pharos issue link contoso/widgets#45 4821
192
+ pharos issue say contoso/widgets#45 --file reply.md
193
+ pharos issue trail 4821 # …or trail contoso/widgets#45
194
+ pharos issue drift # where the two platforms disagree
195
+ pharos issue backfill contoso/widgets --limit 25 --yes
196
+ pharos issue close 4821 --text "Shipped in 1.4.0."
197
+ pharos issue edit contoso/widgets#45 --title "Crash on resize" --yes
198
+ ```
199
+
200
+ Azure DevOps already has a GitHub integration and **the link it makes carries
201
+ nothing**: title, body, comments, labels and state stay on their own island, and
202
+ the transition only ever fires from a commit or a PR merge, never from closing
203
+ an issue. That is the gap, and it is the whole pitch — not "there is no
204
+ integration".
205
+
206
+ **Eight verbs, because `gh` already exists.** Listing, viewing and commenting on
207
+ an issue is `gh`'s job and it is better at it — closing one *end* is `gh issue
208
+ close`, and closing the *pair* is `pharos issue close`; a second GitHub CLI
209
+ would be the drifting duplicate this repo has been bitten by three times.
210
+ Pharos owns what `gh` cannot see: the join.
211
+
212
+ **`edit` is the one verb that overlaps `gh` head on, and it earns that by
213
+ refusing.** `gh issue edit` writes the same fields and writes them well; what it
214
+ cannot do is notice that somebody changed the thing you are overwriting. It is
215
+ last-write-wins — if a colleague renamed the issue while you were composing,
216
+ their rename is gone and neither of you is told. Fine for a human at a terminal,
217
+ wrong for an agent that reads, thinks for a minute, and writes into a world that
218
+ moved. Want an unguarded edit? `gh issue edit` is right there.
219
+
220
+ The guard is a **value compare**, because GitHub offers nothing better:
221
+ `PATCH /repos/{o}/{r}/issues/{n}` answers **400** to `If-Match` (measured), and
222
+ `updated_at` and the ETag both move when somebody merely *comments*, so a
223
+ timestamp guard would refuse a good edit for a change that touched nothing. So
224
+ `edit` re-reads immediately before sending and refuses only if the text it is
225
+ about to overwrite moved. Pass `--if-title` / `--if-body` / `--if-body-file` to
226
+ guard against what **you** saw rather than against this command's own read; the
227
+ output says which, as `fields.guard.window`. It narrows the race to one round
228
+ trip and does not close it, and it says so.
229
+
230
+ Two things GitHub does quietly, which `edit` reports:
231
+ `POST …/labels` **creates** a repository label that does not exist (so an unknown
232
+ one is refused with near misses — `--create-label` is how you mean it), and
233
+ `POST …/assignees` **ignores** a login it will not assign while still answering
234
+ 201 (so read `assignees.ignored`). Labels and assignees go through their own
235
+ add/remove endpoints, never the whole-issue PATCH, which carries them as whole
236
+ arrays and would overwrite every label on every write.
237
+
238
+ `adopt` creates the work item **and** writes both ends of the link:
239
+
240
+ | | where | |
241
+ |---|---|---|
242
+ | 1 | a `Hyperlink` relation on the work item | measured, not chosen — `_apis/githubconnections` answers **401** on Pharos's PAT scopes while a control call answers 200, so the connection id a native `vstfs:///GitHub/Issue/…` link needs cannot be read at all |
243
+ | 2 | one comment on the issue: `AB#4821` **and** `<!-- pharos:v1 repo=… issue=… ado=… -->` | `AB#` is free official linking wherever the Azure Boards app happens to be installed and harmless text where it is not. The trailer is what every machine reads, including the loop guard |
244
+
245
+ **The marker goes in a comment, never the issue body.** Editing a reporter's
246
+ body collides with them, is rude on a public repo, and needs write access we may
247
+ not have on a community issue. A comment is append-only and needs none of that.
248
+
249
+ The relation and the parent ride in the **same patch** as the fields, so a work
250
+ item is never briefly pointing at nothing. Azure DevOps is written first because
251
+ the work item id does not exist until it lands — so the one failure this can
252
+ leave is always the same shape, and it is reported rather than swallowed:
253
+
254
+ ```json
255
+ { "error": { "createdWorkItem": 4821, "halfLinked": true, "wrote": ["hyperlink"],
256
+ "recover": "pharos issue link contoso/widgets#45 4821" } }
257
+ ```
258
+
259
+ `link` writes only the half that is missing, which makes it both the way to join
260
+ an existing pair and the repair for exactly that state.
261
+
262
+ **Adopting an issue twice never makes a second work item, and there are three
263
+ different answers behind that one sentence.** Which one you get says what state
264
+ the pair was in:
265
+
266
+ | the issue carries a marker | exit **0**, `created: false` — the work item it names, and nothing written. The state you asked for already holds |
267
+ | a work item links the issue and the issue says nothing | exit **3**, `halfLinked: true`, carrying `recover` — the same `pharos issue link …` command the failure above handed over. Finishing that link is the repair; a second work item is not |
268
+ | two work items claim one issue | exit **3**, both named, and no `recover` — picking one would be inventing an answer to the question this feature exists to answer |
269
+
270
+ The middle row is the one that used to be a duplicate. The marker is the index
271
+ and in that state it was never posted, so before creating anything `adopt` also
272
+ asks **this machine** — an adoption is written to `~/.config/pharos/half-links.json`
273
+ between the create and the comment, and removed when the comment lands — and
274
+ then **the board**, narrowing on `[System.HyperLinkCount]` and matching the
275
+ relation URLs client-side, which is the only way round Azure DevOps having no
276
+ query over them. The board is the stronger of the two — it sees a half-link
277
+ left on another machine — and the local record covers what it cannot: a board
278
+ query that is refused, a half-link older than that scan's cap, and a refusal
279
+ that costs no round trip. Every run reports `boardCheck`, because "the board
280
+ says nobody has adopted this" and "the board could not be asked" are different
281
+ facts and only one of them means the check happened.
282
+
283
+ `trail` is read-only and answers from **either** end — the output shape does not
284
+ depend on which you passed. Read `evidence`: `["hyperlink","marker"]` is a
285
+ complete link, and either one alone is a link that only half exists.
286
+
287
+ #### `say` — one message, two audiences
288
+
289
+ ```bash
290
+ pharos issue say contoso/widgets#45 --file reply.md
291
+ pharos issue say contoso/widgets#45 --stdin --summary "Fixed in 1.4.2; asked for a crash log"
292
+ ```
293
+
294
+ | | GitHub | Azure DevOps |
295
+ |---|---|---|
296
+ | who reads it | the reporter, who does **not** have an Azure DevOps account and never will | the team |
297
+ | what they get | the full explanation, in their register | a summary, and the URL of the comment carrying the rest |
298
+
299
+ **The asymmetry is the verb.** The same words in both places is `gh issue
300
+ comment` followed by `pharos comment add`, and neither of those knows the other
301
+ happened. Without `--summary` the board gets the first paragraph cut to 200
302
+ characters — a guess, and allowed to be one *only* because the comment's URL
303
+ travels with it. The board is never the record of what was said; GitHub is.
304
+
305
+ **The loop guard keys on content, never on `sender.login`.** GitHub events carry
306
+ an actor and Azure DevOps events do not, which makes the actor look like the easy
307
+ answer and is the trap: every client writes with its own `gh` credential, so
308
+ `sender.login == me` is *also* true of the human typing into the web UI, and
309
+ suppressing on it silently swallows their real comments. So the guard is the
310
+ `pharos:v1` trailer, in two halves:
311
+
312
+ - **Every comment `say` posts carries it**, because the read sweep is only safe
313
+ if `isPharosAuthored` is total. One unmarked Pharos comment anywhere is one the
314
+ sweep reads as a human's, and the loop that starts from it does not stop.
315
+ - **A message that already carries one is refused.** That is Pharos's own output
316
+ being handed back — a mirrored body, a copy-paste, a script re-posting what it
317
+ read — and it is the loop starting one step before any sweep could see it. The
318
+ macOS app's queue refuses exactly the same thing.
319
+
320
+ GitHub is written **first** here, the opposite of `adopt`, because the board's
321
+ comment carries the GitHub comment's URL and that does not exist until the POST
322
+ returns. The work item is read before anything is published, since the GitHub
323
+ half lands on a repository that may not be ours. If the board half then fails:
324
+
325
+ ```json
326
+ { "error": { "halfSaid": true, "wrote": ["github-comment"],
327
+ "github": { "commentUrl": "https://github.com/…#issuecomment-950" },
328
+ "recover": "pharos comment add 4821 --stdin", "adoComment": "…" } }
329
+ ```
330
+
331
+ **Do not re-run it.** A comment is an append, so a second `say` posts a second
332
+ public comment; the error carries the exact text and the command that writes the
333
+ half that is missing.
334
+
335
+ `say` starts from the issue end only. A work item may hyperlink several issues,
336
+ so that end names no single reporter — `pharos issue trail <id>` is how you find
337
+ out which issue you meant.
338
+
339
+ Pull requests are out of scope for v1 and are refused by name rather than
340
+ half-supported: on GitHub's API every PR is also an issue, so `adopt` on a PR
341
+ number would otherwise mirror something the rest of this does not model.
342
+
343
+ #### `drift` — the report no other tool can produce
344
+
345
+ ```bash
346
+ pharos issue drift
347
+ pharos issue drift --repo contoso/widgets --limit 50
348
+ ```
349
+
350
+ `gh` can list issues; Azure DevOps can list work items; **neither holds both
351
+ sides**, so neither can say *these two disagree*. A link nobody checks decays
352
+ quietly — an issue gets closed on GitHub and the board still shows the work in
353
+ progress, or a work item is marked done and the reporter, who has no Azure
354
+ DevOps account and never will, is still waiting.
355
+
356
+ Four kinds, because they want four different actions:
357
+
358
+ | kind | what it means |
359
+ |---|---|
360
+ | `state` | both ends exist and disagree about whether the work is finished |
361
+ | `missing-issue` | the work item names an issue GitHub does not have |
362
+ | `one-sided` | the board has the link and the issue does not — what a failed `adopt` leaves. Carries the `pharos issue link …` that repairs it |
363
+ | `unreadable` | the GitHub end could not be read, so nothing about this pair is known |
364
+
365
+ **It scans links, not issues.** An unadopted issue is not drift — on a repo with
366
+ four hundred of them, counting "not linked" as a problem buries three real
367
+ findings under three hundred and eighty-eight rows of noise. What is *missing*
368
+ from the bridge is `backfill`'s question; what is *wrong* inside it is this one.
369
+
370
+ **"Finished" is read from the project, never guessed.** A process template
371
+ renames every state, and `Resolved` is terminal on a Bug and open on a User
372
+ Story — so each work item is judged against its own type's categories and the
373
+ report says which source it used. When the catalogue cannot be read the stock
374
+ names are used *and said out loud*, because a row is uninterpretable without
375
+ knowing what was counted as finished.
376
+
377
+ An unreadable repository is **information, not a fault, and never fails the
378
+ command**: a work item can hyperlink a repo somebody else adopted on a machine
379
+ configured differently.
380
+
381
+ #### `backfill` — the adoption path, for the repo everybody actually has
382
+
383
+ ```bash
384
+ pharos issue backfill contoso/widgets # previews, then REFUSES
385
+ pharos issue backfill contoso/widgets --dry-run # the same preview, exit 0
386
+ pharos issue backfill contoso/widgets --limit 25 --max-writes 50 --yes
387
+ pharos issue backfill contoso/widgets --label bug --parent 39 --yes
388
+ ```
389
+
390
+ Without a bulk route the bridge only works on a greenfield repository, which is
391
+ not where anybody is. A team with four hundred open issues cannot adopt them one
392
+ command at a time.
393
+
394
+ **Deciding what is already adopted costs two requests, not four hundred.** The
395
+ board is the enumerable side: a work item carries its `Hyperlink` relations on
396
+ its own row, so one WIQL and one `workitemsbatch` return every link at once.
397
+ GitHub cannot answer the equivalent question at all — the marker lives in a
398
+ comment, so asking costs a request per issue. That subtraction is correct
399
+ because `adopt` writes the Azure DevOps end **first**, so the half-state it can
400
+ leave is a hyperlink with no comment and never the reverse. The mirror image is
401
+ still possible if somebody deletes a relation by hand, so the marker is re-read
402
+ per issue **at adoption time** — on the handful being written — and an issue
403
+ that turns out to be claimed already is skipped rather than adopted twice.
404
+
405
+ **It is resumable and needs no state file to be.** The link *is* the progress
406
+ record: a second run reads the board again, finds the ones the first run adopted
407
+ already linked, and carries on. Candidates are ordered by issue number ascending
408
+ so that is true in the obvious way as well as the technical one.
409
+
410
+ **The write budget is the throttle, on purpose.** Each adoption is two writes, so
411
+ four hundred issues is eight hundred against a default cap of twenty. That cost
412
+ is worked out *before* anything is written and a run that cannot finish is
413
+ refused, naming both ways forward — rather than stopping at write nineteen,
414
+ which is the outcome a caller can reason about least.
415
+
416
+ A bare invocation previews and **refuses** (exit 3); `--dry-run` is the same
417
+ preview at exit 0. The preview names the repository, the `gh` account it would
418
+ be reached as, **and the Azure DevOps organisation and project the work items
419
+ would land on**. Nothing in the design pairs a repo with a board, and a
420
+ mis-aimed `adopt` is one work item where a mis-aimed `backfill --yes` is four
421
+ hundred — each with a public comment naming a board its reporter has nothing to
422
+ do with.
423
+
424
+ `--state` defaults to `open`: a closed issue is history, and adopting it creates
425
+ a work item that is finished before anybody looks at it.
426
+
427
+ #### `close` — the ending of the trail
428
+
429
+ ```bash
430
+ pharos issue close 4821 # …or close contoso/widgets#45
431
+ pharos issue close 4821 --to Removed --reason not-planned
432
+ pharos issue close 4821 --file release-note.md
433
+ ```
434
+
435
+ Closes **both** ends, comments on each **pointing at the other**, then **re-reads
436
+ both** and reports what actually moved. Two 200s are not proof: a workflow rule
437
+ can refuse a transition on a field the API happily accepted, so `verified` is
438
+ what was there afterwards rather than what was sent.
439
+
440
+ **It does not compose `Closes #45` / `Fixes AB#123` into a PR body.** Those fire
441
+ on a PR *merge* and pull requests are out of scope for v1 — so there would be
442
+ nothing to compose into and nothing to verify.
443
+
444
+ **There is no hardcodable `Closed`.** The state written comes from the project's
445
+ own categories: exactly one terminal state for that work item type is used,
446
+ several is a **refusal** naming them and `--to`, because `Done` and `Removed` are
447
+ both finished and mean opposite things. If the catalogue cannot be read at all it
448
+ refuses rather than guessing — `drift` degrades to the stock names because a
449
+ misclassified row is a question somebody can dismiss, and the same guess here
450
+ would be a write.
451
+
452
+ `--to`, not `--state`: `--state` already means `open|closed|all` on `backfill`,
453
+ and one flag with two meanings across two verbs of one command is a bug that only
454
+ shows up when somebody runs it.
455
+
456
+ Closing an already-closed pair writes nothing and exits 0. A work item linking
457
+ several issues is refused unless `--all` says so — "close 4821" should not read
458
+ as "close four strangers' issues".
459
+
126
460
  ## Configuration
127
461
 
128
462
  | | |
@@ -143,6 +477,76 @@ items, `vso.wiki_write` for the wiki.
143
477
  **Every person uses their own token.** Board attribution is per-person, and a
144
478
  shared token makes everyone's work read as one account.
145
479
 
480
+ ### GitHub, which follows a different rule for a measured reason
481
+
482
+ The rule above is about `ADO_PAT` and it has not moved. GitHub is a separate
483
+ thing, and it needs two facts that an environment variable cannot carry.
484
+
485
+ **The credential comes from `gh`, so there is no fourth secret to manage.**
486
+ `gh auth token --user <account>` resolves it and the API calls are ordinary
487
+ HTTPS from there; revoking GitHub access never touches the Azure DevOps token.
488
+ Pass `--user` or nothing else: a bare `gh auth token` returns whatever `GH_TOKEN`
489
+ happens to be exported and ignores the keyring entirely, so an exported token in
490
+ somebody's shell would silently become the identity of a sweep.
491
+
492
+ **The account is bound per repository, in `~/.config/pharos/repos.json`.**
493
+
494
+ ```bash
495
+ pharos setup --repo contoso/widgets --gh-account alisina-tibata
496
+ ```
497
+
498
+ Both flags or neither — a repository is a `(repo, account)` **pair**, and there
499
+ is deliberately no fallback to whichever account `gh` has active. Azure DevOps
500
+ binds a project to one GitHub identity; `gh` holds many at once with one active
501
+ machine-wide, and on the machine this was measured on the active account was not
502
+ the one owning the repository behind the project being worked. The failure is
503
+ not a clean error either: two accounts can each see a repository of the same
504
+ name, so the wrong one answers **200** for a different repository. That is what
505
+ `pharos doctor --verify` compares owners for.
506
+
507
+ `$PHAROS_REPOS_FILE` and `$XDG_CONFIG_HOME` both move the file. `gh` is optional
508
+ — without it every `pharos issue` verb is refused by name with the reason, and
509
+ the Azure DevOps verbs are untouched.
510
+
511
+ **And it runs in the other direction too: a verb whose target is a GitHub issue
512
+ needs no `ADO_PAT`.** `pharos comment list contoso/widgets#45` reads over `gh`'s
513
+ credential and touches Azure DevOps at no point, so it works on a machine that
514
+ has never had a PAT. The Azure DevOps configuration is resolved when something
515
+ actually needs Azure DevOps, and not before — which means the `kind: "config"`
516
+ error naming `ADO_PAT` now arrives only from a verb that would have used it.
517
+ `pharos issue` is the exception and honestly so: it writes to both platforms in
518
+ one invocation, so it needs both credentials.
519
+
520
+ **`half-links.json` sits beside it, and it is the only thing this CLI remembers
521
+ between runs.** An adoption is written there between the work item create and
522
+ the GitHub comment and removed when the comment lands, so what survives is
523
+ exactly the set of adoptions that half-happened — the state neither platform can
524
+ be asked about, and the one a retry follows. It is never consulted for what a
525
+ link *is*: the marker and the hyperlink are read fresh every time, and an entry
526
+ is checked against Azure DevOps before it is believed. Delete it and you lose
527
+ nothing but one guard; `$PHAROS_HALF_LINKS_FILE` moves it.
528
+
529
+ ## Checking a machine
530
+
531
+ ```bash
532
+ pharos doctor # three tiers, with a stable id per check
533
+ pharos doctor --verify # prove the credentials with real calls
534
+ pharos setup --install gh # or ask|all|libreoffice,poppler,…
535
+ ```
536
+
537
+ `doctor` is read-only and works before anything is configured, which is what
538
+ makes it the right first move when something behaves oddly. It prints the PATH
539
+ it searched, because a GUI-launched app inherits a minimal one and would
540
+ otherwise be told a fully provisioned machine is bare.
541
+
542
+ It is offline apart from one probe, and that probe is named rather than glossed:
543
+ **`gh auth status` validates every token against github.com**, with no flag to
544
+ stop it, so it runs only when `gh` is installed. Measured on gh 2.87.3, it also
545
+ reports an unreachable host as `The token in keyring is invalid` — a
546
+ connectivity fault worded as a credential fault. `gh-auth` therefore never tells
547
+ you that you are signed out; it reports both causes, because from here they are
548
+ genuinely indistinguishable.
549
+
146
550
  ## Requirements
147
551
 
148
- Node 22 or later.
552
+ Node 22 or later. `gh` for the GitHub bridge, and nothing else is required.
@@ -1 +1 @@
1
- {"version":3,"file":"capabilities.d.ts","sourceRoot":"","sources":["../src/capabilities.ts"],"names":[],"mappings":"AAQA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAEH,MAAM,WAAW,UAAU;IACzB,mEAAmE;IACnE,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,+CAA+C;IAC/C,OAAO,EAAE,MAAM,CAAC;IAChB,yEAAyE;IACzE,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,EAAE,EAAE,OAAO,CAAC;IACZ,qEAAqE;IACrE,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,MAAM,EAAE,MAAM,CAAC;CAChB;AA2BD,eAAO,MAAM,WAAW,EAAE,UAAU,EA6BnC,CAAC;AAEF,qEAAqE;AACrE,wBAAsB,mBAAmB,CACvC,GAAG,EAAE,SAAS,MAAM,EAAE,EACtB,OAAO,GAAE;IACP,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACxB,gEAAgE;IAChE,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACtC,UAAU,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CACnD,GACL,OAAO,CAAC,iBAAiB,EAAE,CAAC,CAgB9B"}
1
+ {"version":3,"file":"capabilities.d.ts","sourceRoot":"","sources":["../src/capabilities.ts"],"names":[],"mappings":"AAQA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAEH,MAAM,WAAW,UAAU;IACzB,mEAAmE;IACnE,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,+CAA+C;IAC/C,OAAO,EAAE,MAAM,CAAC;IAChB,yEAAyE;IACzE,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,EAAE,EAAE,OAAO,CAAC;IACZ,qEAAqE;IACrE,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,MAAM,EAAE,MAAM,CAAC;CAChB;AA0CD,eAAO,MAAM,WAAW,EAAE,UAAU,EAsCnC,CAAC;AAEF,qEAAqE;AACrE,wBAAsB,mBAAmB,CACvC,GAAG,EAAE,SAAS,MAAM,EAAE,EACtB,OAAO,GAAE;IACP,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACxB,gEAAgE;IAChE,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACtC,UAAU,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CACnD,GACL,OAAO,CAAC,iBAAiB,EAAE,CAAC,CAgB9B"}
@@ -26,6 +26,21 @@ const PACKAGES = {
26
26
  apt: ["install", "-y", "poppler-utils"],
27
27
  winget: ["install", "--id", "oschwartz10612.Poppler", "-e"],
28
28
  },
29
+ // **`gh` is a credential source, not a transport** (plan §2, and @#779's
30
+ // clarification of it). Pharos resolves a token with `gh auth token --user
31
+ // <account>` and then makes ordinary HTTPS calls; installing gh is what makes
32
+ // that possible, and it is why there is no fourth Keychain entry to manage.
33
+ //
34
+ // No apt entry, deliberately. GitHub's own Linux instructions add their
35
+ // package repository (https://cli.github.com/packages) rather than pointing at
36
+ // a distro package, and what `apt install gh` gets you varies by release. A
37
+ // command that does not exist here is worse than no command, because somebody
38
+ // will paste it — so Linux gets the honest refusal and the doctor check names
39
+ // where to look.
40
+ gh: {
41
+ brew: ["install", "gh"],
42
+ winget: ["install", "--id", "GitHub.cli", "-e"],
43
+ },
29
44
  };
30
45
  export const INSTALLABLE = [
31
46
  {
@@ -41,6 +56,14 @@ export const INSTALLABLE = [
41
56
  unlocks: "`wiki import` for .docx, .pdf, .rtf, .html and .odt.",
42
57
  size: "7.5 MB download",
43
58
  },
59
+ {
60
+ id: "gh",
61
+ name: "GitHub CLI",
62
+ unlocks: "The GitHub bridge: `pharos issue` adopts, links and mirrors an issue against a work item. "
63
+ + "gh holds the credential, so nothing new is stored and revoking GitHub access never "
64
+ + "touches the Azure DevOps token.",
65
+ size: "~35 MB",
66
+ },
44
67
  {
45
68
  id: "libreoffice",
46
69
  name: "LibreOffice",
@@ -1 +1 @@
1
- {"version":3,"file":"capabilities.js","sourceRoot":"","sources":["../src/capabilities.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"capabilities.js","sourceRoot":"","sources":["../src/capabilities.ts"],"names":[],"mappings":"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"}
package/dist/cli.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import type { AdoClient } from "@floh-solutions/ado-core";
2
+ import type { GitHubClient } from "@floh-solutions/gh-core";
2
3
  import { type ExitCode } from "./output.js";
3
4
  /**
4
5
  * Every command this file dispatches — **one list, used by both the sentence and
@@ -11,7 +12,7 @@ import { type ExitCode } from "./output.js";
11
12
  * expensive kind of wrong. Anything added to the switch belongs here, and the
12
13
  * test below fails if it does not.
13
14
  */
14
- export declare const COMMANDS: readonly ["setup", "whoami", "query", "task", "types", "history", "iterations", "areas", "links", "fields", "create", "update", "link", "unlink", "delete", "restore", "deleted", "attach", "detach", "download", "image", "people", "wiki", "comment", "hooks", "plan", "doctor"];
15
+ export declare const COMMANDS: readonly ["setup", "whoami", "query", "task", "types", "history", "iterations", "areas", "links", "fields", "create", "update", "link", "unlink", "delete", "restore", "deleted", "attach", "detach", "download", "image", "people", "wiki", "comment", "hooks", "plan", "doctor", "issue"];
15
16
  export interface RunOptions {
16
17
  argv: string[];
17
18
  cwd: string;
@@ -22,6 +23,8 @@ export interface RunOptions {
22
23
  readStdin?: (() => Promise<string>) | undefined;
23
24
  /** Injected by tests, so the whole router can run against a mock transport. */
24
25
  client?: AdoClient | undefined;
26
+ /** The same, for the GitHub half of `pharos issue`. */
27
+ github?: GitHubClient | undefined;
25
28
  }
26
29
  export declare function run(options: RunOptions): Promise<ExitCode>;
27
30
  //# sourceMappingURL=cli.d.ts.map
package/dist/cli.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AA6B1D,OAAO,EAKL,KAAK,QAAQ,EAEd,MAAM,aAAa,CAAC;AAyBrB;;;;;;;;;;GAUG;AACH,eAAO,MAAM,QAAQ,oRAMX,CAAC;AA0TX,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC,UAAU,CAAC;IACvB,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAC/B,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAC/B,gDAAgD;IAChD,SAAS,CAAC,EAAE,CAAC,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC,GAAG,SAAS,CAAC;IAChD,+EAA+E;IAC/E,MAAM,CAAC,EAAE,SAAS,GAAG,SAAS,CAAC;CAChC;AAED,wBAAsB,GAAG,CAAC,OAAO,EAAE,UAAU,GAAG,OAAO,CAAC,QAAQ,CAAC,CAOhE"}
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AAC1D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AA8B5D,OAAO,EAKL,KAAK,QAAQ,EAEd,MAAM,aAAa,CAAC;AAyBrB;;;;;;;;;;GAUG;AACH,eAAO,MAAM,QAAQ,6RAMX,CAAC;AA8gBX,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC,UAAU,CAAC;IACvB,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAC/B,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAC/B,gDAAgD;IAChD,SAAS,CAAC,EAAE,CAAC,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC,GAAG,SAAS,CAAC;IAChD,+EAA+E;IAC/E,MAAM,CAAC,EAAE,SAAS,GAAG,SAAS,CAAC;IAC/B,uDAAuD;IACvD,MAAM,CAAC,EAAE,YAAY,GAAG,SAAS,CAAC;CACnC;AAED,wBAAsB,GAAG,CAAC,OAAO,EAAE,UAAU,GAAG,OAAO,CAAC,QAAQ,CAAC,CAOhE"}