@shiyuphan/follow-me 0.5.1 → 0.6.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 +6 -3
- package/dist/src/cli.js +18 -8
- package/dist/src/definition.js +226 -0
- package/dist/src/git.js +69 -13
- package/dist/src/plan.js +12 -1
- package/dist/src/prompt.js +22 -0
- package/dist/src/server.js +15 -0
- package/dist/src/types.js +2 -0
- package/dist/ui/dist/assets/{abnfDiagram-WEYOIA3C-8UsLPznO.js → abnfDiagram-WEYOIA3C-fYSYLdMl.js} +1 -1
- package/dist/ui/dist/assets/{arc-vovjGalz.js → arc-DGuGSnwa.js} +1 -1
- package/dist/ui/dist/assets/{architecture-WOLXFQ4H-BBMK-PUu.js → architecture-WOLXFQ4H-VFox_hV1.js} +1 -1
- package/dist/ui/dist/assets/{architectureDiagram-ZHVYKT53-Bw_ehaQk.js → architectureDiagram-ZHVYKT53-CBVK3nHZ.js} +1 -1
- package/dist/ui/dist/assets/{blockDiagram-RJAE7QA3-CAmD0wKf.js → blockDiagram-RJAE7QA3-BFdsIWEr.js} +1 -1
- package/dist/ui/dist/assets/{c4Diagram-XDPPPE54-DCP-3zgt.js → c4Diagram-XDPPPE54-BGntcPId.js} +1 -1
- package/dist/ui/dist/assets/channel-gz6cS9Z1.js +1 -0
- package/dist/ui/dist/assets/{chunk-2E7WHVMD-BiHSeJMu.js → chunk-2E7WHVMD-CNMuK9zj.js} +1 -1
- package/dist/ui/dist/assets/{chunk-4EMNKR2T-CR3lHKf_.js → chunk-4EMNKR2T-ByeTTJWS.js} +1 -1
- package/dist/ui/dist/assets/{chunk-55XCU76K-CtaoloTT.js → chunk-55XCU76K-99xE7cLH.js} +1 -1
- package/dist/ui/dist/assets/{chunk-7S6XSTFO-eABdc7fu.js → chunk-7S6XSTFO-ZVmT7eMZ.js} +1 -1
- package/dist/ui/dist/assets/{chunk-AQDTSKZB-CPlbJWi-.js → chunk-AQDTSKZB-B981JORL.js} +1 -1
- package/dist/ui/dist/assets/{chunk-DSNLSHJZ-YiMuxsb-.js → chunk-DSNLSHJZ-B2z7FX4h.js} +1 -1
- package/dist/ui/dist/assets/{chunk-HAGFOTED-1VcdL78a.js → chunk-HAGFOTED-CHFbBS-4.js} +1 -1
- package/dist/ui/dist/assets/{chunk-HNE4Q36P-u-sr_0Qu.js → chunk-HNE4Q36P-rq11xv9X.js} +1 -1
- package/dist/ui/dist/assets/{chunk-NQOKRVIF-C_QlWSze.js → chunk-NQOKRVIF-BYmg9kSX.js} +1 -1
- package/dist/ui/dist/assets/{chunk-UHW7PFBP-CNuZhsbU.js → chunk-UHW7PFBP-JvL9IXWg.js} +1 -1
- package/dist/ui/dist/assets/{chunk-VQ5W62QI-DAHWbLqU.js → chunk-VQ5W62QI-CYMQaAKn.js} +1 -1
- package/dist/ui/dist/assets/{chunk-WQQL4CDK-DWQnxFCd.js → chunk-WQQL4CDK-CXnR0PHT.js} +1 -1
- package/dist/ui/dist/assets/{chunk-XXDRQBXY-B0lGgSOR.js → chunk-XXDRQBXY-CDUWJCXi.js} +1 -1
- package/dist/ui/dist/assets/{chunk-YZO67HFG-BDw7efkN.js → chunk-YZO67HFG-mZfXIx0h.js} +1 -1
- package/dist/ui/dist/assets/{chunk-ZAUX2LTX-B5wfHsZv.js → chunk-ZAUX2LTX-DBxVTCHX.js} +1 -1
- package/dist/ui/dist/assets/{chunk-ZCHMQSOR--rWtYtAG.js → chunk-ZCHMQSOR-DrWA4IBo.js} +1 -1
- package/dist/ui/dist/assets/{classDiagram-v2-4MYWR3UA-BKuQ_xoS.js → classDiagram-v2-4MYWR3UA-Beah5uxA.js} +1 -1
- package/dist/ui/dist/assets/{cose-bilkent-JH36ORCC-Dk5fIpU0.js → cose-bilkent-JH36ORCC-UaE-jnHk.js} +1 -1
- package/dist/ui/dist/assets/{cynefin-EF2NZ3EQ-DIykJb2g.js → cynefin-EF2NZ3EQ-DKfGaCR8.js} +1 -1
- package/dist/ui/dist/assets/{cynefinDiagram-WF24UDRP-Cg8tBqUb.js → cynefinDiagram-WF24UDRP-tTUzTs3D.js} +1 -1
- package/dist/ui/dist/assets/{dagre-NK4GIZVX-BC_tnnjz.js → dagre-NK4GIZVX-jSn35_T7.js} +1 -1
- package/dist/ui/dist/assets/{diagram-CBK3MWTC-B_MQGixU.js → diagram-CBK3MWTC-7W2uw8Zc.js} +1 -1
- package/dist/ui/dist/assets/{diagram-I5XIIV2D-DO681pZ5.js → diagram-I5XIIV2D-CcfyQVh9.js} +1 -1
- package/dist/ui/dist/assets/{diagram-ODIL2UCW-D14jLzU6.js → diagram-ODIL2UCW-BjGvaLis.js} +1 -1
- package/dist/ui/dist/assets/{diagram-PFBEJUXE-cML48gUS.js → diagram-PFBEJUXE-jvdCrt8B.js} +1 -1
- package/dist/ui/dist/assets/{diagram-RJA3I4VV-DSoq8jFk.js → diagram-RJA3I4VV-D65_Vpi_.js} +1 -1
- package/dist/ui/dist/assets/{diagram-TYUNFSNR-CaJ4pvnl.js → diagram-TYUNFSNR-C52roQBE.js} +1 -1
- package/dist/ui/dist/assets/{dist-D3j-DODP.js → dist-BJd4jp9Y.js} +1 -1
- package/dist/ui/dist/assets/{ebnfDiagram-V4P3TTPT-CyRpjSix.js → ebnfDiagram-V4P3TTPT-DXysoIBq.js} +1 -1
- package/dist/ui/dist/assets/{elk-QONTBCTQ-ICJdzVFL.js → elk-QONTBCTQ-BVZcsgV3.js} +1 -1
- package/dist/ui/dist/assets/{erDiagram-KYQIGQAA-B57hNTfw.js → erDiagram-KYQIGQAA-BJnT4fVU.js} +1 -1
- package/dist/ui/dist/assets/{eventmodeling-K75KTNOO-BG8tUVIQ.js → eventmodeling-K75KTNOO-C5MzxCMY.js} +1 -1
- package/dist/ui/dist/assets/flowDiagram-IUGLLWBU-BqUgZSCz.js +1 -0
- package/dist/ui/dist/assets/{ganttDiagram-R7GPNTJY-Czt6Klik.js → ganttDiagram-R7GPNTJY-CZRrnuah.js} +1 -1
- package/dist/ui/dist/assets/{gitGraph-VSP46ZUC-De_ZWJLj.js → gitGraph-VSP46ZUC-l58aGSkh.js} +1 -1
- package/dist/ui/dist/assets/{gitGraphDiagram-PDYL7XHY-BMCJoOnT.js → gitGraphDiagram-PDYL7XHY-D4ks1WO-.js} +1 -1
- package/dist/ui/dist/assets/index-BW95UA5q.css +1 -0
- package/dist/ui/dist/assets/index-DOGXgoHF.js +134 -0
- package/dist/ui/dist/assets/{info-OHQRW6UA-D75H9kUq.js → info-OHQRW6UA-BSqfzkdk.js} +1 -1
- package/dist/ui/dist/assets/{infoDiagram-MVDRKAX5-Bsm_I9dt.js → infoDiagram-MVDRKAX5-B6NgVF_N.js} +1 -1
- package/dist/ui/dist/assets/{ishikawaDiagram-HPEHV2GE-98xZAJ9y.js → ishikawaDiagram-HPEHV2GE-DvtfssEZ.js} +1 -1
- package/dist/ui/dist/assets/{journeyDiagram-BOSHW7O7-BWVGuc0P.js → journeyDiagram-BOSHW7O7-DM64gT5v.js} +1 -1
- package/dist/ui/dist/assets/{kanban-definition-FNSA4HC5-DgUE_KDd.js → kanban-definition-FNSA4HC5-AliyRJHy.js} +1 -1
- package/dist/ui/dist/assets/{line-i__8Rvas.js → line-CHncXbKO.js} +1 -1
- package/dist/ui/dist/assets/{linear-BXKj0Fip.js → linear-BWEWydI0.js} +1 -1
- package/dist/ui/dist/assets/{mermaid-parser.core-DxvaorTa.js → mermaid-parser.core-DGpOP8AM.js} +3 -3
- package/dist/ui/dist/assets/{mermaid.core-ujYTsRZz.js → mermaid.core-BJtBFbJx.js} +4 -4
- package/dist/ui/dist/assets/{mindmap-definition-6OY57UG3-B7QD6sjk.js → mindmap-definition-6OY57UG3-NjavnWBi.js} +1 -1
- package/dist/ui/dist/assets/{packet-JDAUHWVQ-B6aIwfD1.js → packet-JDAUHWVQ-zuhpVsME.js} +1 -1
- package/dist/ui/dist/assets/{pegDiagram-ECIUJNBX-_RStHj-a.js → pegDiagram-ECIUJNBX-sL4grM97.js} +1 -1
- package/dist/ui/dist/assets/{pie-XZMESJXO-CNLC-pTe.js → pie-XZMESJXO-C9l1Mp09.js} +1 -1
- package/dist/ui/dist/assets/{pieDiagram-GNMPY74T-0uT7MT-u.js → pieDiagram-GNMPY74T-rlRBr2U4.js} +1 -1
- package/dist/ui/dist/assets/{prism-core-DtxziK1i.js → prism-core-DmuST30i.js} +1 -1
- package/dist/ui/dist/assets/{quadrantDiagram-D57K4MC3-CTMKb5hl.js → quadrantDiagram-D57K4MC3-4oQMip2Q.js} +1 -1
- package/dist/ui/dist/assets/{radar-ABXABTNO-D53h6I0N.js → radar-ABXABTNO-DvGg96L7.js} +1 -1
- package/dist/ui/dist/assets/{railroad-I3PHUGI6-CwjhW4mc.js → railroad-I3PHUGI6-8wdFKMZp.js} +1 -1
- package/dist/ui/dist/assets/{railroad-abnf-LNEFI6M7-DDbQ3RmB.js → railroad-abnf-LNEFI6M7-DUtOO0gn.js} +1 -1
- package/dist/ui/dist/assets/{railroad-ebnf-DWYD2UWJ-Am7YJAqO.js → railroad-ebnf-DWYD2UWJ-5YCGY1_d.js} +1 -1
- package/dist/ui/dist/assets/{railroad-peg-JR7BVNR7-Ci32KoVW.js → railroad-peg-JR7BVNR7-Bwywxjhs.js} +1 -1
- package/dist/ui/dist/assets/{railroadDiagram-Y72NRBL7-BPU5A6XW.js → railroadDiagram-Y72NRBL7-CwO6HSNQ.js} +1 -1
- package/dist/ui/dist/assets/{requirementDiagram-EQYSW5RA-_0Pq4QdV.js → requirementDiagram-EQYSW5RA-Cp0IbfNr.js} +1 -1
- package/dist/ui/dist/assets/{sankeyDiagram-SY24JHO5-c9h-7uf8.js → sankeyDiagram-SY24JHO5-DeQKsO68.js} +1 -1
- package/dist/ui/dist/assets/{sequenceDiagram-BLEHNHQU-B9-S4Rc9.js → sequenceDiagram-BLEHNHQU-DTDalCxF.js} +1 -1
- package/dist/ui/dist/assets/{src-BZ3kG7lO.js → src-XnrqevnL.js} +1 -1
- package/dist/ui/dist/assets/{stateDiagram-v2-A6IESKWZ-py1smSen.js → stateDiagram-v2-A6IESKWZ-2DZ-tKo9.js} +1 -1
- package/dist/ui/dist/assets/{swimlanes-B3ENRKFQ-C1E90aH4.js → swimlanes-B3ENRKFQ-66yZJeRU.js} +1 -1
- package/dist/ui/dist/assets/swimlanesDiagram-AYQRV4AK-rbX6bYG4.js +8 -0
- package/dist/ui/dist/assets/{timeline-definition-JXYKGLGB-DNe1To6B.js → timeline-definition-JXYKGLGB-Dm1xGU-d.js} +1 -1
- package/dist/ui/dist/assets/{treeView-D4JQ5SDB-D6MG89i6.js → treeView-D4JQ5SDB-BvgsbSot.js} +1 -1
- package/dist/ui/dist/assets/{treemap-SAJKECNS-CZpt7Mlh.js → treemap-SAJKECNS-C3lnbG1a.js} +1 -1
- package/dist/ui/dist/assets/{usecaseDiagram-WPOLBNNX-CKsv-03M.js → usecaseDiagram-WPOLBNNX-B6iPNIhZ.js} +1 -1
- package/dist/ui/dist/assets/{vennDiagram-Q3Y3IDCW-DGJ1qlyJ.js → vennDiagram-Q3Y3IDCW-D5dgflg9.js} +1 -1
- package/dist/ui/dist/assets/{wardley-7MLQ67FV-ClOzBemC.js → wardley-7MLQ67FV-Dn9mLIB4.js} +1 -1
- package/dist/ui/dist/assets/{wardleyDiagram-IVAIDKWA-C68ahPyF.js → wardleyDiagram-IVAIDKWA-W1EwzN_X.js} +1 -1
- package/dist/ui/dist/assets/{xychartDiagram-4URNHDU4-DWDamCoH.js → xychartDiagram-4URNHDU4-CKRzMUvp.js} +1 -1
- package/dist/ui/dist/index.html +2 -2
- package/package.json +1 -1
- package/skills/follow-me/SKILL.md +3 -1
- package/dist/ui/dist/assets/channel-B7CT_oIp.js +0 -1
- package/dist/ui/dist/assets/flowDiagram-IUGLLWBU--HPNoVQJ.js +0 -1
- package/dist/ui/dist/assets/index-B8TYcxF8.css +0 -1
- package/dist/ui/dist/assets/index-CYKbk4QS.js +0 -134
- package/dist/ui/dist/assets/swimlanesDiagram-AYQRV4AK-I98Q8AqP.js +0 -8
package/README.md
CHANGED
|
@@ -69,8 +69,11 @@ If the hashes differ, `apply` exits with code 1. The commits are signed when you
|
|
|
69
69
|
|
|
70
70
|
The red blob in the party hat is your branch tip. Nothing follow-me does moves it.
|
|
71
71
|
|
|
72
|
-

|
|
73
|
+
*The review page. The index groups the commits the way they read: the story first, then the steps it calls, then the details. Pick any line, changed or not, and Ask or Flag right under it; the agent answers inline.*
|
|
74
|
+
|
|
75
|
+

|
|
76
|
+
*Cmd-click a name to jump to where it is defined, even in a later commit, and Alt+Left to come back. A definition the branch does not change opens as a peek.*
|
|
74
77
|
|
|
75
78
|

|
|
76
79
|
*Diagrams sit full width under the commit or reply they explain, and open full size on click.*
|
|
@@ -78,7 +81,7 @@ The red blob in the party hat is your branch tip. Nothing follow-me does moves i
|
|
|
78
81
|

|
|
79
82
|
*Submit review. Each item carries the original branch's path and line, so your agent can post it to the merge request as is.*
|
|
80
83
|
|
|
81
|
-
Keys: `j` and `k` move between commits, arrows and Shift+arrows pick rows inside a hunk, `c` comments on the pick, `v` marks a commit viewed, `?` lists the rest. An unfinished review autosaves and comes back when the page is opened again.
|
|
84
|
+
Keys: `[` shows or hides the index, `j` and `k` move between commits, Cmd-click (Ctrl-click) goes to a definition and Alt+Left comes back, arrows and Shift+arrows pick rows inside a hunk, `c` comments on the pick, `v` marks a commit viewed, `?` lists the rest. An unfinished review autosaves and comes back when the page is opened again.
|
|
82
85
|
|
|
83
86
|
### Reviewing someone else's branch
|
|
84
87
|
|
package/dist/src/cli.js
CHANGED
|
@@ -3,13 +3,14 @@ import { readFileSync, writeFileSync } from "node:fs";
|
|
|
3
3
|
import { homedir } from "node:os";
|
|
4
4
|
import { dirname, join, relative } from "node:path";
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
|
6
|
+
import { definitionIndex, isIdentifier } from "./definition.js";
|
|
6
7
|
import { DiffParseError, formatUnitRef, isUnitId } from "./diff.js";
|
|
7
8
|
import { BranchRefusedError, branchHead, GitError, isOwnWork, signing, treeOf, writeBranch, } from "./git.js";
|
|
8
9
|
import { fetchCarried, fetchComments, fetchReport, findServer, followEvents, idleMs, listSessions, openBrowser, pollEvents, portFallbackMessage, postComment, postReply, probeHealth, readServerRecord, registerWith, sessionUrl, spawnDetached, startOrAttach, stateDir, stillHosted, stopServer, swapVersion, } from "./host.js";
|
|
9
10
|
import { findSkillDir, InstallError, runInstall, runUninstall, syncSkills, } from "./install.js";
|
|
10
11
|
import { naiveDraft } from "./naive.js";
|
|
11
12
|
import { isCommentId, isConcernId, isFields, serializePlanFile, validatePlan, } from "./plan.js";
|
|
12
|
-
import { renderEvents, renderPrompt, renderReport, renderShownComment, renderShownConcern, renderShownUnit, reportItems, } from "./prompt.js";
|
|
13
|
+
import { renderEvents, renderPrompt, renderReport, renderShownComment, renderShownConcern, renderShownDefinitions, renderShownUnit, reportItems, } from "./prompt.js";
|
|
13
14
|
import { RegistrationError, sessionIdOf } from "./server.js";
|
|
14
15
|
import { BranchMovedError, cleanSessions, extractSession, MissingDraftError, NotARepositoryError, openSession, SessionFileError, SessionLockedError, sessionComments, sinceLastPlan, } from "./session.js";
|
|
15
16
|
import { checkSyntax } from "./syntax.js";
|
|
@@ -30,9 +31,10 @@ Other commands:
|
|
|
30
31
|
Mermaid source
|
|
31
32
|
explain [--commit <n>] [--unit <id>] [--lines <a-b>] --body <text> [--diagram <file>]
|
|
32
33
|
start a thread on the page without being asked
|
|
33
|
-
show <h_…|c_…|k_…|path:line[@n|@base]>
|
|
34
|
-
|
|
35
|
-
|
|
34
|
+
show <h_…|c_…|k_…|path:line[@n|@base]|name>
|
|
35
|
+
print a unit, a comment's thread, a concern, the
|
|
36
|
+
lines around path:line in commit n, or where a name
|
|
37
|
+
is defined, instead of reading files
|
|
36
38
|
report [--json] [--markdown] print the report of the latest review; --json lists
|
|
37
39
|
the items to post to a merge request
|
|
38
40
|
|
|
@@ -85,11 +87,13 @@ Interactive loop (while the review page is open):
|
|
|
85
87
|
--lines are change lines of the unit, commits count
|
|
86
88
|
from 1; with no target it is about the whole plan.
|
|
87
89
|
watch leaves these out like your replies
|
|
88
|
-
show <h_…|c_…|k_…|path:line[@n|@base]> [--branch <name>]
|
|
90
|
+
show <h_…|c_…|k_…|path:line[@n|@base]|name> [--branch <name>]
|
|
89
91
|
print a unit with the commits that take it, a
|
|
90
92
|
comment with its rows and thread, a concern with its
|
|
91
|
-
lines,
|
|
92
|
-
commit n of the plan, or in the base
|
|
93
|
+
lines, the lines around path:line at the tip, in
|
|
94
|
+
commit n of the plan, or in the base, or each place
|
|
95
|
+
a name is defined at the tip with the commit it
|
|
96
|
+
lands in
|
|
93
97
|
report [--json] [--markdown] [--branch <name>]
|
|
94
98
|
print the report of the open review, or of the last
|
|
95
99
|
one that ended. Markdown lists issues by file and
|
|
@@ -1516,7 +1520,7 @@ async function explain(cwd, args) {
|
|
|
1516
1520
|
next(`run follow-me again; ${STILL_WORKING}`);
|
|
1517
1521
|
return 0;
|
|
1518
1522
|
}
|
|
1519
|
-
const SHOW_USAGE = "usage: follow-me show <h_…|c_…|k_…|path:line[@<commit>|@base]>";
|
|
1523
|
+
const SHOW_USAGE = "usage: follow-me show <h_…|c_…|k_…|path:line[@<commit>|@base]|name>";
|
|
1520
1524
|
/** Every comment the session has, with the plan version it was made on: live ones first. */
|
|
1521
1525
|
async function knownComments(session) {
|
|
1522
1526
|
const open = await openOnServer(session, null);
|
|
@@ -1629,6 +1633,12 @@ async function showRef(cwd, args) {
|
|
|
1629
1633
|
}
|
|
1630
1634
|
throw new UsageError(`no concern ${ref} in a plan that fits the branch`);
|
|
1631
1635
|
}
|
|
1636
|
+
if (isIdentifier(ref)) {
|
|
1637
|
+
const commits = latest === undefined ? [] : projectedCommits(session, latest.version);
|
|
1638
|
+
const index = definitionIndex(session.root, session.diff.tip, units, commits);
|
|
1639
|
+
process.stdout.write(renderShownDefinitions(ref, await index.lookup(ref, null)));
|
|
1640
|
+
return 0;
|
|
1641
|
+
}
|
|
1632
1642
|
process.stdout.write(showLines(session, ref));
|
|
1633
1643
|
return 0;
|
|
1634
1644
|
}
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
import { posix } from "node:path";
|
|
2
|
+
import { splitLines, tipStartOf } from "./diff.js";
|
|
3
|
+
import { blobsAt, grepTip } from "./git.js";
|
|
4
|
+
export const MAX_CANDIDATES = 10;
|
|
5
|
+
const PREVIEW_LINES = 8;
|
|
6
|
+
/** An ASCII identifier as JS, Python, Go, Rust, JVM languages, Ruby and C spell one. */
|
|
7
|
+
export function isIdentifier(s) {
|
|
8
|
+
return /^[A-Za-z_$][A-Za-z0-9_$]{0,127}$/.test(s);
|
|
9
|
+
}
|
|
10
|
+
const JS_MODS = "((static|async|public|private|protected|readonly|override|get|set)\\s+)*";
|
|
11
|
+
const RUST_VIS = "(pub([(][^)]*[)])?\\s+)?";
|
|
12
|
+
const JAVA_MODS = "((public|private|protected|static|final|abstract|synchronized|native|default|strictfp)\\s+)*";
|
|
13
|
+
const JVM_CLASS_MODS = "((public|private|protected|static|final|abstract|sealed|data|open|internal|inner|enum|annotation)\\s+)*";
|
|
14
|
+
const KOTLIN_MODS = "((public|private|protected|internal|override|open|abstract|suspend|inline|operator|infix|tailrec|external|actual|expect)\\s+)*";
|
|
15
|
+
const STATEMENT = /^\s*(return|new|throw|else|case|yield|await|typedef|delete|goto)\b/;
|
|
16
|
+
const LANGUAGES = [
|
|
17
|
+
{
|
|
18
|
+
extensions: [".js", ".jsx", ".mjs", ".cjs", ".ts", ".tsx", ".mts", ".cts"],
|
|
19
|
+
patterns: [
|
|
20
|
+
{
|
|
21
|
+
kind: "function",
|
|
22
|
+
source: "^\\s*(export\\s+)?(default\\s+)?(async\\s+)?function\\s*[*]?\\s*NAME\\s*[(<]",
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
kind: "const",
|
|
26
|
+
source: "^\\s*(export\\s+)?(declare\\s+)?(const|let|var)\\s+NAME\\s*(:[^=]*)?=",
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
kind: "class",
|
|
30
|
+
source: "^\\s*(export\\s+)?(default\\s+)?(declare\\s+)?(abstract\\s+)?class\\s+NAMEEND",
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
kind: "type",
|
|
34
|
+
source: "^\\s*(export\\s+)?(declare\\s+)?(interface|type|(const\\s+)?enum)\\s+NAMEEND",
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
kind: "method",
|
|
38
|
+
source: `^\\s+${JS_MODS}[*]?NAME\\s*(<[^>]*>)?[(].*[)]\\s*(:[^={;]+)?[{]\\s*$`,
|
|
39
|
+
reject: /^\s*(if|for|while|switch|catch|return)\b/,
|
|
40
|
+
},
|
|
41
|
+
],
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
extensions: [".py", ".pyi"],
|
|
45
|
+
patterns: [
|
|
46
|
+
{ kind: "function", source: "^(async\\s+)?def\\s+NAME\\s*[(]" },
|
|
47
|
+
{ kind: "method", source: "^\\s+(async\\s+)?def\\s+NAME\\s*[(]" },
|
|
48
|
+
{ kind: "class", source: "^\\s*class\\s+NAME\\s*[(:]" },
|
|
49
|
+
],
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
extensions: [".go"],
|
|
53
|
+
patterns: [
|
|
54
|
+
{ kind: "function", source: "^func\\s+NAME\\s*[[(]" },
|
|
55
|
+
{ kind: "method", source: "^func\\s*[(][^)]*[)]\\s*NAME\\s*[[(]" },
|
|
56
|
+
{ kind: "type", source: "^\\s*type\\s+NAMEEND" },
|
|
57
|
+
],
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
extensions: [".rs"],
|
|
61
|
+
patterns: [
|
|
62
|
+
{
|
|
63
|
+
kind: "function",
|
|
64
|
+
source: `^${RUST_VIS}((const|async|unsafe|extern)\\s+("[^"]*"\\s+)?)*fn\\s+NAME\\s*[<(]`,
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
kind: "method",
|
|
68
|
+
source: `^\\s+${RUST_VIS}((const|async|unsafe|extern)\\s+("[^"]*"\\s+)?)*fn\\s+NAME\\s*[<(]`,
|
|
69
|
+
},
|
|
70
|
+
{ kind: "class", source: `^\\s*${RUST_VIS}struct\\s+NAMEEND` },
|
|
71
|
+
{ kind: "type", source: `^\\s*${RUST_VIS}(enum|trait|type)\\s+NAMEEND` },
|
|
72
|
+
{ kind: "other", source: "^\\s*impl(<[^>]*>)?\\s+([A-Za-z0-9_:<>, ]+\\s+for\\s+)?NAMEEND" },
|
|
73
|
+
],
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
extensions: [".java", ".kt", ".kts"],
|
|
77
|
+
patterns: [
|
|
78
|
+
{ kind: "class", source: `^\\s*${JVM_CLASS_MODS}(class|object|record)\\s+NAMEEND` },
|
|
79
|
+
{ kind: "type", source: `^\\s*${JVM_CLASS_MODS}(interface|enum|@interface)\\s+NAMEEND` },
|
|
80
|
+
{
|
|
81
|
+
kind: "function",
|
|
82
|
+
source: `^\\s*${KOTLIN_MODS}fun\\s+(<[^>]*>\\s*)?([A-Za-z0-9_.<>]+[.])?NAME\\s*[(]`,
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
kind: "method",
|
|
86
|
+
source: `^\\s*${JAVA_MODS}(<[^>]*>\\s+)?[A-Za-z_][A-Za-z0-9_<>,.?]*(\\[\\])*\\s+NAME\\s*[(][^;]*$`,
|
|
87
|
+
reject: STATEMENT,
|
|
88
|
+
},
|
|
89
|
+
],
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
extensions: [".rb"],
|
|
93
|
+
patterns: [
|
|
94
|
+
{ kind: "method", source: "^\\s*def\\s+(self[.])?NAMEEND" },
|
|
95
|
+
{ kind: "class", source: "^\\s*class\\s+NAMEEND" },
|
|
96
|
+
{ kind: "other", source: "^\\s*module\\s+NAMEEND" },
|
|
97
|
+
],
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
extensions: [".c", ".h", ".cc", ".cpp", ".cxx", ".hh", ".hpp", ".hxx"],
|
|
101
|
+
patterns: [
|
|
102
|
+
{
|
|
103
|
+
kind: "function",
|
|
104
|
+
source: "^[A-Za-z_][A-Za-z0-9_ *&<>,:~]*[ *&]([A-Za-z_][A-Za-z0-9_]*::)*NAME\\s*[(][^;]*$",
|
|
105
|
+
reject: STATEMENT,
|
|
106
|
+
},
|
|
107
|
+
{ kind: "function", source: "^NAME\\s*[(][^;]*$" },
|
|
108
|
+
{ kind: "class", source: "^\\s*(typedef\\s+)?(struct|class|union)\\s+NAME\\s*([{:]|$)" },
|
|
109
|
+
{ kind: "type", source: "^\\s*(typedef\\s+)?enum\\s+(class\\s+)?NAME\\s*([{:]|$)" },
|
|
110
|
+
],
|
|
111
|
+
},
|
|
112
|
+
];
|
|
113
|
+
const END = "([^A-Za-z0-9_$]|$)";
|
|
114
|
+
function spell(source, name) {
|
|
115
|
+
return source
|
|
116
|
+
.replaceAll("NAMEEND", `${name.replaceAll("$", "[$]")}${END}`)
|
|
117
|
+
.replaceAll("NAME", name.replaceAll("$", "[$]"));
|
|
118
|
+
}
|
|
119
|
+
function toEre(source) {
|
|
120
|
+
return source.replaceAll("\\s", "[[:space:]]");
|
|
121
|
+
}
|
|
122
|
+
function languageOf(path) {
|
|
123
|
+
const ext = posix.extname(path).toLowerCase();
|
|
124
|
+
return LANGUAGES.find((l) => l.extensions.includes(ext)) ?? null;
|
|
125
|
+
}
|
|
126
|
+
/** What the match defines by the patterns of its file's language, or null when it is no definition. */
|
|
127
|
+
function kindOf(match, name) {
|
|
128
|
+
const language = languageOf(match.path);
|
|
129
|
+
if (language === null)
|
|
130
|
+
return null;
|
|
131
|
+
const hit = language.patterns.find((p) => new RegExp(spell(p.source, name)).test(match.text) && !p.reject?.test(match.text));
|
|
132
|
+
return hit?.kind ?? null;
|
|
133
|
+
}
|
|
134
|
+
/** Same file as `from` first, then the same directory, then the rest, each in path and line order. */
|
|
135
|
+
export function rank(found, from) {
|
|
136
|
+
const tier = (path) => {
|
|
137
|
+
if (from === null)
|
|
138
|
+
return 2;
|
|
139
|
+
if (path === from)
|
|
140
|
+
return 0;
|
|
141
|
+
return posix.dirname(path) === posix.dirname(from) ? 1 : 2;
|
|
142
|
+
};
|
|
143
|
+
return found
|
|
144
|
+
.map((f, i) => ({ f, i, t: tier(f.path) }))
|
|
145
|
+
.sort((a, b) => a.t - b.t || a.i - b.i)
|
|
146
|
+
.map(({ f }) => f);
|
|
147
|
+
}
|
|
148
|
+
const NOWHERE = { unit: null, row: null, commit: null };
|
|
149
|
+
/**
|
|
150
|
+
* The hunk row that holds tip line `line` of `path`, and the commit that writes it: the owner of a
|
|
151
|
+
* change row, or the first commit taking the unit for a context row.
|
|
152
|
+
*/
|
|
153
|
+
export function place(units, commits, path, line) {
|
|
154
|
+
for (const unit of units.values()) {
|
|
155
|
+
if (unit.kind !== "hunk" || unit.path !== path)
|
|
156
|
+
continue;
|
|
157
|
+
const row = rowAt(unit, tipStartOf(unit, units), line);
|
|
158
|
+
if (row === null)
|
|
159
|
+
continue;
|
|
160
|
+
const body = unit.body[row];
|
|
161
|
+
const change = body !== undefined && body.op !== " " ? body.change : null;
|
|
162
|
+
const takes = (c) => c.refs.some((r) => r.unit === unit.id &&
|
|
163
|
+
(change === null || r.select.kind === "rest" || r.select.changes.includes(change)));
|
|
164
|
+
const k = commits.findIndex(takes);
|
|
165
|
+
return { unit: unit.id, row, commit: k === -1 ? null : k };
|
|
166
|
+
}
|
|
167
|
+
return NOWHERE;
|
|
168
|
+
}
|
|
169
|
+
function rowAt(hunk, tipStart, line) {
|
|
170
|
+
let tipLine = tipStart;
|
|
171
|
+
for (const [row, body] of hunk.body.entries()) {
|
|
172
|
+
if (body.op === "-")
|
|
173
|
+
continue;
|
|
174
|
+
if (tipLine === line)
|
|
175
|
+
return row;
|
|
176
|
+
tipLine += 1;
|
|
177
|
+
}
|
|
178
|
+
return null;
|
|
179
|
+
}
|
|
180
|
+
function stripEol(line) {
|
|
181
|
+
return line.replace(/\r?\n$/, "");
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Where names are defined at one branch tip and where each lands in the plan. Built per hosted
|
|
185
|
+
* version, so a swap starts a fresh one; matches are cached per name and file lines per path.
|
|
186
|
+
*/
|
|
187
|
+
export function definitionIndex(repo, tip, units, commits) {
|
|
188
|
+
const foundByName = new Map();
|
|
189
|
+
const linesByPath = new Map();
|
|
190
|
+
const find = (name) => {
|
|
191
|
+
const cached = foundByName.get(name);
|
|
192
|
+
if (cached !== undefined)
|
|
193
|
+
return cached;
|
|
194
|
+
const patterns = [
|
|
195
|
+
...new Set(LANGUAGES.flatMap((l) => l.patterns.map((p) => toEre(spell(p.source, name))))),
|
|
196
|
+
];
|
|
197
|
+
const pathspecs = LANGUAGES.flatMap((l) => l.extensions.map((e) => `*${e}`));
|
|
198
|
+
const found = grepTip(repo, tip, patterns, pathspecs).then((matches) => matches.flatMap((m) => {
|
|
199
|
+
const kind = kindOf(m, name);
|
|
200
|
+
return kind === null ? [] : [{ path: m.path, newLine: m.line, kind }];
|
|
201
|
+
}));
|
|
202
|
+
foundByName.set(name, found);
|
|
203
|
+
// A failed grep is not an answer; the next request tries again.
|
|
204
|
+
found.catch(() => foundByName.delete(name));
|
|
205
|
+
return found;
|
|
206
|
+
};
|
|
207
|
+
const load = async (paths) => {
|
|
208
|
+
const missing = [...new Set(paths)].filter((p) => !linesByPath.has(p));
|
|
209
|
+
if (missing.length === 0)
|
|
210
|
+
return;
|
|
211
|
+
for (const [path, bytes] of await blobsAt(repo, tip, missing)) {
|
|
212
|
+
linesByPath.set(path, splitLines(bytes).map(stripEol));
|
|
213
|
+
}
|
|
214
|
+
};
|
|
215
|
+
return {
|
|
216
|
+
lookup: async (name, from) => {
|
|
217
|
+
const ranked = rank(await find(name), from).slice(0, MAX_CANDIDATES);
|
|
218
|
+
await load(ranked.map((f) => f.path));
|
|
219
|
+
return ranked.map((f) => ({
|
|
220
|
+
...f,
|
|
221
|
+
...place(units, commits, f.path, f.newLine),
|
|
222
|
+
preview: (linesByPath.get(f.path) ?? []).slice(f.newLine - 1, f.newLine - 1 + PREVIEW_LINES),
|
|
223
|
+
}));
|
|
224
|
+
},
|
|
225
|
+
};
|
|
226
|
+
}
|
package/dist/src/git.js
CHANGED
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
import { execFile } from "node:child_process";
|
|
2
2
|
import { link, mkdir, mkdtemp, readdir, rm } from "node:fs/promises";
|
|
3
3
|
import { join } from "node:path";
|
|
4
|
-
import { isSha, neededBlobs, parseDiff } from "./diff.js";
|
|
4
|
+
import { isRepoPath, isSha, neededBlobs, parseDiff } from "./diff.js";
|
|
5
5
|
export class GitError extends Error {
|
|
6
6
|
name = "GitError";
|
|
7
7
|
args;
|
|
8
|
-
|
|
8
|
+
/** git's exit status, or null when it did not run or was killed. */
|
|
9
|
+
status;
|
|
10
|
+
constructor(args, stderr, status = null) {
|
|
9
11
|
super(`git ${args.join(" ")}: ${stderr}`);
|
|
10
12
|
this.args = args;
|
|
13
|
+
this.status = status;
|
|
11
14
|
}
|
|
12
15
|
}
|
|
13
16
|
function git(repo, args, opts = {}) {
|
|
@@ -18,8 +21,10 @@ function git(repo, args, opts = {}) {
|
|
|
18
21
|
maxBuffer: 1 << 30,
|
|
19
22
|
env: { ...process.env, ...opts.env },
|
|
20
23
|
}, (error, stdout, stderr) => {
|
|
21
|
-
if (error)
|
|
22
|
-
|
|
24
|
+
if (error) {
|
|
25
|
+
const status = typeof error.code === "number" ? error.code : null;
|
|
26
|
+
reject(new GitError(args, stderr.toString("utf8").trim() || error.message, status));
|
|
27
|
+
}
|
|
23
28
|
else
|
|
24
29
|
resolve(stdout);
|
|
25
30
|
});
|
|
@@ -97,25 +102,76 @@ export async function defaultBaseRef(repo) {
|
|
|
97
102
|
}
|
|
98
103
|
throw new Error("neither main nor master exists; pass --base <ref>");
|
|
99
104
|
}
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
if (
|
|
103
|
-
return
|
|
104
|
-
const
|
|
105
|
+
/** The blob named by each object spec, in order, as latin1; throws when one is not a blob. */
|
|
106
|
+
async function catBatch(repo, specs) {
|
|
107
|
+
if (specs.length === 0)
|
|
108
|
+
return [];
|
|
109
|
+
const input = Buffer.from(`${specs.join("\n")}\n`, "latin1");
|
|
110
|
+
const out = await git(repo, ["cat-file", "--batch"], { input });
|
|
111
|
+
const blobs = [];
|
|
105
112
|
let at = 0;
|
|
106
|
-
for (const
|
|
113
|
+
for (const spec of specs) {
|
|
107
114
|
const nl = out.indexOf(0x0a, at);
|
|
108
115
|
const header = out.subarray(at, nl).toString("latin1");
|
|
109
116
|
const m = /^([0-9a-f]+) blob (\d+)$/.exec(header);
|
|
110
|
-
if (nl === -1 || m === null || m[1] !==
|
|
111
|
-
throw new Error(`cat-file returned "${header}" for ${
|
|
117
|
+
if (nl === -1 || m === null || (isSha(spec) && m[1] !== spec)) {
|
|
118
|
+
throw new Error(`cat-file returned "${header}" for ${spec}`);
|
|
112
119
|
}
|
|
113
120
|
const size = Number(m[2]);
|
|
114
|
-
blobs.
|
|
121
|
+
blobs.push(out.subarray(nl + 1, nl + 1 + size).toString("latin1"));
|
|
115
122
|
at = nl + 1 + size + 1;
|
|
116
123
|
}
|
|
117
124
|
return blobs;
|
|
118
125
|
}
|
|
126
|
+
async function catBlobs(repo, shas) {
|
|
127
|
+
const blobs = await catBatch(repo, shas);
|
|
128
|
+
return new Map(shas.map((sha, i) => [sha, blobs[i] ?? ""]));
|
|
129
|
+
}
|
|
130
|
+
/** Each path's bytes at the commit, as latin1. */
|
|
131
|
+
export async function blobsAt(repo, commit, paths) {
|
|
132
|
+
const blobs = await catBatch(repo, paths.map((p) => `${commit}:${p}`));
|
|
133
|
+
return new Map(paths.map((p, i) => [p, blobs[i] ?? ""]));
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Lines of the commit's tree, not the working tree, that match any of the POSIX extended
|
|
137
|
+
* patterns, in files matching the pathspecs. Binary files are skipped.
|
|
138
|
+
*/
|
|
139
|
+
export async function grepTip(repo, commit, patterns, pathspecs) {
|
|
140
|
+
const args = [
|
|
141
|
+
"grep",
|
|
142
|
+
"-n",
|
|
143
|
+
"-E",
|
|
144
|
+
"-I",
|
|
145
|
+
"-z",
|
|
146
|
+
"--no-color",
|
|
147
|
+
"--no-column",
|
|
148
|
+
...patterns.flatMap((p) => ["-e", p]),
|
|
149
|
+
commit,
|
|
150
|
+
"--",
|
|
151
|
+
...pathspecs,
|
|
152
|
+
];
|
|
153
|
+
let out;
|
|
154
|
+
try {
|
|
155
|
+
out = await git(repo, args);
|
|
156
|
+
}
|
|
157
|
+
catch (error) {
|
|
158
|
+
// grep exits 1 when nothing matched.
|
|
159
|
+
if (error instanceof GitError && error.status === 1)
|
|
160
|
+
return [];
|
|
161
|
+
throw error;
|
|
162
|
+
}
|
|
163
|
+
const prefix = `${commit}:`;
|
|
164
|
+
return out
|
|
165
|
+
.toString("latin1")
|
|
166
|
+
.split("\n")
|
|
167
|
+
.flatMap((record) => {
|
|
168
|
+
const [name = "", line = "", ...text] = record.split("\0");
|
|
169
|
+
const path = name.slice(prefix.length);
|
|
170
|
+
if (!name.startsWith(prefix) || !isRepoPath(path) || !/^\d+$/.test(line))
|
|
171
|
+
return [];
|
|
172
|
+
return [{ path, line: Number(line), text: text.join("\0") }];
|
|
173
|
+
});
|
|
174
|
+
}
|
|
119
175
|
const DIFF_FLAGS = ["--no-renames", "--full-index", "--no-color", "--no-ext-diff", "--no-textconv"];
|
|
120
176
|
export async function loadDiff(repo, baseRef, tipRef) {
|
|
121
177
|
const [tip, baseCommit] = await Promise.all([
|
package/dist/src/plan.js
CHANGED
|
@@ -17,6 +17,10 @@ const STATUSES = ["addressed", "declined", "deferred", "answered"];
|
|
|
17
17
|
function isResponseStatus(s) {
|
|
18
18
|
return STATUSES.some((x) => x === s);
|
|
19
19
|
}
|
|
20
|
+
const SECTIONS = ["story", "steps", "details", "wiring"];
|
|
21
|
+
function isCommitSection(s) {
|
|
22
|
+
return SECTIONS.some((x) => x === s);
|
|
23
|
+
}
|
|
20
24
|
const SEVERITIES = ["blocker", "major", "minor"];
|
|
21
25
|
const CONCERN_KINDS = ["bug", "missing-test", "naming", "risk"];
|
|
22
26
|
export function isSeverity(s) {
|
|
@@ -223,6 +227,10 @@ export function parsePlanFile(draft) {
|
|
|
223
227
|
}
|
|
224
228
|
if (typeof c.message !== "string")
|
|
225
229
|
bad(`${at}.message`, "expected a string");
|
|
230
|
+
const section = c.section ?? null;
|
|
231
|
+
if (section !== null && !isCommitSection(section)) {
|
|
232
|
+
bad(`${at}.section`, `expected one of ${SECTIONS.join(", ")}`);
|
|
233
|
+
}
|
|
226
234
|
const extras = parseExtras(c, false);
|
|
227
235
|
if ("error" in extras)
|
|
228
236
|
bad(`${at}.${extras.field}`, extras.error);
|
|
@@ -239,7 +247,8 @@ export function parsePlanFile(draft) {
|
|
|
239
247
|
}
|
|
240
248
|
}
|
|
241
249
|
if (typeof c.message === "string" && !("error" in extras)) {
|
|
242
|
-
|
|
250
|
+
const placed = isCommitSection(section) ? { section } : {};
|
|
251
|
+
commits.push({ message: c.message, ...placed, ...extras, units });
|
|
243
252
|
}
|
|
244
253
|
}
|
|
245
254
|
}
|
|
@@ -287,6 +296,7 @@ export function serializePlanFile(file) {
|
|
|
287
296
|
tip: file.tip,
|
|
288
297
|
commits: file.commits.map((c) => ({
|
|
289
298
|
message: c.message,
|
|
299
|
+
...(c.section === undefined ? {} : { section: c.section }),
|
|
290
300
|
...serializeExtras(c),
|
|
291
301
|
units: c.units.map((ref) => ref.narration === undefined && ref.diagram === undefined && ref.concerns === undefined
|
|
292
302
|
? formatUnitRef(ref)
|
|
@@ -578,6 +588,7 @@ function project(diff, file, claims, issues) {
|
|
|
578
588
|
return {
|
|
579
589
|
index: k,
|
|
580
590
|
message: commit.message,
|
|
591
|
+
...(commit.section === undefined ? {} : { section: commit.section }),
|
|
581
592
|
...withText(commit.narration ?? null, commit.diagram ?? null),
|
|
582
593
|
...(mine.length === 0 ? {} : { concerns: mine }),
|
|
583
594
|
refs,
|
package/dist/src/prompt.js
CHANGED
|
@@ -1065,3 +1065,25 @@ export function renderShownConcern(concern, commit, ctx) {
|
|
|
1065
1065
|
"",
|
|
1066
1066
|
].join("\n");
|
|
1067
1067
|
}
|
|
1068
|
+
function utf8(latin1) {
|
|
1069
|
+
return Buffer.from(latin1, "latin1").toString("utf8");
|
|
1070
|
+
}
|
|
1071
|
+
/** Where a name is defined, each with the commit it lands in and its first lines at the tip. */
|
|
1072
|
+
export function renderShownDefinitions(name, candidates) {
|
|
1073
|
+
if (candidates.length === 0)
|
|
1074
|
+
return `no definition of ${name} at the tip\n`;
|
|
1075
|
+
return `${candidates
|
|
1076
|
+
.flatMap((c) => {
|
|
1077
|
+
const where = c.commit !== null
|
|
1078
|
+
? `in commit ${c.commit + 1}`
|
|
1079
|
+
: c.unit !== null
|
|
1080
|
+
? `in ${c.unit}, which no commit of the plan takes`
|
|
1081
|
+
: "unchanged by the branch";
|
|
1082
|
+
const width = String(c.newLine + c.preview.length - 1).length;
|
|
1083
|
+
return [
|
|
1084
|
+
`${utf8(c.path)}:${c.newLine} ${c.kind}, ${where}`,
|
|
1085
|
+
...c.preview.map((l, i) => ` ${String(c.newLine + i).padStart(width)} ${utf8(l)}`),
|
|
1086
|
+
];
|
|
1087
|
+
})
|
|
1088
|
+
.join("\n")}\n`;
|
|
1089
|
+
}
|
package/dist/src/server.js
CHANGED
|
@@ -5,6 +5,8 @@ import { homedir } from "node:os";
|
|
|
5
5
|
import { basename, isAbsolute, join } from "node:path";
|
|
6
6
|
import { fileURLToPath } from "node:url";
|
|
7
7
|
import { isReplyId, parseComment, parseDraft, parseNewDecision, parseNewReply, } from "./comments.js";
|
|
8
|
+
import { definitionIndex, isIdentifier } from "./definition.js";
|
|
9
|
+
import { isRepoPath } from "./diff.js";
|
|
8
10
|
import { carryConcerns, carrySession } from "./evolve.js";
|
|
9
11
|
import { isConcernId, isPlanVersion } from "./plan.js";
|
|
10
12
|
import { renderReport, reportItems } from "./prompt.js";
|
|
@@ -255,6 +257,7 @@ function viewOf(registration, loaded, draft, earlier = loaded.earlier) {
|
|
|
255
257
|
unitIds: new Set(state.units.map((u) => u.id)),
|
|
256
258
|
hunkRows: new Map(state.units.flatMap((u) => u.kind === "hunk" ? [[u.id, u.body.length]] : [])),
|
|
257
259
|
concernIds: new Set(state.commits.flatMap((c) => (c.concerns ?? []).map((k) => k.id))),
|
|
260
|
+
definitions: definitionIndex(loaded.repo, state.anchor.tip, new Map(state.units.map((u) => [u.id, u])), state.commits),
|
|
258
261
|
draftFile: draftPath(reviewsDir, state.version),
|
|
259
262
|
reviewFile: join(reviewsDir, `v${state.version}.json`),
|
|
260
263
|
};
|
|
@@ -544,6 +547,11 @@ function hostSession(id, registration, loaded, draft, load) {
|
|
|
544
547
|
return waitForEvents(since, timeoutMs, signal);
|
|
545
548
|
},
|
|
546
549
|
report: () => report(review()),
|
|
550
|
+
/** The page reads a definition the plan shows from its hunk, so only others carry a preview. */
|
|
551
|
+
define: async (name, from) => {
|
|
552
|
+
const found = await view.definitions.lookup(name, from !== null && isRepoPath(from) ? from : null);
|
|
553
|
+
return found.map((c) => (c.unit === null ? c : { ...c, preview: [] }));
|
|
554
|
+
},
|
|
547
555
|
reportItems: () => reportItems(review(), view.loaded.state.commits, unitsOf(), reportOptions()),
|
|
548
556
|
add: async (raw) => {
|
|
549
557
|
open();
|
|
@@ -943,6 +951,13 @@ export function startServer(opts) {
|
|
|
943
951
|
const timeout = queryInt(url, "timeout", 0, MAX_POLL_MS, DEFAULT_POLL_MS);
|
|
944
952
|
return sendJson(res, 200, await session.poll(since, timeout, abort.signal));
|
|
945
953
|
}
|
|
954
|
+
if (method === "GET" && rest === "/api/definition") {
|
|
955
|
+
const name = url.searchParams.get("name") ?? "";
|
|
956
|
+
if (!isIdentifier(name))
|
|
957
|
+
throw new HttpError(400, "name must be an identifier");
|
|
958
|
+
const candidates = await session.define(name, url.searchParams.get("from"));
|
|
959
|
+
return sendJson(res, 200, { candidates });
|
|
960
|
+
}
|
|
946
961
|
if (method === "GET" && rest === "/api/report" && url.searchParams.get("format") === "json") {
|
|
947
962
|
return sendJson(res, 200, session.reportItems());
|
|
948
963
|
}
|
package/dist/src/types.js
CHANGED
|
@@ -10,6 +10,8 @@
|
|
|
10
10
|
* session.ts the .follow-me/<slug>/ layout: session anchor, plans, reviews, draft.
|
|
11
11
|
* comments.ts the wire formats of review state: comments, replies, concern decisions, the
|
|
12
12
|
* review file, the autosaved draft and the server's events. Pure parsers.
|
|
13
|
+
* definition.ts where an identifier is defined at the branch tip, and where that lands in the
|
|
14
|
+
* plan: per-language patterns, ranking, placement and a per-tip cache.
|
|
13
15
|
* prompt.ts the planner prompt for an agent, the feedback and the consolidated report.
|
|
14
16
|
* server.ts the review server: a registry of hosted sessions, their JSON API, and ui/.
|
|
15
17
|
* host.ts the one server per user: its record in the state dir, attach, start, stop.
|
package/dist/ui/dist/assets/{abnfDiagram-WEYOIA3C-8UsLPznO.js → abnfDiagram-WEYOIA3C-fYSYLdMl.js}
RENAMED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{m as e,t}from"./mermaid-parser.core-
|
|
1
|
+
import{m as e,t}from"./mermaid-parser.core-DGpOP8AM.js";import{n}from"./chunk-Y2CYZVJY-DsF7k-Jl.js";import{m as r}from"./src-XnrqevnL.js";import"./chunk-DSNLSHJZ-B2z7FX4h.js";import{n as i,r as a,t as o}from"./chunk-2E7WHVMD-CNMuK9zj.js";import{t as s}from"./chunk-JWPE2WC7-DVXcaiue.js";import"./mermaid.core-BJtBFbJx.js";var c=e().RailroadAbnf.parser.LangiumParser,l=n(e=>{let t=e.alternatives.map(u);return t.length===1?t[0]:{type:`choice`,alternatives:t}},`transformAlternation`),u=n(e=>{let t=e.elements.map(f);return t.length===1?t[0]:{type:`sequence`,elements:t}},`transformConcatenation`),d=n(e=>{if(e.includes(`*`)){let[t,n]=e.split(`*`);return{min:t?parseInt(t,10):0,max:n?parseInt(n,10):1/0}}let t=parseInt(e,10);return{min:t,max:t}},`parseRepeat`),f=n(e=>{let t=p(e.primary);if(!e.repeat)return t;let{min:n,max:r}=d(e.repeat);return n===0&&r===1?{type:`optional`,element:t}:{type:`repetition`,element:t,min:n,max:r}},`transformElement`),p=n(e=>{switch(e.$type){case`AbnfStringLiteral`:return{type:`terminal`,value:e.value};case`AbnfNumVal`:return{type:`terminal`,value:e.value};case`AbnfRuleName`:return{type:`nonterminal`,name:e.name};case`AbnfGroup`:return l(e.element);case`AbnfOptionalGroup`:return{type:`optional`,element:l(e.element)};default:throw Error(`Unsupported ABNF primary node: ${e.$type}`)}},`transformPrimary`),m=n(e=>({name:e.name,definition:l(e.definition)}),`transformRule`),h=n(e=>{s(e,o),e.title&&o.setTitle(e.title),e.rules.map(e=>o.addRule(m(e)))},`populateDb`),g={parser:{parse:n(e=>{o.clear(),r.debug(`[ABNF Parser] Starting Langium parse`);let n=c.parse(e);if(n.lexerErrors.length>0||n.parserErrors.length>0)throw new t(n);let i=n.value;r.debug(`[ABNF Parser] Parsed rules:`,i.rules.length),h(i),r.debug(`[ABNF Parser] Parse complete`)},`parse`),parser:{yy:o}},db:o,renderer:a,styles:i};export{g as diagram};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{n as e,t}from"./path-fybaL0A-.js";import{a as n,c as r,d as i,f as a,i as o,l as s,m as c,n as l,o as u,p as d,r as f,u as p}from"./dist-
|
|
1
|
+
import{n as e,t}from"./path-fybaL0A-.js";import{a as n,c as r,d as i,f as a,i as o,l as s,m as c,n as l,o as u,p as d,r as f,u as p}from"./dist-BJd4jp9Y.js";function m(e){return e.innerRadius}function h(e){return e.outerRadius}function g(e){return e.startAngle}function _(e){return e.endAngle}function v(e){return e&&e.padAngle}function y(e,t,n,r,i,a,o,s){var c=n-e,l=r-t,u=o-i,d=s-a,f=d*c-u*l;if(!(f*f<1e-12))return f=(u*(t-a)-d*(e-i))/f,[e+f*c,t+f*l]}function b(e,t,n,r,i,a,o){var c=e-n,l=t-r,u=(o?a:-a)/d(c*c+l*l),f=u*l,p=-u*c,m=e+f,h=t+p,g=n+f,_=r+p,v=(m+g)/2,y=(h+_)/2,b=g-m,x=_-h,S=b*b+x*x,C=i-a,w=m*_-g*h,T=(x<0?-1:1)*d(s(0,C*C*S-w*w)),E=(w*x-b*T)/S,D=(-w*b-x*T)/S,O=(w*x+b*T)/S,k=(-w*b+x*T)/S,A=E-v,j=D-y,M=O-v,N=k-y;return A*A+j*j>M*M+N*N&&(E=O,D=k),{cx:E,cy:D,x01:-f,y01:-p,x11:E*(i/C-1),y11:D*(i/C-1)}}function x(){var s=m,x=h,S=e(0),C=null,w=g,T=_,E=v,D=null,O=t(k);function k(){var e,t,m=+s.apply(this,arguments),h=+x.apply(this,arguments),g=w.apply(this,arguments)-r,_=T.apply(this,arguments)-r,v=l(_-g),k=_>g;if(D||=e=O(),h<m&&(t=h,h=m,m=t),!(h>1e-12))D.moveTo(0,0);else if(v>c-1e-12)D.moveTo(h*u(g),h*a(g)),D.arc(0,0,h,g,_,!k),m>1e-12&&(D.moveTo(m*u(_),m*a(_)),D.arc(0,0,m,_,g,k));else{var A=g,j=_,M=g,N=_,P=v,F=v,I=E.apply(this,arguments)/2,L=I>1e-12&&(C?+C.apply(this,arguments):d(m*m+h*h)),R=p(l(h-m)/2,+S.apply(this,arguments)),z=R,B=R,V,H;if(L>1e-12){var U=o(L/m*a(I)),W=o(L/h*a(I));(P-=U*2)>1e-12?(U*=k?1:-1,M+=U,N-=U):(P=0,M=N=(g+_)/2),(F-=W*2)>1e-12?(W*=k?1:-1,A+=W,j-=W):(F=0,A=j=(g+_)/2)}var G=h*u(A),K=h*a(A),q=m*u(N),J=m*a(N);if(R>1e-12){var Y=h*u(j),X=h*a(j),Z=m*u(M),Q=m*a(M),$;if(v<i){if($=y(G,K,Z,Q,Y,X,q,J)){var ee=G-$[0],te=K-$[1],ne=Y-$[0],re=X-$[1],ie=1/a(f((ee*ne+te*re)/(d(ee*ee+te*te)*d(ne*ne+re*re)))/2),ae=d($[0]*$[0]+$[1]*$[1]);z=p(R,(m-ae)/(ie-1)),B=p(R,(h-ae)/(ie+1))}else z=B=0}}F>1e-12?B>1e-12?(V=b(Z,Q,G,K,h,B,k),H=b(Y,X,q,J,h,B,k),D.moveTo(V.cx+V.x01,V.cy+V.y01),B<R?D.arc(V.cx,V.cy,B,n(V.y01,V.x01),n(H.y01,H.x01),!k):(D.arc(V.cx,V.cy,B,n(V.y01,V.x01),n(V.y11,V.x11),!k),D.arc(0,0,h,n(V.cy+V.y11,V.cx+V.x11),n(H.cy+H.y11,H.cx+H.x11),!k),D.arc(H.cx,H.cy,B,n(H.y11,H.x11),n(H.y01,H.x01),!k))):(D.moveTo(G,K),D.arc(0,0,h,A,j,!k)):D.moveTo(G,K),!(m>1e-12)||!(P>1e-12)?D.lineTo(q,J):z>1e-12?(V=b(q,J,Y,X,m,-z,k),H=b(G,K,Z,Q,m,-z,k),D.lineTo(V.cx+V.x01,V.cy+V.y01),z<R?D.arc(V.cx,V.cy,z,n(V.y01,V.x01),n(H.y01,H.x01),!k):(D.arc(V.cx,V.cy,z,n(V.y01,V.x01),n(V.y11,V.x11),!k),D.arc(0,0,m,n(V.cy+V.y11,V.cx+V.x11),n(H.cy+H.y11,H.cx+H.x11),k),D.arc(H.cx,H.cy,z,n(H.y11,H.x11),n(H.y01,H.x01),!k))):D.arc(0,0,m,N,M,k)}if(D.closePath(),e)return D=null,e+``||null}return k.centroid=function(){var e=(+s.apply(this,arguments)+ +x.apply(this,arguments))/2,t=(+w.apply(this,arguments)+ +T.apply(this,arguments))/2-i/2;return[u(t)*e,a(t)*e]},k.innerRadius=function(t){return arguments.length?(s=typeof t==`function`?t:e(+t),k):s},k.outerRadius=function(t){return arguments.length?(x=typeof t==`function`?t:e(+t),k):x},k.cornerRadius=function(t){return arguments.length?(S=typeof t==`function`?t:e(+t),k):S},k.padRadius=function(t){return arguments.length?(C=t==null?null:typeof t==`function`?t:e(+t),k):C},k.startAngle=function(t){return arguments.length?(w=typeof t==`function`?t:e(+t),k):w},k.endAngle=function(t){return arguments.length?(T=typeof t==`function`?t:e(+t),k):T},k.padAngle=function(t){return arguments.length?(E=typeof t==`function`?t:e(+t),k):E},k.context=function(e){return arguments.length?(D=e??null,k):D},k}export{x as t};
|
package/dist/ui/dist/assets/{architecture-WOLXFQ4H-BBMK-PUu.js → architecture-WOLXFQ4H-VFox_hV1.js}
RENAMED
|
@@ -1 +1 @@
|
|
|
1
|
-
import"./chunk-NGNAAXSQ-C2Wy9Xmz.js";export{M as createArchitectureServices}from"./mermaid-parser.core-
|
|
1
|
+
import"./chunk-NGNAAXSQ-C2Wy9Xmz.js";export{M as createArchitectureServices}from"./mermaid-parser.core-DGpOP8AM.js";
|