@shiyuphan/follow-me 0.1.1 → 0.3.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 +119 -69
- package/dist/src/cli.js +1069 -140
- package/dist/src/comments.js +361 -0
- package/dist/src/git.js +32 -3
- package/dist/src/host.js +106 -9
- package/dist/src/install.js +22 -1
- package/dist/src/naive.js +26 -0
- package/dist/src/plan.js +282 -26
- package/dist/src/prompt.js +711 -98
- package/dist/src/server.js +573 -140
- package/dist/src/session.js +106 -104
- package/dist/src/syntax.js +199 -0
- package/dist/src/types.js +3 -1
- package/dist/ui/dist/assets/abnfDiagram-WEYOIA3C-DcmzRkVZ.js +1 -0
- package/dist/ui/dist/assets/arc-T5QB0T1C.js +1 -0
- package/dist/ui/dist/assets/architecture-WOLXFQ4H-DUIkQTeE.js +1 -0
- package/dist/ui/dist/assets/architectureDiagram-ZHVYKT53-BUYi4-bA.js +36 -0
- package/dist/ui/dist/assets/array-BifhSqXX.js +1 -0
- package/dist/ui/dist/assets/blockDiagram-RJAE7QA3-dyJxtT9e.js +140 -0
- package/dist/ui/dist/assets/c4Diagram-XDPPPE54-CrAgiaZR.js +38 -0
- package/dist/ui/dist/assets/channel-wW7ebm0i.js +1 -0
- package/dist/ui/dist/assets/chunk-2E7WHVMD-C6fAf4Vj.js +88 -0
- package/dist/ui/dist/assets/chunk-2Q5K7J3B-C1jixKkw.js +1 -0
- package/dist/ui/dist/assets/chunk-4EMNKR2T-BLZjwZ4Q.js +1 -0
- package/dist/ui/dist/assets/chunk-55XCU76K-DGLK2Q-L.js +1 -0
- package/dist/ui/dist/assets/chunk-5VM5RSS4-ZNzvKenW.js +15 -0
- package/dist/ui/dist/assets/chunk-7S6XSTFO-BablszTX.js +2 -0
- package/dist/ui/dist/assets/chunk-AQDTSKZB-Cuk3B_vb.js +72 -0
- package/dist/ui/dist/assets/chunk-DSNLSHJZ-DiC78RqE.js +127 -0
- package/dist/ui/dist/assets/chunk-DUW6YSOI-Dgx8z5s3.js +1 -0
- package/dist/ui/dist/assets/chunk-HAGFOTED-X_RbvixX.js +62 -0
- package/dist/ui/dist/assets/chunk-HNE4Q36P-C14_x5ck.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-Dxd0dtYR.js +213 -0
- package/dist/ui/dist/assets/chunk-UHW7PFBP-BIxePNUS.js +1 -0
- package/dist/ui/dist/assets/chunk-VQ5W62QI-CKSKLbj4.js +1 -0
- package/dist/ui/dist/assets/chunk-WQQL4CDK-CWNmZGlR.js +10 -0
- package/dist/ui/dist/assets/chunk-XXDRQBXY-D-I5QHgG.js +1 -0
- package/dist/ui/dist/assets/chunk-Y2CYZVJY-DsF7k-Jl.js +1 -0
- package/dist/ui/dist/assets/chunk-YZO67HFG-DFP68CFd.js +1 -0
- package/dist/ui/dist/assets/chunk-ZAUX2LTX-DY90d4QG.js +1 -0
- package/dist/ui/dist/assets/chunk-ZCHMQSOR-Dlpr-5UC.js +2 -0
- package/dist/ui/dist/assets/classDiagram-v2-4MYWR3UA-CH9uhoLJ.js +217 -0
- package/dist/ui/dist/assets/cose-bilkent-JH36ORCC-DtYHrp8D.js +1 -0
- package/dist/ui/dist/assets/cynefin-EF2NZ3EQ-BtslbtrY.js +1 -0
- package/dist/ui/dist/assets/cynefinDiagram-WF24UDRP-DUU1Jcc7.js +62 -0
- package/dist/ui/dist/assets/cytoscape.esm-BegBNzOV.js +321 -0
- package/dist/ui/dist/assets/dagre-NK4GIZVX-CDlP-dRW.js +4 -0
- package/dist/ui/dist/assets/defaultLocale-BFoDCU3G.js +1 -0
- package/dist/ui/dist/assets/diagram-CBK3MWTC-DiV2fn-r.js +24 -0
- package/dist/ui/dist/assets/diagram-I5XIIV2D-Bg5RehGi.js +24 -0
- package/dist/ui/dist/assets/diagram-ODIL2UCW-CFmazz3f.js +30 -0
- package/dist/ui/dist/assets/diagram-PFBEJUXE-uw3ZBqDp.js +3 -0
- package/dist/ui/dist/assets/diagram-RJA3I4VV-uEOYYRYa.js +41 -0
- package/dist/ui/dist/assets/diagram-TYUNFSNR-BN58LF_3.js +200 -0
- package/dist/ui/dist/assets/dist-tWS_lXDS.js +1 -0
- package/dist/ui/dist/assets/ebnfDiagram-V4P3TTPT-B68Tz-w8.js +1 -0
- package/dist/ui/dist/assets/elk-QONTBCTQ-DZZohNea.js +27 -0
- package/dist/ui/dist/assets/erDiagram-KYQIGQAA-BH_m74pv.js +99 -0
- package/dist/ui/dist/assets/eventmodeling-K75KTNOO-CTp5FHos.js +1 -0
- package/dist/ui/dist/assets/flowDiagram-IUGLLWBU-CsCvexry.js +1 -0
- package/dist/ui/dist/assets/ganttDiagram-R7GPNTJY-BDau7_0f.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-Bv84IoCm.js +1 -0
- package/dist/ui/dist/assets/gitGraphDiagram-PDYL7XHY-DzXcMlm8.js +106 -0
- package/dist/ui/dist/assets/graphlib-DS17s2tU.js +1 -0
- package/dist/ui/dist/assets/index-BTJoqVVN.js +133 -0
- package/dist/ui/dist/assets/index-D60U3smc.css +1 -0
- package/dist/ui/dist/assets/info-OHQRW6UA-222J0O_j.js +1 -0
- package/dist/ui/dist/assets/infoDiagram-MVDRKAX5-DE2sa3Vz.js +2 -0
- package/dist/ui/dist/assets/init-C-OQMol4.js +1 -0
- package/dist/ui/dist/assets/ishikawaDiagram-HPEHV2GE-BJDk0Zcj.js +70 -0
- package/dist/ui/dist/assets/journeyDiagram-BOSHW7O7-JSUXX5Hb.js +139 -0
- package/dist/ui/dist/assets/kanban-definition-FNSA4HC5-Bg79b49M.js +89 -0
- package/dist/ui/dist/assets/katex-B_JcIwqj.js +257 -0
- package/dist/ui/dist/assets/line-Bk5qQSIa.js +1 -0
- package/dist/ui/dist/assets/linear-PFnVMru5.js +1 -0
- package/dist/ui/dist/assets/mermaid-parser.core-Dcxlzc-S.js +7 -0
- package/dist/ui/dist/assets/mermaid.core-TeOHQJ00.js +44 -0
- package/dist/ui/dist/assets/mindmap-definition-6OY57UG3-IaNk8NnW.js +98 -0
- package/dist/ui/dist/assets/ordinal-BDEzSJ7C.js +1 -0
- package/dist/ui/dist/assets/packet-JDAUHWVQ-DMff5cVK.js +1 -0
- package/dist/ui/dist/assets/path-fybaL0A-.js +1 -0
- package/dist/ui/dist/assets/pegDiagram-ECIUJNBX-66Xu4YRc.js +1 -0
- package/dist/ui/dist/assets/pie-XZMESJXO-GEtzLCIM.js +1 -0
- package/dist/ui/dist/assets/pieDiagram-GNMPY74T-7jMBi-kU.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-FDHlWQ43.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-8hIWayC2.js +7 -0
- package/dist/ui/dist/assets/radar-ABXABTNO-BTdwUOOF.js +1 -0
- package/dist/ui/dist/assets/railroad-I3PHUGI6-DcMbDqBL.js +1 -0
- package/dist/ui/dist/assets/railroad-abnf-LNEFI6M7-gTzUTWeI.js +1 -0
- package/dist/ui/dist/assets/railroad-ebnf-DWYD2UWJ-CNtdUPtk.js +1 -0
- package/dist/ui/dist/assets/railroad-peg-JR7BVNR7-DOiExAtE.js +1 -0
- package/dist/ui/dist/assets/railroadDiagram-Y72NRBL7-2kE_ygvM.js +1 -0
- package/dist/ui/dist/assets/requirementDiagram-EQYSW5RA-CoIAHRq2.js +84 -0
- package/dist/ui/dist/assets/rough.esm-Dy-Kn_BL.js +1 -0
- package/dist/ui/dist/assets/sankeyDiagram-SY24JHO5-7p8D3wim.js +40 -0
- package/dist/ui/dist/assets/sequenceDiagram-BLEHNHQU-C6QFnMvn.js +169 -0
- package/dist/ui/dist/assets/sizeCapture-INFHLROL-3uHXrFqA.js +1 -0
- package/dist/ui/dist/assets/src-FgijoBVs.js +1 -0
- package/dist/ui/dist/assets/stateDiagram-v2-A6IESKWZ-n9eAb_bT.js +291 -0
- package/dist/ui/dist/assets/swimlanes-B3ENRKFQ-U18mtUzX.js +1 -0
- package/dist/ui/dist/assets/swimlanesDiagram-AYQRV4AK-DX07QVy9.js +8 -0
- package/dist/ui/dist/assets/timeline-definition-JXYKGLGB-CD2Z8QGP.js +120 -0
- package/dist/ui/dist/assets/treeView-D4JQ5SDB-sqUjH5gB.js +1 -0
- package/dist/ui/dist/assets/treemap-SAJKECNS-DvZ38FP7.js +1 -0
- package/dist/ui/dist/assets/usecaseDiagram-WPOLBNNX-CqOGidaZ.js +328 -0
- package/dist/ui/dist/assets/vennDiagram-Q3Y3IDCW-D1SXSBQ5.js +34 -0
- package/dist/ui/dist/assets/wardley-7MLQ67FV-Bpwio-Dx.js +1 -0
- package/dist/ui/dist/assets/wardleyDiagram-IVAIDKWA-JSUXHMnk.js +78 -0
- package/dist/ui/dist/assets/xychartDiagram-4URNHDU4-B4IFqQTN.js +7 -0
- package/dist/ui/dist/index.html +15 -0
- package/dist/ui/dist/style.css +121 -0
- package/package.json +31 -8
- package/skills/follow-me/SKILL.md +15 -61
- package/dist/src/anthropic.js +0 -101
- package/dist/ui/app.js +0 -797
- package/dist/ui/index.html +0 -40
- package/dist/ui/playback.js +0 -175
- package/dist/ui/style.css +0 -466
|
@@ -1,65 +1,19 @@
|
|
|
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
|
-
follow-me
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
3.
|
|
19
|
-
|
|
20
|
-
|
|
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 --detach`,
|
|
27
|
-
which prints the page URL and returns; one server hosts every session the user has open.
|
|
28
|
-
|
|
29
|
-
## After a review
|
|
30
|
-
|
|
31
|
-
When the user says they finished reviewing:
|
|
32
|
-
|
|
33
|
-
1. `follow-me feedback` prints the review as markdown. Every comment has an id like `c_abc234`.
|
|
34
|
-
2. `follow-me prompt` now includes the previous plan and the open comments. Write a new draft that
|
|
35
|
-
changes the plan where you agree and lists one entry per comment in `responses`:
|
|
36
|
-
`{ "comment": "c_abc234", "status": "addressed" | "declined" | "deferred", "note": "..." }`.
|
|
37
|
-
Say why when you decline. Do not leave a comment unanswered.
|
|
38
|
-
3. `follow-me check`, repair until green, then `follow-me apply`, and ask the user to review again.
|
|
39
|
-
|
|
40
|
-
## As a reviewer
|
|
41
|
-
|
|
42
|
-
`follow-me walk` is for someone reading a branch they did not write. It plays the branch back
|
|
43
|
-
commit by commit in the browser, typing each line out while it reads your narration aloud, and the
|
|
44
|
-
reviewer comments on the code. Nothing is applied and no branch is written.
|
|
45
|
-
|
|
46
|
-
1. `follow-me walk [--branch <name>]` opens the session. If there is no plan yet and no API key, it
|
|
47
|
-
prints two commands and stops. Run the first, `follow-me prompt --walk`, and read all of it.
|
|
48
|
-
2. Write the draft it names. The order rules are the same, and every commit also gets a
|
|
49
|
-
`narration`: what the author is thinking while writing it, first person, present tense, two or
|
|
50
|
-
three sentences, no markdown. Give a unit narration as `{ "unit": "h_x:0-3", "narration": "..." }`
|
|
51
|
-
when the why of that hunk is not obvious from the commit's narration.
|
|
52
|
-
3. `follow-me check` until green, then tell the user to run `follow-me walk` again. Do not apply.
|
|
53
|
-
|
|
54
|
-
When you are the author's agent and `follow-me feedback` starts with `# Code review`, the review
|
|
55
|
-
came from a walk. It quotes each commented line as a diff excerpt with its line number on the
|
|
56
|
-
branch. Address every item in the code on the branch itself, then tell the user what you changed
|
|
57
|
-
and what you declined and why. Do not write a follow-me draft or `responses` for it; those answer
|
|
58
|
-
plan reviews only.
|
|
59
|
-
|
|
60
|
-
## Rules
|
|
61
|
-
|
|
62
|
-
- The draft is the only file you write. Never run `git rebase`, `reset` or `commit` for this.
|
|
63
|
-
- Use unit ids exactly as printed. `h_x:0-3,7` takes change lines 0 to 3 and 7 of that hunk.
|
|
64
|
-
A bare `h_x` takes whatever lines no other commit claimed.
|
|
65
|
-
- `f_` and `m_` units are whole; they never take line lists.
|
|
6
|
+
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.
|
|
7
|
+
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.
|
|
8
|
+
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.
|
|
9
|
+
3. When it prints the review page URL, tell the user and keep running `follow-me`: each call listens up to 60 seconds and prints what the user flagged or asked, with comment ids.
|
|
10
|
+
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 "..."`. Flags the user is still collecting stay hidden from you until they press Submit.
|
|
11
|
+
5. 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.
|
|
12
|
+
6. Never run `git rebase`, `reset` or `commit` for this. The draft is the only file you write.
|
|
13
|
+
## Porting a review
|
|
14
|
+
Only when the user asks to post it.
|
|
15
|
+
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.
|
|
16
|
+
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.
|
|
17
|
+
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.
|
|
18
|
+
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`.
|
|
19
|
+
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,101 +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 narratedUnit = {
|
|
13
|
-
type: "object",
|
|
14
|
-
properties: { unit: { type: "string" }, narration: { type: "string" } },
|
|
15
|
-
required: ["unit", "narration"],
|
|
16
|
-
additionalProperties: false,
|
|
17
|
-
};
|
|
18
|
-
const unitList = { type: "array", items: { anyOf: [{ type: "string" }, narratedUnit] } };
|
|
19
|
-
/**
|
|
20
|
-
* Mirrors PlanFile on the wire. Structured outputs need every property required and closed
|
|
21
|
-
* objects, so commit narration is nullable instead of optional; the plan parser reads null as none.
|
|
22
|
-
*/
|
|
23
|
-
export const PLAN_SCHEMA = {
|
|
24
|
-
type: "object",
|
|
25
|
-
properties: {
|
|
26
|
-
schema: { type: "string", enum: ["follow-me/plan@1"] },
|
|
27
|
-
base: { type: "string" },
|
|
28
|
-
tip: { type: "string" },
|
|
29
|
-
commits: {
|
|
30
|
-
type: "array",
|
|
31
|
-
items: {
|
|
32
|
-
type: "object",
|
|
33
|
-
properties: {
|
|
34
|
-
message: { type: "string" },
|
|
35
|
-
narration: { anyOf: [{ type: "string" }, { type: "null" }] },
|
|
36
|
-
units: unitList,
|
|
37
|
-
},
|
|
38
|
-
required: ["message", "narration", "units"],
|
|
39
|
-
additionalProperties: false,
|
|
40
|
-
},
|
|
41
|
-
},
|
|
42
|
-
responses: {
|
|
43
|
-
type: "array",
|
|
44
|
-
items: {
|
|
45
|
-
type: "object",
|
|
46
|
-
properties: {
|
|
47
|
-
comment: { type: "string" },
|
|
48
|
-
status: { type: "string", enum: ["addressed", "declined", "deferred"] },
|
|
49
|
-
note: { type: "string" },
|
|
50
|
-
},
|
|
51
|
-
required: ["comment", "status", "note"],
|
|
52
|
-
additionalProperties: false,
|
|
53
|
-
},
|
|
54
|
-
},
|
|
55
|
-
},
|
|
56
|
-
required: ["schema", "base", "tip", "commits", "responses"],
|
|
57
|
-
additionalProperties: false,
|
|
58
|
-
};
|
|
59
|
-
const sendWithAnthropic = async ({ model, prompt }) => {
|
|
60
|
-
const client = new Anthropic();
|
|
61
|
-
// Streaming because a 64k-token reply outlives the SDK's non-streaming timeout.
|
|
62
|
-
const stream = client.beta.messages.stream({
|
|
63
|
-
model,
|
|
64
|
-
max_tokens: MAX_TOKENS,
|
|
65
|
-
output_config: { effort: "high", format: { type: "json_schema", schema: PLAN_SCHEMA } },
|
|
66
|
-
betas: ["server-side-fallback-2026-07-01"],
|
|
67
|
-
fallbacks: "default",
|
|
68
|
-
messages: [{ role: "user", content: prompt }],
|
|
69
|
-
});
|
|
70
|
-
const message = await stream.finalMessage();
|
|
71
|
-
const text = message.content.flatMap((block) => (block.type === "text" ? [block.text] : []));
|
|
72
|
-
return {
|
|
73
|
-
stopReason: message.stop_reason,
|
|
74
|
-
text: text.join(""),
|
|
75
|
-
refusalExplanation: message.stop_details?.explanation ?? null,
|
|
76
|
-
};
|
|
77
|
-
};
|
|
78
|
-
/**
|
|
79
|
-
* Asks the model for a plan and returns the parsed JSON. The caller validates it as a plan,
|
|
80
|
-
* because a schema-conforming reply can still name units that do not exist.
|
|
81
|
-
*/
|
|
82
|
-
export async function requestPlan(prompt, opts = {}) {
|
|
83
|
-
const send = opts.send ?? sendWithAnthropic;
|
|
84
|
-
const reply = await send({ model: opts.model ?? DEFAULT_MODEL, prompt });
|
|
85
|
-
if (reply.stopReason === "refusal") {
|
|
86
|
-
const why = reply.refusalExplanation ? `: ${reply.refusalExplanation}` : "";
|
|
87
|
-
throw new PlanRequestError("refusal", `the model declined to write a plan${why}`);
|
|
88
|
-
}
|
|
89
|
-
if (reply.stopReason === "max_tokens") {
|
|
90
|
-
throw new PlanRequestError("truncated", `the plan did not fit in ${MAX_TOKENS} tokens; try a smaller diff`);
|
|
91
|
-
}
|
|
92
|
-
if (reply.text.trim() === "") {
|
|
93
|
-
throw new PlanRequestError("empty", "the model returned no text");
|
|
94
|
-
}
|
|
95
|
-
try {
|
|
96
|
-
return JSON.parse(reply.text);
|
|
97
|
-
}
|
|
98
|
-
catch {
|
|
99
|
-
throw new PlanRequestError("not-json", "the model's reply was not valid JSON");
|
|
100
|
-
}
|
|
101
|
-
}
|