@floh-solutions/pharos-cli 0.24.0 → 0.26.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 (53) hide show
  1. package/README.md +97 -10
  2. package/dist/adopt-type.d.ts +58 -0
  3. package/dist/adopt-type.d.ts.map +1 -0
  4. package/dist/adopt-type.js +61 -0
  5. package/dist/adopt-type.js.map +1 -0
  6. package/dist/bridge.d.ts +156 -0
  7. package/dist/bridge.d.ts.map +1 -0
  8. package/dist/bridge.js +157 -0
  9. package/dist/bridge.js.map +1 -0
  10. package/dist/cli.d.ts +1 -1
  11. package/dist/cli.d.ts.map +1 -1
  12. package/dist/cli.js +176 -31
  13. package/dist/cli.js.map +1 -1
  14. package/dist/commands/bridge.d.ts +30 -0
  15. package/dist/commands/bridge.d.ts.map +1 -0
  16. package/dist/commands/bridge.js +255 -0
  17. package/dist/commands/bridge.js.map +1 -0
  18. package/dist/commands/comment-github.d.ts +138 -0
  19. package/dist/commands/comment-github.d.ts.map +1 -0
  20. package/dist/commands/comment-github.js +427 -0
  21. package/dist/commands/comment-github.js.map +1 -0
  22. package/dist/commands/comment.d.ts.map +1 -1
  23. package/dist/commands/comment.js +114 -19
  24. package/dist/commands/comment.js.map +1 -1
  25. package/dist/commands/doctor.d.ts +169 -0
  26. package/dist/commands/doctor.d.ts.map +1 -1
  27. package/dist/commands/doctor.js +241 -44
  28. package/dist/commands/doctor.js.map +1 -1
  29. package/dist/commands/issue.d.ts +133 -0
  30. package/dist/commands/issue.d.ts.map +1 -1
  31. package/dist/commands/issue.js +292 -110
  32. package/dist/commands/issue.js.map +1 -1
  33. package/dist/commands/setup.d.ts +45 -0
  34. package/dist/commands/setup.d.ts.map +1 -1
  35. package/dist/commands/setup.js +74 -0
  36. package/dist/commands/setup.js.map +1 -1
  37. package/dist/half-link.d.ts +14 -4
  38. package/dist/half-link.d.ts.map +1 -1
  39. package/dist/half-link.js +14 -4
  40. package/dist/half-link.js.map +1 -1
  41. package/dist/output.d.ts.map +1 -1
  42. package/dist/output.js +123 -1
  43. package/dist/output.js.map +1 -1
  44. package/dist/session.d.ts +51 -2
  45. package/dist/session.d.ts.map +1 -1
  46. package/dist/session.js +62 -11
  47. package/dist/session.js.map +1 -1
  48. package/dist/target.d.ts +40 -4
  49. package/dist/target.d.ts.map +1 -1
  50. package/dist/target.js +14 -3
  51. package/dist/target.js.map +1 -1
  52. package/package.json +4 -4
  53. package/skill/SKILL.md +167 -7
package/skill/SKILL.md CHANGED
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: pharos
3
- description: "Use for AZURE DEVOPS work — when the user says Azure DevOps, ADO or dev.azure.com, or names an ADO work item by number (\"pick up 4821\", \"what's on 210\"). Covers reading or updating an ADO work item, ticket, bug, story or epic; the ADO board, backlog, sprint or iteration; what is assigned to you in Azure DevOps; reading or writing an ADO project wiki page and its comments; linking a plan to an epic; attaching a file to a work item or taking one off; inline images in a description, comment or wiki page; @mentioning somebody so they are actually notified; importing Markdown, Word or PDF files as wiki pages; and whether a failed ADO call is worth retrying. ALSO the GITHUB ISSUE ↔ work item EDGE: adopting an issue as a work item linked at both ends, bulk-adopting a repo, one reply reaching the reporter and the board at once, where they have drifted, and closing both ends together. NOT a GitHub CLI: listing, viewing or commenting on an issue is `gh`'s job. NOT for other trackers — the Argus board, Jira or Linear."
3
+ description: "Use for AZURE DEVOPS work — when the user says Azure DevOps, ADO or dev.azure.com, or names an ADO work item by number (\"pick up 4821\", \"what's on 210\"). Covers reading or updating an ADO work item, ticket, bug, story or epic; the ADO board, backlog, sprint or iteration; what is assigned to you; reading or writing an ADO wiki page and its comments; linking a plan to an epic; attaching a file to a work item or taking one off; inline images in a field or wiki page; @mentioning somebody so they are notified; importing Markdown, Word or PDF as wiki pages; and whether a failed call is retryable. ALSO the GITHUB ISSUE ↔ work item EDGE: adopting an issue as a work item linked at both ends, bulk-adopting a repo, one reply reaching reporter and board, where they have drifted, closing both ends together, and changing a comment that ALREADY EXISTS: editing, deleting, reacting, hiding, pinning. NOT a GitHub CLI: listing or viewing an issue, or POSTING a comment, is `gh`'s job. NOT for other trackers — Argus, Jira, Linear."
4
4
  license: Proprietary
5
5
  ---
6
6
 
@@ -59,8 +59,16 @@ wiki image <file> a picture for a PAGE or page comment. Different
59
59
  wiki import <file...> .md .txt .docx .pdf .rtf .html -> pages. A
60
60
  .pptx is refused, on purpose — see below
61
61
  --under <path> --as <name>
62
- comment list | add | edit | delete <target> is a work item id OR a wiki path
63
- comment react | unreact | reactors like dislike heart hooray smile confused
62
+ comment list | add | edit | delete <target> is a work item id, a wiki path,
63
+ comment react | unreact | reactors OR a GitHub issue (contoso/widgets#45)
64
+ comment hide | unhide | pin | unpin GITHUB ONLY — Azure DevOps has neither
65
+ REACTIONS ARE NOT ONE VOCABULARY. Azure DevOps:
66
+ like dislike heart hooray smile confused. GitHub:
67
+ thumbs-up thumbs-down laugh hooray confused heart
68
+ rocket eyes. Passing one platform's name to the
69
+ other is refused, not translated
70
+ `comment add` is REFUSED on a GitHub issue — see
71
+ "Editing a GitHub comment" below
64
72
  issue adopt <owner/name#45> a GitHub ISSUE becomes a work item, with BOTH
65
73
  ends of the link written. --type --title --parent
66
74
  issue link <owner/name#45> <id> join an existing pair — or finish a join that
@@ -76,6 +84,11 @@ issue backfill <owner/name> bulk adopt every issue the board does not link
76
84
  yet. Needs --yes. --limit --state --label
77
85
  issue close <id|owner/name#45> close BOTH ends, then RE-READ both to say what
78
86
  actually moved. --to --reason --all
87
+ issue edit <owner/name#45> the issue's OWN fields, behind a lost-update
88
+ guard. Needs --yes. --title --text/--file/--stdin
89
+ --milestone --add-label --remove-label
90
+ --add-assignee --remove-assignee --if-title
91
+ --if-body. `gh issue edit` is the UNGUARDED one
79
92
  hooks list | check | create | repoint | delete service hooks for realtime.
80
93
  `check` needs --hub <url>; `list` shows the URL
81
94
  already in use
@@ -87,6 +100,14 @@ setup org, project, token → keychain + shell profile
87
100
  --repo owner/name --gh-account <login> bind a
88
101
  repository to the GitHub account that reaches
89
102
  it. BOTH or NEITHER — `issue` needs this first
103
+ bridge show|labels what an adopted issue BECOMES: which work item
104
+ type each GitHub label maps to. `labels --repo
105
+ owner/name` shows what is mapped, what an
106
+ unmapped one would default to, and which of
107
+ those it is
108
+ bridge map|unmap|digest edit that mapping (--label is an ID, not a name
109
+ — ids survive a rename), and when a digest is
110
+ written back to the work item
90
111
  doctor what is installed on THIS machine, what is
91
112
  missing, and what each missing thing costs
92
113
  ```
@@ -632,6 +653,25 @@ plainly commenting on an issue is `gh`'s job and `gh` is better at it; closing
632
653
  one *end* is `gh issue close`. Pharos owns the one thing `gh` cannot see: which
633
654
  work item tracks this issue, written so it survives in both databases.
634
655
 
656
+ The one place that line moved: **a comment that already exists** is reachable
657
+ through `pharos comment <verb> <owner/name#45>` — edit, delete, react, unreact,
658
+ reactors, hide, unhide, pin, unpin. Two reasons, and neither is symmetry for its
659
+ own sake. Editing or deleting one can destroy the link record, so it needs the
660
+ guard below rather than a bare `gh api`. And hiding or pinning has **no
661
+ first-class `gh` verb at all** — they are GraphQL mutations with no REST route,
662
+ which is the "nothing else can do this" test the rest of these verbs pass.
663
+ Posting a *new* comment is still not ours.
664
+
665
+ The second place it moved, and it is the only verb here that overlaps `gh` head
666
+ on: **`pharos issue edit` exists to REFUSE.** `gh issue edit` does the same
667
+ write and does it well — what it cannot do is notice that somebody changed the
668
+ thing you were overwriting. It reads nothing and compares nothing, so if a
669
+ colleague renamed the issue while you were composing, their rename is gone and
670
+ neither of you is told. That is fine for a human at a terminal and wrong for an
671
+ agent, which reads an issue, spends a minute thinking, and writes back into a
672
+ world that moved. **If you want an unguarded edit, use `gh issue edit` — it is
673
+ right there and it is better at it.**
674
+
635
675
  Azure DevOps has its own GitHub integration and **the link it makes carries
636
676
  nothing** — title, body, comments, labels and state stay on their own island,
637
677
  and the transition only ever fires from a commit or a PR merge, never from
@@ -645,8 +685,52 @@ pharos issue say contoso/widgets#45 --file reply.md
645
685
  pharos issue drift # where the two disagree
646
686
  pharos issue backfill contoso/widgets --limit 25 --yes
647
687
  pharos issue close 4821 --text "Shipped in 1.4.0."
688
+ pharos issue edit contoso/widgets#45 --title "Crash on resize" --yes
648
689
  ```
649
690
 
691
+ ### Editing an issue, and the guard GitHub does not give us
692
+
693
+ **There is no conditional write.** `PATCH /repos/{o}/{r}/issues/{n}` answers
694
+ **400** to `If-Match` — measured, not assumed — so the `{"op":"test","path":
695
+ "/rev"}` guarantee behind every Azure DevOps patch has no GitHub equivalent.
696
+ Never send `If-Match` here yourself either: it is not inert, it fails the write,
697
+ and the 400 reads like a malformed body.
698
+
699
+ **And do not guard on `updated_at` or the ETag.** Both move when somebody merely
700
+ *comments* on the issue, so a guard keyed on either refuses a good edit for a
701
+ change that touched nothing — it fires on the case that is *not* a collision.
702
+
703
+ So the guard compares the **value of the field you are overwriting**: re-read
704
+ immediately before sending, and refuse only if that text moved. A comment cannot
705
+ cause it, which is what makes the refusal worth reading.
706
+
707
+ | you pass | the base is | what it protects |
708
+ |---|---|---|
709
+ | nothing | this command's own read | one round trip. Honest, and small |
710
+ | `--if-title` / `--if-body` / `--if-body-file` | what **you** saw | the whole gap between your read and your write |
711
+
712
+ Pass `--if-*` whenever you read the issue and then thought about it — that gap is
713
+ where a collision actually happens. The output says which window it guarded, as
714
+ `fields.guard.window`: `in-command` or `caller`.
715
+
716
+ A refusal is `kind: "conflict"`, exit **1**, and carries `conflicts` (base,
717
+ remote and proposed for each field) plus `attribution` naming who renamed it.
718
+ Re-read, fold their change into yours, edit again with `--if-title` set to what
719
+ you just read. **It is not atomic and nothing client-side can make it so** — it
720
+ narrows the race to one round trip rather than closing it.
721
+
722
+ Two things GitHub does *quietly*, which this verb reports and a bare `gh api`
723
+ would not:
724
+
725
+ | endpoint | the silence |
726
+ |---|---|
727
+ | `POST …/labels` | **creates** a repository label that does not exist — 200, grey, permanent, spelled exactly as you typed it. So an unknown label is REFUSED with the near misses named; `--create-label` is how you mean it |
728
+ | `POST …/assignees` | **ignores** a login it will not assign and still answers 201. Read `assignees.ignored` — a status code is not the answer here |
729
+
730
+ Labels and assignees go through those add/remove endpoints and never through the
731
+ whole-issue PATCH, which carries them as *whole arrays* — one label write would
732
+ otherwise blindly overwrite every label on the issue.
733
+
650
734
  ### Bind the repository first, or none of this runs
651
735
 
652
736
  ```bash
@@ -829,6 +913,71 @@ something none of this models. `close` does not compose `Closes #45` /
829
913
  `Fixes AB#123` into a PR body either: those fire on a PR *merge*, so there would
830
914
  be nothing to compose into and nothing to verify.
831
915
 
916
+ ### Editing a GitHub comment, and the trailer you must not break
917
+
918
+ A comment that already exists is `pharos comment`, with the issue as the target.
919
+ The verb set is the one you already know from work items and wiki pages:
920
+
921
+ ```bash
922
+ pharos comment list contoso/widgets#45 # ids, state, and what you MAY do
923
+ pharos comment edit contoso/widgets#45 900 --file fixed.md
924
+ pharos comment delete contoso/widgets#45 900 --yes
925
+ pharos comment react contoso/widgets#45 900 thumbs-up
926
+ pharos comment hide contoso/widgets#45 900 off-topic
927
+ pharos comment pin contoso/widgets#45 900
928
+ ```
929
+
930
+ **Start with `list`.** It is the only way to learn a comment id, and it also
931
+ answers the question that saves a failed write: every row carries a `may` object
932
+ — `edit`, `delete`, `hide`, `pin`, `react` — read from GitHub's own view of what
933
+ this account may do. A comment you may not edit is `"edit": false` before you
934
+ try, not a 403 afterwards. Each row also carries `pharosAuthored` and
935
+ `workItemId`, so you can see at a glance which comment is the link.
936
+
937
+ Two ids come back per comment and **both are printed because neither can be
938
+ derived from the other**: `id` is the number that edit, delete and reactions
939
+ address, `nodeId` is what hide and pin take. Either is accepted anywhere a
940
+ comment is named; passing back the one the verb wants saves a request.
941
+
942
+ **An edit cannot double or strip the `pharos:v1` trailer, and this is enforced
943
+ rather than requested.** That trailer is the durable record of which work item
944
+ tracks this issue *and* the guard that stops the two platforms summarising each
945
+ other's summaries. So:
946
+
947
+ | what you send | what happens |
948
+ |---|---|
949
+ | new prose, no trailer | the existing trailer is **restored**, and `markerPreserved: true` says so |
950
+ | the body you read back, trailer intact | sent unchanged |
951
+ | two trailers | refused — `doubled` |
952
+ | a trailer naming a different work item | refused — `repointed`; `pharos issue link` is the verb for that |
953
+ | a trailer on a comment that had none | refused — `forged` |
954
+
955
+ You do not have to think about any of this: hand over the words you want and the
956
+ record survives. It is written down because the refusals name a reason, and the
957
+ reason is actionable.
958
+
959
+ **Deleting the comment that carries the trailer destroys the GitHub half of the
960
+ link.** It is allowed — a mis-adoption is a real thing — but the `--yes` refusal
961
+ says so first, names the work item, and gives you the `pharos issue link` command
962
+ that puts it back. Under `--dry-run` you get the same preview and exit 0. A
963
+ delete that finds the comment already gone reports `alreadyGone: true` and
964
+ succeeds, so a retry after a lost reply is safe.
965
+
966
+ **`hide` and `pin` exist on GitHub and nowhere else** — they are GraphQL
967
+ mutations with no REST route, which is why `gh` has no verb for them. Hiding
968
+ needs a classifier and there is no neutral default, because GitHub shows it
969
+ beside the hidden comment: `spam`, `abuse`, `off-topic`, `outdated`,
970
+ `duplicate`, `resolved`, `low-quality`. Ask for `off-topic` and `off-topic` is
971
+ what reads back. On a work item or a wiki page these four verbs are refused with
972
+ "Azure DevOps has neither" rather than as an unknown verb — there is no endpoint
973
+ to go looking for.
974
+
975
+ **`pharos comment add` is refused on a GitHub issue.** Posting is already
976
+ covered twice and which one you want depends on whether the board should hear:
977
+ `pharos issue say` writes to the reporter *and* the work item, `gh issue comment`
978
+ writes to GitHub only. A third door here would post on an adopted issue that the
979
+ work item never hears about — the exact asymmetry `say` exists to prevent.
980
+
832
981
  ## What `pharos` does NOT do — read this before you go looking
833
982
 
834
983
  - **Free-text and code search.** Nothing here covers it. For work items,
@@ -839,10 +988,12 @@ be nothing to compose into and nothing to verify.
839
988
  - **Wiki content search.** `wiki tree` then `wiki read` is the only way through;
840
989
  there is no grep across pages.
841
990
  - Pull requests, builds, pipelines.
842
- - **Anything on GitHub that is not the join.** No issue list, no issue view, no
843
- plain comment, no labels or milestones, no pull requests — `gh` does all of it
844
- better and a second GitHub CLI would only drift from it. The seven `issue`
845
- verbs are the edge and deliberately nothing else.
991
+ - **Almost anything on GitHub that is not the join.** No issue list, no issue
992
+ view, no *posting* a comment, no labels or milestones, no pull requests — `gh`
993
+ does all of it better and a second GitHub CLI would only drift from it. The
994
+ seven `issue` verbs are the edge. The one addition is a comment that already
995
+ exists (`pharos comment <verb> <owner/name#45>`): editing or deleting one can
996
+ break the link record, and hiding or pinning has no `gh` verb at all.
846
997
  - **Jira, Linear, and the Argus board.** Not covered, not planned. "Task",
847
998
  "todo" and "board" mean something else there.
848
999
 
@@ -1072,3 +1223,12 @@ an `issue` verb names `repos.json` rather than an environment variable.
1072
1223
  ```bash
1073
1224
  pharos setup --repo contoso/widgets --gh-account alisina-tibata
1074
1225
  ```
1226
+
1227
+ **A verb whose target is a GitHub issue does not need `ADO_PAT` at all.**
1228
+ `pharos comment list|edit|delete|react|unreact|reactors|hide|unhide|pin|unpin
1229
+ <owner/name#n>` runs on `gh`'s credential alone, on a machine that has never had
1230
+ an Azure DevOps token. So a `config` error naming `ADO_PAT` from one of those is
1231
+ a defect worth reporting, not something to go and provision a token over. The
1232
+ two credentials are independent — separate keychain entries, and `pharos doctor`
1233
+ reports them separately — and `pharos issue` is the one family that genuinely
1234
+ needs both, because it writes to both platforms in one invocation.