@shiyuphan/follow-me 0.2.0 → 0.4.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 (154) hide show
  1. package/README.md +44 -55
  2. package/dist/src/cli.js +740 -194
  3. package/dist/src/comments.js +109 -6
  4. package/dist/src/diff.js +11 -0
  5. package/dist/src/evolve.js +123 -0
  6. package/dist/src/git.js +12 -0
  7. package/dist/src/host.js +39 -8
  8. package/dist/src/install.js +21 -0
  9. package/dist/src/plan.js +1 -1
  10. package/dist/src/prompt.js +384 -51
  11. package/dist/src/server.js +195 -67
  12. package/dist/src/session.js +53 -12
  13. package/dist/ui/dist/assets/abnfDiagram-WEYOIA3C-byISxePP.js +1 -0
  14. package/dist/ui/dist/assets/arc-CBzsAsP2.js +1 -0
  15. package/dist/ui/dist/assets/architecture-WOLXFQ4H-0OSkIwdB.js +1 -0
  16. package/dist/ui/dist/assets/architectureDiagram-ZHVYKT53-ixHybNqO.js +36 -0
  17. package/dist/ui/dist/assets/array-BifhSqXX.js +1 -0
  18. package/dist/ui/dist/assets/blockDiagram-RJAE7QA3-1F8XL_Ou.js +140 -0
  19. package/dist/ui/dist/assets/c4Diagram-XDPPPE54-D9PFBXnc.js +38 -0
  20. package/dist/ui/dist/assets/channel-B-fwsHf1.js +1 -0
  21. package/dist/ui/dist/assets/chunk-2E7WHVMD-zZdNfMkj.js +88 -0
  22. package/dist/ui/dist/assets/chunk-2Q5K7J3B-C1jixKkw.js +1 -0
  23. package/dist/ui/dist/assets/chunk-4EMNKR2T-CTKoxSSj.js +1 -0
  24. package/dist/ui/dist/assets/chunk-55XCU76K-ifFnDlcS.js +1 -0
  25. package/dist/ui/dist/assets/chunk-5VM5RSS4-ZNzvKenW.js +15 -0
  26. package/dist/ui/dist/assets/chunk-7S6XSTFO-BCMVdi_1.js +2 -0
  27. package/dist/ui/dist/assets/chunk-AQDTSKZB-DC-5Whju.js +72 -0
  28. package/dist/ui/dist/assets/chunk-DSNLSHJZ-DASbe_gb.js +127 -0
  29. package/dist/ui/dist/assets/chunk-DUW6YSOI-Dgx8z5s3.js +1 -0
  30. package/dist/ui/dist/assets/chunk-HAGFOTED-CArytDjj.js +62 -0
  31. package/dist/ui/dist/assets/chunk-HNE4Q36P-Dnl-Wtud.js +1 -0
  32. package/dist/ui/dist/assets/chunk-J5ZVWO5B-oDCd-gWO.js +1 -0
  33. package/dist/ui/dist/assets/chunk-JWPE2WC7-DVXcaiue.js +1 -0
  34. package/dist/ui/dist/assets/chunk-NGNAAXSQ-C2Wy9Xmz.js +136 -0
  35. package/dist/ui/dist/assets/chunk-NQOKRVIF-wvSLaeaK.js +213 -0
  36. package/dist/ui/dist/assets/chunk-UHW7PFBP-CckaOGiQ.js +1 -0
  37. package/dist/ui/dist/assets/chunk-VQ5W62QI-kQBMDJ7V.js +1 -0
  38. package/dist/ui/dist/assets/chunk-WQQL4CDK-CMg6bbLU.js +10 -0
  39. package/dist/ui/dist/assets/chunk-XXDRQBXY-CYlFr19K.js +1 -0
  40. package/dist/ui/dist/assets/chunk-Y2CYZVJY-DsF7k-Jl.js +1 -0
  41. package/dist/ui/dist/assets/chunk-YZO67HFG-CqkG1vVk.js +1 -0
  42. package/dist/ui/dist/assets/chunk-ZAUX2LTX-BUNEdx-e.js +1 -0
  43. package/dist/ui/dist/assets/chunk-ZCHMQSOR-CFTbvcrm.js +2 -0
  44. package/dist/ui/dist/assets/classDiagram-v2-4MYWR3UA-t2OdJ0LA.js +217 -0
  45. package/dist/ui/dist/assets/cose-bilkent-JH36ORCC-FGm030gc.js +1 -0
  46. package/dist/ui/dist/assets/cynefin-EF2NZ3EQ-BtU33Lsu.js +1 -0
  47. package/dist/ui/dist/assets/cynefinDiagram-WF24UDRP-DUr-I7hS.js +62 -0
  48. package/dist/ui/dist/assets/cytoscape.esm-BegBNzOV.js +321 -0
  49. package/dist/ui/dist/assets/dagre-NK4GIZVX-BWTHwJKt.js +4 -0
  50. package/dist/ui/dist/assets/defaultLocale-BFoDCU3G.js +1 -0
  51. package/dist/ui/dist/assets/diagram-CBK3MWTC-PcHrsEBn.js +24 -0
  52. package/dist/ui/dist/assets/diagram-I5XIIV2D-CkGtKjYj.js +24 -0
  53. package/dist/ui/dist/assets/diagram-ODIL2UCW-B_HdBB1w.js +30 -0
  54. package/dist/ui/dist/assets/diagram-PFBEJUXE-DL9zayE6.js +3 -0
  55. package/dist/ui/dist/assets/diagram-RJA3I4VV-By5wl8nK.js +41 -0
  56. package/dist/ui/dist/assets/diagram-TYUNFSNR-ziWPVayL.js +200 -0
  57. package/dist/ui/dist/assets/dist-NDVqCM7k.js +1 -0
  58. package/dist/ui/dist/assets/ebnfDiagram-V4P3TTPT-DH4YGkPc.js +1 -0
  59. package/dist/ui/dist/assets/elk-QONTBCTQ-BL2RlCEv.js +27 -0
  60. package/dist/ui/dist/assets/erDiagram-KYQIGQAA-BKbTMaRp.js +99 -0
  61. package/dist/ui/dist/assets/eventmodeling-K75KTNOO-Dk5RCWc-.js +1 -0
  62. package/dist/ui/dist/assets/flowDiagram-IUGLLWBU-Dkjb3PCK.js +1 -0
  63. package/dist/ui/dist/assets/ganttDiagram-R7GPNTJY-3vM9gxeT.js +292 -0
  64. package/dist/ui/dist/assets/geist-cyrillic-ext-wght-normal-DjL33-gN.woff2 +0 -0
  65. package/dist/ui/dist/assets/geist-cyrillic-wght-normal-BEAKL7Jp.woff2 +0 -0
  66. package/dist/ui/dist/assets/geist-latin-ext-wght-normal-DC-KSUi6.woff2 +0 -0
  67. package/dist/ui/dist/assets/geist-latin-wght-normal-BgDaEnEv.woff2 +0 -0
  68. package/dist/ui/dist/assets/geist-vietnamese-wght-normal-6IgcOCM7.woff2 +0 -0
  69. package/dist/ui/dist/assets/gitGraph-VSP46ZUC-CXGFhz54.js +1 -0
  70. package/dist/ui/dist/assets/gitGraphDiagram-PDYL7XHY-Cl4fwUuA.js +106 -0
  71. package/dist/ui/dist/assets/graphlib-DS17s2tU.js +1 -0
  72. package/dist/ui/dist/assets/index-B8TYcxF8.css +1 -0
  73. package/dist/ui/dist/assets/index-CBbx_pJk.js +134 -0
  74. package/dist/ui/dist/assets/info-OHQRW6UA-DfSsZ8ib.js +1 -0
  75. package/dist/ui/dist/assets/infoDiagram-MVDRKAX5-B_7tyrL5.js +2 -0
  76. package/dist/ui/dist/assets/init-C-OQMol4.js +1 -0
  77. package/dist/ui/dist/assets/ishikawaDiagram-HPEHV2GE-DUvHljpg.js +70 -0
  78. package/dist/ui/dist/assets/journeyDiagram-BOSHW7O7-BCgqzrb8.js +139 -0
  79. package/dist/ui/dist/assets/kanban-definition-FNSA4HC5-DOzZ3NmE.js +89 -0
  80. package/dist/ui/dist/assets/katex-B_JcIwqj.js +257 -0
  81. package/dist/ui/dist/assets/line-B-mPFVll.js +1 -0
  82. package/dist/ui/dist/assets/linear-k7o0jout.js +1 -0
  83. package/dist/ui/dist/assets/mermaid-parser.core-CAyLSQpH.js +7 -0
  84. package/dist/ui/dist/assets/mermaid.core-vfVMjgx2.js +44 -0
  85. package/dist/ui/dist/assets/mindmap-definition-6OY57UG3-B6y-B_r4.js +98 -0
  86. package/dist/ui/dist/assets/ordinal-BDEzSJ7C.js +1 -0
  87. package/dist/ui/dist/assets/packet-JDAUHWVQ-C_r4ed30.js +1 -0
  88. package/dist/ui/dist/assets/path-fybaL0A-.js +1 -0
  89. package/dist/ui/dist/assets/pegDiagram-ECIUJNBX-B8VUm3Bh.js +1 -0
  90. package/dist/ui/dist/assets/pie-XZMESJXO-DcwtbH9y.js +1 -0
  91. package/dist/ui/dist/assets/pieDiagram-GNMPY74T-CvUNBi2F.js +39 -0
  92. package/dist/ui/dist/assets/prism-bash-D6zCJ74D.js +1 -0
  93. package/dist/ui/dist/assets/prism-c-04YixN25.js +1 -0
  94. package/dist/ui/dist/assets/prism-clike-DapgWyxx.js +1 -0
  95. package/dist/ui/dist/assets/prism-core-_2ju85Ya.js +1 -0
  96. package/dist/ui/dist/assets/prism-cpp-C-lJbC-6.js +1 -0
  97. package/dist/ui/dist/assets/prism-css-CPgOzxXp.js +1 -0
  98. package/dist/ui/dist/assets/prism-go-C_8qAH5n.js +1 -0
  99. package/dist/ui/dist/assets/prism-java-6wtusvIl.js +1 -0
  100. package/dist/ui/dist/assets/prism-javascript-BnO-swvr.js +1 -0
  101. package/dist/ui/dist/assets/prism-json-Dp_-W-Hv.js +1 -0
  102. package/dist/ui/dist/assets/prism-jsx-D-D0NWtC.js +1 -0
  103. package/dist/ui/dist/assets/prism-kotlin-BUlJgC-o.js +1 -0
  104. package/dist/ui/dist/assets/prism-markdown-Bc9-36OC.js +1 -0
  105. package/dist/ui/dist/assets/prism-markup-Cp04rIa1.js +1 -0
  106. package/dist/ui/dist/assets/prism-markup-templating-KkSMBj6Y.js +1 -0
  107. package/dist/ui/dist/assets/prism-php-Co4fM40K.js +1 -0
  108. package/dist/ui/dist/assets/prism-python-CfiSVtM6.js +1 -0
  109. package/dist/ui/dist/assets/prism-ruby-DFCASkuD.js +1 -0
  110. package/dist/ui/dist/assets/prism-rust-Bu9N8X3N.js +1 -0
  111. package/dist/ui/dist/assets/prism-sql-CYhVDMEE.js +1 -0
  112. package/dist/ui/dist/assets/prism-swift-B0kWc-ne.js +1 -0
  113. package/dist/ui/dist/assets/prism-toml-DKb54GAT.js +1 -0
  114. package/dist/ui/dist/assets/prism-tsx-p6J2Kc_4.js +1 -0
  115. package/dist/ui/dist/assets/prism-typescript-BMqONzxf.js +1 -0
  116. package/dist/ui/dist/assets/prism-yaml-C0JHu8gr.js +1 -0
  117. package/dist/ui/dist/assets/quadrantDiagram-D57K4MC3-Dg2eVS3j.js +7 -0
  118. package/dist/ui/dist/assets/radar-ABXABTNO-xCNTcZE9.js +1 -0
  119. package/dist/ui/dist/assets/railroad-I3PHUGI6-BI-sIL2Q.js +1 -0
  120. package/dist/ui/dist/assets/railroad-abnf-LNEFI6M7-Ddvk40Wx.js +1 -0
  121. package/dist/ui/dist/assets/railroad-ebnf-DWYD2UWJ-h35gE183.js +1 -0
  122. package/dist/ui/dist/assets/railroad-peg-JR7BVNR7-Zim9iPdp.js +1 -0
  123. package/dist/ui/dist/assets/railroadDiagram-Y72NRBL7-Cz6fdmOQ.js +1 -0
  124. package/dist/ui/dist/assets/requirementDiagram-EQYSW5RA-DTJkCrcD.js +84 -0
  125. package/dist/ui/dist/assets/rough.esm-Dy-Kn_BL.js +1 -0
  126. package/dist/ui/dist/assets/sankeyDiagram-SY24JHO5-Bg1iiVla.js +40 -0
  127. package/dist/ui/dist/assets/sequenceDiagram-BLEHNHQU-9XfetsRA.js +169 -0
  128. package/dist/ui/dist/assets/sizeCapture-INFHLROL-3uHXrFqA.js +1 -0
  129. package/dist/ui/dist/assets/src-Dym5afQ5.js +1 -0
  130. package/dist/ui/dist/assets/stateDiagram-v2-A6IESKWZ-CMmcMV8z.js +291 -0
  131. package/dist/ui/dist/assets/swimlanes-B3ENRKFQ-B2PcshTY.js +1 -0
  132. package/dist/ui/dist/assets/swimlanesDiagram-AYQRV4AK-DfB3XTTn.js +8 -0
  133. package/dist/ui/dist/assets/timeline-definition-JXYKGLGB-Cr1hCNtW.js +120 -0
  134. package/dist/ui/dist/assets/treeView-D4JQ5SDB-jH5ksKnh.js +1 -0
  135. package/dist/ui/dist/assets/treemap-SAJKECNS-BV90e8QL.js +1 -0
  136. package/dist/ui/dist/assets/usecaseDiagram-WPOLBNNX-BNwq5aJV.js +328 -0
  137. package/dist/ui/dist/assets/vennDiagram-Q3Y3IDCW-D-lLyZCL.js +34 -0
  138. package/dist/ui/dist/assets/wardley-7MLQ67FV-CWfHQUGt.js +1 -0
  139. package/dist/ui/dist/assets/wardleyDiagram-IVAIDKWA-BL_qNKVd.js +78 -0
  140. package/dist/ui/dist/assets/xychartDiagram-4URNHDU4-BHPK5Snp.js +7 -0
  141. package/dist/ui/dist/index.html +15 -0
  142. package/dist/ui/dist/style.css +121 -0
  143. package/package.json +31 -10
  144. package/skills/follow-me/SKILL.md +19 -89
  145. package/dist/src/anthropic.js +0 -133
  146. package/dist/ui/app.js +0 -2201
  147. package/dist/ui/concerns.js +0 -165
  148. package/dist/ui/highlight.js +0 -221
  149. package/dist/ui/index.html +0 -94
  150. package/dist/ui/live.js +0 -184
  151. package/dist/ui/playback.js +0 -579
  152. package/dist/ui/questions.js +0 -189
  153. package/dist/ui/style.css +0 -1331
  154. package/dist/ui/summary.js +0 -45
@@ -1,93 +1,23 @@
1
1
  ---
2
2
  name: follow-me
3
- description: Split the current branch's net change into fine, story-ordered commits that read like the author's thought process, then hand them to a human for hunk-by-hunk review. Also plans a narrated walk-through of someone else's branch for a reviewer. Use when the user asks to clean up, reorder, split or "tell the story" of a branch's commits, to walk through or review a branch, or to act on a follow-me review or code review.
3
+ description: Review a branch with the user, hunk by hunk in a local page, and carry the review to the merge request. Use when the user asks to review, split, clean up or reorder a branch's commits, to read someone else's branch, or to post a follow-me review to GitLab or GitHub.
4
4
  ---
5
-
6
5
  # follow-me
7
-
8
- follow-me rewrites a branch as small commits ordered so a reader follows your thinking. It never
9
- touches your working tree or index. It writes a new branch and keeps state in `.follow-me/<slug>/`.
10
-
11
- Run every command from the repo root. If `follow-me` is not on PATH, ask the user how to run it.
12
-
13
- ## First pass
14
-
15
- 1. `follow-me extract [--base main]` snapshots the diff from the base branch to the tip.
16
- 2. `follow-me prompt` prints your instructions and every unit (hunk, file or mode change) with an id.
17
- Read all of it. It names the exact path to write, `.follow-me/<slug>/draft.json`.
18
- 3. Write that draft. Order commits so scaffolding and types come before their uses, a failing test
19
- comes before its fix, renames come before edits, and each commit carries one idea. Commits need
20
- not compile. Every change line is used exactly once, and the last commit must cover every unit.
21
- 4. `follow-me check` validates the draft and prints every issue at once. Fix the draft and re-run
22
- until it is green. Do not edit around an issue by hand-waving it; unassigned and duplicate
23
- lines are real mistakes.
24
- 5. `follow-me apply` freezes the plan as the next version and writes the branch.
25
- 6. Tell the user to run `follow-me review`. It opens a local page where they comment on messages,
26
- positions, hunks and single lines. If you start it yourself, use `follow-me review --wait --no-open`,
27
- which prints the page URL to give the user and returns once they end the review, so you can go
28
- straight on to `follow-me feedback`; one server hosts every session the user has open.
29
-
30
- ## After a review
31
-
32
- When the user says they finished reviewing:
33
-
34
- 1. `follow-me feedback` prints the review as markdown. Every comment has an id like `c_abc234`.
35
- 2. `follow-me prompt` now includes the previous plan and the open comments. Write a new draft that
36
- changes the plan where you agree and lists one entry per comment in `responses`:
37
- `{ "comment": "c_abc234", "status": "addressed" | "declined" | "deferred", "note": "..." }`.
38
- Say why when you decline. Do not leave a comment unanswered.
39
- 3. `follow-me check`, repair until green, then `follow-me apply`, and ask the user to review again.
40
-
41
- ## As a reviewer
42
-
43
- `follow-me walk` is for someone reading a branch they did not write. It plays the branch back
44
- commit by commit in the browser, one chunk of change at a time: it reads your narration aloud,
45
- then shows the lines it explains. The reviewer comments on the code. Nothing is applied and no branch is written.
46
-
47
- 1. `follow-me walk [--branch <name>]` opens the session. If there is no plan yet and no API key, it
48
- prints two commands and stops. Run the first, `follow-me prompt --walk`, and read all of it.
49
- 2. Write the draft it names. The order rules are the same, and every commit also gets a
50
- `narration`: what the author is thinking while writing it, first person, present tense, two or
51
- three sentences, no markdown. Give a unit narration as `{ "unit": "h_x:0-3", "narration": "..." }`
52
- when the why of that hunk is not obvious from the commit's narration. Narration is spoken, so
53
- it names no symbols the listener has to see spelled out and never describes a picture.
54
- Optionally add a `diagram`, Mermaid source drawn beside the code, to a commit or a unit
55
- (`{ "unit": "h_x", "diagram": "flowchart LR\n a --> b" }`). Use one only for what prose
56
- carries badly: data flow between modules, a state machine, a call graph, or the before and
57
- after shape of a structure. Most commits need none. Its first line must name the diagram type,
58
- such as `flowchart`, `sequenceDiagram` or `stateDiagram-v2`; `follow-me check` rejects others.
59
- 3. `follow-me check` until green, then tell the user to run `follow-me walk` again. Do not apply.
60
-
61
- When you are the author's agent and `follow-me feedback --walk` prints a `# Code review`, the review
62
- came from a walk. It quotes each commented line as a diff excerpt with its line number on the
63
- branch. Address every item in the code on the branch itself, then tell the user what you changed
64
- and what you declined and why. Do not write a follow-me draft or `responses` for it; those answer
65
- plan reviews only.
66
-
67
- ## Rules
68
-
69
- - The draft is the only file you write. Never run `git rebase`, `reset` or `commit` for this.
70
- - Use unit ids exactly as printed. `h_x:0-3,7` takes change lines 0 to 3 and 7 of that hunk.
71
- A bare `h_x` takes whatever lines no other commit claimed.
72
- - `f_` and `m_` units are whole; they never take line lists.
73
-
74
- ## Interactive loop
75
-
76
- While the review page is open the reviewer comments and asks questions live, and you answer without
77
- closing the page. Everything goes through the review server.
78
-
79
- 1. `follow-me watch [--since <seq>] [--timeout <seconds>]` blocks until the reviewer does something and
80
- prints it (`--json` for one JSON object per line, `--follow` to stream until the review ends). It
81
- exits 0 with news, 2 when nothing happened, and prints the `--since` to use next. Run it again in a
82
- loop; stop when it reports that the review ended.
83
- 2. Answer a question or comment in place with `follow-me reply <commentId> --body "..."`, adding
84
- `--diagram <file>` with Mermaid source when a picture explains it. This needs no new plan.
85
- 3. When the plan itself should change, write a new draft whose `responses` answer the comments
86
- (`"status": "answered"` with `"at"` for questions), `follow-me check`, then `follow-me apply`. While the
87
- page is open, apply swaps it to the new version at the same URL and keeps the old comments.
88
- 4. At the end, `follow-me report` prints one consolidated report of the issues you and the reviewer
89
- raised, by file and line, with questions and answers last. It is also saved as
90
- `.follow-me/<slug>/reviews/vN.md`. Give it to the author.
91
-
92
- In a walk you may also raise `concerns` (see `follow-me prompt --walk`); the reviewer keeps or dismisses
93
- each, and only kept ones reach the report.
6
+ follow-me is interactive. After it launches the review page, keep running `follow-me` until it prints the report. Each run waits up to 90 seconds for the reviewer.
7
+ Do not stop, do not summarise, and do not ask the user whether to continue. The user is on the page.
8
+
9
+ The user never types a follow-me command. If `follow-me` is not on PATH, tell them to run `npm i -g @shiyuphan/follow-me`; otherwise you run everything. They use the page, press Submit, and ask you to post the review.
10
+ 1. From the repo root run `follow-me`. It does the next missing thing, prints what happened, and ends with a `next:` line. Do what it says, then run `follow-me` again.
11
+ 2. When it says to write `.follow-me/<slug>/draft.json` from `prompt.md`, read the prompt, write the draft, and run `follow-me` (it checks the draft, prints every issue, and applies when valid). Put the why in each commit body.
12
+ 3. When it prints the review page URL, tell the user once and keep running `follow-me`. Each call listens up to 90 seconds and prints what the user flagged or asked, with comment ids, the moment they post it.
13
+ 4. Answer a question in place with `follow-me reply <id> --body "<markdown>" [--diagram file]` (Mermaid source for a picture). Start a thread yourself with `follow-me explain [--commit n] [--unit id] [--lines a-b] --body "..."`.
14
+ 5. When the user asks for a code change, make it on the original branch and commit it there. Then run `follow-me`. It carries the plan over, asks you to place any new units in the draft, and swaps the open page to the new code with the thread kept. Then `follow-me reply <id>` on that comment saying what changed and where (file and line), and run `follow-me` again.
15
+ 6. Repeat until it prints the report and `next: done`. Fix each issue in the code, or say why you decline. On someone else's branch you only report.
16
+ 7. Never run `git rebase` or `reset` for this, and never touch the `-split` branch. The only commits you make are the requested fixes on the original branch.
17
+ ## Porting a review
18
+ Only when the user asks to post it.
19
+ 1. `follow-me report --json` prints items with `path`, `oldPath`, `newLine`, `oldLine`, `startNewLine`, `startOldLine`, `severity`, `kind`, `body` (markdown, ready to post), `thread`, `resolved`, `excluded`. Skip `excluded: true`. Lines are the branch tip's, or the base's for a removed line. Tell the user the count and first lines, ask which merge request or pull request, and never post without a yes.
20
+ 2. Fetch the target with `glab mr view <iid> --output json` (`diff_refs`) or `gh pr view <n> --json headRefOid,baseRefName`. Its head must equal the tip on the first line of `follow-me status`; if not, say so and stop, because the line numbers would be wrong.
21
+ 3. GitHub: post one review with `gh api repos/{owner}/{repo}/pulls/<n>/reviews --input review.json`, `"event": "COMMENT"`, each item in `comments[]` as `path`, `body`, and `line`+`side` (`newLine` with `RIGHT`, or `oldLine` with `LEFT`), plus `start_line` and `start_side` for a range; items without `path` go in the review `body`. Never APPROVE or REQUEST_CHANGES unless asked.
22
+ 4. GitLab: one discussion per item with `glab api --method POST projects/:id/merge_requests/<iid>/discussions` and `position[position_type]=text`, `base_sha`, `start_sha`, `head_sha` from `diff_refs`, and `new_path`, `old_path`, `new_line` (added), `old_line` (removed) or both (context) from the item. Items without `path`: `glab mr note`.
23
+ 5. Report what was posted with links and what failed; do not retry blindly. follow-me never holds a token; `gh` and `glab` do, and you run them.
@@ -1,133 +0,0 @@
1
- import Anthropic from "@anthropic-ai/sdk";
2
- export const DEFAULT_MODEL = "claude-opus-5-5";
3
- const MAX_TOKENS = 64000;
4
- export class PlanRequestError extends Error {
5
- failure;
6
- constructor(failure, message) {
7
- super(message);
8
- this.name = "PlanRequestError";
9
- this.failure = failure;
10
- }
11
- }
12
- const optionalText = { anyOf: [{ type: "string" }, { type: "null" }] };
13
- const concern = {
14
- type: "object",
15
- properties: {
16
- severity: { type: "string", enum: ["blocker", "major", "minor"] },
17
- kind: { type: "string", enum: ["bug", "missing-test", "naming", "risk"] },
18
- text: { type: "string" },
19
- lines: { anyOf: [{ type: "array", items: { type: "integer" } }, { type: "null" }] },
20
- },
21
- required: ["severity", "kind", "text", "lines"],
22
- additionalProperties: false,
23
- };
24
- const concerns = { anyOf: [{ type: "array", items: concern }, { type: "null" }] };
25
- const annotatedUnit = {
26
- type: "object",
27
- properties: {
28
- unit: { type: "string" },
29
- narration: optionalText,
30
- diagram: optionalText,
31
- concerns,
32
- },
33
- required: ["unit", "narration", "diagram", "concerns"],
34
- additionalProperties: false,
35
- };
36
- const unitList = { type: "array", items: { anyOf: [{ type: "string" }, annotatedUnit] } };
37
- /**
38
- * Mirrors PlanFile on the wire. Structured outputs need every property required and closed
39
- * objects, so narration, diagram, concerns and a response's `at` are nullable instead of
40
- * optional; the plan parser reads null as none.
41
- */
42
- export const PLAN_SCHEMA = {
43
- type: "object",
44
- properties: {
45
- schema: { type: "string", enum: ["follow-me/plan@1"] },
46
- base: { type: "string" },
47
- tip: { type: "string" },
48
- commits: {
49
- type: "array",
50
- items: {
51
- type: "object",
52
- properties: {
53
- message: { type: "string" },
54
- narration: optionalText,
55
- diagram: optionalText,
56
- concerns,
57
- units: unitList,
58
- },
59
- required: ["message", "narration", "diagram", "concerns", "units"],
60
- additionalProperties: false,
61
- },
62
- },
63
- responses: {
64
- type: "array",
65
- items: {
66
- type: "object",
67
- properties: {
68
- comment: { type: "string" },
69
- status: { type: "string", enum: ["addressed", "declined", "deferred", "answered"] },
70
- note: { type: "string" },
71
- at: {
72
- anyOf: [
73
- {
74
- type: "object",
75
- properties: { commit: { type: "integer" }, unit: optionalText },
76
- required: ["commit", "unit"],
77
- additionalProperties: false,
78
- },
79
- { type: "null" },
80
- ],
81
- },
82
- },
83
- required: ["comment", "status", "note", "at"],
84
- additionalProperties: false,
85
- },
86
- },
87
- },
88
- required: ["schema", "base", "tip", "commits", "responses"],
89
- additionalProperties: false,
90
- };
91
- const sendWithAnthropic = async ({ model, prompt }) => {
92
- const client = new Anthropic();
93
- // Streaming because a 64k-token reply outlives the SDK's non-streaming timeout.
94
- const stream = client.beta.messages.stream({
95
- model,
96
- max_tokens: MAX_TOKENS,
97
- output_config: { effort: "high", format: { type: "json_schema", schema: PLAN_SCHEMA } },
98
- betas: ["server-side-fallback-2026-07-01"],
99
- fallbacks: "default",
100
- messages: [{ role: "user", content: prompt }],
101
- });
102
- const message = await stream.finalMessage();
103
- const text = message.content.flatMap((block) => (block.type === "text" ? [block.text] : []));
104
- return {
105
- stopReason: message.stop_reason,
106
- text: text.join(""),
107
- refusalExplanation: message.stop_details?.explanation ?? null,
108
- };
109
- };
110
- /**
111
- * Asks the model for a plan and returns the parsed JSON. The caller validates it as a plan,
112
- * because a schema-conforming reply can still name units that do not exist.
113
- */
114
- export async function requestPlan(prompt, opts = {}) {
115
- const send = opts.send ?? sendWithAnthropic;
116
- const reply = await send({ model: opts.model ?? DEFAULT_MODEL, prompt });
117
- if (reply.stopReason === "refusal") {
118
- const why = reply.refusalExplanation ? `: ${reply.refusalExplanation}` : "";
119
- throw new PlanRequestError("refusal", `the model declined to write a plan${why}`);
120
- }
121
- if (reply.stopReason === "max_tokens") {
122
- throw new PlanRequestError("truncated", `the plan did not fit in ${MAX_TOKENS} tokens; try a smaller diff`);
123
- }
124
- if (reply.text.trim() === "") {
125
- throw new PlanRequestError("empty", "the model returned no text");
126
- }
127
- try {
128
- return JSON.parse(reply.text);
129
- }
130
- catch {
131
- throw new PlanRequestError("not-json", "the model's reply was not valid JSON");
132
- }
133
- }