@shiyuphan/follow-me 0.2.0 → 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 +44 -55
- package/dist/src/cli.js +566 -188
- package/dist/src/comments.js +66 -5
- package/dist/src/git.js +12 -0
- package/dist/src/host.js +19 -8
- package/dist/src/install.js +21 -0
- package/dist/src/plan.js +1 -1
- package/dist/src/prompt.js +343 -39
- package/dist/src/server.js +176 -67
- package/dist/src/session.js +13 -11
- 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 -10
- package/skills/follow-me/SKILL.md +15 -89
- package/dist/src/anthropic.js +0 -133
- package/dist/ui/app.js +0 -2201
- package/dist/ui/concerns.js +0 -165
- package/dist/ui/highlight.js +0 -221
- package/dist/ui/index.html +0 -94
- package/dist/ui/live.js +0 -184
- package/dist/ui/playback.js +0 -579
- package/dist/ui/questions.js +0 -189
- package/dist/ui/style.css +0 -1331
- package/dist/ui/summary.js +0 -45
package/dist/src/comments.js
CHANGED
|
@@ -14,8 +14,9 @@ function isIndex(v) {
|
|
|
14
14
|
return typeof v === "number" && Number.isInteger(v) && v >= 0;
|
|
15
15
|
}
|
|
16
16
|
function parseAnchor(v) {
|
|
17
|
-
if (!isFields(v))
|
|
17
|
+
if (!isFields(v)) {
|
|
18
18
|
return "anchor is not an object";
|
|
19
|
+
}
|
|
19
20
|
if (v.kind === "plan")
|
|
20
21
|
return { kind: "plan" };
|
|
21
22
|
if (!isIndex(v.commit))
|
|
@@ -27,12 +28,25 @@ function parseAnchor(v) {
|
|
|
27
28
|
return "anchor.kind is unknown";
|
|
28
29
|
if (typeof v.unit !== "string" || !isUnitId(v.unit))
|
|
29
30
|
return "anchor.unit is not a unit id";
|
|
30
|
-
if (v.changes === null)
|
|
31
|
+
if (v.changes === null) {
|
|
32
|
+
if (v.offsets !== undefined)
|
|
33
|
+
return "anchor.offsets needs anchor.changes to be a list";
|
|
31
34
|
return { kind: "hunk", unit: v.unit, commit, changes: null };
|
|
35
|
+
}
|
|
32
36
|
if (!isArray(v.changes) || !v.changes.every(isIndex)) {
|
|
33
37
|
return "anchor.changes is not null or a list of change indices";
|
|
34
38
|
}
|
|
35
|
-
|
|
39
|
+
const changes = v.changes.filter(isIndex);
|
|
40
|
+
if (v.offsets === undefined)
|
|
41
|
+
return { kind: "hunk", unit: v.unit, commit, changes };
|
|
42
|
+
if (!isArray(v.offsets) || v.offsets.length === 0 || !v.offsets.every(isIndex)) {
|
|
43
|
+
return "anchor.offsets is not a non-empty list of row indices";
|
|
44
|
+
}
|
|
45
|
+
const offsets = v.offsets.filter(isIndex);
|
|
46
|
+
if (!offsets.every((o, i) => i === 0 || o > (offsets[i - 1] ?? 0))) {
|
|
47
|
+
return "anchor.offsets is not strictly ascending";
|
|
48
|
+
}
|
|
49
|
+
return { kind: "hunk", unit: v.unit, commit, changes, offsets };
|
|
36
50
|
}
|
|
37
51
|
/** A stored reply. Replies a client posts go through parseNewReply, which mints id and time. */
|
|
38
52
|
export function parseReply(v) {
|
|
@@ -101,6 +115,17 @@ export function parseComment(v) {
|
|
|
101
115
|
}
|
|
102
116
|
if (v.kind !== undefined && v.kind !== "question")
|
|
103
117
|
return 'kind is not "question"';
|
|
118
|
+
if (v.author !== undefined && v.author !== "agent")
|
|
119
|
+
return 'author is not "agent"';
|
|
120
|
+
if (v.diagram !== undefined) {
|
|
121
|
+
if (typeof v.diagram !== "string")
|
|
122
|
+
return "diagram is not a string";
|
|
123
|
+
if (v.author !== "agent")
|
|
124
|
+
return "diagram is only for an agent's comment";
|
|
125
|
+
const problem = diagramProblem(v.diagram);
|
|
126
|
+
if (problem !== null)
|
|
127
|
+
return `diagram: ${problem}`;
|
|
128
|
+
}
|
|
104
129
|
const follows = v.follows;
|
|
105
130
|
if (follows !== undefined && !(typeof follows === "string" && isCommentId(follows))) {
|
|
106
131
|
return "follows is not a comment id";
|
|
@@ -108,6 +133,11 @@ export function parseComment(v) {
|
|
|
108
133
|
const severity = v.severity;
|
|
109
134
|
if (severity !== undefined && !isSeverity(severity))
|
|
110
135
|
return "severity is unknown";
|
|
136
|
+
if (v.pending !== undefined && v.pending !== true)
|
|
137
|
+
return "pending is not true";
|
|
138
|
+
if (v.pending === true && (v.kind === "question" || v.author === "agent")) {
|
|
139
|
+
return "only a reviewer's flag can be pending";
|
|
140
|
+
}
|
|
111
141
|
const replies = parseList(v.replies, parseReply, "replies");
|
|
112
142
|
if (typeof replies === "string")
|
|
113
143
|
return replies;
|
|
@@ -124,7 +154,10 @@ export function parseComment(v) {
|
|
|
124
154
|
...(v.kind === "question" ? { kind: "question" } : {}),
|
|
125
155
|
...(follows === undefined ? {} : { follows }),
|
|
126
156
|
...(severity === undefined ? {} : { severity }),
|
|
157
|
+
...(v.pending === true ? { pending: true } : {}),
|
|
127
158
|
...(replies.length === 0 ? {} : { replies }),
|
|
159
|
+
...(v.author === "agent" ? { author: "agent" } : {}),
|
|
160
|
+
...(v.diagram === undefined ? {} : { diagram: v.diagram }),
|
|
128
161
|
};
|
|
129
162
|
}
|
|
130
163
|
function withDecision(concern, v) {
|
|
@@ -135,11 +168,15 @@ function withDecision(concern, v) {
|
|
|
135
168
|
return "severity is unknown";
|
|
136
169
|
if (v.note !== undefined && typeof v.note !== "string")
|
|
137
170
|
return "note is not a string";
|
|
171
|
+
if (v.decidedAt !== undefined && typeof v.decidedAt !== "string") {
|
|
172
|
+
return "decidedAt is not a string";
|
|
173
|
+
}
|
|
138
174
|
return {
|
|
139
175
|
concern,
|
|
140
176
|
decision: v.decision,
|
|
141
177
|
...(v.severity === undefined ? {} : { severity: v.severity }),
|
|
142
178
|
...(v.note === undefined || v.note.trim() === "" ? {} : { note: v.note }),
|
|
179
|
+
...(v.decidedAt === undefined ? {} : { decidedAt: v.decidedAt }),
|
|
143
180
|
};
|
|
144
181
|
}
|
|
145
182
|
/** A stored decision. */
|
|
@@ -150,11 +187,11 @@ export function parseDecision(v) {
|
|
|
150
187
|
return "concern is not a concern id";
|
|
151
188
|
return withDecision(v.concern, v);
|
|
152
189
|
}
|
|
153
|
-
/** The body of POST .../concerns/<id>; the id comes from the URL. */
|
|
190
|
+
/** The body of POST .../concerns/<id>; the id comes from the URL and the time is the server's. */
|
|
154
191
|
export function parseNewDecision(concern, v) {
|
|
155
192
|
if (!isFields(v))
|
|
156
193
|
return "the decision is not an object";
|
|
157
|
-
return withDecision(concern, v);
|
|
194
|
+
return withDecision(concern, { ...v, decidedAt: undefined });
|
|
158
195
|
}
|
|
159
196
|
/** Absent reads as none. The first item that does not parse names its index and reason. */
|
|
160
197
|
function parseList(raw, parse, what) {
|
|
@@ -171,6 +208,21 @@ function parseList(raw, parse, what) {
|
|
|
171
208
|
}
|
|
172
209
|
return out;
|
|
173
210
|
}
|
|
211
|
+
function parseConcernId(item) {
|
|
212
|
+
return isConcernId(item) ? item : "not a concern id";
|
|
213
|
+
}
|
|
214
|
+
/** Absent reads as nothing excluded. */
|
|
215
|
+
export function parseExcluded(v) {
|
|
216
|
+
if (v === undefined)
|
|
217
|
+
return { comments: [], concerns: [] };
|
|
218
|
+
if (!isFields(v))
|
|
219
|
+
return "excluded is not an object";
|
|
220
|
+
const comments = parseList(v.comments, parseCommentId, "excluded.comments");
|
|
221
|
+
if (typeof comments === "string")
|
|
222
|
+
return comments;
|
|
223
|
+
const concerns = parseList(v.concerns, parseConcernId, "excluded.concerns");
|
|
224
|
+
return typeof concerns === "string" ? concerns : { comments, concerns };
|
|
225
|
+
}
|
|
174
226
|
function asCommentId(item) {
|
|
175
227
|
return typeof item === "string" && isCommentId(item) ? item : null;
|
|
176
228
|
}
|
|
@@ -203,6 +255,10 @@ export function parseReview(v) {
|
|
|
203
255
|
const resolved = parseList(v.resolved, parseCommentId, "resolved");
|
|
204
256
|
if (typeof resolved === "string")
|
|
205
257
|
return resolved;
|
|
258
|
+
const excluded = parseExcluded(v.excluded);
|
|
259
|
+
if (typeof excluded === "string")
|
|
260
|
+
return excluded;
|
|
261
|
+
const anyExcluded = excluded.comments.length + excluded.concerns.length > 0;
|
|
206
262
|
return {
|
|
207
263
|
schema: v.schema,
|
|
208
264
|
mode,
|
|
@@ -212,6 +268,7 @@ export function parseReview(v) {
|
|
|
212
268
|
comments,
|
|
213
269
|
...(concerns.length === 0 ? {} : { concerns }),
|
|
214
270
|
...(resolved.length === 0 ? {} : { resolved }),
|
|
271
|
+
...(anyExcluded ? { excluded } : {}),
|
|
215
272
|
};
|
|
216
273
|
}
|
|
217
274
|
function lenient(items, parse) {
|
|
@@ -230,10 +287,12 @@ export function parseDraft(v) {
|
|
|
230
287
|
if (!isFields(v) || !isArray(v.comments)) {
|
|
231
288
|
return "not a review draft";
|
|
232
289
|
}
|
|
290
|
+
const excluded = parseExcluded(v.excluded);
|
|
233
291
|
return {
|
|
234
292
|
comments: lenient(v.comments, parseComment),
|
|
235
293
|
concerns: lenient(v.concerns, parseDecision),
|
|
236
294
|
resolved: lenient(v.resolved, parseCommentId),
|
|
295
|
+
excluded: typeof excluded === "string" ? { comments: [], concerns: [] } : excluded,
|
|
237
296
|
};
|
|
238
297
|
}
|
|
239
298
|
export function parseEvent(v) {
|
|
@@ -280,6 +339,8 @@ export function parseEvent(v) {
|
|
|
280
339
|
return "malformed version event";
|
|
281
340
|
return { seq, type: v.type, version: v.version, hash: v.hash };
|
|
282
341
|
}
|
|
342
|
+
case "submitted":
|
|
343
|
+
return { seq, type: v.type };
|
|
283
344
|
case "ended":
|
|
284
345
|
return typeof v.review === "string"
|
|
285
346
|
? { seq, type: v.type, review: v.review }
|
package/dist/src/git.js
CHANGED
|
@@ -134,6 +134,18 @@ export async function loadDiff(repo, baseRef, tipRef) {
|
|
|
134
134
|
const blobs = await catBlobs(repo, neededBlobs(rawZ, patchText));
|
|
135
135
|
return parseDiff({ base, tip, baseTree, tipTree, rawZ, patch: patchText, blobs });
|
|
136
136
|
}
|
|
137
|
+
/**
|
|
138
|
+
* Whether the configured user wrote every commit of base..tip, which is how follow-me tells its
|
|
139
|
+
* author's own branch from one it is asked to review. False when no user.email is set.
|
|
140
|
+
*/
|
|
141
|
+
export async function isOwnWork(repo, base, tip) {
|
|
142
|
+
const me = await gitText(repo, ["config", "--default=", "user.email"]);
|
|
143
|
+
if (me === "")
|
|
144
|
+
return false;
|
|
145
|
+
const authors = await gitText(repo, ["log", "--format=%ae", `${base}..${tip}`]);
|
|
146
|
+
const emails = authors.split("\n").filter((line) => line !== "");
|
|
147
|
+
return emails.length > 0 && emails.every((email) => email === me);
|
|
148
|
+
}
|
|
137
149
|
export async function signing(repo, mode) {
|
|
138
150
|
const configured = await gitText(repo, [
|
|
139
151
|
"config",
|
package/dist/src/host.js
CHANGED
|
@@ -125,7 +125,7 @@ async function loadHosted(registration, held) {
|
|
|
125
125
|
if (!verdict.ok) {
|
|
126
126
|
throw new RegistrationError(`plan v${version} no longer fits ${branch}; check it again`);
|
|
127
127
|
}
|
|
128
|
-
const reviewsDir = session.reviewsDir(
|
|
128
|
+
const reviewsDir = session.reviewsDir();
|
|
129
129
|
const comments = await loadDraftComments(reviewsDir, version);
|
|
130
130
|
const prior = session.priorReview(mode, version);
|
|
131
131
|
const previous = prior === null || prior.comments.length === 0
|
|
@@ -135,7 +135,7 @@ async function loadHosted(registration, held) {
|
|
|
135
135
|
comments: prior.comments,
|
|
136
136
|
responses: stored.file.responses,
|
|
137
137
|
};
|
|
138
|
-
const command =
|
|
138
|
+
const command = "review";
|
|
139
139
|
// A version swap runs inside the server that already owns this session's lock.
|
|
140
140
|
const release = held ??
|
|
141
141
|
(lockToken === null ? session.lock(command) : session.adoptLock(lockToken, command)).release;
|
|
@@ -250,14 +250,16 @@ async function call(port, method, path, body) {
|
|
|
250
250
|
function parseSummary(raw) {
|
|
251
251
|
if (!isFields(raw))
|
|
252
252
|
return null;
|
|
253
|
-
const { id, url, repo, branch, mode, version, comments } = raw;
|
|
253
|
+
const { id, url, repo, branch, mode, version, comments, openedAt } = raw;
|
|
254
254
|
if (typeof url !== "string" || typeof repo !== "string")
|
|
255
255
|
return null;
|
|
256
256
|
if (typeof branch !== "string" || (mode !== "split" && mode !== "walk"))
|
|
257
257
|
return null;
|
|
258
258
|
if (!isSessionId(id) || !isPlanVersion(version) || typeof comments !== "number")
|
|
259
259
|
return null;
|
|
260
|
-
|
|
260
|
+
if (typeof openedAt !== "string")
|
|
261
|
+
return null;
|
|
262
|
+
return { id, url, repo, branch, mode, version, comments, openedAt };
|
|
261
263
|
}
|
|
262
264
|
/** Registers a session with the server on this port; the same session yields the same URL. */
|
|
263
265
|
export async function registerWith(port, registration) {
|
|
@@ -275,7 +277,7 @@ export async function swapVersion(port, summary, version, hash) {
|
|
|
275
277
|
}
|
|
276
278
|
/** One long poll of the session's event log: what happened after `since`, or nothing at timeout. */
|
|
277
279
|
export async function pollEvents(port, summary, since, timeoutMs) {
|
|
278
|
-
const path = `${summary.url}api/events?since=${since}&timeout=${timeoutMs}`;
|
|
280
|
+
const path = `${summary.url}api/events?since=${since}&timeout=${timeoutMs}&audience=agent`;
|
|
279
281
|
const batch = parseEventBatch(await call(port, "GET", path));
|
|
280
282
|
if (typeof batch === "string")
|
|
281
283
|
throw new RegistrationError(`the server sent bad events: ${batch}`);
|
|
@@ -286,7 +288,7 @@ export async function pollEvents(port, summary, since, timeoutMs) {
|
|
|
286
288
|
* the server closes the stream, which it does after the `ended` event.
|
|
287
289
|
*/
|
|
288
290
|
export async function followEvents(port, summary, since, onEvent) {
|
|
289
|
-
const res = await fetch(`${sessionUrl(port, summary)}api/events/stream?since=${since}`);
|
|
291
|
+
const res = await fetch(`${sessionUrl(port, summary)}api/events/stream?since=${since}&audience=agent`);
|
|
290
292
|
if (!res.ok || res.body === null) {
|
|
291
293
|
throw new RegistrationError(`GET api/events/stream failed with ${res.status}`);
|
|
292
294
|
}
|
|
@@ -317,9 +319,18 @@ export async function postReply(port, summary, commentId, reply) {
|
|
|
317
319
|
throw new RegistrationError("the server answered without a reply id");
|
|
318
320
|
return id;
|
|
319
321
|
}
|
|
322
|
+
/** Posts a comment the agent starts and returns its id; the server mints id and time. */
|
|
323
|
+
export async function postComment(port, summary, comment) {
|
|
324
|
+
const body = await call(port, "POST", `${summary.url}api/comments`, comment);
|
|
325
|
+
const id = isFields(body) ? body["id"] : undefined;
|
|
326
|
+
if (typeof id !== "string") {
|
|
327
|
+
throw new RegistrationError("the server answered without a comment id");
|
|
328
|
+
}
|
|
329
|
+
return id;
|
|
330
|
+
}
|
|
320
331
|
/** The consolidated report of the hosted session as it stands. */
|
|
321
|
-
export async function fetchReport(port, summary) {
|
|
322
|
-
const res = await fetch(`${sessionUrl(port, summary)}api/report`);
|
|
332
|
+
export async function fetchReport(port, summary, format = "markdown") {
|
|
333
|
+
const res = await fetch(`${sessionUrl(port, summary)}api/report?format=${format}`);
|
|
323
334
|
if (!res.ok)
|
|
324
335
|
throw new RegistrationError(`GET api/report failed with ${res.status}`);
|
|
325
336
|
return res.text();
|
package/dist/src/install.js
CHANGED
|
@@ -122,6 +122,27 @@ export function runInstall(options, home, repoRoot, say, skillDir = findSkillDir
|
|
|
122
122
|
}
|
|
123
123
|
});
|
|
124
124
|
}
|
|
125
|
+
/**
|
|
126
|
+
* Keeps the skill current in every harness found at user scope: writes what is missing or differs
|
|
127
|
+
* from the shipped copy and says so, one line per file; says nothing when all is current. A host
|
|
128
|
+
* with no harness, or a package without its skill folder, is not an error here.
|
|
129
|
+
*/
|
|
130
|
+
export function syncSkills(home, say, skillDir) {
|
|
131
|
+
try {
|
|
132
|
+
const dir = skillDir ?? findSkillDir();
|
|
133
|
+
const targets = { harness: undefined, all: false, project: false, dryRun: false };
|
|
134
|
+
for (const file of planSkillFiles(targets, home, home)) {
|
|
135
|
+
const line = writeSkill(dir, file, false);
|
|
136
|
+
if (line.startsWith("wrote "))
|
|
137
|
+
say(line);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
catch (error) {
|
|
141
|
+
if (error instanceof InstallError)
|
|
142
|
+
return;
|
|
143
|
+
process.stderr.write(`follow-me: could not update the skill: ${error instanceof Error ? error.message : error}\n`);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
125
146
|
export function runUninstall(options, home, repoRoot, say) {
|
|
126
147
|
return reportingErrors(() => {
|
|
127
148
|
for (const file of planSkillFiles(options, home, repoRoot)) {
|
package/dist/src/plan.js
CHANGED
|
@@ -645,7 +645,7 @@ export function validatePlan(diff, draft, opts = {}) {
|
|
|
645
645
|
const warnings = review === null
|
|
646
646
|
? []
|
|
647
647
|
: review.comments
|
|
648
|
-
.filter((c) => !answered.has(c.id))
|
|
648
|
+
.filter((c) => c.author !== "agent" && !answered.has(c.id))
|
|
649
649
|
.map((c) => ({ kind: "unanswered", comment: c.id, review: review.planVersion }));
|
|
650
650
|
file.commits.forEach((c, commit) => {
|
|
651
651
|
const length = (c.message.split("\n")[0] ?? "").length;
|