@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.
- package/README.md +44 -55
- package/dist/src/cli.js +740 -194
- package/dist/src/comments.js +109 -6
- package/dist/src/diff.js +11 -0
- package/dist/src/evolve.js +123 -0
- package/dist/src/git.js +12 -0
- package/dist/src/host.js +39 -8
- package/dist/src/install.js +21 -0
- package/dist/src/plan.js +1 -1
- package/dist/src/prompt.js +384 -51
- package/dist/src/server.js +195 -67
- package/dist/src/session.js +53 -12
- package/dist/ui/dist/assets/abnfDiagram-WEYOIA3C-byISxePP.js +1 -0
- package/dist/ui/dist/assets/arc-CBzsAsP2.js +1 -0
- package/dist/ui/dist/assets/architecture-WOLXFQ4H-0OSkIwdB.js +1 -0
- package/dist/ui/dist/assets/architectureDiagram-ZHVYKT53-ixHybNqO.js +36 -0
- package/dist/ui/dist/assets/array-BifhSqXX.js +1 -0
- package/dist/ui/dist/assets/blockDiagram-RJAE7QA3-1F8XL_Ou.js +140 -0
- package/dist/ui/dist/assets/c4Diagram-XDPPPE54-D9PFBXnc.js +38 -0
- package/dist/ui/dist/assets/channel-B-fwsHf1.js +1 -0
- package/dist/ui/dist/assets/chunk-2E7WHVMD-zZdNfMkj.js +88 -0
- package/dist/ui/dist/assets/chunk-2Q5K7J3B-C1jixKkw.js +1 -0
- package/dist/ui/dist/assets/chunk-4EMNKR2T-CTKoxSSj.js +1 -0
- package/dist/ui/dist/assets/chunk-55XCU76K-ifFnDlcS.js +1 -0
- package/dist/ui/dist/assets/chunk-5VM5RSS4-ZNzvKenW.js +15 -0
- package/dist/ui/dist/assets/chunk-7S6XSTFO-BCMVdi_1.js +2 -0
- package/dist/ui/dist/assets/chunk-AQDTSKZB-DC-5Whju.js +72 -0
- package/dist/ui/dist/assets/chunk-DSNLSHJZ-DASbe_gb.js +127 -0
- package/dist/ui/dist/assets/chunk-DUW6YSOI-Dgx8z5s3.js +1 -0
- package/dist/ui/dist/assets/chunk-HAGFOTED-CArytDjj.js +62 -0
- package/dist/ui/dist/assets/chunk-HNE4Q36P-Dnl-Wtud.js +1 -0
- package/dist/ui/dist/assets/chunk-J5ZVWO5B-oDCd-gWO.js +1 -0
- package/dist/ui/dist/assets/chunk-JWPE2WC7-DVXcaiue.js +1 -0
- package/dist/ui/dist/assets/chunk-NGNAAXSQ-C2Wy9Xmz.js +136 -0
- package/dist/ui/dist/assets/chunk-NQOKRVIF-wvSLaeaK.js +213 -0
- package/dist/ui/dist/assets/chunk-UHW7PFBP-CckaOGiQ.js +1 -0
- package/dist/ui/dist/assets/chunk-VQ5W62QI-kQBMDJ7V.js +1 -0
- package/dist/ui/dist/assets/chunk-WQQL4CDK-CMg6bbLU.js +10 -0
- package/dist/ui/dist/assets/chunk-XXDRQBXY-CYlFr19K.js +1 -0
- package/dist/ui/dist/assets/chunk-Y2CYZVJY-DsF7k-Jl.js +1 -0
- package/dist/ui/dist/assets/chunk-YZO67HFG-CqkG1vVk.js +1 -0
- package/dist/ui/dist/assets/chunk-ZAUX2LTX-BUNEdx-e.js +1 -0
- package/dist/ui/dist/assets/chunk-ZCHMQSOR-CFTbvcrm.js +2 -0
- package/dist/ui/dist/assets/classDiagram-v2-4MYWR3UA-t2OdJ0LA.js +217 -0
- package/dist/ui/dist/assets/cose-bilkent-JH36ORCC-FGm030gc.js +1 -0
- package/dist/ui/dist/assets/cynefin-EF2NZ3EQ-BtU33Lsu.js +1 -0
- package/dist/ui/dist/assets/cynefinDiagram-WF24UDRP-DUr-I7hS.js +62 -0
- package/dist/ui/dist/assets/cytoscape.esm-BegBNzOV.js +321 -0
- package/dist/ui/dist/assets/dagre-NK4GIZVX-BWTHwJKt.js +4 -0
- package/dist/ui/dist/assets/defaultLocale-BFoDCU3G.js +1 -0
- package/dist/ui/dist/assets/diagram-CBK3MWTC-PcHrsEBn.js +24 -0
- package/dist/ui/dist/assets/diagram-I5XIIV2D-CkGtKjYj.js +24 -0
- package/dist/ui/dist/assets/diagram-ODIL2UCW-B_HdBB1w.js +30 -0
- package/dist/ui/dist/assets/diagram-PFBEJUXE-DL9zayE6.js +3 -0
- package/dist/ui/dist/assets/diagram-RJA3I4VV-By5wl8nK.js +41 -0
- package/dist/ui/dist/assets/diagram-TYUNFSNR-ziWPVayL.js +200 -0
- package/dist/ui/dist/assets/dist-NDVqCM7k.js +1 -0
- package/dist/ui/dist/assets/ebnfDiagram-V4P3TTPT-DH4YGkPc.js +1 -0
- package/dist/ui/dist/assets/elk-QONTBCTQ-BL2RlCEv.js +27 -0
- package/dist/ui/dist/assets/erDiagram-KYQIGQAA-BKbTMaRp.js +99 -0
- package/dist/ui/dist/assets/eventmodeling-K75KTNOO-Dk5RCWc-.js +1 -0
- package/dist/ui/dist/assets/flowDiagram-IUGLLWBU-Dkjb3PCK.js +1 -0
- package/dist/ui/dist/assets/ganttDiagram-R7GPNTJY-3vM9gxeT.js +292 -0
- package/dist/ui/dist/assets/geist-cyrillic-ext-wght-normal-DjL33-gN.woff2 +0 -0
- package/dist/ui/dist/assets/geist-cyrillic-wght-normal-BEAKL7Jp.woff2 +0 -0
- package/dist/ui/dist/assets/geist-latin-ext-wght-normal-DC-KSUi6.woff2 +0 -0
- package/dist/ui/dist/assets/geist-latin-wght-normal-BgDaEnEv.woff2 +0 -0
- package/dist/ui/dist/assets/geist-vietnamese-wght-normal-6IgcOCM7.woff2 +0 -0
- package/dist/ui/dist/assets/gitGraph-VSP46ZUC-CXGFhz54.js +1 -0
- package/dist/ui/dist/assets/gitGraphDiagram-PDYL7XHY-Cl4fwUuA.js +106 -0
- package/dist/ui/dist/assets/graphlib-DS17s2tU.js +1 -0
- package/dist/ui/dist/assets/index-B8TYcxF8.css +1 -0
- package/dist/ui/dist/assets/index-CBbx_pJk.js +134 -0
- package/dist/ui/dist/assets/info-OHQRW6UA-DfSsZ8ib.js +1 -0
- package/dist/ui/dist/assets/infoDiagram-MVDRKAX5-B_7tyrL5.js +2 -0
- package/dist/ui/dist/assets/init-C-OQMol4.js +1 -0
- package/dist/ui/dist/assets/ishikawaDiagram-HPEHV2GE-DUvHljpg.js +70 -0
- package/dist/ui/dist/assets/journeyDiagram-BOSHW7O7-BCgqzrb8.js +139 -0
- package/dist/ui/dist/assets/kanban-definition-FNSA4HC5-DOzZ3NmE.js +89 -0
- package/dist/ui/dist/assets/katex-B_JcIwqj.js +257 -0
- package/dist/ui/dist/assets/line-B-mPFVll.js +1 -0
- package/dist/ui/dist/assets/linear-k7o0jout.js +1 -0
- package/dist/ui/dist/assets/mermaid-parser.core-CAyLSQpH.js +7 -0
- package/dist/ui/dist/assets/mermaid.core-vfVMjgx2.js +44 -0
- package/dist/ui/dist/assets/mindmap-definition-6OY57UG3-B6y-B_r4.js +98 -0
- package/dist/ui/dist/assets/ordinal-BDEzSJ7C.js +1 -0
- package/dist/ui/dist/assets/packet-JDAUHWVQ-C_r4ed30.js +1 -0
- package/dist/ui/dist/assets/path-fybaL0A-.js +1 -0
- package/dist/ui/dist/assets/pegDiagram-ECIUJNBX-B8VUm3Bh.js +1 -0
- package/dist/ui/dist/assets/pie-XZMESJXO-DcwtbH9y.js +1 -0
- package/dist/ui/dist/assets/pieDiagram-GNMPY74T-CvUNBi2F.js +39 -0
- package/dist/ui/dist/assets/prism-bash-D6zCJ74D.js +1 -0
- package/dist/ui/dist/assets/prism-c-04YixN25.js +1 -0
- package/dist/ui/dist/assets/prism-clike-DapgWyxx.js +1 -0
- package/dist/ui/dist/assets/prism-core-_2ju85Ya.js +1 -0
- package/dist/ui/dist/assets/prism-cpp-C-lJbC-6.js +1 -0
- package/dist/ui/dist/assets/prism-css-CPgOzxXp.js +1 -0
- package/dist/ui/dist/assets/prism-go-C_8qAH5n.js +1 -0
- package/dist/ui/dist/assets/prism-java-6wtusvIl.js +1 -0
- package/dist/ui/dist/assets/prism-javascript-BnO-swvr.js +1 -0
- package/dist/ui/dist/assets/prism-json-Dp_-W-Hv.js +1 -0
- package/dist/ui/dist/assets/prism-jsx-D-D0NWtC.js +1 -0
- package/dist/ui/dist/assets/prism-kotlin-BUlJgC-o.js +1 -0
- package/dist/ui/dist/assets/prism-markdown-Bc9-36OC.js +1 -0
- package/dist/ui/dist/assets/prism-markup-Cp04rIa1.js +1 -0
- package/dist/ui/dist/assets/prism-markup-templating-KkSMBj6Y.js +1 -0
- package/dist/ui/dist/assets/prism-php-Co4fM40K.js +1 -0
- package/dist/ui/dist/assets/prism-python-CfiSVtM6.js +1 -0
- package/dist/ui/dist/assets/prism-ruby-DFCASkuD.js +1 -0
- package/dist/ui/dist/assets/prism-rust-Bu9N8X3N.js +1 -0
- package/dist/ui/dist/assets/prism-sql-CYhVDMEE.js +1 -0
- package/dist/ui/dist/assets/prism-swift-B0kWc-ne.js +1 -0
- package/dist/ui/dist/assets/prism-toml-DKb54GAT.js +1 -0
- package/dist/ui/dist/assets/prism-tsx-p6J2Kc_4.js +1 -0
- package/dist/ui/dist/assets/prism-typescript-BMqONzxf.js +1 -0
- package/dist/ui/dist/assets/prism-yaml-C0JHu8gr.js +1 -0
- package/dist/ui/dist/assets/quadrantDiagram-D57K4MC3-Dg2eVS3j.js +7 -0
- package/dist/ui/dist/assets/radar-ABXABTNO-xCNTcZE9.js +1 -0
- package/dist/ui/dist/assets/railroad-I3PHUGI6-BI-sIL2Q.js +1 -0
- package/dist/ui/dist/assets/railroad-abnf-LNEFI6M7-Ddvk40Wx.js +1 -0
- package/dist/ui/dist/assets/railroad-ebnf-DWYD2UWJ-h35gE183.js +1 -0
- package/dist/ui/dist/assets/railroad-peg-JR7BVNR7-Zim9iPdp.js +1 -0
- package/dist/ui/dist/assets/railroadDiagram-Y72NRBL7-Cz6fdmOQ.js +1 -0
- package/dist/ui/dist/assets/requirementDiagram-EQYSW5RA-DTJkCrcD.js +84 -0
- package/dist/ui/dist/assets/rough.esm-Dy-Kn_BL.js +1 -0
- package/dist/ui/dist/assets/sankeyDiagram-SY24JHO5-Bg1iiVla.js +40 -0
- package/dist/ui/dist/assets/sequenceDiagram-BLEHNHQU-9XfetsRA.js +169 -0
- package/dist/ui/dist/assets/sizeCapture-INFHLROL-3uHXrFqA.js +1 -0
- package/dist/ui/dist/assets/src-Dym5afQ5.js +1 -0
- package/dist/ui/dist/assets/stateDiagram-v2-A6IESKWZ-CMmcMV8z.js +291 -0
- package/dist/ui/dist/assets/swimlanes-B3ENRKFQ-B2PcshTY.js +1 -0
- package/dist/ui/dist/assets/swimlanesDiagram-AYQRV4AK-DfB3XTTn.js +8 -0
- package/dist/ui/dist/assets/timeline-definition-JXYKGLGB-Cr1hCNtW.js +120 -0
- package/dist/ui/dist/assets/treeView-D4JQ5SDB-jH5ksKnh.js +1 -0
- package/dist/ui/dist/assets/treemap-SAJKECNS-BV90e8QL.js +1 -0
- package/dist/ui/dist/assets/usecaseDiagram-WPOLBNNX-BNwq5aJV.js +328 -0
- package/dist/ui/dist/assets/vennDiagram-Q3Y3IDCW-D-lLyZCL.js +34 -0
- package/dist/ui/dist/assets/wardley-7MLQ67FV-CWfHQUGt.js +1 -0
- package/dist/ui/dist/assets/wardleyDiagram-IVAIDKWA-BL_qNKVd.js +78 -0
- package/dist/ui/dist/assets/xychartDiagram-4URNHDU4-BHPK5Snp.js +7 -0
- package/dist/ui/dist/index.html +15 -0
- package/dist/ui/dist/style.css +121 -0
- package/package.json +31 -10
- package/skills/follow-me/SKILL.md +19 -89
- package/dist/src/anthropic.js +0 -133
- package/dist/ui/app.js +0 -2201
- package/dist/ui/concerns.js +0 -165
- package/dist/ui/highlight.js +0 -221
- package/dist/ui/index.html +0 -94
- package/dist/ui/live.js +0 -184
- package/dist/ui/playback.js +0 -579
- package/dist/ui/questions.js +0 -189
- package/dist/ui/style.css +0 -1331
- package/dist/ui/summary.js +0 -45
|
@@ -1,93 +1,23 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: follow-me
|
|
3
|
-
description:
|
|
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
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
5.
|
|
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.
|
package/dist/src/anthropic.js
DELETED
|
@@ -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
|
-
}
|