@renaiss-shipflow/cli 0.9.0 → 0.11.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.
Files changed (51) hide show
  1. package/dist/client.d.ts +19 -0
  2. package/dist/client.d.ts.map +1 -1
  3. package/dist/client.js +5 -0
  4. package/dist/client.js.map +1 -1
  5. package/dist/commands/config.d.ts.map +1 -1
  6. package/dist/commands/config.js +3 -1
  7. package/dist/commands/config.js.map +1 -1
  8. package/dist/commands/git-identity.d.ts +3 -0
  9. package/dist/commands/git-identity.d.ts.map +1 -0
  10. package/dist/commands/git-identity.js +77 -0
  11. package/dist/commands/git-identity.js.map +1 -0
  12. package/dist/commands/issue.d.ts.map +1 -1
  13. package/dist/commands/issue.js +49 -6
  14. package/dist/commands/issue.js.map +1 -1
  15. package/dist/commands/login.d.ts.map +1 -1
  16. package/dist/commands/login.js +16 -2
  17. package/dist/commands/login.js.map +1 -1
  18. package/dist/commands/pr.d.ts.map +1 -1
  19. package/dist/commands/pr.js +140 -2
  20. package/dist/commands/pr.js.map +1 -1
  21. package/dist/commands/stats.d.ts +7 -0
  22. package/dist/commands/stats.d.ts.map +1 -1
  23. package/dist/commands/stats.js +48 -1
  24. package/dist/commands/stats.js.map +1 -1
  25. package/dist/config.d.ts +7 -0
  26. package/dist/config.d.ts.map +1 -1
  27. package/dist/config.js +9 -0
  28. package/dist/config.js.map +1 -1
  29. package/dist/escalation-format.d.ts +35 -2
  30. package/dist/escalation-format.d.ts.map +1 -1
  31. package/dist/escalation-format.js +81 -4
  32. package/dist/escalation-format.js.map +1 -1
  33. package/dist/gh.d.ts +43 -1
  34. package/dist/gh.d.ts.map +1 -1
  35. package/dist/gh.js +93 -4
  36. package/dist/gh.js.map +1 -1
  37. package/dist/git-local.d.ts +4 -0
  38. package/dist/git-local.d.ts.map +1 -1
  39. package/dist/git-local.js +37 -0
  40. package/dist/git-local.js.map +1 -1
  41. package/dist/index.js +2 -0
  42. package/dist/index.js.map +1 -1
  43. package/dist/packet.d.ts +70 -0
  44. package/dist/packet.d.ts.map +1 -0
  45. package/dist/packet.js +261 -0
  46. package/dist/packet.js.map +1 -0
  47. package/dist/review-contract.d.ts +59 -0
  48. package/dist/review-contract.d.ts.map +1 -0
  49. package/dist/review-contract.js +129 -0
  50. package/dist/review-contract.js.map +1 -0
  51. package/package.json +1 -1
package/dist/packet.js ADDED
@@ -0,0 +1,261 @@
1
+ // Review-packet assembly (Superpowers-6 technique: pre-bake everything the
2
+ // loop's PR reviewer needs into ONE command output, instead of the reviewer
3
+ // spending tokens and wall time re-deriving it with a pile of gh calls).
4
+ //
5
+ // The packet deliberately leads with the SPEC/BRIEF section: a diff-only
6
+ // packet tempts a reviewer into confidently redefining "spec" as whatever the
7
+ // diff implies — the exact failure family Superpowers 6 measured (0/5 flagged
8
+ // a missing brief). When no linked issue exists, the packet says so loudly.
9
+ // Budgets mirror the server-side pr_review runner so both reviewers see
10
+ // comparably sized diffs.
11
+ export const PACKET_PER_FILE_CAP = 6000;
12
+ export const PACKET_TOTAL_CAP = 60000;
13
+ export const PACKET_BRIEF_CAP = 8000; // acceptance criteria sit at the BOTTOM of issue bodies — a tight cap silently drops the spec's checklist
14
+ // Noise paths excluded from the packet diff — lockfiles, generated output,
15
+ // vendored deps, binary-adjacent assets. Kept in sync (by convention) with the
16
+ // server's featuremap noise lists.
17
+ const NOISE_SUBSTRINGS = [
18
+ "node_modules/", "vendor/", "dist/", "build/", ".next/", "__generated__/",
19
+ "__snapshots__/", "coverage/", "storybook-static/", ".turbo/", "out/",
20
+ ];
21
+ const NOISE_SUFFIXES = [
22
+ ".lock", ".sum", "package-lock.json", "bun.lockb", ".min.js", ".min.css",
23
+ ".map", ".snap", ".png", ".jpg", ".jpeg", ".gif", ".ico", ".svg",
24
+ ".woff", ".woff2", ".ttf", ".eot", ".pb.go", "_generated.go", ".gen.go",
25
+ ];
26
+ export function isNoiseDiffPath(path) {
27
+ const p = path.toLowerCase();
28
+ return (NOISE_SUBSTRINGS.some((s) => p.includes(s)) ||
29
+ NOISE_SUFFIXES.some((s) => p.endsWith(s)));
30
+ }
31
+ /** Split a unified `gh pr diff` output into per-file sections. */
32
+ export function splitUnifiedDiff(diff) {
33
+ const sections = [];
34
+ const lines = diff.split("\n");
35
+ let current = null;
36
+ for (const line of lines) {
37
+ const m = /^diff --git a\/(.+) b\/(.+)$/.exec(line);
38
+ if (m) {
39
+ if (current)
40
+ sections.push(current);
41
+ current = { path: m[2], body: line };
42
+ }
43
+ else if (current) {
44
+ current.body += "\n" + line;
45
+ }
46
+ }
47
+ if (current)
48
+ sections.push(current);
49
+ return sections;
50
+ }
51
+ /** Noise-filter and budget the diff (per-file + total caps). */
52
+ export function filterDiffForPacket(diff) {
53
+ const out = [];
54
+ let shown = 0;
55
+ let omittedNoise = 0;
56
+ let omittedBudget = 0;
57
+ let truncatedFiles = 0;
58
+ let total = 0;
59
+ for (const s of splitUnifiedDiff(diff)) {
60
+ if (isNoiseDiffPath(s.path)) {
61
+ omittedNoise++;
62
+ continue;
63
+ }
64
+ if (total >= PACKET_TOTAL_CAP) {
65
+ omittedBudget++;
66
+ continue;
67
+ }
68
+ let body = s.body;
69
+ if (body.length > PACKET_PER_FILE_CAP) {
70
+ body = body.slice(0, PACKET_PER_FILE_CAP) + "\n… (file diff truncated)";
71
+ truncatedFiles++;
72
+ }
73
+ out.push(body);
74
+ total += body.length;
75
+ shown++;
76
+ }
77
+ return { text: out.join("\n"), shown, omittedNoise, omittedBudget, truncatedFiles };
78
+ }
79
+ /** Lines from PR comments that carry test evidence / health captions. */
80
+ export function extractEvidenceLines(comments) {
81
+ const lines = [];
82
+ for (const c of comments) {
83
+ const body = c.body ?? "";
84
+ if (!body.includes("Test evidence") && !/health \d+/.test(body))
85
+ continue;
86
+ for (const line of body.split("\n")) {
87
+ if (line.includes("Test evidence") || /health \d+→\d+|health \d+ ?→/.test(line) || line.startsWith("Verified:") || line.trimStart().startsWith("- Verified")) {
88
+ lines.push(line.trim());
89
+ }
90
+ }
91
+ }
92
+ return lines.slice(0, 12);
93
+ }
94
+ /** Names of the features whose path prefixes match any non-noise diff path,
95
+ * in feature-map order, deduped. */
96
+ export function touchedFeatures(diffPaths, features) {
97
+ const changed = diffPaths.filter((p) => !isNoiseDiffPath(p));
98
+ const out = [];
99
+ for (const f of features) {
100
+ const prefixes = (f.paths ?? []).map((p) => p.replace(/^\.\//, ""));
101
+ // Directory-boundary match: "src/app/admin" owns "src/app/admin/…" and the
102
+ // exact path itself, but never "src/app/admin-shop/…".
103
+ const owns = (prefix, path) => path === prefix || path.startsWith(prefix.endsWith("/") ? prefix : prefix + "/");
104
+ if (prefixes.some((prefix) => prefix && changed.some((path) => owns(prefix, path)))) {
105
+ out.push(f.name || f.key);
106
+ }
107
+ }
108
+ return out;
109
+ }
110
+ /** The deterministic layer under the per-feature evidence rule: a PR that
111
+ * touches more than one feature needs at least one proof per feature. The
112
+ * semantic judgment (which proof covers which feature) stays with the
113
+ * reviewer; this only counts — conservatively, by comment. */
114
+ export function assessEvidenceCoverage(touched, comments) {
115
+ const evidenceItems = comments.filter((c) => {
116
+ const body = c.body ?? "";
117
+ // Anchored: a structured caption line ("- Verified: …") or the 🧪 bundle
118
+ // header at line start. A bare substring match would count prose like
119
+ // "not yet verified:" or quoted captions and silently suppress the warning.
120
+ return /^\s*🧪.*Test evidence/m.test(body) || /^\s*-?\s*\*{0,2}verified:/im.test(body);
121
+ }).length;
122
+ if (touched.length <= 1 || evidenceItems >= touched.length) {
123
+ return { evidenceItems, warning: null };
124
+ }
125
+ return {
126
+ evidenceItems,
127
+ warning: `⚠️ ${touched.length} features touched, ${evidenceItems} evidence item(s) — ` +
128
+ `need ≥1 proof per feature on a multi-feature PR; treat each unproven feature ` +
129
+ `as an unresolved thread (request_changes) unless every touched feature maps ` +
130
+ `to a proof.`,
131
+ };
132
+ }
133
+ /** The "### Deviations from brief" section of a PR body — where the worker
134
+ * logs off-brief pivots (field-guide pattern: deviations must be visible to
135
+ * review, not buried in commit messages). Empty string when absent. */
136
+ export function extractDeviations(prBody) {
137
+ const m = /^###\s*Deviations from brief\s*$/im.exec(prBody);
138
+ if (!m)
139
+ return "";
140
+ const rest = prBody.slice(m.index + m[0].length);
141
+ const end = rest.search(/^#{1,3}\s/m);
142
+ return (end >= 0 ? rest.slice(0, end) : rest).trim();
143
+ }
144
+ /** Render the full review packet as markdown. */
145
+ export function buildReviewPacket(input) {
146
+ const { pr, threads, diff, issue } = input;
147
+ const b = [];
148
+ b.push(`# Review packet — PR #${pr.number}: ${pr.title}`);
149
+ const meta = [];
150
+ if (pr.headRefName)
151
+ meta.push(`${pr.headRefName} → ${pr.baseRefName ?? "?"}`);
152
+ if (pr.isDraft)
153
+ meta.push("DRAFT");
154
+ if (pr.mergeable)
155
+ meta.push(`mergeable: ${pr.mergeable}`);
156
+ if (pr.labels?.length)
157
+ meta.push(`labels: ${pr.labels.map((l) => l.name).join(", ")}`);
158
+ b.push(meta.join(" · "));
159
+ // Spec first — the reviewer judges against THIS, not against the diff.
160
+ b.push("\n## Spec / acceptance brief");
161
+ if (issue) {
162
+ b.push(`Issue #${issue.number} (${issue.linkKind}): ${issue.title}`);
163
+ const body = (issue.body ?? "").trim();
164
+ b.push(body.length > PACKET_BRIEF_CAP ? body.slice(0, PACKET_BRIEF_CAP) + "\n… (brief truncated)" : body || "_(issue has no body)_");
165
+ }
166
+ else {
167
+ b.push("⚠️ **No linked issue/brief found.** Do NOT infer the spec from the diff — " +
168
+ "reviewing against a self-derived spec is a known silent failure. Flag the missing brief in your verdict.");
169
+ }
170
+ const prBody = (pr.body ?? "").trim();
171
+ if (prBody) {
172
+ b.push("\n## PR description");
173
+ b.push(prBody.length > PACKET_BRIEF_CAP ? prBody.slice(0, PACKET_BRIEF_CAP) + "\n… (truncated)" : prBody);
174
+ }
175
+ const deviations = extractDeviations(pr.body ?? "");
176
+ if (deviations) {
177
+ b.push("\n## Deviations from brief");
178
+ b.push(deviations);
179
+ b.push("_Verify each deviation: conservative? justified? does the spec still hold?_");
180
+ }
181
+ b.push("\n## CI");
182
+ const checks = pr.statusCheckRollup ?? [];
183
+ if (checks.length === 0) {
184
+ b.push("no checks reported");
185
+ }
186
+ else {
187
+ let pass = 0, fail = 0, pending = 0;
188
+ const failing = [];
189
+ for (const c of checks) {
190
+ const concl = (c.conclusion ?? c.state ?? "").toUpperCase();
191
+ if (concl === "SUCCESS" || concl === "NEUTRAL" || concl === "SKIPPED")
192
+ pass++;
193
+ else if (concl === "" || concl === "PENDING" || (c.status ?? "").toUpperCase() === "IN_PROGRESS")
194
+ pending++;
195
+ else {
196
+ fail++;
197
+ failing.push(c.name ?? "unnamed");
198
+ }
199
+ }
200
+ b.push(`${pass} passing · ${fail} failing · ${pending} pending${failing.length ? ` — failing: ${failing.join(", ")}` : ""}`);
201
+ }
202
+ const unresolved = threads.filter((t) => !t.isResolved);
203
+ b.push(`\n## External review threads (unresolved: ${unresolved.length})`);
204
+ if (unresolved.length === 0) {
205
+ b.push("none");
206
+ }
207
+ else {
208
+ for (const t of unresolved.slice(0, 20)) {
209
+ const anchor = t.path ? `${t.path}${t.line ? `:${t.line}` : ""}` : "(top-level)";
210
+ b.push(`- ${anchor} @${t.author || "unknown"} — ${t.body.replace(/\s+/g, " ").slice(0, 140)}`);
211
+ }
212
+ }
213
+ const evidence = extractEvidenceLines(pr.comments ?? []);
214
+ b.push("\n## Evidence / health");
215
+ if (input.features?.length) {
216
+ const touched = touchedFeatures(splitUnifiedDiff(diff).map((s) => s.path), input.features);
217
+ if (touched.length) {
218
+ b.push(`Features touched (${touched.length}): ${touched.join(", ")}`);
219
+ const cov = assessEvidenceCoverage(touched, pr.comments ?? []);
220
+ if (cov.warning)
221
+ b.push(cov.warning);
222
+ }
223
+ }
224
+ b.push(evidence.length ? evidence.join("\n") : "no evidence caption posted");
225
+ // Features (relevant slice) — kills the reviewer's mandatory full-map pull:
226
+ // touched features in detail, same-layer neighbors by name, full map only
227
+ // on demand (issue #83).
228
+ if (input.features?.length) {
229
+ const diffPaths = splitUnifiedDiff(diff).map((s) => s.path);
230
+ const touchedNames = new Set(touchedFeatures(diffPaths, input.features));
231
+ if (touchedNames.size) {
232
+ const touched = input.features.filter((f) => touchedNames.has(f.name || f.key)).slice(0, 12);
233
+ b.push("\n## Features (relevant slice)");
234
+ for (const f of touched) {
235
+ const layer = f.layer ? ` [${f.layer}]` : "";
236
+ const tp = f.testPriority ? ` · test_priority: ${f.testPriority}` : "";
237
+ const desc = f.description ? ` — ${f.description}` : "";
238
+ b.push(`- ${f.name || f.key}${layer}${tp}${desc}`);
239
+ }
240
+ const layers = new Set(touched.map((f) => f.layer).filter(Boolean));
241
+ const neighbors = input.features
242
+ .filter((f) => !touchedNames.has(f.name || f.key) && f.layer && layers.has(f.layer))
243
+ .map((f) => f.name || f.key)
244
+ .slice(0, 15);
245
+ if (neighbors.length)
246
+ b.push(`Same-layer neighbors: ${neighbors.join(", ")}`);
247
+ b.push("_This slice replaces the full map for most reviews — run `renaiss-shipflow features --json` only if you need beyond it._");
248
+ }
249
+ }
250
+ const filtered = filterDiffForPacket(diff);
251
+ b.push(`\n## Diff (${filtered.shown} file(s) shown` +
252
+ (filtered.omittedNoise ? `, ${filtered.omittedNoise} noise file(s) omitted` : "") +
253
+ (filtered.omittedBudget ? `, ${filtered.omittedBudget} over budget` : "") +
254
+ (filtered.truncatedFiles ? `, ${filtered.truncatedFiles} truncated` : "") +
255
+ ")");
256
+ b.push("```diff");
257
+ b.push(filtered.text);
258
+ b.push("```");
259
+ return b.join("\n");
260
+ }
261
+ //# sourceMappingURL=packet.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"packet.js","sourceRoot":"","sources":["../src/packet.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,59 @@
1
+ /** Unified verdict vocabulary. The loop reviewer's `approve`/`request_changes`/
2
+ * `reject` plus the server's advisory `comment`. */
3
+ export type Verdict = "approve" | "comment" | "request_changes" | "reject";
4
+ /** Severity badge — byte-identical to the server's severityBadge(). */
5
+ export declare function severityBadge(severity: string): string;
6
+ /** CodeRabbit-style fix-size tag after the badge — mirrors the server. */
7
+ export declare function effortTag(effort?: string): string;
8
+ /** Verdict header, shared by the inline review body and any fallback comment. */
9
+ export declare function verdictHeader(verdict: Verdict): string;
10
+ export interface ReviewFinding {
11
+ path: string;
12
+ line: number;
13
+ severity: string;
14
+ issue: string;
15
+ why?: string;
16
+ fix?: string;
17
+ effort?: string;
18
+ suggestion?: string;
19
+ code?: string;
20
+ }
21
+ /** One finding's comment body — mirrors renderInlineFinding(): badge + effort +
22
+ * issue, why, fix, and a committable ```suggestion block when present. */
23
+ export declare function renderFindingBody(f: ReviewFinding): string;
24
+ /** Repo-relative path in one canonical shape: forward slashes, no leading
25
+ * "./". Applied to both diff keys and finding paths so `./src/x` / `src\\x`
26
+ * still anchor. NOT a filesystem realpath — diff paths aren't fs paths. */
27
+ export declare function normPath(p: string): string;
28
+ /** Map of path → set of new-file line numbers that GitHub will accept a
29
+ * RIGHT-side inline comment on (context + added lines). Mirrors the server's
30
+ * annotatePatch line numbering so a bad anchor can't 422 the whole review. */
31
+ export declare function diffAnchors(diff: string): Map<string, Set<number>>;
32
+ /** Partition findings into ones that anchor to a real diff line (postable
33
+ * inline) and ones that must fold into the summary body (bad line / file). */
34
+ export declare function splitAnchorable(findings: ReviewFinding[], anchors: Map<string, Set<number>>): {
35
+ inline: ReviewFinding[];
36
+ body: ReviewFinding[];
37
+ };
38
+ export interface ReviewComment {
39
+ path: string;
40
+ line: number;
41
+ side: "RIGHT";
42
+ body: string;
43
+ }
44
+ export interface ReviewPayload {
45
+ event: "COMMENT";
46
+ body: string;
47
+ comments: ReviewComment[];
48
+ }
49
+ /** Build the GitHub "create review" payload: a COMMENT-event review (ShipFlow
50
+ * reviews are advisory — approval authority stays with the label gate, so the
51
+ * event is never APPROVE/REQUEST_CHANGES; the verdict lives in the header),
52
+ * with on-diff findings as inline comments and the rest folded into the body. */
53
+ export declare function buildReviewPayload(opts: {
54
+ summary: string;
55
+ verdict: Verdict;
56
+ findings: ReviewFinding[];
57
+ anchors: Map<string, Set<number>>;
58
+ }): ReviewPayload;
59
+ //# sourceMappingURL=review-contract.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"review-contract.d.ts","sourceRoot":"","sources":["../src/review-contract.ts"],"names":[],"mappings":"AAKA;qDACqD;AACrD,MAAM,MAAM,OAAO,GAAG,SAAS,GAAG,SAAS,GAAG,iBAAiB,GAAG,QAAQ,CAAC;AAE3E,uEAAuE;AACvE,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAOtD;AAED,0EAA0E;AAC1E,wBAAgB,SAAS,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAMjD;AAED,iFAAiF;AACjF,wBAAgB,aAAa,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,CAOtD;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAID;2EAC2E;AAC3E,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,aAAa,GAAG,MAAM,CAS1D;AAED;;4EAE4E;AAC5E,wBAAgB,QAAQ,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAE1C;AAKD;;+EAE+E;AAC/E,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC,CAkBlE;AAED;+EAC+E;AAC/E,wBAAgB,eAAe,CAC7B,QAAQ,EAAE,aAAa,EAAE,EACzB,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC,GAChC;IAAE,MAAM,EAAE,aAAa,EAAE,CAAC;IAAC,IAAI,EAAE,aAAa,EAAE,CAAA;CAAE,CAQpD;AAED,MAAM,WAAW,aAAa;IAAG,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,OAAO,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;CAAE;AAC3F,MAAM,WAAW,aAAa;IAAG,KAAK,EAAE,SAAS,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,aAAa,EAAE,CAAC;CAAE;AAE7F;;;kFAGkF;AAClF,wBAAgB,kBAAkB,CAAC,IAAI,EAAE;IACvC,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,aAAa,EAAE,CAAC;IAC1B,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC;CACnC,GAAG,aAAa,CAqBhB"}
@@ -0,0 +1,129 @@
1
+ // Canonical review contract shared by the loop reviewer (this CLI) — the
2
+ // single source of truth for verdict vocabulary, severity badges, finding
3
+ // rendering, and diff anchoring, mirroring the server's runner_pr_review.go so
4
+ // the two reviewers post CONSISTENTLY (epic #96). Pure + testable; no I/O.
5
+ /** Severity badge — byte-identical to the server's severityBadge(). */
6
+ export function severityBadge(severity) {
7
+ switch (severity.trim().toLowerCase()) {
8
+ case "critical": return "🔴 **critical**";
9
+ case "high": return "🟠 **high**";
10
+ case "low": return "🔵 **low**";
11
+ default: return "🟡 **medium**";
12
+ }
13
+ }
14
+ /** CodeRabbit-style fix-size tag after the badge — mirrors the server. */
15
+ export function effortTag(effort) {
16
+ switch ((effort ?? "").trim().toLowerCase()) {
17
+ case "quick": return " · ⚡ quick fix";
18
+ case "involved": return " · 🔨 involved";
19
+ default: return "";
20
+ }
21
+ }
22
+ /** Verdict header, shared by the inline review body and any fallback comment. */
23
+ export function verdictHeader(verdict) {
24
+ switch (verdict) {
25
+ case "approve": return "**✅ APPROVE — ShipFlow review**";
26
+ case "request_changes": return "**🔴 CHANGES REQUESTED — ShipFlow review**";
27
+ case "reject": return "**⛔ REJECT — ShipFlow review**";
28
+ default: return "**💬 ShipFlow review — comments**";
29
+ }
30
+ }
31
+ const REVIEW_MARKER = "<!-- shipflow:loop-review -->";
32
+ /** One finding's comment body — mirrors renderInlineFinding(): badge + effort +
33
+ * issue, why, fix, and a committable ```suggestion block when present. */
34
+ export function renderFindingBody(f) {
35
+ let b = `${severityBadge(f.severity)}${effortTag(f.effort)} ${f.issue}`;
36
+ if (f.why?.trim())
37
+ b += `\n\n${f.why.trim()}`;
38
+ if (f.fix?.trim())
39
+ b += `\n\n**Fix:** ${f.fix.trim()}`;
40
+ const sug = (f.suggestion ?? "").replace(/\n+$/, "");
41
+ const code = (f.code ?? "").replace(/\n+$/, "");
42
+ if (sug.trim())
43
+ b += `\n\`\`\`suggestion\n${sug}\n\`\`\``;
44
+ else if (code.trim())
45
+ b += `\n\`\`\`\n${code}\n\`\`\``;
46
+ return `${b}\n\n${REVIEW_MARKER}`;
47
+ }
48
+ /** Repo-relative path in one canonical shape: forward slashes, no leading
49
+ * "./". Applied to both diff keys and finding paths so `./src/x` / `src\\x`
50
+ * still anchor. NOT a filesystem realpath — diff paths aren't fs paths. */
51
+ export function normPath(p) {
52
+ return p.replace(/\\/g, "/").replace(/^\.\//, "").trim();
53
+ }
54
+ const HUNK_RE = /^@@ -\d+(?:,\d+)? \+(\d+)(?:,\d+)? @@/;
55
+ const FILE_RE = /^\+\+\+ b\/(.+)$/;
56
+ /** Map of path → set of new-file line numbers that GitHub will accept a
57
+ * RIGHT-side inline comment on (context + added lines). Mirrors the server's
58
+ * annotatePatch line numbering so a bad anchor can't 422 the whole review. */
59
+ export function diffAnchors(diff) {
60
+ const out = new Map();
61
+ let path = "";
62
+ let ln = 0;
63
+ for (const raw of diff.split(/\r?\n/)) {
64
+ const fm = FILE_RE.exec(raw);
65
+ if (fm) {
66
+ path = normPath(fm[1]);
67
+ ln = 0;
68
+ continue;
69
+ }
70
+ const hm = HUNK_RE.exec(raw);
71
+ if (hm) {
72
+ ln = parseInt(hm[1], 10);
73
+ continue;
74
+ }
75
+ if (!path || ln === 0)
76
+ continue;
77
+ if (raw.startsWith("-"))
78
+ continue; // removed: no RIGHT-side line
79
+ if (raw.startsWith("+") || raw.startsWith(" ")) {
80
+ if (!out.has(path))
81
+ out.set(path, new Set());
82
+ out.get(path).add(ln);
83
+ ln++;
84
+ }
85
+ }
86
+ return out;
87
+ }
88
+ /** Partition findings into ones that anchor to a real diff line (postable
89
+ * inline) and ones that must fold into the summary body (bad line / file). */
90
+ export function splitAnchorable(findings, anchors) {
91
+ const inline = [];
92
+ const body = [];
93
+ for (const f of findings) {
94
+ if (f.line > 0 && anchors.get(normPath(f.path))?.has(f.line))
95
+ inline.push(f);
96
+ else
97
+ body.push(f);
98
+ }
99
+ return { inline, body };
100
+ }
101
+ /** Build the GitHub "create review" payload: a COMMENT-event review (ShipFlow
102
+ * reviews are advisory — approval authority stays with the label gate, so the
103
+ * event is never APPROVE/REQUEST_CHANGES; the verdict lives in the header),
104
+ * with on-diff findings as inline comments and the rest folded into the body. */
105
+ export function buildReviewPayload(opts) {
106
+ const { inline, body: unanchored } = splitAnchorable(opts.findings, opts.anchors);
107
+ const lines = [verdictHeader(opts.verdict)];
108
+ if (opts.summary.trim())
109
+ lines.push("", opts.summary.trim());
110
+ if (unanchored.length) {
111
+ lines.push("", "**Further findings (outside the annotated diff lines):**");
112
+ for (const f of unanchored) {
113
+ const anchor = f.line > 0 ? `${f.path}:${f.line}` : f.path;
114
+ const fbLines = renderFindingBody(f).split("\n\n" + REVIEW_MARKER)[0].split("\n");
115
+ lines.push(`- \`${anchor}\` — ${fbLines[0]}`);
116
+ // Indent why/fix/suggestion continuation two spaces so GitHub keeps them
117
+ // nested under the bullet instead of rendering as top-level paragraphs.
118
+ for (let i = 1; i < fbLines.length; i++)
119
+ lines.push(fbLines[i] ? ` ${fbLines[i]}` : "");
120
+ }
121
+ }
122
+ lines.push("", REVIEW_MARKER);
123
+ return {
124
+ event: "COMMENT",
125
+ body: lines.join("\n"),
126
+ comments: inline.map((f) => ({ path: f.path, line: f.line, side: "RIGHT", body: renderFindingBody(f) })),
127
+ };
128
+ }
129
+ //# sourceMappingURL=review-contract.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"review-contract.js","sourceRoot":"","sources":["../src/review-contract.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@renaiss-shipflow/cli",
3
- "version": "0.9.0",
3
+ "version": "0.11.0",
4
4
  "description": "CLI tool for RenaissShipFlow - AI-powered project management automation",
5
5
  "type": "module",
6
6
  "bin": {