@shiyuphan/follow-me 0.5.1 → 0.7.1
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 +9 -3
- package/dist/src/cli.js +112 -19
- package/dist/src/comments.js +6 -1
- package/dist/src/coverage.js +397 -0
- package/dist/src/definition.js +226 -0
- package/dist/src/git.js +87 -13
- package/dist/src/host.js +4 -0
- package/dist/src/plan.js +12 -1
- package/dist/src/prompt.js +80 -23
- package/dist/src/server.js +65 -8
- package/dist/src/session.js +33 -0
- package/dist/src/types.js +4 -0
- package/dist/ui/dist/assets/{abnfDiagram-WEYOIA3C-8UsLPznO.js → abnfDiagram-WEYOIA3C-cHEJvcBn.js} +1 -1
- package/dist/ui/dist/assets/{arc-vovjGalz.js → arc-xolFyc0v.js} +1 -1
- package/dist/ui/dist/assets/{architecture-WOLXFQ4H-BBMK-PUu.js → architecture-WOLXFQ4H-B3hvvNV2.js} +1 -1
- package/dist/ui/dist/assets/{architectureDiagram-ZHVYKT53-Bw_ehaQk.js → architectureDiagram-ZHVYKT53-BNTMbPJW.js} +1 -1
- package/dist/ui/dist/assets/{blockDiagram-RJAE7QA3-CAmD0wKf.js → blockDiagram-RJAE7QA3-NYY6qeuY.js} +1 -1
- package/dist/ui/dist/assets/{c4Diagram-XDPPPE54-DCP-3zgt.js → c4Diagram-XDPPPE54-B_bNpXHQ.js} +1 -1
- package/dist/ui/dist/assets/channel-DPDY0XJP.js +1 -0
- package/dist/ui/dist/assets/{chunk-2E7WHVMD-BiHSeJMu.js → chunk-2E7WHVMD-BTet9STn.js} +1 -1
- package/dist/ui/dist/assets/{chunk-4EMNKR2T-CR3lHKf_.js → chunk-4EMNKR2T-qoQt4Fpx.js} +1 -1
- package/dist/ui/dist/assets/{chunk-55XCU76K-CtaoloTT.js → chunk-55XCU76K-C3I6EB-H.js} +1 -1
- package/dist/ui/dist/assets/{chunk-7S6XSTFO-eABdc7fu.js → chunk-7S6XSTFO-CL-9s_8p.js} +1 -1
- package/dist/ui/dist/assets/{chunk-AQDTSKZB-CPlbJWi-.js → chunk-AQDTSKZB-DHoGnGyP.js} +1 -1
- package/dist/ui/dist/assets/{chunk-DSNLSHJZ-YiMuxsb-.js → chunk-DSNLSHJZ-Bk8WO5JF.js} +1 -1
- package/dist/ui/dist/assets/{chunk-HAGFOTED-1VcdL78a.js → chunk-HAGFOTED-CwAC85lg.js} +1 -1
- package/dist/ui/dist/assets/{chunk-HNE4Q36P-u-sr_0Qu.js → chunk-HNE4Q36P-CIllywWr.js} +1 -1
- package/dist/ui/dist/assets/{chunk-NQOKRVIF-C_QlWSze.js → chunk-NQOKRVIF-DDlpyYiH.js} +1 -1
- package/dist/ui/dist/assets/{chunk-UHW7PFBP-CNuZhsbU.js → chunk-UHW7PFBP-Cxdndcon.js} +1 -1
- package/dist/ui/dist/assets/{chunk-VQ5W62QI-DAHWbLqU.js → chunk-VQ5W62QI-CL9d9_iJ.js} +1 -1
- package/dist/ui/dist/assets/{chunk-WQQL4CDK-DWQnxFCd.js → chunk-WQQL4CDK-C4kzN8QU.js} +1 -1
- package/dist/ui/dist/assets/{chunk-XXDRQBXY-B0lGgSOR.js → chunk-XXDRQBXY-Cv_2FSiY.js} +1 -1
- package/dist/ui/dist/assets/{chunk-YZO67HFG-BDw7efkN.js → chunk-YZO67HFG-oHiuYaCJ.js} +1 -1
- package/dist/ui/dist/assets/{chunk-ZAUX2LTX-B5wfHsZv.js → chunk-ZAUX2LTX-Cy_8d0XT.js} +1 -1
- package/dist/ui/dist/assets/{chunk-ZCHMQSOR--rWtYtAG.js → chunk-ZCHMQSOR-piIpIcQi.js} +1 -1
- package/dist/ui/dist/assets/{classDiagram-v2-4MYWR3UA-BKuQ_xoS.js → classDiagram-v2-4MYWR3UA-DNKRIujO.js} +1 -1
- package/dist/ui/dist/assets/{cose-bilkent-JH36ORCC-Dk5fIpU0.js → cose-bilkent-JH36ORCC-BWKTGCHY.js} +1 -1
- package/dist/ui/dist/assets/{cynefin-EF2NZ3EQ-DIykJb2g.js → cynefin-EF2NZ3EQ-yx7KKkAk.js} +1 -1
- package/dist/ui/dist/assets/{cynefinDiagram-WF24UDRP-Cg8tBqUb.js → cynefinDiagram-WF24UDRP-BTnZ7QVL.js} +1 -1
- package/dist/ui/dist/assets/{dagre-NK4GIZVX-BC_tnnjz.js → dagre-NK4GIZVX-CPh5Zn5I.js} +1 -1
- package/dist/ui/dist/assets/{diagram-CBK3MWTC-B_MQGixU.js → diagram-CBK3MWTC-pI6NFdrD.js} +1 -1
- package/dist/ui/dist/assets/{diagram-I5XIIV2D-DO681pZ5.js → diagram-I5XIIV2D-DUIfARoS.js} +1 -1
- package/dist/ui/dist/assets/{diagram-ODIL2UCW-D14jLzU6.js → diagram-ODIL2UCW-lafSPvAw.js} +1 -1
- package/dist/ui/dist/assets/{diagram-PFBEJUXE-cML48gUS.js → diagram-PFBEJUXE-u1lZas3a.js} +1 -1
- package/dist/ui/dist/assets/{diagram-RJA3I4VV-DSoq8jFk.js → diagram-RJA3I4VV-mc0GLMHo.js} +1 -1
- package/dist/ui/dist/assets/{diagram-TYUNFSNR-CaJ4pvnl.js → diagram-TYUNFSNR-1NRbBrZW.js} +1 -1
- package/dist/ui/dist/assets/{dist-D3j-DODP.js → dist-CHZ9mrl_.js} +1 -1
- package/dist/ui/dist/assets/{ebnfDiagram-V4P3TTPT-CyRpjSix.js → ebnfDiagram-V4P3TTPT-DdStjTDc.js} +1 -1
- package/dist/ui/dist/assets/{elk-QONTBCTQ-ICJdzVFL.js → elk-QONTBCTQ-BzRoFO45.js} +1 -1
- package/dist/ui/dist/assets/{erDiagram-KYQIGQAA-B57hNTfw.js → erDiagram-KYQIGQAA-DlMkgQxj.js} +1 -1
- package/dist/ui/dist/assets/{eventmodeling-K75KTNOO-BG8tUVIQ.js → eventmodeling-K75KTNOO-BRYIOrod.js} +1 -1
- package/dist/ui/dist/assets/flowDiagram-IUGLLWBU-C1IBh_GZ.js +1 -0
- package/dist/ui/dist/assets/{ganttDiagram-R7GPNTJY-Czt6Klik.js → ganttDiagram-R7GPNTJY-D8fQZ7Ah.js} +1 -1
- package/dist/ui/dist/assets/{gitGraph-VSP46ZUC-De_ZWJLj.js → gitGraph-VSP46ZUC-C7ZydyWX.js} +1 -1
- package/dist/ui/dist/assets/{gitGraphDiagram-PDYL7XHY-BMCJoOnT.js → gitGraphDiagram-PDYL7XHY-DCItam0v.js} +1 -1
- package/dist/ui/dist/assets/index-8BehwQ7E.js +134 -0
- package/dist/ui/dist/assets/index-mC2OW2Av.css +1 -0
- package/dist/ui/dist/assets/{info-OHQRW6UA-D75H9kUq.js → info-OHQRW6UA-C3KneBKT.js} +1 -1
- package/dist/ui/dist/assets/{infoDiagram-MVDRKAX5-Bsm_I9dt.js → infoDiagram-MVDRKAX5-6wK09D32.js} +1 -1
- package/dist/ui/dist/assets/{ishikawaDiagram-HPEHV2GE-98xZAJ9y.js → ishikawaDiagram-HPEHV2GE-DTP5C8VX.js} +1 -1
- package/dist/ui/dist/assets/{journeyDiagram-BOSHW7O7-BWVGuc0P.js → journeyDiagram-BOSHW7O7-B--UIMtq.js} +1 -1
- package/dist/ui/dist/assets/{kanban-definition-FNSA4HC5-DgUE_KDd.js → kanban-definition-FNSA4HC5-Ku4N9QGt.js} +1 -1
- package/dist/ui/dist/assets/{line-i__8Rvas.js → line-C4-H5h1-.js} +1 -1
- package/dist/ui/dist/assets/{linear-BXKj0Fip.js → linear-DhAID3hm.js} +1 -1
- package/dist/ui/dist/assets/{mermaid-parser.core-DxvaorTa.js → mermaid-parser.core-CMPNc-u6.js} +3 -3
- package/dist/ui/dist/assets/{mermaid.core-ujYTsRZz.js → mermaid.core-Nd_xOg2y.js} +4 -4
- package/dist/ui/dist/assets/{mindmap-definition-6OY57UG3-B7QD6sjk.js → mindmap-definition-6OY57UG3-BlVsym5h.js} +1 -1
- package/dist/ui/dist/assets/{packet-JDAUHWVQ-B6aIwfD1.js → packet-JDAUHWVQ-rsk7SM7S.js} +1 -1
- package/dist/ui/dist/assets/{pegDiagram-ECIUJNBX-_RStHj-a.js → pegDiagram-ECIUJNBX-C0L20M8z.js} +1 -1
- package/dist/ui/dist/assets/{pie-XZMESJXO-CNLC-pTe.js → pie-XZMESJXO-B23-iZSN.js} +1 -1
- package/dist/ui/dist/assets/{pieDiagram-GNMPY74T-0uT7MT-u.js → pieDiagram-GNMPY74T-BqFhe4Oh.js} +1 -1
- package/dist/ui/dist/assets/{prism-core-DtxziK1i.js → prism-core-DYsiqJeR.js} +1 -1
- package/dist/ui/dist/assets/{quadrantDiagram-D57K4MC3-CTMKb5hl.js → quadrantDiagram-D57K4MC3-BKP75B-m.js} +1 -1
- package/dist/ui/dist/assets/{radar-ABXABTNO-D53h6I0N.js → radar-ABXABTNO-BrEk2mQ7.js} +1 -1
- package/dist/ui/dist/assets/{railroad-I3PHUGI6-CwjhW4mc.js → railroad-I3PHUGI6-wWO4Eapc.js} +1 -1
- package/dist/ui/dist/assets/{railroad-abnf-LNEFI6M7-DDbQ3RmB.js → railroad-abnf-LNEFI6M7-DfY-IPvE.js} +1 -1
- package/dist/ui/dist/assets/{railroad-ebnf-DWYD2UWJ-Am7YJAqO.js → railroad-ebnf-DWYD2UWJ-19wedWGk.js} +1 -1
- package/dist/ui/dist/assets/{railroad-peg-JR7BVNR7-Ci32KoVW.js → railroad-peg-JR7BVNR7-9vIslnoJ.js} +1 -1
- package/dist/ui/dist/assets/{railroadDiagram-Y72NRBL7-BPU5A6XW.js → railroadDiagram-Y72NRBL7-DVnmXs8w.js} +1 -1
- package/dist/ui/dist/assets/{requirementDiagram-EQYSW5RA-_0Pq4QdV.js → requirementDiagram-EQYSW5RA-BEoMdlzR.js} +1 -1
- package/dist/ui/dist/assets/{sankeyDiagram-SY24JHO5-c9h-7uf8.js → sankeyDiagram-SY24JHO5-D7Tn2Gbs.js} +1 -1
- package/dist/ui/dist/assets/{sequenceDiagram-BLEHNHQU-B9-S4Rc9.js → sequenceDiagram-BLEHNHQU-CyjUDBaQ.js} +1 -1
- package/dist/ui/dist/assets/{src-BZ3kG7lO.js → src-BiC9XNcI.js} +1 -1
- package/dist/ui/dist/assets/{stateDiagram-v2-A6IESKWZ-py1smSen.js → stateDiagram-v2-A6IESKWZ-CMp1VCO9.js} +1 -1
- package/dist/ui/dist/assets/{swimlanes-B3ENRKFQ-C1E90aH4.js → swimlanes-B3ENRKFQ-KEciLd6r.js} +1 -1
- package/dist/ui/dist/assets/swimlanesDiagram-AYQRV4AK-DzestpDE.js +8 -0
- package/dist/ui/dist/assets/{timeline-definition-JXYKGLGB-DNe1To6B.js → timeline-definition-JXYKGLGB-C8YQtv7M.js} +1 -1
- package/dist/ui/dist/assets/{treeView-D4JQ5SDB-D6MG89i6.js → treeView-D4JQ5SDB-DgAzPaOO.js} +1 -1
- package/dist/ui/dist/assets/{treemap-SAJKECNS-CZpt7Mlh.js → treemap-SAJKECNS-eCRqZMYc.js} +1 -1
- package/dist/ui/dist/assets/{usecaseDiagram-WPOLBNNX-CKsv-03M.js → usecaseDiagram-WPOLBNNX-Dirryw9G.js} +1 -1
- package/dist/ui/dist/assets/{vennDiagram-Q3Y3IDCW-DGJ1qlyJ.js → vennDiagram-Q3Y3IDCW-BjxQbAUx.js} +1 -1
- package/dist/ui/dist/assets/{wardley-7MLQ67FV-ClOzBemC.js → wardley-7MLQ67FV-CqHX5RUz.js} +1 -1
- package/dist/ui/dist/assets/{wardleyDiagram-IVAIDKWA-C68ahPyF.js → wardleyDiagram-IVAIDKWA-Ds1HoEUY.js} +1 -1
- package/dist/ui/dist/assets/{xychartDiagram-4URNHDU4-DWDamCoH.js → xychartDiagram-4URNHDU4-kodzDb12.js} +1 -1
- package/dist/ui/dist/index.html +2 -2
- package/package.json +1 -1
- package/skills/follow-me/SKILL.md +5 -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,14 @@ 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.*
|
|
77
|
+
|
|
78
|
+

|
|
79
|
+
*Coverage while reading. Your agent runs the tests with coverage and passes the lcov to `follow-me coverage`; follow-me never runs anything. Bars show covered, partly covered and uncovered lines, `x` jumps to the next uncovered line, and clicking a bar lists the tests that ran it.*
|
|
74
80
|
|
|
75
81
|

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

|
|
79
85
|
*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
86
|
|
|
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.
|
|
87
|
+
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, `g` shows or hides coverage and `x` jumps to the next uncovered line, `?` lists the rest. An unfinished review autosaves and comes back when the page is opened again.
|
|
82
88
|
|
|
83
89
|
### Reviewing someone else's branch
|
|
84
90
|
|
package/dist/src/cli.js
CHANGED
|
@@ -1,21 +1,27 @@
|
|
|
1
1
|
import { execFileSync } from "node:child_process";
|
|
2
2
|
import { readFileSync, writeFileSync } from "node:fs";
|
|
3
3
|
import { homedir } from "node:os";
|
|
4
|
-
import { dirname, join, relative } from "node:path";
|
|
4
|
+
import { dirname, join, relative, resolve } from "node:path";
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
|
6
|
+
import { changedLines, LcovError, parseLcov, parseLocArg, recordRun, runOf, tipPlacer, totalOf, } from "./coverage.js";
|
|
7
|
+
import { definitionIndex, isIdentifier } from "./definition.js";
|
|
6
8
|
import { DiffParseError, formatUnitRef, isUnitId } from "./diff.js";
|
|
7
|
-
import { BranchRefusedError, branchHead, GitError, isOwnWork, signing, treeOf, writeBranch, } from "./git.js";
|
|
8
|
-
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
|
+
import { BranchRefusedError, branchHead, GitError, isOwnWork, pathsAt, pathsChanged, signing, treeOf, writeBranch, } from "./git.js";
|
|
10
|
+
import { fetchCarried, fetchComments, fetchReport, findServer, followEvents, idleMs, listSessions, openBrowser, pollEvents, portFallbackMessage, postComment, postCoverage, postReply, probeHealth, readServerRecord, registerWith, sessionUrl, spawnDetached, startOrAttach, stateDir, stillHosted, stopServer, swapVersion, } from "./host.js";
|
|
9
11
|
import { findSkillDir, InstallError, runInstall, runUninstall, syncSkills, } from "./install.js";
|
|
10
12
|
import { naiveDraft } from "./naive.js";
|
|
11
13
|
import { isCommentId, isConcernId, isFields, serializePlanFile, validatePlan, } from "./plan.js";
|
|
12
|
-
import { renderEvents, renderPrompt, renderReport, renderShownComment, renderShownConcern, renderShownUnit, reportItems, } from "./prompt.js";
|
|
14
|
+
import { renderEvents, renderPrompt, renderReport, renderShownComment, renderShownConcern, renderShownDefinitions, renderShownUnit, reportItems, } from "./prompt.js";
|
|
13
15
|
import { RegistrationError, sessionIdOf } from "./server.js";
|
|
14
|
-
import { BranchMovedError, cleanSessions, extractSession, MissingDraftError, NotARepositoryError, openSession, SessionFileError, SessionLockedError, sessionComments, sinceLastPlan, } from "./session.js";
|
|
16
|
+
import { BranchMovedError, cleanSessions, coverageAt, extractSession, MissingDraftError, NotARepositoryError, openSession, SessionFileError, SessionLockedError, sessionComments, sinceLastPlan, } from "./session.js";
|
|
15
17
|
import { checkSyntax } from "./syntax.js";
|
|
16
18
|
class UsageError extends Error {
|
|
17
19
|
name = "UsageError";
|
|
18
20
|
}
|
|
21
|
+
/** A test run follow-me will not record, with the reason the agent can act on. */
|
|
22
|
+
class CoverageError extends Error {
|
|
23
|
+
name = "CoverageError";
|
|
24
|
+
}
|
|
19
25
|
const SHORT_USAGE = `usage: follow-me [--wait <seconds>] [--branch <name>]
|
|
20
26
|
|
|
21
27
|
Run follow-me with no command. Each call does the next thing that is missing and returns: it
|
|
@@ -30,11 +36,14 @@ Other commands:
|
|
|
30
36
|
Mermaid source
|
|
31
37
|
explain [--commit <n>] [--unit <id>] [--lines <a-b>] --body <text> [--diagram <file>]
|
|
32
38
|
start a thread on the page without being asked
|
|
33
|
-
show <h_…|c_…|k_…|path:line[@n|@base]>
|
|
34
|
-
|
|
35
|
-
|
|
39
|
+
show <h_…|c_…|k_…|path:line[@n|@base]|name>
|
|
40
|
+
print a unit, a comment's thread, a concern, the
|
|
41
|
+
lines around path:line in commit n, or where a name
|
|
42
|
+
is defined, instead of reading files
|
|
36
43
|
report [--json] [--markdown] print the report of the latest review; --json lists
|
|
37
44
|
the items to post to a merge request
|
|
45
|
+
coverage <lcov> [--test <id> [--at <path:line>]]
|
|
46
|
+
record a test run taken at the tip for the page
|
|
38
47
|
|
|
39
48
|
help --all lists every command.`;
|
|
40
49
|
const USAGE = `usage: follow-me <command> [options]
|
|
@@ -85,17 +94,24 @@ Interactive loop (while the review page is open):
|
|
|
85
94
|
--lines are change lines of the unit, commits count
|
|
86
95
|
from 1; with no target it is about the whole plan.
|
|
87
96
|
watch leaves these out like your replies
|
|
88
|
-
show <h_…|c_…|k_…|path:line[@n|@base]> [--branch <name>]
|
|
97
|
+
show <h_…|c_…|k_…|path:line[@n|@base]|name> [--branch <name>]
|
|
89
98
|
print a unit with the commits that take it, a
|
|
90
99
|
comment with its rows and thread, a concern with its
|
|
91
|
-
lines,
|
|
92
|
-
commit n of the plan, or in the base
|
|
100
|
+
lines, the lines around path:line at the tip, in
|
|
101
|
+
commit n of the plan, or in the base, or each place
|
|
102
|
+
a name is defined at the tip with the commit it
|
|
103
|
+
lands in
|
|
93
104
|
report [--json] [--markdown] [--branch <name>]
|
|
94
105
|
print the report of the open review, or of the last
|
|
95
106
|
one that ended. Markdown lists issues by file and
|
|
96
107
|
line in both voices, then questions with answers;
|
|
97
108
|
--json lists the items to post to a merge request
|
|
98
109
|
(path, lines, severity, body, thread, excluded)
|
|
110
|
+
coverage <lcov> [--test <id> [--at <path:line>]] [--branch <name>]
|
|
111
|
+
record the lcov of a test run at the branch tip;
|
|
112
|
+
refuses when a covered file in the working tree
|
|
113
|
+
differs from the tip. A plain run replaces the last;
|
|
114
|
+
--test runs add one test each, --at is where it is
|
|
99
115
|
apply, while the page is open, swaps it to the new plan version in place, same URL.
|
|
100
116
|
|
|
101
117
|
Old names, still working:
|
|
@@ -905,8 +921,8 @@ function reviewedPlan(session) {
|
|
|
905
921
|
return null;
|
|
906
922
|
}
|
|
907
923
|
/** Prints the finished review's report, which is what the agent works from from here on. */
|
|
908
|
-
function finished(session, stored) {
|
|
909
|
-
const text = renderStored(session, stored);
|
|
924
|
+
async function finished(session, stored) {
|
|
925
|
+
const text = await renderStored(session, stored);
|
|
910
926
|
if (text === null)
|
|
911
927
|
return 1;
|
|
912
928
|
// A review that swapped versions holds what its earlier versions said too.
|
|
@@ -1169,6 +1185,9 @@ async function entry(cwd, args) {
|
|
|
1169
1185
|
const result = await hostReview(session, mode, detached, async () => prep, reportSaved);
|
|
1170
1186
|
if (result.kind !== "elsewhere")
|
|
1171
1187
|
return exitCode(result);
|
|
1188
|
+
if (session.coverage() === null) {
|
|
1189
|
+
next("if the project has tests, run them with coverage at the tip and pass the lcov to follow-me coverage");
|
|
1190
|
+
}
|
|
1172
1191
|
say("follow-me is interactive: keep running it until it prints the report");
|
|
1173
1192
|
return listen(session, result, args);
|
|
1174
1193
|
}
|
|
@@ -1226,7 +1245,7 @@ async function walkPlan(session, args) {
|
|
|
1226
1245
|
return alreadyReviewed(session, latest.version, args) ? null : latest;
|
|
1227
1246
|
}
|
|
1228
1247
|
/** The consolidated report of a saved review, or null after saying why it cannot be rendered. */
|
|
1229
|
-
function renderStored(session, stored, format = "markdown") {
|
|
1248
|
+
async function renderStored(session, stored, format = "markdown") {
|
|
1230
1249
|
const plan = session.plans().find((p) => p.version === stored.version);
|
|
1231
1250
|
if (plan === undefined) {
|
|
1232
1251
|
console.error(`follow-me: review v${stored.version} refers to a plan that is missing`);
|
|
@@ -1254,7 +1273,13 @@ function renderStored(session, stored, format = "markdown") {
|
|
|
1254
1273
|
concerns: stored.review.carriedConcerns ?? [],
|
|
1255
1274
|
},
|
|
1256
1275
|
};
|
|
1257
|
-
const
|
|
1276
|
+
const coverage = await coverageAt(session.root, session.dir, session.anchor.tip);
|
|
1277
|
+
const opts = {
|
|
1278
|
+
branch: session.anchor.branch,
|
|
1279
|
+
responses,
|
|
1280
|
+
...earlier,
|
|
1281
|
+
...(coverage === null ? {} : { coverage }),
|
|
1282
|
+
};
|
|
1258
1283
|
if (format === "json") {
|
|
1259
1284
|
const items = reportItems(stored.review, verdict.plan.commits, session.diff.units, opts);
|
|
1260
1285
|
return `${JSON.stringify(items, null, 2)}\n`;
|
|
@@ -1284,12 +1309,67 @@ async function report(cwd, args, walkOnly = false) {
|
|
|
1284
1309
|
console.error("follow-me: no review is open and none has ended; run `follow-me review` first");
|
|
1285
1310
|
return 1;
|
|
1286
1311
|
}
|
|
1287
|
-
const text = renderStored(session, stored, format);
|
|
1312
|
+
const text = await renderStored(session, stored, format);
|
|
1288
1313
|
if (text === null)
|
|
1289
1314
|
return 1;
|
|
1290
1315
|
process.stdout.write(text);
|
|
1291
1316
|
return 0;
|
|
1292
1317
|
}
|
|
1318
|
+
const COVERAGE_USAGE = "usage: follow-me coverage <lcov> [--test <id> [--at <path:line>]]";
|
|
1319
|
+
/**
|
|
1320
|
+
* Records the lcov of a test run the agent made at the tip in the session's coverage.json and
|
|
1321
|
+
* prints how much of the branch's changed code it ran. follow-me never runs the tests itself.
|
|
1322
|
+
*/
|
|
1323
|
+
async function coverage(cwd, args) {
|
|
1324
|
+
const [file, extra] = args.positionals;
|
|
1325
|
+
const id = args.values.get("test");
|
|
1326
|
+
const atArg = args.values.get("at");
|
|
1327
|
+
if (file === undefined || extra !== undefined)
|
|
1328
|
+
throw new UsageError(COVERAGE_USAGE);
|
|
1329
|
+
if (atArg !== undefined && id === undefined) {
|
|
1330
|
+
throw new UsageError(`--at needs --test\n${COVERAGE_USAGE}`);
|
|
1331
|
+
}
|
|
1332
|
+
let text;
|
|
1333
|
+
try {
|
|
1334
|
+
text = readFileSync(resolve(cwd, file), "utf8");
|
|
1335
|
+
}
|
|
1336
|
+
catch {
|
|
1337
|
+
throw new UsageError(`cannot read ${file}`);
|
|
1338
|
+
}
|
|
1339
|
+
const records = parseLcov(text);
|
|
1340
|
+
const session = await openFresh(cwd, args);
|
|
1341
|
+
const { tip } = session.anchor;
|
|
1342
|
+
const place = tipPlacer(session.root, cwd, await pathsAt(session.root, tip));
|
|
1343
|
+
const run = runOf(records, place);
|
|
1344
|
+
if (run.files.size === 0) {
|
|
1345
|
+
throw new CoverageError(`none of the ${plural(new Set(records.map((r) => r.path)).size, "file")} in ${file} is in ${session.anchor.branch} at the tip; run follow-me coverage from the directory the tests ran in`);
|
|
1346
|
+
}
|
|
1347
|
+
const changed = await pathsChanged(session.root, tip, null);
|
|
1348
|
+
const differ = [...run.files.keys()].filter((p) => changed.has(p));
|
|
1349
|
+
if (differ.length > 0) {
|
|
1350
|
+
throw new CoverageError(`coverage must be taken on the tip; these files differ: ${differ.map(show).join(", ")}`);
|
|
1351
|
+
}
|
|
1352
|
+
let test;
|
|
1353
|
+
if (id !== undefined) {
|
|
1354
|
+
const at = atArg === undefined ? undefined : parseLocArg(atArg, place);
|
|
1355
|
+
if (typeof at === "string")
|
|
1356
|
+
throw new UsageError(at);
|
|
1357
|
+
test = { id, ...(at === undefined ? {} : { at }) };
|
|
1358
|
+
}
|
|
1359
|
+
const takenAt = new Date().toISOString();
|
|
1360
|
+
const recorded = recordRun(session.coverage(), run, {
|
|
1361
|
+
tip,
|
|
1362
|
+
takenAt,
|
|
1363
|
+
...(test === undefined ? {} : { test }),
|
|
1364
|
+
});
|
|
1365
|
+
session.writeCoverage(recorded);
|
|
1366
|
+
const open = await openOnServer(session, null);
|
|
1367
|
+
if (open !== null)
|
|
1368
|
+
await postCoverage(open.port, open.summary);
|
|
1369
|
+
const { covered, executable } = totalOf(changedLines(session.diff.units, recorded.files).values());
|
|
1370
|
+
say(`coverage for ${plural(recorded.files.size, "file")} at ${short(tip)}; ${covered} of ${executable} changed lines covered`);
|
|
1371
|
+
return 0;
|
|
1372
|
+
}
|
|
1293
1373
|
/** The old name of report. */
|
|
1294
1374
|
async function feedback(cwd, args) {
|
|
1295
1375
|
console.error("follow-me: feedback is now report; printing the same");
|
|
@@ -1298,9 +1378,12 @@ async function feedback(cwd, args) {
|
|
|
1298
1378
|
function isCommentEvent(event) {
|
|
1299
1379
|
return event.type === "comment" || event.type === "comment-updated";
|
|
1300
1380
|
}
|
|
1301
|
-
/**
|
|
1381
|
+
/**
|
|
1382
|
+
* Events the reviewer caused; the agent's own replies, explanations, version swaps and coverage
|
|
1383
|
+
* are not news.
|
|
1384
|
+
*/
|
|
1302
1385
|
function isNews(event) {
|
|
1303
|
-
if (event.type === "version")
|
|
1386
|
+
if (event.type === "version" || event.type === "coverage")
|
|
1304
1387
|
return false;
|
|
1305
1388
|
if (event.type === "comment" || event.type === "comment-updated") {
|
|
1306
1389
|
return event.comment.author !== "agent";
|
|
@@ -1516,7 +1599,7 @@ async function explain(cwd, args) {
|
|
|
1516
1599
|
next(`run follow-me again; ${STILL_WORKING}`);
|
|
1517
1600
|
return 0;
|
|
1518
1601
|
}
|
|
1519
|
-
const SHOW_USAGE = "usage: follow-me show <h_…|c_…|k_…|path:line[@<commit>|@base]>";
|
|
1602
|
+
const SHOW_USAGE = "usage: follow-me show <h_…|c_…|k_…|path:line[@<commit>|@base]|name>";
|
|
1520
1603
|
/** Every comment the session has, with the plan version it was made on: live ones first. */
|
|
1521
1604
|
async function knownComments(session) {
|
|
1522
1605
|
const open = await openOnServer(session, null);
|
|
@@ -1629,6 +1712,12 @@ async function showRef(cwd, args) {
|
|
|
1629
1712
|
}
|
|
1630
1713
|
throw new UsageError(`no concern ${ref} in a plan that fits the branch`);
|
|
1631
1714
|
}
|
|
1715
|
+
if (isIdentifier(ref)) {
|
|
1716
|
+
const commits = latest === undefined ? [] : projectedCommits(session, latest.version);
|
|
1717
|
+
const index = definitionIndex(session.root, session.diff.tip, units, commits);
|
|
1718
|
+
process.stdout.write(renderShownDefinitions(ref, await index.lookup(ref, null)));
|
|
1719
|
+
return 0;
|
|
1720
|
+
}
|
|
1632
1721
|
process.stdout.write(showLines(session, ref));
|
|
1633
1722
|
return 0;
|
|
1634
1723
|
}
|
|
@@ -1868,6 +1957,8 @@ export async function main(argv, cwd) {
|
|
|
1868
1957
|
}
|
|
1869
1958
|
case "show":
|
|
1870
1959
|
return await showRef(cwd, parseArgs(rest, { values: ["branch"] }));
|
|
1960
|
+
case "coverage":
|
|
1961
|
+
return await coverage(cwd, parseArgs(rest, { values: ["test", "at", "branch"] }));
|
|
1871
1962
|
case "report":
|
|
1872
1963
|
return await report(cwd, parseArgs(rest, { values: ["branch"], flags: ["json", "markdown"] }));
|
|
1873
1964
|
case "clean": {
|
|
@@ -1908,6 +1999,8 @@ export async function main(argv, cwd) {
|
|
|
1908
1999
|
catch (error) {
|
|
1909
2000
|
const known = [
|
|
1910
2001
|
UsageError,
|
|
2002
|
+
CoverageError,
|
|
2003
|
+
LcovError,
|
|
1911
2004
|
BranchMovedError,
|
|
1912
2005
|
BranchRefusedError,
|
|
1913
2006
|
SessionFileError,
|
package/dist/src/comments.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { isRepoPath, isUnitId } from "./diff.js";
|
|
1
|
+
import { isRepoPath, isSha, isUnitId } from "./diff.js";
|
|
2
2
|
import { diagramProblem, isArray, isCommentId, isConcernId, isFields, isPlanVersion, isSeverity, parsePlacedConcern, } from "./plan.js";
|
|
3
3
|
/**
|
|
4
4
|
* The wire formats of review state, parsed once at the boundary: comments, replies, concern
|
|
@@ -452,6 +452,11 @@ export function parseEvent(v) {
|
|
|
452
452
|
return carried;
|
|
453
453
|
return { seq, type: v.type, version: v.version, hash: v.hash, carried };
|
|
454
454
|
}
|
|
455
|
+
case "coverage":
|
|
456
|
+
if (typeof v.tip !== "string" || !isSha(v.tip) || typeof v.takenAt !== "string") {
|
|
457
|
+
return "malformed coverage event";
|
|
458
|
+
}
|
|
459
|
+
return { seq, type: v.type, tip: v.tip, takenAt: v.takenAt };
|
|
455
460
|
case "submitted":
|
|
456
461
|
return { seq, type: v.type };
|
|
457
462
|
case "ended":
|